diff options
Diffstat (limited to 'src')
7 files changed, 275 insertions, 92 deletions
diff --git a/src/buildtool/file_system/symlinks_map/TARGETS b/src/buildtool/file_system/symlinks_map/TARGETS index 125a4f52..0d958168 100644 --- a/src/buildtool/file_system/symlinks_map/TARGETS +++ b/src/buildtool/file_system/symlinks_map/TARGETS @@ -5,6 +5,7 @@ , "srcs": ["resolve_symlinks_map.cpp"] , "deps": [ "pragma_special" + , ["src/buildtool/file_system", "git_cas"] , ["src/buildtool/file_system", "git_repo"] , ["src/buildtool/file_system", "object_type"] , ["src/buildtool/multithreading", "async_map_consumer"] @@ -14,7 +15,11 @@ ] , "stage": ["src", "buildtool", "file_system", "symlinks_map"] , "private-deps": - [["@", "fmt", "", "fmt"], ["src/buildtool/storage", "config"]] + [ ["@", "fmt", "", "fmt"] + , ["src/buildtool/file_system", "object_type"] + , ["src/buildtool/storage", "config"] + , ["src/utils/cpp", "gsl"] + ] } , "pragma_special": { "type": ["@", "rules", "CC", "library"] diff --git a/src/buildtool/file_system/symlinks_map/resolve_symlinks_map.cpp b/src/buildtool/file_system/symlinks_map/resolve_symlinks_map.cpp index 5c9dd89b..198629bb 100644 --- a/src/buildtool/file_system/symlinks_map/resolve_symlinks_map.cpp +++ b/src/buildtool/file_system/symlinks_map/resolve_symlinks_map.cpp @@ -15,19 +15,104 @@ #include "src/buildtool/file_system/symlinks_map/resolve_symlinks_map.hpp" #include "fmt/core.h" -#include "src/buildtool/file_system/git_repo.hpp" +#include "src/buildtool/file_system/object_type.hpp" #include "src/buildtool/storage/config.hpp" +#include "src/utils/cpp/gsl.hpp" namespace { +/// \brief Ensures that a given blob is in the target repo. +/// On errors, calls logger with fatal and returns false. +[[nodiscard]] auto EnsureBlobExists(GitObjectToResolve const& obj, + GitRepo::TreeEntryInfo const& entry_info, + ResolveSymlinksMap::LoggerPtr const& logger) + -> bool { + ExpectsAudit(IsBlobObject(entry_info.type)); + // check if entry is in target repo + auto target_git_repo = GitRepo::Open(obj.target_cas); + if (not target_git_repo) { + (*logger)("ResolveSymlinks: could not open target Git repository!", + /*fatal=*/true); + return false; + } + auto wrapped_logger = std::make_shared<GitRepo::anon_logger_t>( + [logger, id = entry_info.id](auto const& msg, bool fatal) { + (*logger)(fmt::format("ResolveSymlinks: while checking blob {} " + "exists in target Git repository:\n{}", + id, + msg), + fatal); + }); + auto has_blob = + target_git_repo->CheckBlobExists(entry_info.id, wrapped_logger); + if (not has_blob) { + return false; + } + if (not *has_blob) { + // copy blob from source repo to target repo, if source is not target + if (obj.source_cas.get() == obj.target_cas.get()) { + (*logger)( + fmt::format("ResolveSymlinks: unexpectedly missing blob {} in " + "both source and target Git repositories", + entry_info.id), + /*fatal=*/true); + return false; + } + auto source_git_repo = GitRepo::Open(obj.source_cas); + if (not source_git_repo) { + (*logger)("ResolveSymlinks: could not open source Git repository", + /*fatal=*/true); + return false; + } + wrapped_logger = std::make_shared<GitRepo::anon_logger_t>( + [logger, id = entry_info.id](auto const& msg, bool fatal) { + (*logger)(fmt::format("ResolveSymlinks: while checking blob {} " + "exists in source Git repository:\n{}", + id, + msg), + fatal); + }); + auto res = source_git_repo->TryReadBlob(entry_info.id, wrapped_logger); + if (not res.first) { + return false; // fatal failure + } + if (not res.second.has_value()) { + (*logger)(fmt::format("ResolveSymlinks: unexpectedly missing " + "blob {} in source Git repository", + entry_info.id), + /*fatal=*/true); + return false; + } + // write blob in target repository + wrapped_logger = std::make_shared<GitRepo::anon_logger_t>( + [logger, id = entry_info.id](auto const& msg, bool fatal) { + (*logger)(fmt::format("ResolveSymlinks: while writing blob " + "{} into Git cache:\n{}", + id, + msg), + fatal); + }); + if (not target_git_repo->WriteBlob(res.second.value(), + wrapped_logger)) { + return false; + } + } + return true; // success! +} + +/// \brief Method to handle entries by their known type. +/// Guarantees to either call logger with fatal or call setter on returning. void ResolveKnownEntry(GitObjectToResolve const& obj, GitRepo::TreeEntryInfo const& entry_info, - GitCASPtr const& just_git_cas, ResolveSymlinksMap::SetterPtr const& setter, ResolveSymlinksMap::LoggerPtr const& logger, ResolveSymlinksMap::SubCallerPtr const& subcaller) { // differentiated treatment based on object type if (IsFileObject(entry_info.type)) { + // ensure target repository has the entry + if (not EnsureBlobExists(obj, entry_info, logger)) { + return; + } // files are already resolved, so return the hash directly (*setter)(ResolvedGitObject{.id = entry_info.id, .type = entry_info.type, @@ -36,13 +121,13 @@ void ResolveKnownEntry(GitObjectToResolve const& obj, else if (IsTreeObject(entry_info.type)) { // for tree types we resolve by rebuilding the tree from the // resolved children - auto just_git_repo = GitRepo::Open(just_git_cas); - if (not just_git_repo) { - (*logger)("ResolveSymlinks: could not open Git cache repository!", + auto source_git_repo = GitRepo::Open(obj.source_cas); + if (not source_git_repo) { + (*logger)("ResolveSymlinks: could not open source Git repository!", /*fatal=*/true); return; } - auto children = just_git_repo->ReadTree( + auto children = source_git_repo->ReadTree( entry_info.id, [](std::vector<bazel_re::Digest> const& /*unused*/) { return true; @@ -66,7 +151,7 @@ void ResolveKnownEntry(GitObjectToResolve const& obj, obj.pragma_special != PragmaSpecial::Ignore) { // children info is known, so pass this forward if (IsSymlinkObject(e.type)) { - if (auto target = just_git_cas->ReadObject(raw_id)) { + if (auto target = obj.source_cas->ReadObject(raw_id)) { children_info.emplace_back( obj.root_tree_id, obj.rel_path / e.name, @@ -74,7 +159,9 @@ void ResolveKnownEntry(GitObjectToResolve const& obj, std::make_optional(GitRepo::TreeEntryInfo{ .id = ToHexString(raw_id), .type = e.type, - .symlink_content = *target})); + .symlink_content = *target}), + obj.source_cas, + obj.target_cas); } else { (*logger)( @@ -94,14 +181,16 @@ void ResolveKnownEntry(GitObjectToResolve const& obj, GitRepo::TreeEntryInfo{ .id = ToHexString(raw_id), .type = e.type, - .symlink_content = std::nullopt}); + .symlink_content = std::nullopt}, + obj.source_cas, + obj.target_cas); } } } } (*subcaller)( children_info, - [children_info, parent = obj, just_git_cas, setter, logger]( + [children_info, parent = obj, setter, logger]( auto const& resolved_entries) { // create the entries map of the children GitRepo::tree_entries_t entries{}; @@ -114,18 +203,18 @@ void ResolveKnownEntry(GitObjectToResolve const& obj, p.filename().string(), // we only need the name resolved_entries[i]->type); } - // create the tree inside our Git CAS, which is already - // existing by this point. Also, this operation is - // guarded internally, so no need for the - // critical_git_op map - auto just_git_repo = GitRepo::Open(just_git_cas); - if (not just_git_repo) { + // create the tree inside target repo, which should already be + // existing. This operation is guarded internally, so no need + // for extra locking + auto target_git_repo = GitRepo::Open(parent.target_cas); + if (not target_git_repo) { (*logger)( - "ResolveSymlinks: could not open Git cache repository!", + "ResolveSymlinks: could not open target Git " + "repository!", /*fatal=*/true); return; } - auto tree_raw_id = just_git_repo->CreateTree(entries); + auto tree_raw_id = target_git_repo->CreateTree(entries); if (not tree_raw_id) { (*logger)(fmt::format("ResolveSymlinks: failed to create " "resolved tree {} in root tree {}", @@ -170,9 +259,13 @@ void ResolveKnownEntry(GitObjectToResolve const& obj, /*fatal=*/true); return; } - // if partially resolved, return non-upwards symlinks as-is + // if resolving partially, return a non-upwards symlink as-is if (obj.pragma_special == PragmaSpecial::ResolvePartially and PathIsNonUpwards(*entry_info.symlink_content)) { + // ensure target repository has the entry + if (not EnsureBlobExists(obj, entry_info, logger)) { + return; + } // return as symlink object (*setter)(ResolvedGitObject{.id = entry_info.id, .type = ObjectType::Symlink, @@ -186,7 +279,9 @@ void ResolveKnownEntry(GitObjectToResolve const& obj, {GitObjectToResolve(obj.root_tree_id, n_target, obj.pragma_special, - /*known_info=*/std::nullopt)}, + /*known_info=*/std::nullopt, + obj.source_cas, + obj.target_cas)}, [setter](auto const& values) { (*setter)(ResolvedGitObject{*values[0]}); }, @@ -202,31 +297,26 @@ auto CreateResolveSymlinksMap() -> ResolveSymlinksMap { auto logger, auto subcaller, auto const& key) { - // look up entry by its relative path - auto just_git_cas = GitCAS::Open(StorageConfig::GitRoot()); - if (not just_git_cas) { - (*logger)("ResolveSymlinks: could not open Git cache database!", - /*fatal=*/true); - return; - } - auto just_git_repo = GitRepo::Open(just_git_cas); - if (not just_git_repo) { - (*logger)("ResolveSymlinks: could not open Git cache repository!", - /*fatal=*/true); - return; + auto entry_info = key.known_info; + if (not entry_info) { + // look up entry by its relative path inside root tree if not known + auto source_git_repo = GitRepo::Open(key.source_cas); + if (not source_git_repo) { + (*logger)( + "ResolveSymlinks: could not open source Git repository!", + /*fatal=*/true); + return; + } + entry_info = source_git_repo->GetObjectByPathFromTree( + key.root_tree_id, key.rel_path); } - auto entry_info = key.known_info - ? key.known_info - : just_git_repo->GetObjectByPathFromTree( - key.root_tree_id, key.rel_path); // differentiate between existing path and non-existing if (entry_info) { - ResolveKnownEntry( - key, *entry_info, just_git_cas, setter, logger, subcaller); + ResolveKnownEntry(key, *entry_info, setter, logger, subcaller); } else { - // non-existing paths come from symlinks, so treat accordingly + // non-existing paths come from symlinks, so treat accordingly; // sanity check: pragma ignore special should not be set if here if (key.pragma_special == PragmaSpecial::Ignore) { (*logger)( @@ -251,11 +341,12 @@ auto CreateResolveSymlinksMap() -> ResolveSymlinksMap { {GitObjectToResolve(key.root_tree_id, parent_path, key.pragma_special, - /*known_info=*/std::nullopt)}, + /*known_info=*/std::nullopt, + key.source_cas, + key.target_cas)}, [key, parent_path, filename = key.rel_path.filename(), - just_git_cas, setter, logger, subcaller](auto const& values) { @@ -271,24 +362,25 @@ auto CreateResolveSymlinksMap() -> ResolveSymlinksMap { return; } // check if filename exists in resolved parent tree - auto just_git_repo = GitRepo::Open(just_git_cas); - if (not just_git_repo) { + auto target_git_repo = GitRepo::Open(key.target_cas); + if (not target_git_repo) { (*logger)( "ResolveSymlinks: could not open Git cache " "repository!", /*fatal=*/true); return; } - auto entry_info = just_git_repo->GetObjectByPathFromTree( + auto entry_info = target_git_repo->GetObjectByPathFromTree( resolved_parent.id, filename); if (entry_info) { ResolveKnownEntry( GitObjectToResolve(key.root_tree_id, resolved_parent.path / filename, key.pragma_special, - /*known_info=*/std::nullopt), + /*known_info=*/std::nullopt, + key.source_cas, + key.target_cas), std::move(*entry_info), - just_git_cas, setter, logger, subcaller); diff --git a/src/buildtool/file_system/symlinks_map/resolve_symlinks_map.hpp b/src/buildtool/file_system/symlinks_map/resolve_symlinks_map.hpp index 41986c23..fc706268 100644 --- a/src/buildtool/file_system/symlinks_map/resolve_symlinks_map.hpp +++ b/src/buildtool/file_system/symlinks_map/resolve_symlinks_map.hpp @@ -22,6 +22,7 @@ #include <string> #include <utility> // std::move +#include "src/buildtool/file_system/git_cas.hpp" #include "src/buildtool/file_system/git_repo.hpp" #include "src/buildtool/file_system/object_type.hpp" #include "src/buildtool/file_system/symlinks_map/pragma_special.hpp" @@ -31,7 +32,9 @@ #include "src/utils/cpp/path_hash.hpp" /// \brief Information needed to resolve an object (blob or tree) given its -/// path relative to the path of a root tree in a given CAS. +/// path relative to the path of a root tree in a given CAS. The unresolved +/// entries should be available in the specified source Git repository, and the +/// resolved entries being made available in the target Git repository. struct GitObjectToResolve { // hash of the root tree std::string root_tree_id{}; /* key */ @@ -42,17 +45,28 @@ struct GitObjectToResolve { // sometimes the info of the object at the required path is already known, // so leverage this to avoid extra work std::optional<GitRepo::TreeEntryInfo> known_info{std::nullopt}; + // object db to use as source of unresolved entries; it is guaranteed that + // this repository is treated as read-only if it differs from target_cas + GitCASPtr source_cas{}; + // object db to use as target for resolved entries; can be the same as + // source_cas and usually it is the Git cache; as the caller has access to + // such a pointer, it reduces the overhead from opening the Git cache often + GitCASPtr target_cas{}; GitObjectToResolve() = default; // needed for cycle detection only! GitObjectToResolve(std::string root_tree_id_, std::filesystem::path const& rel_path_, PragmaSpecial const& pragma_special_, - std::optional<GitRepo::TreeEntryInfo> known_info_) + std::optional<GitRepo::TreeEntryInfo> known_info_, + GitCASPtr source_cas_, + GitCASPtr target_cas_) : root_tree_id{std::move(root_tree_id_)}, rel_path{ToNormalPath(rel_path_)}, pragma_special{pragma_special_}, - known_info{std::move(known_info_)} {}; + known_info{std::move(known_info_)}, + source_cas{std::move(source_cas_)}, + target_cas{std::move(target_cas_)} {}; [[nodiscard]] auto operator==( GitObjectToResolve const& other) const noexcept -> bool { diff --git a/src/buildtool/serve_api/serve_service/source_tree.cpp b/src/buildtool/serve_api/serve_service/source_tree.cpp index 7a4a8828..4eeadf3f 100644 --- a/src/buildtool/serve_api/serve_service/source_tree.cpp +++ b/src/buildtool/serve_api/serve_service/source_tree.cpp @@ -375,6 +375,7 @@ auto SourceTreeService::SyncArchive(std::string const& tree_id, auto SourceTreeService::ResolveContentTree( std::string const& tree_id, std::filesystem::path const& repo_path, + bool repo_is_git_cache, std::optional<PragmaSpecial> const& resolve_special, bool sync_tree, ServeArchiveTreeResponse* response) -> ::grpc::Status { @@ -396,7 +397,26 @@ auto SourceTreeService::ResolveContentTree( return SyncArchive( *resolved_tree_id, repo_path, sync_tree, response); } - // resolve tree + // resolve tree; target repository is always the Git cache + auto target_cas = GitCAS::Open(StorageConfig::GitRoot()); + if (not target_cas) { + auto str = fmt::format("Failed to open Git ODB at {}", + StorageConfig::GitRoot().string()); + logger_->Emit(LogLevel::Error, str); + response->set_status(ServeArchiveTreeResponse::INTERNAL_ERROR); + return ::grpc::Status::OK; + } + auto source_cas = target_cas; + if (not repo_is_git_cache) { + source_cas = GitCAS::Open(repo_path); + if (not source_cas) { + auto str = fmt::format("Failed to open Git ODB at {}", + repo_path.string()); + logger_->Emit(LogLevel::Error, str); + response->set_status(ServeArchiveTreeResponse::INTERNAL_ERROR); + return ::grpc::Status::OK; + } + } ResolvedGitObject resolved_tree{}; bool failed{false}; { @@ -406,7 +426,9 @@ auto SourceTreeService::ResolveContentTree( {GitObjectToResolve{tree_id, ".", *resolve_special, - /*known_info=*/std::nullopt}}, + /*known_info=*/std::nullopt, + source_cas, + target_cas}}, [&resolved_tree](auto hashes) { resolved_tree = *hashes[0]; }, [logger = logger_, tree_id, &failed](auto const& msg, bool fatal) { @@ -625,6 +647,7 @@ auto SourceTreeService::ArchiveImportToGit( } return ResolveContentTree(*subtree_id, StorageConfig::GitRoot(), + /*repo_is_git_cache=*/true, resolve_special, sync_tree, response); @@ -689,6 +712,7 @@ auto SourceTreeService::ServeArchiveTree( if (std::holds_alternative<std::string>(res)) { return ResolveContentTree(std::get<std::string>(res), // tree_id StorageConfig::GitRoot(), + /*repo_is_git_cache=*/true, resolve_special, request->sync_tree(), response); @@ -709,6 +733,7 @@ auto SourceTreeService::ServeArchiveTree( return ResolveContentTree( std::get<std::string>(res), // tree_id path, + /*repo_is_git_cache=*/false, resolve_special, request->sync_tree(), response); diff --git a/src/buildtool/serve_api/serve_service/source_tree.hpp b/src/buildtool/serve_api/serve_service/source_tree.hpp index f2585f40..d8d117e0 100644 --- a/src/buildtool/serve_api/serve_service/source_tree.hpp +++ b/src/buildtool/serve_api/serve_service/source_tree.hpp @@ -173,9 +173,12 @@ class SourceTreeService final ServeArchiveTreeResponse* response) -> ::grpc::Status; + /// \brief Resolves a tree from given repository with respect to symlinks. + /// The resolved tree will always be placed in the Git cache. [[nodiscard]] auto ResolveContentTree( std::string const& tree_id, std::filesystem::path const& repo_path, + bool repo_is_git_cache, std::optional<PragmaSpecial> const& resolve_special, bool sync_tree, ServeArchiveTreeResponse* response) -> ::grpc::Status; diff --git a/src/other_tools/root_maps/content_git_map.cpp b/src/other_tools/root_maps/content_git_map.cpp index 2a6cad56..d3b0e35b 100644 --- a/src/other_tools/root_maps/content_git_map.cpp +++ b/src/other_tools/root_maps/content_git_map.cpp @@ -171,6 +171,7 @@ void EnsureRootAsAbsent( void ResolveContentTree( ArchiveRepoInfo const& key, std::string const& tree_hash, + GitCASPtr const& just_git_cas, bool is_cache_hit, bool is_absent, bool serve_api_exists, @@ -212,13 +213,15 @@ void ResolveContentTree( } } else { - // resolve tree + // resolve tree; both source and target repos are the Git cache resolve_symlinks_map->ConsumeAfterKeysReady( ts, {GitObjectToResolve(tree_hash, ".", *key.pragma_special, - /*known_info=*/std::nullopt)}, + /*known_info=*/std::nullopt, + just_git_cas, + just_git_cas)}, [resolve_symlinks_map, tree_hash, tree_id_file, @@ -357,6 +360,7 @@ void WriteIdFileAndSetWSRoot( // resolve tree and set workspace root ResolveContentTree(key, *subtree_hash, + just_git_cas, false, /*is_cache_hit*/ is_absent, serve_api_exists, @@ -543,6 +547,7 @@ auto CreateContentGitMap( ResolveContentTree( key, *subtree_hash, + op_result.git_cas, /*is_cache_hit = */ true, /*is_absent = */ (key.absent and not fetch_absent), serve_api_exists, diff --git a/src/other_tools/root_maps/fpath_git_map.cpp b/src/other_tools/root_maps/fpath_git_map.cpp index 7f05e8b5..a38a2255 100644 --- a/src/other_tools/root_maps/fpath_git_map.cpp +++ b/src/other_tools/root_maps/fpath_git_map.cpp @@ -95,6 +95,8 @@ void ResolveFilePathTree( std::string const& target_path, std::string const& tree_hash, std::optional<PragmaSpecial> const& pragma_special, + GitCASPtr const& source_cas, + GitCASPtr const& target_cas, bool absent, gsl::not_null<ResolveSymlinksMap*> const& resolve_symlinks_map, bool serve_api_exists, @@ -117,9 +119,10 @@ void ResolveFilePathTree( return; } // if serve endpoint is given, try to ensure it has this tree - // available to be able to build against it + // available to be able to build against it; the tree is resolved, + // so it is in our Git cache CheckServeAndSetRoot(*resolved_tree_id, - repo_root, + StorageConfig::GitRoot().string(), absent, serve_api_exists, remote_api, @@ -133,10 +136,11 @@ void ResolveFilePathTree( {GitObjectToResolve(tree_hash, ".", *pragma_special, - /*known_info=*/std::nullopt)}, + /*known_info=*/std::nullopt, + source_cas, + target_cas)}, [resolve_symlinks_map, tree_hash, - repo_root, tree_id_file, absent, serve_api_exists, @@ -173,9 +177,10 @@ void ResolveFilePathTree( return; } // if serve endpoint is given, try to ensure it has this - // tree available to be able to build against it + // tree available to be able to build against it; the + // resolved tree is in the Git cache CheckServeAndSetRoot(resolved_tree.id, - repo_root, + StorageConfig::GitRoot().string(), absent, serve_api_exists, remote_api, @@ -243,22 +248,6 @@ auto CreateFilePathGitMap( return; } if (not repo_root->empty()) { // if repo root found - auto git_cas = GitCAS::Open(*repo_root); - if (not git_cas) { - (*logger)(fmt::format("Could not open object database for " - "repository {}", - repo_root->string()), - /*fatal=*/true); - return; - } - auto git_repo = - GitRepoRemote::Open(git_cas); // link fake repo to odb - if (not git_repo) { - (*logger)(fmt::format("Could not open repository {}", - repo_root->string()), - /*fatal=*/true); - return; - } // get head commit GitOpKey op_key = {.params = { @@ -273,8 +262,8 @@ auto CreateFilePathGitMap( [fpath = key.fpath, pragma_special = key.pragma_special, absent = key.absent, - git_cas = std::move(git_cas), repo_root = std::move(*repo_root), + critical_git_op_map, resolve_symlinks_map, serve_api_exists, remote_api, @@ -288,8 +277,8 @@ auto CreateFilePathGitMap( /*fatal=*/true); return; } - auto git_repo = - GitRepoRemote::Open(git_cas); // link fake repo to odb + auto git_repo = GitRepoRemote::Open( + op_result.git_cas); // link fake repo to odb if (not git_repo) { (*logger)(fmt::format("Could not open repository {}", repo_root.string()), @@ -312,18 +301,65 @@ auto CreateFilePathGitMap( if (not tree_hash) { return; } - // resolve tree and set workspace root - ResolveFilePathTree(repo_root.string(), - fpath.string(), - *tree_hash, - pragma_special, - absent, - resolve_symlinks_map, - serve_api_exists, - remote_api, - ts, - setter, - logger); + // resolve tree and set workspace root; tree gets resolved + // from source repo into the Git cache, which we first need + // to ensure is initialized + GitOpKey op_key = { + .params = + { + StorageConfig::GitRoot(), // target_path + "", // git_hash + "", // branch + std::nullopt, // message + true // init_bare + }, + .op_type = GitOpType::ENSURE_INIT}; + critical_git_op_map->ConsumeAfterKeysReady( + ts, + {std::move(op_key)}, + [repo_root, + fpath, + tree_hash, + pragma_special, + source_cas = op_result.git_cas, + absent, + resolve_symlinks_map, + serve_api_exists, + remote_api, + ts, + setter, + logger](auto const& values) { + GitOpValue op_result = *values[0]; + // check flag + if (not op_result.result) { + (*logger)("Git init failed", + /*fatal=*/true); + return; + } + ResolveFilePathTree( + repo_root.string(), + fpath.string(), + *tree_hash, + pragma_special, + source_cas, + op_result.git_cas, /*just_git_cas*/ + absent, + resolve_symlinks_map, + serve_api_exists, + remote_api, + ts, + setter, + logger); + }, + [logger, target_path = StorageConfig::GitRoot()]( + auto const& msg, bool fatal) { + (*logger)( + fmt::format("While running critical Git op " + "ENSURE_INIT for target {}:\n{}", + target_path.string(), + msg), + fatal); + }); }, [logger, target_path = *repo_root](auto const& msg, bool fatal) { @@ -388,11 +424,14 @@ auto CreateFilePathGitMap( } // we only need the tree std::string tree = values[0]->first; - // resolve tree and set workspace root + // resolve tree and set workspace root; + // we work on the Git CAS directly ResolveFilePathTree(StorageConfig::GitRoot().string(), fpath.string(), tree, pragma_special, + values[0]->second, /*source_cas*/ + values[0]->second, /*target_cas*/ absent, resolve_symlinks_map, serve_api_exists, |