summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buildtool/file_system/git_repo.cpp102
-rw-r--r--src/buildtool/file_system/git_repo.hpp15
2 files changed, 117 insertions, 0 deletions
diff --git a/src/buildtool/file_system/git_repo.cpp b/src/buildtool/file_system/git_repo.cpp
index 12613007..78ae1c86 100644
--- a/src/buildtool/file_system/git_repo.cpp
+++ b/src/buildtool/file_system/git_repo.cpp
@@ -1642,6 +1642,108 @@ auto GitRepo::GetConfigSnapshot() const noexcept
return nullptr;
}
+auto GitRepo::ImportToGit(
+ StorageConfig const& storage_config,
+ std::filesystem::path const& source_dir,
+ std::string const& commit_message,
+ gsl::not_null<std::mutex*> const& tagging_lock) noexcept
+ -> expected<std::string, std::string> {
+ // the repository path that imports the content must be separate from the
+ // content path, to avoid polluting the entries
+ auto tmp_dir = storage_config.CreateTypedTmpDir("import_repo");
+ if (tmp_dir == nullptr) {
+ return unexpected{
+ std::string("Failed to create tmp path for import repository")};
+ }
+
+ // wrap logger for GitRepo call
+ std::string err;
+ auto logger = std::make_shared<GitRepo::anon_logger_t>(
+ [&err](auto const& msg, bool fatal) {
+ if (fatal) {
+ err = msg;
+ }
+ });
+
+ auto const& repo_path = tmp_dir->GetPath();
+ // do the initial commit; no need to guard, as the tmp location is unique
+ auto temp_repo = GitRepo::InitAndOpen(repo_path, /*is_bare=*/false);
+ if (not temp_repo.has_value()) {
+ return unexpected{fmt::format("Could not initialize repository {}",
+ repo_path.string())};
+ }
+
+ // stage and commit all
+ err.clear();
+ auto const commit_hash =
+ temp_repo->CommitDirectory(source_dir, commit_message, logger);
+ if (not commit_hash.has_value()) {
+ return unexpected{
+ fmt::format("While committing directory {} in repository {}:\n{}",
+ source_dir.string(),
+ repo_path.string(),
+ std::move(err))};
+ }
+
+ // open the Git CAS repo
+ auto const just_git_cas = GitCAS::Open(storage_config.GitRoot());
+ if (just_git_cas == nullptr) {
+ return unexpected{fmt::format("Failed to open Git ODB at {}",
+ storage_config.GitRoot().string())};
+ }
+ auto just_git_repo = GitRepo::Open(just_git_cas);
+ if (not just_git_repo.has_value()) {
+ return unexpected{fmt::format("Failed to open Git repository {}",
+ storage_config.GitRoot().string())};
+ }
+ // fetch the new commit into the Git CAS via tmp directory; the call is
+ // thread-safe, so it needs no guarding
+ err.clear();
+ if (not just_git_repo->LocalFetchViaTmpRepo(storage_config,
+ repo_path.string(),
+ /*branch=*/std::nullopt,
+ logger)) {
+ return unexpected{fmt::format("While fetching in repository {}:\n{}",
+ storage_config.GitRoot().string(),
+ std::move(err))};
+ }
+
+ // tag commit and keep it in Git CAS
+ {
+ // this is a non-thread-safe Git operation, so it must be guarded!
+ std::unique_lock slock{*tagging_lock};
+ // open real repository at Git CAS location
+ auto git_repo = GitRepo::Open(storage_config.GitRoot());
+ if (not git_repo.has_value()) {
+ return unexpected{
+ fmt::format("Failed to open Git CAS repository {}",
+ storage_config.GitRoot().string())};
+ }
+ // Important: message must be consistent with just-mr!
+ err.clear();
+ if (not git_repo->KeepTag(*commit_hash,
+ /*message=*/"Keep referenced tree alive",
+ logger)) {
+ return unexpected{
+ fmt::format("While tagging commit {} in repository {}:\n{}",
+ *commit_hash,
+ storage_config.GitRoot().string(),
+ std::move(err))};
+ }
+ }
+ // get the root tree of this commit; this is thread-safe
+ err.clear();
+ auto result_tree =
+ just_git_repo->GetSubtreeFromCommit(*commit_hash, ".", logger);
+ if (not result_tree) {
+ return unexpected{
+ fmt::format("While retrieving tree id of commit {}:\n{}",
+ *commit_hash,
+ std::move(err))};
+ }
+ return *std::move(result_tree);
+}
+
auto GitRepo::IsRepoFake() const noexcept -> bool {
return is_repo_fake_;
}
diff --git a/src/buildtool/file_system/git_repo.hpp b/src/buildtool/file_system/git_repo.hpp
index 798f9ffb..1e403fbe 100644
--- a/src/buildtool/file_system/git_repo.hpp
+++ b/src/buildtool/file_system/git_repo.hpp
@@ -18,6 +18,7 @@
#include <filesystem>
#include <functional>
#include <memory>
+#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
@@ -315,6 +316,20 @@ class GitRepo {
[[nodiscard]] auto GetConfigSnapshot() const noexcept
-> std::shared_ptr<git_config>;
+ /// \brief Import source directory to target git repository
+ /// \param storage_config Storage where the source must be imported to
+ /// \param source_dir Directory to import
+ /// \param commit_message Message of the commit
+ /// \param tagging_lock Mutex to protect critical git operations
+ /// \return The tree id of the commited directory on success or an
+ /// unexpected error as string on failure.
+ [[nodiscard]] static auto ImportToGit(
+ StorageConfig const& storage_config,
+ std::filesystem::path const& source_dir,
+ std::string const& commit_message,
+ gsl::not_null<std::mutex*> const& tagging_lock) noexcept
+ -> expected<std::string, std::string>;
+
private:
GitCASPtr git_cas_;
// default to real repo, as that is non-thread-safe