diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f8bb6e378a50..87721f874fb6 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -795,7 +795,10 @@ StorePath EvalState::getZoneStorePath(std::string_view zonePath) // Eager mode: immediate copy from git ODB auto repo = getWorldRepo(); // exportIgnore=true: honor .gitattributes for zone content (unlike world accessor) - GitAccessorOptions opts{.exportIgnore = true, .smudgeLfs = false}; + // smudgeLfs=true + lfsCommitRev: zones may contain LFS files; the accessor uses a tree + // SHA so we must pass the commit SHA separately for lfs::Fetch attribute lookup. + auto commitHash = Hash::parseNonSRIUnprefixed(requireTectonixGitSha(), HashAlgorithm::SHA1); + GitAccessorOptions opts{.exportIgnore = true, .smudgeLfs = true, .lfsCommitRev = commitHash}; auto accessor = repo->getAccessor(treeSha, opts, "zone"); std::string name = "zone-" + sanitizeZoneNameForStore(zonePath); @@ -840,7 +843,10 @@ StorePath EvalState::mountZoneByTreeSha(const Hash & treeSha, std::string_view z // race to mount the same zone, but we check again before inserting. auto repo = getWorldRepo(); // exportIgnore=true: honor .gitattributes for zone content (unlike world accessor) - GitAccessorOptions opts{.exportIgnore = true, .smudgeLfs = false}; + // smudgeLfs=true + lfsCommitRev: zones may contain LFS files; the accessor uses a tree + // SHA so we must pass the commit SHA separately for lfs::Fetch attribute lookup. + auto commitHash = Hash::parseNonSRIUnprefixed(requireTectonixGitSha(), HashAlgorithm::SHA1); + GitAccessorOptions opts{.exportIgnore = true, .smudgeLfs = true, .lfsCommitRev = commitHash}; auto accessor = repo->getAccessor(treeSha, opts, "zone"); // Generate name from zone path (sanitized for store path requirements) @@ -947,8 +953,9 @@ StorePath EvalState::getZoneFromCheckout(std::string_view zonePath, const boost: auto makeDirtyAccessor = [&]() -> ref { auto repo = getWorldRepo(); + auto commitHash = Hash::parseNonSRIUnprefixed(requireTectonixGitSha(), HashAlgorithm::SHA1); auto baseAccessor = repo->getAccessor( - getWorldTreeSha(zone), {.exportIgnore = true, .smudgeLfs = false}, "zone"); + getWorldTreeSha(zone), {.exportIgnore = true, .smudgeLfs = true, .lfsCommitRev = commitHash}, "zone"); boost::unordered_flat_set zoneDirtyFiles; if (dirtyFiles) { auto zonePrefix = zone + "/"; diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 9e79cdbff8d3..9f8fb8cedb8a 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -799,7 +799,8 @@ ref GitRepo::openRepo(const std::filesystem::path & path, GitRepo::Opti std::string GitAccessorOptions::makeFingerprint(const Hash & rev) const { - return "git:" + rev.gitRev() + (exportIgnore ? ";e" : "") + (smudgeLfs ? ";l" : ""); + return "git:" + rev.gitRev() + (exportIgnore ? ";e" : "") + (smudgeLfs ? ";l" : "") + + (lfsCommitRev ? ";lc:" + lfsCommitRev->gitRev() : ""); } /** @@ -821,7 +822,8 @@ struct GitSourceAccessor : SourceAccessor : state_{State{ .repo = repo_, .root = peelToTreeOrBlob(lookupObject(*repo_, hashToOID(rev)).get()), - .lfsFetch = options.smudgeLfs ? std::make_optional(lfs::Fetch(*repo_, hashToOID(rev))) : std::nullopt, + .lfsFetch = options.smudgeLfs ? std::make_optional(lfs::Fetch(*repo_, + options.lfsCommitRev ? hashToOID(*options.lfsCommitRev) : hashToOID(rev))) : std::nullopt, .options = options, }} { diff --git a/src/libfetchers/include/nix/fetchers/git-utils.hh b/src/libfetchers/include/nix/fetchers/git-utils.hh index fd14cab555b6..d0c5ac3258d0 100644 --- a/src/libfetchers/include/nix/fetchers/git-utils.hh +++ b/src/libfetchers/include/nix/fetchers/git-utils.hh @@ -28,6 +28,14 @@ struct GitAccessorOptions bool smudgeLfs = false; bool submodules = false; // Currently implemented in GitInputScheme rather than GitAccessor + /** + * When set, use this commit hash for git-lfs attribute lookup instead of + * the tree hash passed to getAccessor(). Required when the accessor is + * created from a tree SHA rather than a commit SHA, since lfs::Fetch needs + * a commit OID for GIT_ATTR_CHECK_INCLUDE_COMMIT to find .gitattributes. + */ + std::optional lfsCommitRev; + std::string makeFingerprint(const Hash & rev) const; };