summaryrefslogtreecommitdiff
path: root/src/other_tools/git_operations/git_repo_remote.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/other_tools/git_operations/git_repo_remote.cpp')
-rw-r--r--src/other_tools/git_operations/git_repo_remote.cpp146
1 files changed, 106 insertions, 40 deletions
diff --git a/src/other_tools/git_operations/git_repo_remote.cpp b/src/other_tools/git_operations/git_repo_remote.cpp
index 4965f121..6cff2a82 100644
--- a/src/other_tools/git_operations/git_repo_remote.cpp
+++ b/src/other_tools/git_operations/git_repo_remote.cpp
@@ -15,8 +15,10 @@
#include <src/other_tools/git_operations/git_repo_remote.hpp>
#include "fmt/core.h"
+#include "nlohmann/json.hpp"
#include "src/buildtool/file_system/git_utils.hpp"
#include "src/buildtool/logging/logger.hpp"
+#include "src/buildtool/system/system_command.hpp"
#include "src/other_tools/git_operations/git_config_settings.hpp"
extern "C" {
@@ -26,6 +28,21 @@ extern "C" {
namespace {
+/// \brief Basic check for libgit2 protocols we support. For all other cases, we
+/// should default to shell out to git instead.
+[[nodiscard]] auto IsSupported(std::string const& url) -> bool {
+ // look for explicit schemes
+ if (url.starts_with("git://") or url.starts_with("http://") or
+ url.starts_with("https://") or url.starts_with("file://")) {
+ return true;
+ }
+ // look for url as existing filesystem directory path
+ if (FileSystemManager::IsDirectory(std::filesystem::path(url))) {
+ return true;
+ }
+ return false; // as default
+}
+
struct FetchIntoODBBackend {
git_odb_backend parent;
git_odb* target_odb; // the odb where the fetch objects will end up into
@@ -414,56 +431,105 @@ auto GitRepoRemote::UpdateCommitViaTmpRepo(
}
}
-auto GitRepoRemote::FetchViaTmpRepo(std::filesystem::path const& tmp_repo_path,
+auto GitRepoRemote::FetchViaTmpRepo(std::filesystem::path const& tmp_dir,
std::string const& repo_url,
std::optional<std::string> const& branch,
+ std::vector<std::string> const& launcher,
anon_logger_ptr const& logger) noexcept
-> bool {
try {
- // preferably with a "fake" repository!
- if (not IsRepoFake()) {
- Logger::Log(LogLevel::Debug,
- "Branch fetch called on a real repository");
- }
- // 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) {
+ // check for internally supported protocols
+ if (IsSupported(repo_url)) {
+ // preferably with a "fake" repository!
+ if (not IsRepoFake()) {
+ Logger::Log(LogLevel::Debug,
+ "Branch fetch called on a real repository");
+ }
+ // 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_dir, /*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);
+ });
+ // get the config of the correct target repo
+ auto cfg = GetConfigSnapshot();
+ if (cfg == nullptr) {
+ (*logger)(
+ fmt::format("retrieving config object in fetch via "
+ "tmp repo failed with:\n{}",
+ GitLastError()),
+ true /*fatal*/);
+ return false;
+ }
+ return tmp_repo->FetchFromRemote(
+ cfg, repo_url, branch, wrapped_logger);
+ }
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);
- });
- // get the config of the correct target repo
- auto cfg = GetConfigSnapshot();
- if (cfg == nullptr) {
- (*logger)(fmt::format("retrieving config object in fetch via "
- "tmp repo failed with:\n{}",
- GitLastError()),
- true /*fatal*/);
- return false;
+ // default to shelling out to git for non-explicitly supported protocols
+ auto cmdline = launcher;
+ // Note: Because we fetch with URL, not a known remote, no refs are
+ // being updated by default, so git has no reason to get a lock
+ // file. This however does not imply automatically that fetches
+ // might not internally wait for each other through other means.
+ cmdline.insert(cmdline.end(),
+ {"git",
+ "fetch",
+ "--no-auto-gc",
+ "--no-write-fetch-head",
+ repo_url});
+ if (branch) {
+ cmdline.push_back(*branch);
+ }
+ // run command
+ SystemCommand system{repo_url};
+ auto const command_output = system.Execute(cmdline,
+ {}, // caller env
+ GetGitPath(),
+ tmp_dir);
+ if (not command_output) {
+ std::string out_str{};
+ std::string err_str{};
+ auto cmd_out =
+ FileSystemManager::ReadFile(command_output->stdout_file);
+ auto cmd_err =
+ FileSystemManager::ReadFile(command_output->stderr_file);
+ if (cmd_out) {
+ out_str = *cmd_out;
+ }
+ if (cmd_err) {
+ err_str = *cmd_err;
+ }
+ std::string output{};
+ if (!out_str.empty() || !err_str.empty()) {
+ output = fmt::format(" with output:\n{}{}", out_str, err_str);
}
- return tmp_repo->FetchFromRemote(
- cfg, repo_url, branch, wrapped_logger);
+ (*logger)(fmt::format("Fetch command {} failed{}",
+ nlohmann::json(cmdline).dump(),
+ output),
+ /*fatal=*/true);
+ return false;
}
- return false;
+ return true; // fetch successful
} catch (std::exception const& ex) {
- Logger::Log(
- LogLevel::Error, "fetch via tmp repo failed with:\n{}", ex.what());
+ Logger::Log(LogLevel::Error, "Fetch failed with:\n{}", ex.what());
return false;
}
}