From 8f22e91bd3713e8da1630271b587be61be61379d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 24 Mar 2026 13:55:44 +0100 Subject: [PATCH 1/3] Make getLegacyGitAccessor() less spammy --- src/libfetchers/git.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 7b1447b2ea9..4c15bb1c09c 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -813,10 +813,11 @@ struct GitInputScheme : InputScheme options.submodules ? [&]() { // Nix < 2.20 used `git checkout` for repos with submodules. - runProgram({.program = "git", .args = {"init", tmpDir}}); + runProgram({.program = "git", .args = {"init", tmpDir, "-b", "master"}}); runProgram({.program = "git", .args = {"-C", tmpDir, "remote", "add", "origin", repoDir}}); - runProgram({.program = "git", .args = {"-C", tmpDir, "fetch", "origin", rev.gitRev()}}); - runProgram({.program = "git", .args = {"-C", tmpDir, "checkout", rev.gitRev()}}); + runProgram( + {.program = "git", .args = {"-C", tmpDir, "fetch", "--quiet", "origin", rev.gitRev()}}); + runProgram({.program = "git", .args = {"-C", tmpDir, "checkout", "--quiet", rev.gitRev()}}); PathFilter filter = [&](const Path & path) { return baseNameOf(path) != ".git"; }; return store.addToStore( "source", From 84f79c0c0a7adb8ee0862b5b054ed3c446dcc44f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 24 Mar 2026 15:25:13 +0100 Subject: [PATCH 2/3] Cache getLegacyGitAccessor() This prevents repeated calls to git checkout / git archive. --- src/libfetchers/git.cc | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 4c15bb1c09c..5b928e0393b 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -800,12 +800,39 @@ struct GitInputScheme : InputScheme * checkout`. */ ref getLegacyGitAccessor( + const Settings & settings, Store & store, RepoInfo & repoInfo, const std::filesystem::path & repoDir, const Hash & rev, GitAccessorOptions & options) const { + if (!options.submodules) + options.exportIgnore = true; + + auto fingerprint = options.makeFingerprint(rev) + ";legacy"; + + auto cacheKey = makeSourcePathToHashCacheKey(fingerprint, ContentAddressMethod::Raw::NixArchive, "/"); + + auto makeAccessor = [&](const auto & storePath) -> ref { + auto accessor = store.getFSAccessor(storePath); + accessor->fingerprint = fingerprint; + return ref{accessor}; + }; + + if (auto res = settings.getCache()->lookup(cacheKey)) { + auto hash = Hash::parseSRI(fetchers::getStrAttr(*res, "hash")); + auto storePath = store.makeFixedOutputPathFromCA( + "source", ContentAddressWithReferences::fromParts(ContentAddressMethod::Raw::NixArchive, hash, {})); + store.addTempRoot(storePath); + if (store.maybeQueryPathInfo(storePath)) { + debug("using cached legacy export of revision '%s'", rev.gitRev()); + return makeAccessor(storePath); + } + } + + debug("doing legacy export of revision '%s'", rev.gitRev()); + auto tmpDir = createTempDir(); AutoDelete delTmpDir(tmpDir, true); @@ -829,8 +856,6 @@ struct GitInputScheme : InputScheme }() : [&]() { // Nix < 2.20 used `git archive` for repos without submodules. - options.exportIgnore = true; - auto source = sinkToSource([&](Sink & sink) { runProgram2( {.program = "git", @@ -843,11 +868,10 @@ struct GitInputScheme : InputScheme return store.addToStore("source", {getFSSourceAccessor(), CanonPath(tmpDir.string())}); }(); - auto accessor = store.getFSAccessor(storePath); - - accessor->fingerprint = options.makeFingerprint(rev) + ";legacy"; + settings.getCache()->upsert( + cacheKey, {{"hash", store.queryPathInfo(storePath)->narHash.to_string(HashFormat::SRI, true)}}); - return ref{accessor}; + return makeAccessor(storePath); } std::optional, Input>> getAccessorFromCommit( @@ -1006,7 +1030,7 @@ struct GitInputScheme : InputScheme * as well. */ warn("Using Nix 2.19 semantics to export Git repository '%s'.", input.to_string()); auto accessorModern = accessor; - accessor = getLegacyGitAccessor(store, repoInfo, repoDir, rev, options); + accessor = getLegacyGitAccessor(settings, store, repoInfo, repoDir, rev, options); if (expectedNarHash) { auto narHashLegacy = fetchToStore2(settings, store, {accessor}, FetchMode::DryRun, input.getName()).second; @@ -1024,7 +1048,7 @@ struct GitInputScheme : InputScheme if (expectedNarHash) { auto narHashNew = fetchToStore2(settings, store, {accessor}, FetchMode::DryRun, input.getName()).second; if (expectedNarHash != narHashNew) { - auto accessorLegacy = getLegacyGitAccessor(store, repoInfo, repoDir, rev, options); + auto accessorLegacy = getLegacyGitAccessor(settings, store, repoInfo, repoDir, rev, options); auto narHashLegacy = fetchToStore2(settings, store, {accessorLegacy}, FetchMode::DryRun, input.getName()).second; if (expectedNarHash == narHashLegacy) { From 39a204a866e7e66ca69aebfaba3b2d28a338418b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 24 Mar 2026 18:29:47 +0100 Subject: [PATCH 3/3] Remove unused gitInitialBranch constant --- src/libfetchers/git.cc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 5b928e0393b..2cdcb6ef7c2 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -32,12 +32,6 @@ namespace nix::fetchers { namespace { -// Explicit initial branch of our bare repo to suppress warnings from new version of git. -// The value itself does not matter, since we always fetch a specific revision or branch. -// It is set with `-c init.defaultBranch=` instead of `--initial-branch=` to stay compatible with -// old version of git, which will ignore unrecognized `-c` options. -const std::string gitInitialBranch = "__nix_dummy_branch"; - bool isCacheFileWithinTtl(time_t now, const struct stat & st) { return st.st_mtime + static_cast(settings.tarballTtl) > now; @@ -118,7 +112,7 @@ std::optional readHeadCached(const std::string & actualUrl, bool sh std::optional cachedRef; if (stat(headRefFile.string().c_str(), &st) == 0) { cachedRef = readHead(cacheDir); - if (cachedRef != std::nullopt && *cachedRef != gitInitialBranch && isCacheFileWithinTtl(now, st)) { + if (cachedRef != std::nullopt && isCacheFileWithinTtl(now, st)) { debug("using cached HEAD ref '%s' for repo '%s'", *cachedRef, actualUrl); return cachedRef; }