summaryrefslogtreecommitdiff
path: root/src/buildtool/file_system/git_repo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildtool/file_system/git_repo.cpp')
-rw-r--r--src/buildtool/file_system/git_repo.cpp399
1 files changed, 15 insertions, 384 deletions
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> {