summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buildtool/file_system/TARGETS16
-rw-r--r--src/buildtool/file_system/git_cas.cpp38
-rw-r--r--src/buildtool/file_system/git_cas.hpp1
-rw-r--r--src/buildtool/file_system/git_repo.cpp399
-rw-r--r--src/buildtool/file_system/git_repo.hpp59
-rw-r--r--src/buildtool/file_system/git_utils.cpp51
-rw-r--r--src/buildtool/file_system/git_utils.hpp13
-rw-r--r--src/other_tools/git_operations/TARGETS15
-rw-r--r--src/other_tools/git_operations/git_operations.cpp18
-rw-r--r--src/other_tools/git_operations/git_repo_remote.cpp405
-rw-r--r--src/other_tools/git_operations/git_repo_remote.hpp105
-rw-r--r--src/other_tools/just_mr/main.cpp3
-rw-r--r--src/other_tools/ops_maps/TARGETS4
-rw-r--r--src/other_tools/ops_maps/git_update_map.cpp2
-rw-r--r--src/other_tools/ops_maps/git_update_map.hpp2
-rw-r--r--src/other_tools/ops_maps/import_to_git_map.cpp5
-rw-r--r--src/other_tools/ops_maps/import_to_git_map.hpp2
-rw-r--r--src/other_tools/root_maps/TARGETS4
-rw-r--r--src/other_tools/root_maps/commit_git_map.cpp6
-rw-r--r--src/other_tools/root_maps/content_git_map.cpp5
-rw-r--r--src/other_tools/root_maps/fpath_git_map.cpp7
21 files changed, 653 insertions, 507 deletions
diff --git a/src/buildtool/file_system/TARGETS b/src/buildtool/file_system/TARGETS
index c453ec82..323f6fdc 100644
--- a/src/buildtool/file_system/TARGETS
+++ b/src/buildtool/file_system/TARGETS
@@ -39,12 +39,7 @@
, "name": ["git_cas"]
, "hdrs": ["git_cas.hpp"]
, "srcs": ["git_cas.cpp"]
- , "deps":
- [ "object_type"
- , "git_context"
- , "git_utils"
- , ["@", "gsl-lite", "", "gsl-lite"]
- ]
+ , "deps": ["git_context", "git_utils", ["@", "gsl-lite", "", "gsl-lite"]]
, "stage": ["src", "buildtool", "file_system"]
, "private-deps":
[ ["src/buildtool/logging", "logging"]
@@ -98,9 +93,14 @@
, "name": ["git_utils"]
, "hdrs": ["git_utils.hpp"]
, "srcs": ["git_utils.cpp"]
- , "deps": [["@", "gsl-lite", "", "gsl-lite"]]
+ , "deps": [["@", "gsl-lite", "", "gsl-lite"], "object_type"]
, "stage": ["src", "buildtool", "file_system"]
- , "private-deps": [["", "libgit2"]]
+ , "private-deps":
+ [ ["", "libgit2"]
+ , ["@", "fmt", "", "fmt"]
+ , ["src/buildtool/logging", "logging"]
+ , ["src/utils/cpp", "hex_string"]
+ ]
}
, "file_root":
{ "type": ["@", "rules", "CC", "library"]
diff --git a/src/buildtool/file_system/git_cas.cpp b/src/buildtool/file_system/git_cas.cpp
index efb464ef..2a4a7142 100644
--- a/src/buildtool/file_system/git_cas.cpp
+++ b/src/buildtool/file_system/git_cas.cpp
@@ -30,44 +30,6 @@ extern "C" {
namespace {
-constexpr std::size_t kOIDRawSize{GIT_OID_RAWSZ};
-constexpr std::size_t kOIDHexSize{GIT_OID_HEXSZ};
-
-[[nodiscard]] auto GitLastError() noexcept -> std::string {
- git_error const* err{nullptr};
- if ((err = git_error_last()) != nullptr and err->message != nullptr) {
- return fmt::format("error code {}: {}", err->klass, err->message);
- }
- return "<unknown error>";
-}
-
-[[nodiscard]] auto GitObjectID(std::string const& id,
- bool is_hex_id = false) noexcept
- -> std::optional<git_oid> {
- if (id.size() < (is_hex_id ? kOIDHexSize : kOIDRawSize)) {
- Logger::Log(LogLevel::Error,
- "invalid git object id {}",
- is_hex_id ? id : ToHexString(id));
- return std::nullopt;
- }
- git_oid oid{};
- if (is_hex_id and git_oid_fromstr(&oid, id.c_str()) == 0) {
- return oid;
- }
- if (not is_hex_id and
- git_oid_fromraw(
- &oid,
- reinterpret_cast<unsigned char const*>(id.data()) // NOLINT
- ) == 0) {
- return oid;
- }
- Logger::Log(LogLevel::Error,
- "parsing git object id {} failed with:\n{}",
- is_hex_id ? id : ToHexString(id),
- GitLastError());
- return std::nullopt;
-}
-
[[nodiscard]] auto GitTypeToObjectType(git_object_t const& type) noexcept
-> std::optional<ObjectType> {
switch (type) {
diff --git a/src/buildtool/file_system/git_cas.hpp b/src/buildtool/file_system/git_cas.hpp
index 58e07616..03b772bc 100644
--- a/src/buildtool/file_system/git_cas.hpp
+++ b/src/buildtool/file_system/git_cas.hpp
@@ -23,7 +23,6 @@
#include "src/buildtool/file_system/git_context.hpp"
#include "src/buildtool/file_system/git_utils.hpp"
-#include "src/buildtool/file_system/object_type.hpp"
class GitCAS;
using GitCASPtr = std::shared_ptr<GitCAS const>;
diff --git a/src/buildtool/file_system/git_repo.cpp b/src/buildtool/file_system/git_repo.cpp
index d2cec500..baac7cf5 100644
--- a/src/buildtool/file_system/git_repo.cpp
+++ b/src/buildtool/file_system/git_repo.cpp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <src/buildtool/file_system/git_repo.hpp>
+#include "src/buildtool/file_system/git_repo.hpp"
#include "src/buildtool/logging/logger.hpp"
#include "src/utils/cpp/hex_string.hpp"
@@ -26,46 +26,6 @@ extern "C" {
#ifndef BOOTSTRAP_BUILD_TOOL
namespace {
-constexpr std::size_t kWaitTime{2}; // time in ms between tries for git locks
-
-constexpr std::size_t kOIDRawSize{GIT_OID_RAWSZ};
-constexpr std::size_t kOIDHexSize{GIT_OID_HEXSZ};
-
-[[nodiscard]] auto GitLastError() noexcept -> std::string {
- git_error const* err{nullptr};
- if ((err = git_error_last()) != nullptr and err->message != nullptr) {
- return fmt::format("error code {}: {}", err->klass, err->message);
- }
- return "<unknown error>";
-}
-
-[[nodiscard]] auto GitObjectID(std::string const& id,
- bool is_hex_id = false) noexcept
- -> std::optional<git_oid> {
- if (id.size() < (is_hex_id ? kOIDHexSize : kOIDRawSize)) {
- Logger::Log(LogLevel::Error,
- "invalid git object id {}",
- is_hex_id ? id : ToHexString(id));
- return std::nullopt;
- }
- git_oid oid{};
- if (is_hex_id and git_oid_fromstr(&oid, id.c_str()) == 0) {
- return oid;
- }
- if (not is_hex_id and
- git_oid_fromraw(
- &oid,
- reinterpret_cast<unsigned char const*>(id.data()) // NOLINT
- ) == 0) {
- return oid;
- }
- Logger::Log(LogLevel::Error,
- "parsing git object id {} failed with:\n{}",
- is_hex_id ? id : ToHexString(id),
- GitLastError());
- return std::nullopt;
-}
-
[[nodiscard]] auto ToHexString(git_oid const& oid) noexcept
-> std::optional<std::string> {
std::string hex_id(GIT_OID_HEXSZ, '\0');
@@ -278,90 +238,6 @@ void backend_free(git_odb_backend* /*_backend*/) {}
// A backend that can be used to read and create tree objects in-memory.
auto const kInMemoryODBParent = CreateInMemoryODBParent();
-struct FetchIntoODBBackend {
- git_odb_backend parent;
- git_odb* target_odb; // the odb where the fetch objects will end up into
-};
-
-[[nodiscard]] auto fetch_backend_writepack(git_odb_writepack** _writepack,
- git_odb_backend* _backend,
- [[maybe_unused]] git_odb* odb,
- git_indexer_progress_cb progress_cb,
- void* progress_payload) -> int {
- if (_backend != nullptr) {
- auto* b = reinterpret_cast<FetchIntoODBBackend*>(_backend); // NOLINT
- return git_odb_write_pack(
- _writepack, b->target_odb, progress_cb, progress_payload);
- }
- return GIT_ERROR;
-}
-
-[[nodiscard]] auto fetch_backend_exists(git_odb_backend* _backend,
- const git_oid* oid) -> int {
- if (_backend != nullptr) {
- auto* b = reinterpret_cast<FetchIntoODBBackend*>(_backend); // NOLINT
- return git_odb_exists(b->target_odb, oid);
- }
- return GIT_ERROR;
-}
-
-void fetch_backend_free(git_odb_backend* /*_backend*/) {}
-
-[[nodiscard]] auto CreateFetchIntoODBParent() -> git_odb_backend {
- git_odb_backend b{};
- b.version = GIT_ODB_BACKEND_VERSION;
- // only populate the functions needed
- b.writepack = &fetch_backend_writepack; // needed for fetch
- b.exists = &fetch_backend_exists;
- b.free = &fetch_backend_free;
- return b;
-}
-
-// A backend that can be used to fetch from the remote of another repository.
-auto const kFetchIntoODBParent = CreateFetchIntoODBParent();
-
-// callback to enable SSL certificate check for remote fetch
-const auto certificate_check_cb = [](git_cert* /*cert*/,
- int /*valid*/,
- const char* /*host*/,
- void* /*payload*/) -> int { return 1; };
-
-// callback to remote fetch without an SSL certificate check
-const auto certificate_passthrough_cb = [](git_cert* /*cert*/,
- int /*valid*/,
- const char* /*host*/,
- void* /*payload*/) -> int {
- return 0;
-};
-
-/// \brief Set a custom SSL certificate check callback to honor the existing Git
-/// configuration of a repository trying to connect to a remote.
-[[nodiscard]] auto SetCustomSSLCertificateCheckCallback(git_repository* repo)
- -> git_transport_certificate_check_cb {
- // check SSL verification settings, from most to least specific
- std::optional<int> check_cert{std::nullopt};
- // check gitconfig; ignore errors
- git_config* cfg{nullptr};
- int tmp{};
- if (git_repository_config(&cfg, repo) == 0 and
- git_config_get_bool(&tmp, cfg, "http.sslVerify") == 0) {
- check_cert = tmp;
- }
- if (not check_cert) {
- // check for GIT_SSL_NO_VERIFY environment variable
- const char* ssl_no_verify_var{std::getenv("GIT_SSL_NO_VERIFY")};
- if (ssl_no_verify_var != nullptr and
- git_config_parse_bool(&tmp, ssl_no_verify_var) == 0) {
- check_cert = tmp;
- }
- }
- // cleanup memory
- git_config_free(cfg);
- // set callback
- return (check_cert and check_cert.value() == 0) ? certificate_passthrough_cb
- : certificate_check_cb;
-}
-
} // namespace
#endif // BOOTSTRAP_BUILD_TOOL
@@ -521,6 +397,20 @@ auto GitRepo::GetGitCAS() const noexcept -> GitCASPtr {
return git_cas_;
}
+auto GitRepo::GetRepoRef() const noexcept
+ -> std::unique_ptr<git_repository, decltype(&repo_closer)> const& {
+ return repo_;
+}
+
+auto GitRepo::GetGitPath() const noexcept -> std::filesystem::path const& {
+ return git_cas_->git_path_;
+}
+
+auto GitRepo::GetGitOdb() const noexcept
+ -> std::unique_ptr<git_odb, decltype(&odb_closer)> const& {
+ return git_cas_->odb_;
+}
+
auto GitRepo::StageAndCommitAllAnonymous(std::string const& message,
anon_logger_ptr const& logger) noexcept
-> std::optional<std::string> {
@@ -753,182 +643,6 @@ auto GitRepo::GetHeadCommit(anon_logger_ptr const& logger) noexcept
#endif // BOOTSTRAP_BUILD_TOOL
}
-auto GitRepo::GetCommitFromRemote(std::string const& repo_url,
- std::string const& branch,
- anon_logger_ptr const& logger) noexcept
- -> std::optional<std::string> {
-#ifdef BOOTSTRAP_BUILD_TOOL
- return std::nullopt;
-#else
- try {
- // only possible for real repository!
- if (IsRepoFake()) {
- (*logger)("cannot update commit using a fake repository!",
- true /*fatal*/);
- return std::nullopt;
- }
- // create remote
- git_remote* remote_ptr{nullptr};
- if (git_remote_create_anonymous(
- &remote_ptr, repo_.get(), repo_url.c_str()) != 0) {
- (*logger)(
- fmt::format("creating anonymous remote for git repository {} "
- "failed with:\n{}",
- GetGitCAS()->git_path_.string(),
- GitLastError()),
- true /*fatal*/);
- git_remote_free(remote_ptr);
- return std::nullopt;
- }
- auto remote = std::unique_ptr<git_remote, decltype(&remote_closer)>(
- remote_ptr, remote_closer);
-
- // connect to remote
- git_remote_callbacks callbacks{};
- git_remote_init_callbacks(&callbacks, GIT_REMOTE_CALLBACKS_VERSION);
-
- // set custom SSL verification callback
- callbacks.certificate_check =
- SetCustomSSLCertificateCheckCallback(repo_.get());
-
- git_proxy_options proxy_opts{};
- git_proxy_options_init(&proxy_opts, GIT_PROXY_OPTIONS_VERSION);
-
- // set the option to auto-detect proxy settings
- proxy_opts.type = GIT_PROXY_AUTO;
-
- if (git_remote_connect(remote.get(),
- GIT_DIRECTION_FETCH,
- &callbacks,
- &proxy_opts,
- nullptr) != 0) {
- (*logger)(
- fmt::format("connecting to remote {} for git repository {} "
- "failed with:\n{}",
- repo_url,
- GetGitCAS()->git_path_.string(),
- GitLastError()),
- true /*fatal*/);
- return std::nullopt;
- }
- // get the list of refs from remote
- // NOTE: refs will be owned by remote, so we DON'T have to free it!
- git_remote_head const** refs = nullptr;
- size_t refs_len = 0;
- if (git_remote_ls(&refs, &refs_len, remote.get()) != 0) {
- (*logger)(
- fmt::format("refs retrieval from remote {} failed with:\n{}",
- repo_url,
- GitLastError()),
- true /*fatal*/);
- return std::nullopt;
- }
- // figure out what remote branch the local one is tracking
- for (size_t i = 0; i < refs_len; ++i) {
- // by treating each read reference string as a path we can easily
- // check for the branch name
- // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
- std::filesystem::path ref_name_as_path{refs[i]->name};
- if (ref_name_as_path.filename() == branch) {
- // branch found!
- // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
- std::string new_commit_hash{git_oid_tostr_s(&refs[i]->oid)};
- return new_commit_hash;
- }
- }
- (*logger)(fmt::format("could not find branch {} for remote {}",
- branch,
- repo_url,
- GitLastError()),
- true /*fatal*/);
- return std::nullopt;
- } catch (std::exception const& ex) {
- Logger::Log(LogLevel::Error,
- "get commit from remote failed with:\n{}",
- ex.what());
- return std::nullopt;
- }
-#endif // BOOTSTRAP_BUILD_TOOL
-}
-
-auto GitRepo::FetchFromRemote(std::string const& repo_url,
- std::optional<std::string> const& branch,
- anon_logger_ptr const& logger) noexcept -> bool {
-#ifdef BOOTSTRAP_BUILD_TOOL
- return false;
-#else
- try {
- // only possible for real repository!
- if (IsRepoFake()) {
- (*logger)("cannot fetch commit using a fake repository!",
- true /*fatal*/);
- return false;
- }
- // create remote from repo
- git_remote* remote_ptr{nullptr};
- if (git_remote_create_anonymous(
- &remote_ptr, repo_.get(), repo_url.c_str()) != 0) {
- (*logger)(fmt::format("creating remote {} for git repository {} "
- "failed with:\n{}",
- repo_url,
- GetGitCAS()->git_path_.string(),
- GitLastError()),
- true /*fatal*/);
- // cleanup resources
- git_remote_free(remote_ptr);
- return false;
- }
- // wrap remote object
- auto remote = std::unique_ptr<git_remote, decltype(&remote_closer)>(
- remote_ptr, remote_closer);
-
- // define default fetch options
- git_fetch_options fetch_opts{};
- git_fetch_options_init(&fetch_opts, GIT_FETCH_OPTIONS_VERSION);
-
- // set the option to auto-detect proxy settings
- fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
-
- // set custom SSL verification callback
- fetch_opts.callbacks.certificate_check =
- SetCustomSSLCertificateCheckCallback(repo_.get());
-
- // disable update of the FETCH_HEAD pointer
- fetch_opts.update_fetchhead = 0;
-
- // setup fetch refspecs array
- git_strarray refspecs_array_obj{};
- if (branch) {
- // make sure we check for tags as well
- std::string tag = fmt::format("+refs/tags/{}", *branch);
- std::string head = fmt::format("+refs/heads/{}", *branch);
- PopulateStrarray(&refspecs_array_obj, {tag, head});
- }
- auto refspecs_array =
- std::unique_ptr<git_strarray, decltype(&strarray_deleter)>(
- &refspecs_array_obj, strarray_deleter);
-
- if (git_remote_fetch(
- remote.get(), refspecs_array.get(), &fetch_opts, nullptr) !=
- 0) {
- (*logger)(
- fmt::format("fetching{} in git repository {} failed "
- "with:\n{}",
- branch ? fmt::format(" branch {}", *branch) : "",
- GetGitCAS()->git_path_.string(),
- GitLastError()),
- true /*fatal*/);
- return false;
- }
- return true; // success!
- } catch (std::exception const& ex) {
- Logger::Log(
- LogLevel::Error, "fetch from remote failed with:\n{}", ex.what());
- return false;
- }
-#endif // BOOTSTRAP_BUILD_TOOL
-}
-
auto GitRepo::GetSubtreeFromCommit(std::string const& commit,
std::string const& subdir,
anon_logger_ptr const& logger) noexcept
@@ -1183,89 +897,6 @@ auto GitRepo::CheckCommitExists(std::string const& commit,
#endif // BOOTSTRAP_BUILD_TOOL
}
-auto GitRepo::UpdateCommitViaTmpRepo(std::filesystem::path const& tmp_repo_path,
- std::string const& repo_url,
- std::string const& branch,
- anon_logger_ptr const& logger)
- const noexcept -> std::optional<std::string> {
-#ifdef BOOTSTRAP_BUILD_TOOL
- return std::nullopt;
-#else
- try {
- // preferably with a "fake" repository!
- if (not IsRepoFake()) {
- (*logger)("WARNING: commit update called on a real repository!\n",
- false /*fatal*/);
- }
- // create the temporary real repository
- auto tmp_repo = GitRepo::InitAndOpen(tmp_repo_path, /*is_bare=*/true);
- if (tmp_repo == std::nullopt) {
- return std::nullopt;
- }
- // setup wrapped logger
- auto wrapped_logger = std::make_shared<anon_logger_t>(
- [logger](auto const& msg, bool fatal) {
- (*logger)(
- fmt::format("While doing commit update via tmp repo:\n{}",
- msg),
- fatal);
- });
- return tmp_repo->GetCommitFromRemote(repo_url, branch, wrapped_logger);
- } catch (std::exception const& ex) {
- Logger::Log(LogLevel::Error,
- "update commit via tmp repo failed with:\n{}",
- ex.what());
- return std::nullopt;
- }
-#endif // BOOTSTRAP_BUILD_TOOL
-}
-
-auto GitRepo::FetchViaTmpRepo(std::filesystem::path const& tmp_repo_path,
- std::string const& repo_url,
- std::optional<std::string> const& branch,
- anon_logger_ptr const& logger) noexcept -> bool {
-#ifdef BOOTSTRAP_BUILD_TOOL
- return false;
-#else
- try {
- // preferably with a "fake" repository!
- if (not IsRepoFake()) {
- (*logger)("WARNING: branch fetch called on a real repository!\n",
- false /*fatal*/);
- }
- // create the temporary real repository
- // it can be bare, as the refspecs for this fetch will be given
- // explicitly.
- auto tmp_repo = GitRepo::InitAndOpen(tmp_repo_path, /*is_bare=*/true);
- if (tmp_repo == std::nullopt) {
- return false;
- }
- // add backend, with max priority
- FetchIntoODBBackend b{kFetchIntoODBParent, git_cas_->odb_.get()};
- if (git_odb_add_backend(
- tmp_repo->GetGitCAS()->odb_.get(),
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- reinterpret_cast<git_odb_backend*>(&b),
- std::numeric_limits<int>::max()) == 0) {
- // setup wrapped logger
- auto wrapped_logger = std::make_shared<anon_logger_t>(
- [logger](auto const& msg, bool fatal) {
- (*logger)(
- fmt::format(
- "While doing branch fetch via tmp repo:\n{}", msg),
- fatal);
- });
- return tmp_repo->FetchFromRemote(repo_url, branch, wrapped_logger);
- }
- return false;
- } catch (std::exception const& ex) {
- Logger::Log(
- LogLevel::Error, "fetch via tmp repo failed with:\n{}", ex.what());
- return false;
- }
-#endif // BOOTSTRAP_BUILD_TOOL
-}
-
auto GitRepo::GetRepoRootFromPath(std::filesystem::path const& fpath,
anon_logger_ptr const& logger) noexcept
-> std::optional<std::filesystem::path> {
diff --git a/src/buildtool/file_system/git_repo.hpp b/src/buildtool/file_system/git_repo.hpp
index 160a67df..a5a8da7a 100644
--- a/src/buildtool/file_system/git_repo.hpp
+++ b/src/buildtool/file_system/git_repo.hpp
@@ -138,25 +138,6 @@ class GitRepo {
[[nodiscard]] auto GetHeadCommit(anon_logger_ptr const& logger) noexcept
-> std::optional<std::string>;
- /// \brief Retrieve commit hash from remote branch given its name.
- /// Only possible with real repository and thus non-thread-safe.
- /// Returns the retrieved commit hash, or nullopt if failure.
- /// It guarantees the logger is called exactly once with fatal if failure.
- [[nodiscard]] auto GetCommitFromRemote(
- std::string const& repo_url,
- std::string const& branch,
- anon_logger_ptr const& logger) noexcept -> std::optional<std::string>;
-
- /// \brief Fetch from given remote. It can either fetch a given named
- /// branch, or it can fetch with base refspecs.
- /// Only possible with real repository and thus non-thread-safe.
- /// Returns a success flag.
- /// It guarantees the logger is called exactly once with fatal if failure.
- [[nodiscard]] auto FetchFromRemote(std::string const& repo_url,
- std::optional<std::string> const& branch,
- anon_logger_ptr const& logger) noexcept
- -> bool;
-
/// \brief Get the tree id of a subtree given the root commit
/// Calling it from a fake repository allows thread-safe use.
/// Returns the subtree hash, as a string, or nullopt if failure.
@@ -195,36 +176,6 @@ class GitRepo {
anon_logger_ptr const& logger) noexcept
-> std::optional<bool>;
- /// \brief Get commit from remote via a temporary repository.
- /// Calling it from a fake repository allows thread-safe use.
- /// Creates a temporary real repository at the given location and uses it to
- /// retrieve from the remote the commit of a branch given its name.
- /// Caller needs to make sure the temporary directory exists and that the
- /// given path is thread- and process-safe!
- /// Returns the commit hash, as a string, or nullopt if failure.
- /// It guarantees the logger is called exactly once with fatal if failure.
- [[nodiscard]] auto UpdateCommitViaTmpRepo(
- std::filesystem::path const& tmp_repo_path,
- std::string const& repo_url,
- std::string const& branch,
- anon_logger_ptr const& logger) const noexcept
- -> std::optional<std::string>;
-
- /// \brief Fetch from a remote via a temporary repository.
- /// Calling it from a fake repository allows thread-safe use.
- /// Creates a temporary real repository at the given location and uses a
- /// custom backend to redirect the fetched objects into the desired odb.
- /// Caller needs to make sure the temporary directory exists and that the
- /// given path is thread- and process-safe!
- /// Uses either a given branch, or fetches using base refspecs.
- /// Returns a success flag.
- /// It guarantees the logger is called exactly once with fatal if failure.
- [[nodiscard]] auto FetchViaTmpRepo(
- std::filesystem::path const& tmp_repo_path,
- std::string const& repo_url,
- std::optional<std::string> const& branch,
- anon_logger_ptr const& logger) noexcept -> bool;
-
/// \brief Try to retrieve the root of the repository containing the
/// given path, if the path is actually part of a repository.
/// Returns the git folder if path is in a git repo, empty string if path is
@@ -244,11 +195,21 @@ class GitRepo {
// default to real repo, as that is non-thread-safe
bool is_repo_fake_{false};
+ protected:
/// \brief Open "fake" repository wrapper for existing CAS.
explicit GitRepo(GitCASPtr git_cas) noexcept;
/// \brief Open real repository at given location.
explicit GitRepo(std::filesystem::path const& repo_path) noexcept;
+ [[nodiscard]] auto GetRepoRef() const noexcept
+ -> std::unique_ptr<git_repository, decltype(&repo_closer)> const&;
+
+ [[nodiscard]] auto GetGitPath() const noexcept
+ -> std::filesystem::path const&;
+
+ [[nodiscard]] auto GetGitOdb() const noexcept
+ -> std::unique_ptr<git_odb, decltype(&odb_closer)> const&;
+
/// \brief Helper function to allocate and populate the char** pointer of a
/// git_strarray from a vector of standard strings. User MUST use
/// git_strarray_dispose to deallocate the inner pointer when the strarray
diff --git a/src/buildtool/file_system/git_utils.cpp b/src/buildtool/file_system/git_utils.cpp
index ec3b5f6d..9fccbd9d 100644
--- a/src/buildtool/file_system/git_utils.cpp
+++ b/src/buildtool/file_system/git_utils.cpp
@@ -14,10 +14,61 @@
#include "src/buildtool/file_system/git_utils.hpp"
+#include "fmt/core.h"
+#include "src/buildtool/logging/logger.hpp"
+#include "src/utils/cpp/hex_string.hpp"
+
extern "C" {
#include <git2.h>
}
+#ifndef BOOTSTRAP_BUILD_TOOL
+namespace {
+constexpr std::size_t kOIDRawSize{GIT_OID_RAWSZ};
+constexpr std::size_t kOIDHexSize{GIT_OID_HEXSZ};
+} // namespace
+#endif // BOOTSTRAP_BUILD_TOOL
+
+auto GitLastError() noexcept -> std::string {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_error const* err{nullptr};
+ if ((err = git_error_last()) != nullptr and err->message != nullptr) {
+ return fmt::format("error code {}: {}", err->klass, err->message);
+ }
+#endif // BOOTSTRAP_BUILD_TOOL
+ return "<unknown error>";
+}
+
+auto GitObjectID(std::string const& id, bool is_hex_id) noexcept
+ -> std::optional<git_oid> {
+#ifdef BOOTSTRAP_BUILD_TOOL
+ return std::nullopt;
+#else
+ if (id.size() < (is_hex_id ? kOIDHexSize : kOIDRawSize)) {
+ Logger::Log(LogLevel::Error,
+ "invalid git object id {}",
+ is_hex_id ? id : ToHexString(id));
+ return std::nullopt;
+ }
+ git_oid oid{};
+ if (is_hex_id and git_oid_fromstr(&oid, id.c_str()) == 0) {
+ return oid;
+ }
+ if (not is_hex_id and
+ git_oid_fromraw(
+ &oid,
+ reinterpret_cast<unsigned char const*>(id.data()) // NOLINT
+ ) == 0) {
+ return oid;
+ }
+ Logger::Log(LogLevel::Error,
+ "parsing git object id {} failed with:\n{}",
+ is_hex_id ? id : ToHexString(id),
+ GitLastError());
+ return std::nullopt;
+#endif // BOOTSTRAP_BUILD_TOOL
+}
+
void repo_closer(gsl::owner<git_repository*> repo) {
#ifndef BOOTSTRAP_BUILD_TOOL
git_repository_free(repo);
diff --git a/src/buildtool/file_system/git_utils.hpp b/src/buildtool/file_system/git_utils.hpp
index 74f21500..d301912a 100644
--- a/src/buildtool/file_system/git_utils.hpp
+++ b/src/buildtool/file_system/git_utils.hpp
@@ -15,9 +15,13 @@
#ifndef INCLUDED_SRC_BUILDTOOL_FILE_SYSTEM_GIT_UTILS_HPP
#define INCLUDED_SRC_BUILDTOOL_FILE_SYSTEM_GIT_UTILS_HPP
+#include <optional>
+
#include "gsl-lite/gsl-lite.hpp"
+#include "src/buildtool/file_system/object_type.hpp"
extern "C" {
+struct git_oid;
struct git_odb;
struct git_repository;
struct git_tree;
@@ -31,6 +35,15 @@ struct git_commit;
struct git_tree_entry;
}
+constexpr std::size_t kWaitTime{2}; // time in ms between tries for git locks
+
+[[nodiscard]] auto GitObjectID(std::string const& id,
+ bool is_hex_id = false) noexcept
+ -> std::optional<git_oid>;
+
+/// \brief Retrieve error message of last libgit2 call.
+[[nodiscard]] auto GitLastError() noexcept -> std::string;
+
void repo_closer(gsl::owner<git_repository*> repo);
void odb_closer(gsl::owner<git_odb*> odb);
diff --git a/src/other_tools/git_operations/TARGETS b/src/other_tools/git_operations/TARGETS
index ac41c117..28e514f0 100644
--- a/src/other_tools/git_operations/TARGETS
+++ b/src/other_tools/git_operations/TARGETS
@@ -16,8 +16,21 @@
, "stage": ["src", "other_tools", "git_operations"]
, "private-deps":
[ ["src/buildtool/file_system", "file_system_manager"]
- , ["src/buildtool/file_system", "git_repo"]
+ , "git_repo_remote"
, ["src/buildtool/logging", "logging"]
]
}
+, "git_repo_remote":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["git_repo_remote"]
+ , "hdrs": ["git_repo_remote.hpp"]
+ , "srcs": ["git_repo_remote.cpp"]
+ , "deps": [["src/buildtool/file_system", "git_repo"]]
+ , "stage": ["src", "other_tools", "git_operations"]
+ , "private-deps":
+ [ ["src/buildtool/logging", "logging"]
+ , ["@", "fmt", "", "fmt"]
+ , ["", "libgit2"]
+ ]
+ }
}
diff --git a/src/other_tools/git_operations/git_operations.cpp b/src/other_tools/git_operations/git_operations.cpp
index cdefaef9..b68c54e7 100644
--- a/src/other_tools/git_operations/git_operations.cpp
+++ b/src/other_tools/git_operations/git_operations.cpp
@@ -17,15 +17,15 @@
#include <vector>
#include "src/buildtool/file_system/file_system_manager.hpp"
-#include "src/buildtool/file_system/git_repo.hpp"
#include "src/buildtool/logging/logger.hpp"
+#include "src/other_tools/git_operations/git_repo_remote.hpp"
auto CriticalGitOps::GitInitialCommit(GitOpParams const& crit_op_params,
AsyncMapConsumerLoggerPtr const& logger)
-> GitOpValue {
- // Create and open a GitRepo at given target location
- auto git_repo =
- GitRepo::InitAndOpen(crit_op_params.target_path, /*is_bare=*/false);
+ // Create and open a GitRepoRemote at given target location
+ auto git_repo = GitRepoRemote::InitAndOpen(crit_op_params.target_path,
+ /*is_bare=*/false);
if (git_repo == std::nullopt) {
(*logger)(fmt::format("could not initialize git repository {}",
crit_op_params.target_path.string()),
@@ -60,9 +60,9 @@ auto CriticalGitOps::GitEnsureInit(GitOpParams const& crit_op_params,
return GitOpValue({nullptr, std::nullopt});
}
// Create and open a GitRepo at given target location
- auto git_repo =
- GitRepo::InitAndOpen(crit_op_params.target_path,
- /*is_bare=*/crit_op_params.init_bare.value());
+ auto git_repo = GitRepoRemote::InitAndOpen(
+ crit_op_params.target_path,
+ /*is_bare=*/crit_op_params.init_bare.value());
if (git_repo == std::nullopt) {
(*logger)(
fmt::format("could not initialize {} git repository {}",
@@ -86,7 +86,7 @@ auto CriticalGitOps::GitKeepTag(GitOpParams const& crit_op_params,
return GitOpValue({nullptr, std::nullopt});
}
// Open a GitRepo at given location
- auto git_repo = GitRepo::Open(crit_op_params.target_path);
+ auto git_repo = GitRepoRemote::Open(crit_op_params.target_path);
if (git_repo == std::nullopt) {
(*logger)(fmt::format("could not open git repository {}",
crit_op_params.target_path.string()),
@@ -120,7 +120,7 @@ auto CriticalGitOps::GitGetHeadId(GitOpParams const& crit_op_params,
return GitOpValue({nullptr, std::nullopt});
}
// Open a GitRepo at given location
- auto git_repo = GitRepo::Open(crit_op_params.target_path);
+ auto git_repo = GitRepoRemote::Open(crit_op_params.target_path);
if (git_repo == std::nullopt) {
(*logger)(fmt::format("could not open git repository {}",
crit_op_params.target_path.string()),
diff --git a/src/other_tools/git_operations/git_repo_remote.cpp b/src/other_tools/git_operations/git_repo_remote.cpp
new file mode 100644
index 00000000..177a4301
--- /dev/null
+++ b/src/other_tools/git_operations/git_repo_remote.cpp
@@ -0,0 +1,405 @@
+// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <src/other_tools/git_operations/git_repo_remote.hpp>
+
+#include "fmt/core.h"
+#include "src/buildtool/logging/logger.hpp"
+
+extern "C" {
+#include <git2.h>
+#include <git2/sys/odb_backend.h>
+}
+
+namespace {
+
+struct FetchIntoODBBackend {
+ git_odb_backend parent;
+ git_odb* target_odb; // the odb where the fetch objects will end up into
+};
+
+[[nodiscard]] auto fetch_backend_writepack(git_odb_writepack** _writepack,
+ git_odb_backend* _backend,
+ [[maybe_unused]] git_odb* odb,
+ git_indexer_progress_cb progress_cb,
+ void* progress_payload) -> int {
+ if (_backend != nullptr) {
+ auto* b = reinterpret_cast<FetchIntoODBBackend*>(_backend); // NOLINT
+ return git_odb_write_pack(
+ _writepack, b->target_odb, progress_cb, progress_payload);
+ }
+ return GIT_ERROR;
+}
+
+[[nodiscard]] auto fetch_backend_exists(git_odb_backend* _backend,
+ const git_oid* oid) -> int {
+ if (_backend != nullptr) {
+ auto* b = reinterpret_cast<FetchIntoODBBackend*>(_backend); // NOLINT
+ return git_odb_exists(b->target_odb, oid);
+ }
+ return GIT_ERROR;
+}
+
+void fetch_backend_free(git_odb_backend* /*_backend*/) {}
+
+[[nodiscard]] auto CreateFetchIntoODBParent() -> git_odb_backend {
+ git_odb_backend b{};
+ b.version = GIT_ODB_BACKEND_VERSION;
+ // only populate the functions needed
+ b.writepack = &fetch_backend_writepack; // needed for fetch
+ b.exists = &fetch_backend_exists;
+ b.free = &fetch_backend_free;
+ return b;
+}
+
+// A backend that can be used to fetch from the remote of another repository.
+auto const kFetchIntoODBParent = CreateFetchIntoODBParent();
+
+// callback to enable SSL certificate check for remote fetch
+const auto certificate_check_cb = [](git_cert* /*cert*/,
+ int /*valid*/,
+ const char* /*host*/,
+ void* /*payload*/) -> int { return 1; };
+
+// callback to remote fetch without an SSL certificate check
+const auto certificate_passthrough_cb = [](git_cert* /*cert*/,
+ int /*valid*/,
+ const char* /*host*/,
+ void* /*payload*/) -> int {
+ return 0;
+};
+
+/// \brief Set a custom SSL certificate check callback to honor the existing Git
+/// configuration of a repository trying to connect to a remote.
+[[nodiscard]] auto SetCustomSSLCertificateCheckCallback(git_repository* repo)
+ -> git_transport_certificate_check_cb {
+ // check SSL verification settings, from most to least specific
+ std::optional<int> check_cert{std::nullopt};
+ // check gitconfig; ignore errors
+ git_config* cfg{nullptr};
+ int tmp{};
+ if (git_repository_config(&cfg, repo) == 0 and
+ git_config_get_bool(&tmp, cfg, "http.sslVerify") == 0) {
+ check_cert = tmp;
+ }
+ if (not check_cert) {
+ // check for GIT_SSL_NO_VERIFY environment variable
+ const char* ssl_no_verify_var{std::getenv("GIT_SSL_NO_VERIFY")};
+ if (ssl_no_verify_var != nullptr and
+ git_config_parse_bool(&tmp, ssl_no_verify_var) == 0) {
+ check_cert = tmp;
+ }
+ }
+ // cleanup memory
+ git_config_free(cfg);
+ // set callback
+ return (check_cert and check_cert.value() == 0) ? certificate_passthrough_cb
+ : certificate_check_cb;
+}
+
+} // namespace
+
+auto GitRepoRemote::Open(GitCASPtr git_cas) noexcept
+ -> std::optional<GitRepoRemote> {
+ auto repo = GitRepoRemote(std::move(git_cas));
+ if (not repo.GetRepoRef()) {
+ return std::nullopt;
+ }
+ return repo;
+}
+
+auto GitRepoRemote::Open(std::filesystem::path const& repo_path) noexcept
+ -> std::optional<GitRepoRemote> {
+ auto repo = GitRepoRemote(repo_path);
+ if (not repo.GetRepoRef()) {
+ return std::nullopt;
+ }
+ return repo;
+}
+
+GitRepoRemote::GitRepoRemote(GitCASPtr git_cas) noexcept
+ : GitRepo(std::move(git_cas)) {}
+
+GitRepoRemote::GitRepoRemote(std::filesystem::path const& repo_path) noexcept
+ : GitRepo(repo_path) {}
+
+GitRepoRemote::GitRepoRemote(GitRepo&& other) noexcept
+ : GitRepo(std::move(other)) {}
+
+GitRepoRemote::GitRepoRemote(GitRepoRemote&& other) noexcept
+ : GitRepo(std::move(other)) {}
+
+auto GitRepoRemote::operator=(GitRepoRemote&& other) noexcept
+ -> GitRepoRemote& {
+ GitRepo::operator=(std::move(other));
+ return *this;
+}
+
+auto GitRepoRemote::InitAndOpen(std::filesystem::path const& repo_path,
+ bool is_bare) noexcept
+ -> std::optional<GitRepoRemote> {
+ auto res = GitRepo::InitAndOpen(repo_path, is_bare);
+ if (res) {
+ return GitRepoRemote(std::move(*res));
+ }
+ return std::nullopt;
+}
+
+auto GitRepoRemote::GetCommitFromRemote(std::string const& repo_url,
+ std::string const& branch,
+ anon_logger_ptr const& logger) noexcept
+ -> std::optional<std::string> {
+ try {
+ // only possible for real repository!
+ if (IsRepoFake()) {
+ (*logger)("cannot update commit using a fake repository!",
+ true /*fatal*/);
+ return std::nullopt;
+ }
+ // create remote
+ git_remote* remote_ptr{nullptr};
+ if (git_remote_create_anonymous(
+ &remote_ptr, GetRepoRef().get(), repo_url.c_str()) != 0) {
+ (*logger)(
+ fmt::format("creating anonymous remote for git repository {} "
+ "failed with:\n{}",
+ GetGitPath().string(),
+ GitLastError()),
+ true /*fatal*/);
+ git_remote_free(remote_ptr);
+ return std::nullopt;
+ }
+ auto remote = std::unique_ptr<git_remote, decltype(&remote_closer)>(
+ remote_ptr, remote_closer);
+
+ // connect to remote
+ git_remote_callbacks callbacks{};
+ git_remote_init_callbacks(&callbacks, GIT_REMOTE_CALLBACKS_VERSION);
+
+ // set custom SSL verification callback
+ callbacks.certificate_check =
+ SetCustomSSLCertificateCheckCallback(GetRepoRef().get());
+
+ git_proxy_options proxy_opts{};
+ git_proxy_options_init(&proxy_opts, GIT_PROXY_OPTIONS_VERSION);
+
+ // set the option to auto-detect proxy settings
+ proxy_opts.type = GIT_PROXY_AUTO;
+
+ if (git_remote_connect(remote.get(),
+ GIT_DIRECTION_FETCH,
+ &callbacks,
+ &proxy_opts,
+ nullptr) != 0) {
+ (*logger)(
+ fmt::format("connecting to remote {} for git repository {} "
+ "failed with:\n{}",
+ repo_url,
+ GetGitPath().string(),
+ GitLastError()),
+ true /*fatal*/);
+ return std::nullopt;
+ }
+ // get the list of refs from remote
+ // NOTE: refs will be owned by remote, so we DON'T have to free it!
+ git_remote_head const** refs = nullptr;
+ size_t refs_len = 0;
+ if (git_remote_ls(&refs, &refs_len, remote.get()) != 0) {
+ (*logger)(
+ fmt::format("refs retrieval from remote {} failed with:\n{}",
+ repo_url,
+ GitLastError()),
+ true /*fatal*/);
+ return std::nullopt;
+ }
+ // figure out what remote branch the local one is tracking
+ for (size_t i = 0; i < refs_len; ++i) {
+ // by treating each read reference string as a path we can easily
+ // check for the branch name
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+ std::filesystem::path ref_name_as_path{refs[i]->name};
+ if (ref_name_as_path.filename() == branch) {
+ // branch found!
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+ std::string new_commit_hash{git_oid_tostr_s(&refs[i]->oid)};
+ return new_commit_hash;
+ }
+ }
+ (*logger)(fmt::format("could not find branch {} for remote {}",
+ branch,
+ repo_url,
+ GitLastError()),
+ true /*fatal*/);
+ return std::nullopt;
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "get commit from remote failed with:\n{}",
+ ex.what());
+ return std::nullopt;
+ }
+}
+
+auto GitRepoRemote::FetchFromRemote(std::string const& repo_url,
+ std::optional<std::string> const& branch,
+ anon_logger_ptr const& logger) noexcept
+ -> bool {
+ try {
+ // only possible for real repository!
+ if (IsRepoFake()) {
+ (*logger)("cannot fetch commit using a fake repository!",
+ true /*fatal*/);
+ return false;
+ }
+ // create remote from repo
+ git_remote* remote_ptr{nullptr};
+ if (git_remote_create_anonymous(
+ &remote_ptr, GetRepoRef().get(), repo_url.c_str()) != 0) {
+ (*logger)(fmt::format("creating remote {} for git repository {} "
+ "failed with:\n{}",
+ repo_url,
+ GetGitPath().string(),
+ GitLastError()),
+ true /*fatal*/);
+ // cleanup resources
+ git_remote_free(remote_ptr);
+ return false;
+ }
+ // wrap remote object
+ auto remote = std::unique_ptr<git_remote, decltype(&remote_closer)>(
+ remote_ptr, remote_closer);
+
+ // define default fetch options
+ git_fetch_options fetch_opts{};
+ git_fetch_options_init(&fetch_opts, GIT_FETCH_OPTIONS_VERSION);
+
+ // set the option to auto-detect proxy settings
+ fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
+
+ // set custom SSL verification callback
+ fetch_opts.callbacks.certificate_check =
+ SetCustomSSLCertificateCheckCallback(GetRepoRef().get());
+
+ // disable update of the FETCH_HEAD pointer
+ fetch_opts.update_fetchhead = 0;
+
+ // setup fetch refspecs array
+ git_strarray refspecs_array_obj{};
+ if (branch) {
+ // make sure we check for tags as well
+ std::string tag = fmt::format("+refs/tags/{}", *branch);
+ std::string head = fmt::format("+refs/heads/{}", *branch);
+ PopulateStrarray(&refspecs_array_obj, {tag, head});
+ }
+ auto refspecs_array =
+ std::unique_ptr<git_strarray, decltype(&strarray_deleter)>(
+ &refspecs_array_obj, strarray_deleter);
+
+ if (git_remote_fetch(
+ remote.get(), refspecs_array.get(), &fetch_opts, nullptr) !=
+ 0) {
+ (*logger)(
+ fmt::format("fetching{} in git repository {} failed "
+ "with:\n{}",
+ branch ? fmt::format(" branch {}", *branch) : "",
+ GetGitPath().string(),
+ GitLastError()),
+ true /*fatal*/);
+ return false;
+ }
+ return true; // success!
+ } catch (std::exception const& ex) {
+ Logger::Log(
+ LogLevel::Error, "fetch from remote failed with:\n{}", ex.what());
+ return false;
+ }
+}
+
+auto GitRepoRemote::UpdateCommitViaTmpRepo(
+ std::filesystem::path const& tmp_repo_path,
+ std::string const& repo_url,
+ std::string const& branch,
+ anon_logger_ptr const& logger) const noexcept
+ -> std::optional<std::string> {
+ try {
+ // preferably with a "fake" repository!
+ if (not IsRepoFake()) {
+ (*logger)("WARNING: commit update called on a real repository!\n",
+ false /*fatal*/);
+ }
+ // create the temporary real repository
+ auto tmp_repo =
+ GitRepoRemote::InitAndOpen(tmp_repo_path, /*is_bare=*/true);
+ if (tmp_repo == std::nullopt) {
+ return std::nullopt;
+ }
+ // setup wrapped logger
+ auto wrapped_logger = std::make_shared<anon_logger_t>(
+ [logger](auto const& msg, bool fatal) {
+ (*logger)(
+ fmt::format("While doing commit update via tmp repo:\n{}",
+ msg),
+ fatal);
+ });
+ return tmp_repo->GetCommitFromRemote(repo_url, branch, wrapped_logger);
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "update commit via tmp repo failed with:\n{}",
+ ex.what());
+ return std::nullopt;
+ }
+}
+
+auto GitRepoRemote::FetchViaTmpRepo(std::filesystem::path const& tmp_repo_path,
+ std::string const& repo_url,
+ std::optional<std::string> const& branch,
+ anon_logger_ptr const& logger) noexcept
+ -> bool {
+ try {
+ // preferably with a "fake" repository!
+ if (not IsRepoFake()) {
+ (*logger)("WARNING: branch fetch called on a real repository!\n",
+ false /*fatal*/);
+ }
+ // create the temporary real repository
+ // it can be bare, as the refspecs for this fetch will be given
+ // explicitly.
+ auto tmp_repo =
+ GitRepoRemote::InitAndOpen(tmp_repo_path, /*is_bare=*/true);
+ if (tmp_repo == std::nullopt) {
+ return false;
+ }
+ // add backend, with max priority
+ FetchIntoODBBackend b{kFetchIntoODBParent, GetGitOdb().get()};
+ if (git_odb_add_backend(
+ tmp_repo->GetGitOdb().get(),
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ reinterpret_cast<git_odb_backend*>(&b),
+ std::numeric_limits<int>::max()) == 0) {
+ // setup wrapped logger
+ auto wrapped_logger = std::make_shared<anon_logger_t>(
+ [logger](auto const& msg, bool fatal) {
+ (*logger)(
+ fmt::format(
+ "While doing branch fetch via tmp repo:\n{}", msg),
+ fatal);
+ });
+ return tmp_repo->FetchFromRemote(repo_url, branch, wrapped_logger);
+ }
+ return false;
+ } catch (std::exception const& ex) {
+ Logger::Log(
+ LogLevel::Error, "fetch via tmp repo failed with:\n{}", ex.what());
+ return false;
+ }
+}
diff --git a/src/other_tools/git_operations/git_repo_remote.hpp b/src/other_tools/git_operations/git_repo_remote.hpp
new file mode 100644
index 00000000..83099994
--- /dev/null
+++ b/src/other_tools/git_operations/git_repo_remote.hpp
@@ -0,0 +1,105 @@
+// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef INCLUDED_SRC_OTHER_TOOLS_GIT_OPERATIONS_GIT_REPO_REMOTE_HPP
+#define INCLUDED_SRC_OTHER_TOOLS_GIT_OPERATIONS_GIT_REPO_REMOTE_HPP
+
+#include "src/buildtool/file_system/git_repo.hpp"
+
+/// \brief Extension to a Git repository, allowing remote Git operations.
+class GitRepoRemote : public GitRepo {
+ public:
+ GitRepoRemote() = delete; // no default ctor
+ ~GitRepoRemote() noexcept = default;
+
+ // allow only move, no copy
+ GitRepoRemote(GitRepoRemote const&) = delete;
+ GitRepoRemote(GitRepoRemote&&) noexcept;
+ auto operator=(GitRepoRemote const&) = delete;
+ auto operator=(GitRepoRemote&& other) noexcept -> GitRepoRemote&;
+
+ /// \brief Factory to wrap existing open CAS in a "fake" repository.
+ [[nodiscard]] static auto Open(GitCASPtr git_cas) noexcept
+ -> std::optional<GitRepoRemote>;
+
+ /// \brief Factory to open existing real repository at given location.
+ [[nodiscard]] static auto Open(
+ std::filesystem::path const& repo_path) noexcept
+ -> std::optional<GitRepoRemote>;
+
+ /// \brief Factory to initialize and open new real repository at location.
+ /// Returns nullopt if repository init fails even after repeated tries.
+ [[nodiscard]] static auto InitAndOpen(
+ std::filesystem::path const& repo_path,
+ bool is_bare) noexcept -> std::optional<GitRepoRemote>;
+
+ /// \brief Retrieve commit hash from remote branch given its name.
+ /// Only possible with real repository and thus non-thread-safe.
+ /// Returns the retrieved commit hash, or nullopt if failure.
+ /// It guarantees the logger is called exactly once with fatal if failure.
+ [[nodiscard]] auto GetCommitFromRemote(
+ std::string const& repo_url,
+ std::string const& branch,
+ anon_logger_ptr const& logger) noexcept -> std::optional<std::string>;
+
+ /// \brief Fetch from given remote. It can either fetch a given named
+ /// branch, or it can fetch with base refspecs.
+ /// Only possible with real repository and thus non-thread-safe.
+ /// Returns a success flag.
+ /// It guarantees the logger is called exactly once with fatal if failure.
+ [[nodiscard]] auto FetchFromRemote(std::string const& repo_url,
+ std::optional<std::string> const& branch,
+ anon_logger_ptr const& logger) noexcept
+ -> bool;
+
+ /// \brief Get commit from remote via a temporary repository.
+ /// Calling it from a fake repository allows thread-safe use.
+ /// Creates a temporary real repository at the given location and uses it to
+ /// retrieve from the remote the commit of a branch given its name.
+ /// Caller needs to make sure the temporary directory exists and that the
+ /// given path is thread- and process-safe!
+ /// Returns the commit hash, as a string, or nullopt if failure.
+ /// It guarantees the logger is called exactly once with fatal if failure.
+ [[nodiscard]] auto UpdateCommitViaTmpRepo(
+ std::filesystem::path const& tmp_repo_path,
+ std::string const& repo_url,
+ std::string const& branch,
+ anon_logger_ptr const& logger) const noexcept
+ -> std::optional<std::string>;
+
+ /// \brief Fetch from a remote via a temporary repository.
+ /// Calling it from a fake repository allows thread-safe use.
+ /// Creates a temporary real repository at the given location and uses a
+ /// custom backend to redirect the fetched objects into the desired odb.
+ /// Caller needs to make sure the temporary directory exists and that the
+ /// given path is thread- and process-safe!
+ /// Uses either a given branch, or fetches using base refspecs.
+ /// Returns a success flag.
+ /// It guarantees the logger is called exactly once with fatal if failure.
+ [[nodiscard]] auto FetchViaTmpRepo(
+ std::filesystem::path const& tmp_repo_path,
+ std::string const& repo_url,
+ std::optional<std::string> const& branch,
+ anon_logger_ptr const& logger) noexcept -> bool;
+
+ private:
+ /// \brief Open "fake" repository wrapper for existing CAS.
+ explicit GitRepoRemote(GitCASPtr git_cas) noexcept;
+ /// \brief Open real repository at given location.
+ explicit GitRepoRemote(std::filesystem::path const& repo_path) noexcept;
+ /// \brief Construct from inherited class.
+ explicit GitRepoRemote(GitRepo&&) noexcept;
+};
+
+#endif // INCLUDED_SRC_OTHER_TOOLS_GIT_OPERATIONS_GIT_REPO_REMOTE_HPP
diff --git a/src/other_tools/just_mr/main.cpp b/src/other_tools/just_mr/main.cpp
index 08f82f32..62704426 100644
--- a/src/other_tools/just_mr/main.cpp
+++ b/src/other_tools/just_mr/main.cpp
@@ -839,7 +839,8 @@ void DefaultReachableRepositories(
return kExitUpdateError;
}
// Init and open git repo
- auto git_repo = GitRepo::InitAndOpen(tmp_dir->GetPath(), /*is_bare=*/true);
+ auto git_repo =
+ GitRepoRemote::InitAndOpen(tmp_dir->GetPath(), /*is_bare=*/true);
if (not git_repo) {
Logger::Log(LogLevel::Error,
"Failed to initialize repository in tmp dir {} for git "
diff --git a/src/other_tools/ops_maps/TARGETS b/src/other_tools/ops_maps/TARGETS
index bedf8cba..5c99658a 100644
--- a/src/other_tools/ops_maps/TARGETS
+++ b/src/other_tools/ops_maps/TARGETS
@@ -17,7 +17,7 @@
, "srcs": ["import_to_git_map.cpp"]
, "deps":
[ "critical_git_op_map"
- , ["src/buildtool/file_system", "git_repo"]
+ , ["src/other_tools/git_operations", "git_repo_remote"]
, ["@", "fmt", "", "fmt"]
, ["src/utils/cpp", "path"]
]
@@ -34,7 +34,7 @@
, "hdrs": ["git_update_map.hpp"]
, "srcs": ["git_update_map.cpp"]
, "deps":
- [ ["src/buildtool/file_system", "git_repo"]
+ [ ["src/other_tools/git_operations", "git_repo_remote"]
, ["src/buildtool/multithreading", "async_map_consumer"]
, ["src/utils/cpp", "hash_combine"]
, ["@", "fmt", "", "fmt"]
diff --git a/src/other_tools/ops_maps/git_update_map.cpp b/src/other_tools/ops_maps/git_update_map.cpp
index f2f6c3e5..c68d6f94 100644
--- a/src/other_tools/ops_maps/git_update_map.cpp
+++ b/src/other_tools/ops_maps/git_update_map.cpp
@@ -27,7 +27,7 @@ auto CreateGitUpdateMap(GitCASPtr const& git_cas, std::size_t jobs)
auto /* unused */,
auto const& key) {
// perform git update commit
- auto git_repo = GitRepo::Open(git_cas); // wrap the tmp odb
+ auto git_repo = GitRepoRemote::Open(git_cas); // wrap the tmp odb
if (not git_repo) {
(*logger)(
fmt::format("Failed to open tmp Git repository for remote {}",
diff --git a/src/other_tools/ops_maps/git_update_map.hpp b/src/other_tools/ops_maps/git_update_map.hpp
index a69057bf..d4804545 100644
--- a/src/other_tools/ops_maps/git_update_map.hpp
+++ b/src/other_tools/ops_maps/git_update_map.hpp
@@ -15,8 +15,8 @@
#ifndef INCLUDED_SRC_OTHER_TOOLS_OPS_MAPS_GIT_UPDATE_MAP_HPP
#define INCLUDED_SRC_OTHER_TOOLS_OPS_MAPS_GIT_UPDATE_MAP_HPP
-#include "src/buildtool/file_system/git_repo.hpp"
#include "src/buildtool/multithreading/async_map_consumer.hpp"
+#include "src/other_tools/git_operations/git_repo_remote.hpp"
#include "src/utils/cpp/hash_combine.hpp"
using StringPair = std::pair<std::string, std::string>;
diff --git a/src/other_tools/ops_maps/import_to_git_map.cpp b/src/other_tools/ops_maps/import_to_git_map.cpp
index 137a719b..aa3faa2e 100644
--- a/src/other_tools/ops_maps/import_to_git_map.cpp
+++ b/src/other_tools/ops_maps/import_to_git_map.cpp
@@ -81,7 +81,8 @@ auto CreateImportToGitMap(
return;
}
// fetch all into Git cache
- auto just_git_repo = GitRepo::Open(op_result.git_cas);
+ auto just_git_repo =
+ GitRepoRemote::Open(op_result.git_cas);
if (not just_git_repo) {
(*logger)(fmt::format("Could not open Git "
"repository {}",
@@ -188,7 +189,7 @@ void KeepCommitAndSetTree(
/*fatal=*/true);
return;
}
- auto git_repo = GitRepo::Open(git_cas);
+ auto git_repo = GitRepoRemote::Open(git_cas);
if (not git_repo) {
(*logger)(fmt::format("Could not open Git repository {}",
target_path.string()),
diff --git a/src/other_tools/ops_maps/import_to_git_map.hpp b/src/other_tools/ops_maps/import_to_git_map.hpp
index 32b7b638..5a949189 100644
--- a/src/other_tools/ops_maps/import_to_git_map.hpp
+++ b/src/other_tools/ops_maps/import_to_git_map.hpp
@@ -18,7 +18,7 @@
#include <filesystem>
#include <string>
-#include "src/buildtool/file_system/git_repo.hpp"
+#include "src/other_tools/git_operations/git_repo_remote.hpp"
#include "src/other_tools/ops_maps/critical_git_op_map.hpp"
#include "src/utils/cpp/path.hpp"
diff --git a/src/other_tools/root_maps/TARGETS b/src/other_tools/root_maps/TARGETS
index 0d5c18f9..d609b7d3 100644
--- a/src/other_tools/root_maps/TARGETS
+++ b/src/other_tools/root_maps/TARGETS
@@ -32,7 +32,9 @@
]
, "stage": ["src", "other_tools", "root_maps"]
, "private-deps":
- [["src/buildtool/file_system", "git_repo"], ["src/utils/cpp", "tmp_dir"]]
+ [ ["src/other_tools/git_operations", "git_repo_remote"]
+ , ["src/utils/cpp", "tmp_dir"]
+ ]
}
, "fpath_git_map":
{ "type": ["@", "rules", "CC", "library"]
diff --git a/src/other_tools/root_maps/commit_git_map.cpp b/src/other_tools/root_maps/commit_git_map.cpp
index 86ab3447..5d34182b 100644
--- a/src/other_tools/root_maps/commit_git_map.cpp
+++ b/src/other_tools/root_maps/commit_git_map.cpp
@@ -16,7 +16,7 @@
#include <algorithm>
-#include "src/buildtool/file_system/git_repo.hpp"
+#include "src/other_tools/git_operations/git_repo_remote.hpp"
#include "src/utils/cpp/tmp_dir.hpp"
namespace {
@@ -111,7 +111,7 @@ void EnsureCommit(GitRepoInfo const& repo_info,
CommitGitMap::SetterPtr const& ws_setter,
CommitGitMap::LoggerPtr const& logger) {
// ensure commit exists, and fetch if needed
- auto git_repo = GitRepo::Open(git_cas); // link fake repo to odb
+ 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()),
@@ -193,7 +193,7 @@ void EnsureCommit(GitRepoInfo const& repo_info,
}
// ensure commit exists, and fetch if needed
auto git_repo =
- GitRepo::Open(git_cas); // link fake repo to odb
+ GitRepoRemote::Open(git_cas); // link fake repo to odb
if (not git_repo) {
(*logger)(fmt::format("Could not open repository {}",
repo_root.string()),
diff --git a/src/other_tools/root_maps/content_git_map.cpp b/src/other_tools/root_maps/content_git_map.cpp
index 9d1130e5..11adf136 100644
--- a/src/other_tools/root_maps/content_git_map.cpp
+++ b/src/other_tools/root_maps/content_git_map.cpp
@@ -89,7 +89,7 @@ auto CreateContentGitMap(
return;
}
// open fake repo wrap for GitCAS
- auto just_git_repo = GitRepo::Open(op_result.git_cas);
+ auto just_git_repo = GitRepoRemote::Open(op_result.git_cas);
if (not just_git_repo) {
(*logger)("Could not open Git cache repository!",
/*fatal=*/true);
@@ -205,7 +205,8 @@ auto CreateContentGitMap(
return;
}
// fetch all into Git cache
- auto just_git_repo = GitRepo::Open(just_git_cas);
+ auto just_git_repo =
+ GitRepoRemote::Open(just_git_cas);
if (not just_git_repo) {
(*logger)(
"Could not open Git cache repository!",
diff --git a/src/other_tools/root_maps/fpath_git_map.cpp b/src/other_tools/root_maps/fpath_git_map.cpp
index 17605ddc..70aff153 100644
--- a/src/other_tools/root_maps/fpath_git_map.cpp
+++ b/src/other_tools/root_maps/fpath_git_map.cpp
@@ -37,7 +37,7 @@ auto CreateFilePathGitMap(
fatal);
});
// check if path is a part of a git repo
- auto repo_root = GitRepo::GetRepoRootFromPath(
+ auto repo_root = GitRepoRemote::GetRepoRootFromPath(
key, wrapped_logger); // static function
if (not repo_root) {
return;
@@ -51,7 +51,8 @@ auto CreateFilePathGitMap(
/*fatal=*/true);
return;
}
- auto git_repo = GitRepo::Open(git_cas); // link fake repo to odb
+ 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()),
@@ -81,7 +82,7 @@ auto CreateFilePathGitMap(
return;
}
auto git_repo =
- GitRepo::Open(git_cas); // link fake repo to odb
+ GitRepoRemote::Open(git_cas); // link fake repo to odb
if (not git_repo) {
(*logger)(fmt::format("Could not open repository {}",
repo_root.string()),