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.cpp23
-rw-r--r--src/buildtool/file_system/git_cas.hpp9
-rw-r--r--src/buildtool/file_system/git_repo.cpp320
-rw-r--r--src/buildtool/file_system/git_repo.hpp13
-rw-r--r--src/buildtool/file_system/git_utils.cpp99
-rw-r--r--src/buildtool/file_system/git_utils.hpp60
7 files changed, 351 insertions, 189 deletions
diff --git a/src/buildtool/file_system/TARGETS b/src/buildtool/file_system/TARGETS
index ad9838b9..bb1d3503 100644
--- a/src/buildtool/file_system/TARGETS
+++ b/src/buildtool/file_system/TARGETS
@@ -29,7 +29,12 @@
, "name": ["git_cas"]
, "hdrs": ["git_cas.hpp"]
, "srcs": ["git_cas.cpp"]
- , "deps": ["object_type", "git_context", ["@", "gsl-lite", "", "gsl-lite"]]
+ , "deps":
+ [ "object_type"
+ , "git_context"
+ , "git_utils"
+ , ["@", "gsl-lite", "", "gsl-lite"]
+ ]
, "stage": ["src", "buildtool", "file_system"]
, "private-deps":
[ ["src/buildtool/logging", "logging"]
@@ -78,6 +83,15 @@
, ["src/utils/cpp", "hex_string"]
]
}
+, "git_utils":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["git_utils"]
+ , "hdrs": ["git_utils.hpp"]
+ , "srcs": ["git_utils.cpp"]
+ , "deps": [["@", "gsl-lite", "", "gsl-lite"]]
+ , "stage": ["src", "buildtool", "file_system"]
+ , "private-deps": [["", "libgit2"]]
+ }
, "file_root":
{ "type": ["@", "rules", "CC", "library"]
, "name": ["file_root"]
diff --git a/src/buildtool/file_system/git_cas.cpp b/src/buildtool/file_system/git_cas.cpp
index d0bda197..efb464ef 100644
--- a/src/buildtool/file_system/git_cas.cpp
+++ b/src/buildtool/file_system/git_cas.cpp
@@ -104,21 +104,12 @@ auto GitCAS::Open(std::filesystem::path const& repo_path) noexcept
return nullptr;
}
-GitCAS::~GitCAS() noexcept {
-#ifndef BOOTSTRAP_BUILD_TOOL
- if (odb_ != nullptr) {
- git_odb_free(odb_);
- odb_ = nullptr;
- }
-#endif
-}
-
auto GitCAS::ReadObject(std::string const& id, bool is_hex_id) const noexcept
-> std::optional<std::string> {
#ifdef BOOTSTRAP_BUILD_TOOL
return std::nullopt;
#else
- if (odb_ == nullptr) {
+ if (not odb_) {
return std::nullopt;
}
@@ -128,7 +119,7 @@ auto GitCAS::ReadObject(std::string const& id, bool is_hex_id) const noexcept
}
git_odb_object* obj = nullptr;
- if (git_odb_read(&obj, odb_, &oid.value()) != 0) {
+ if (git_odb_read(&obj, odb_.get(), &oid.value()) != 0) {
Logger::Log(LogLevel::Error,
"reading git object {} from database failed with:\n{}",
is_hex_id ? id : ToHexString(id),
@@ -147,7 +138,7 @@ auto GitCAS::ReadObject(std::string const& id, bool is_hex_id) const noexcept
auto GitCAS::ReadHeader(std::string const& id, bool is_hex_id) const noexcept
-> std::optional<std::pair<std::size_t, ObjectType>> {
#ifndef BOOTSTRAP_BUILD_TOOL
- if (odb_ == nullptr) {
+ if (not odb_) {
return std::nullopt;
}
@@ -158,7 +149,7 @@ auto GitCAS::ReadHeader(std::string const& id, bool is_hex_id) const noexcept
std::size_t size{};
git_object_t type{};
- if (git_odb_read_header(&size, &type, odb_, &oid.value()) != 0) {
+ if (git_odb_read_header(&size, &type, odb_.get(), &oid.value()) != 0) {
Logger::Log(LogLevel::Error,
"reading git object header {} from database failed "
"with:\n{}",
@@ -189,14 +180,16 @@ auto GitCAS::OpenODB(std::filesystem::path const& repo_path) noexcept -> bool {
GitLastError());
return false;
}
- git_repository_odb(&odb_, repo);
+ git_odb* odb_ptr{nullptr};
+ git_repository_odb(&odb_ptr, repo);
+ odb_.reset(odb_ptr); // retain odb pointer
// set root
git_path_ = std::filesystem::weakly_canonical(std::filesystem::absolute(
std::filesystem::path(git_repository_path(repo))));
// release resources
git_repository_free(repo);
}
- if (odb_ == nullptr) {
+ if (not odb_) {
Logger::Log(LogLevel::Error,
"obtaining git object database {} failed with:\n{}",
repo_path.string(),
diff --git a/src/buildtool/file_system/git_cas.hpp b/src/buildtool/file_system/git_cas.hpp
index 0d637c5e..58e07616 100644
--- a/src/buildtool/file_system/git_cas.hpp
+++ b/src/buildtool/file_system/git_cas.hpp
@@ -22,12 +22,9 @@
#include <vector>
#include "src/buildtool/file_system/git_context.hpp"
+#include "src/buildtool/file_system/git_utils.hpp"
#include "src/buildtool/file_system/object_type.hpp"
-extern "C" {
-using git_odb = struct git_odb;
-}
-
class GitCAS;
using GitCASPtr = std::shared_ptr<GitCAS const>;
@@ -38,7 +35,7 @@ class GitCAS {
-> GitCASPtr;
GitCAS() noexcept = default;
- ~GitCAS() noexcept;
+ ~GitCAS() noexcept = default;
// prohibit moves and copies
GitCAS(GitCAS const&) = delete;
@@ -67,7 +64,7 @@ class GitCAS {
private:
// IMPORTANT: the GitContext needs to be initialized before any git object!
GitContext git_context_{}; // maintains a Git context while CAS is alive
- git_odb* odb_{nullptr};
+ std::unique_ptr<git_odb, decltype(&odb_closer)> odb_{nullptr, odb_closer};
// git folder path of repo; used for logging
std::filesystem::path git_path_{};
diff --git a/src/buildtool/file_system/git_repo.cpp b/src/buildtool/file_system/git_repo.cpp
index 0aae1176..e51c404e 100644
--- a/src/buildtool/file_system/git_repo.cpp
+++ b/src/buildtool/file_system/git_repo.cpp
@@ -146,18 +146,6 @@ constexpr std::size_t kOIDHexSize{GIT_OID_HEXSZ};
}
#endif
-auto const tree_closer = [](gsl::owner<git_tree*> tree) {
- if (tree != nullptr) {
- git_tree_free(tree);
- }
-};
-
-auto const treebuilder_closer = [](gsl::owner<git_treebuilder*> builder) {
- if (builder != nullptr) {
- git_treebuilder_free(builder);
- }
-};
-
[[nodiscard]] auto flat_tree_walker(const char* /*root*/,
const git_tree_entry* entry,
void* payload) noexcept -> int {
@@ -345,7 +333,7 @@ auto GitRepo::Open(GitCASPtr git_cas) noexcept -> std::optional<GitRepo> {
return std::nullopt;
#else
auto repo = GitRepo(std::move(git_cas));
- if (repo.repo_ == nullptr) {
+ if (not repo.repo_) {
return std::nullopt;
}
return repo;
@@ -358,7 +346,7 @@ auto GitRepo::Open(std::filesystem::path const& repo_path) noexcept
return std::nullopt;
#else
auto repo = GitRepo(repo_path);
- if (repo.repo_ == nullptr) {
+ if (not repo.repo_) {
return std::nullopt;
}
return repo;
@@ -368,13 +356,14 @@ auto GitRepo::Open(std::filesystem::path const& repo_path) noexcept
GitRepo::GitRepo(GitCASPtr git_cas) noexcept {
#ifndef BOOTSTRAP_BUILD_TOOL
if (git_cas != nullptr) {
- if (git_repository_wrap_odb(&repo_, git_cas->odb_) != 0) {
+ git_repository* repo_ptr{nullptr};
+ if (git_repository_wrap_odb(&repo_ptr, git_cas->odb_.get()) != 0) {
Logger::Log(LogLevel::Error,
"could not create wrapper for git repository");
- git_repository_free(repo_);
- repo_ = nullptr;
+ git_repository_free(repo_ptr);
return;
}
+ repo_.reset(repo_ptr); // retain repo
is_repo_fake_ = true;
git_cas_ = std::move(git_cas);
}
@@ -392,51 +381,54 @@ GitRepo::GitRepo(std::filesystem::path const& repo_path) noexcept {
std::unique_lock lock{repo_mutex};
auto cas = std::make_shared<GitCAS>();
// open repo, but retain it
- if (git_repository_open(&repo_, repo_path.c_str()) != 0) {
+ git_repository* repo_ptr{nullptr};
+ if (git_repository_open(&repo_ptr, repo_path.c_str()) != 0) {
Logger::Log(LogLevel::Error,
"opening git repository {} failed with:\n{}",
repo_path.string(),
GitLastError());
- git_repository_free(repo_);
+ git_repository_free(repo_ptr);
repo_ = nullptr;
return;
}
+ repo_.reset(repo_ptr); // retain repo pointer
// get odb
- git_repository_odb(&cas->odb_, repo_);
- if (cas->odb_ == nullptr) {
+ git_odb* odb_ptr{nullptr};
+ git_repository_odb(&odb_ptr, repo_.get());
+ if (odb_ptr == nullptr) {
Logger::Log(LogLevel::Error,
"retrieving odb of git repository {} failed with:\n{}",
repo_path.string(),
GitLastError());
- git_repository_free(repo_);
- repo_ = nullptr;
+ // release resources
+ git_odb_free(odb_ptr);
return;
}
+ cas->odb_.reset(odb_ptr); // retain odb pointer
is_repo_fake_ = false;
// save root path
cas->git_path_ = ToNormalPath(std::filesystem::absolute(
- std::filesystem::path(git_repository_path(repo_))));
+ std::filesystem::path(git_repository_path(repo_.get()))));
// retain the pointer
git_cas_ = std::static_pointer_cast<GitCAS const>(cas);
} catch (std::exception const& ex) {
Logger::Log(LogLevel::Error,
"opening git object database failed with:\n{}",
ex.what());
- repo_ = nullptr;
}
#endif // BOOTSTRAP_BUILD_TOOL
}
GitRepo::GitRepo(GitRepo&& other) noexcept
: git_cas_{std::move(other.git_cas_)},
- repo_{other.repo_},
+ repo_{std::move(other.repo_)},
is_repo_fake_{other.is_repo_fake_} {
- other.repo_ = nullptr;
+ other.git_cas_ = nullptr;
}
auto GitRepo::operator=(GitRepo&& other) noexcept -> GitRepo& {
git_cas_ = std::move(other.git_cas_);
- repo_ = other.repo_;
+ repo_ = std::move(other.repo_);
is_repo_fake_ = other.is_repo_fake_;
other.git_cas_ = nullptr;
return *this;
@@ -506,40 +498,42 @@ auto GitRepo::StageAndCommitAllAnonymous(std::string const& message,
return std::nullopt;
}
// add all files to be staged
- git_index* index = nullptr;
- git_repository_index(&index, repo_);
- git_strarray array{};
- PopulateStrarray(&array, {"."});
+ git_index* index_ptr{nullptr};
+ git_repository_index(&index_ptr, repo_.get());
+ auto index = std::unique_ptr<git_index, decltype(&index_closer)>(
+ index_ptr, index_closer);
- if (git_index_add_all(index, &array, 0, nullptr, nullptr) != 0) {
+ git_strarray array_obj{};
+ PopulateStrarray(&array_obj, {"."});
+ auto array = std::unique_ptr<git_strarray, decltype(&strarray_deleter)>(
+ &array_obj, strarray_deleter);
+
+ if (git_index_add_all(index.get(), array.get(), 0, nullptr, nullptr) !=
+ 0) {
(*logger)(fmt::format(
"staging files in git repository {} failed with:\n{}",
GetGitCAS()->git_path_.string(),
GitLastError()),
true /*fatal*/);
- // cleanup resources
- git_index_free(index);
- git_strarray_dispose(&array);
return std::nullopt;
}
// release unused resources
- git_strarray_dispose(&array);
+ array.reset(nullptr);
// build tree from staged files
git_oid tree_oid;
- if (git_index_write_tree(&tree_oid, index) != 0) {
+ if (git_index_write_tree(&tree_oid, index.get()) != 0) {
(*logger)(fmt::format("building tree from index in git repository "
"{} failed with:\n{}",
GetGitCAS()->git_path_.string(),
GitLastError()),
true /*fatal*/);
- // cleanup resources
- git_index_free(index);
return std::nullopt;
}
+
// set committer signature
- git_signature* signature = nullptr;
+ git_signature* signature_ptr{nullptr};
if (git_signature_new(
- &signature, "Nobody", "nobody@example.org", 0, 0) != 0) {
+ &signature_ptr, "Nobody", "nobody@example.org", 0, 0) != 0) {
(*logger)(
fmt::format("creating signature in git repository {} failed "
"with:\n{}",
@@ -547,56 +541,53 @@ auto GitRepo::StageAndCommitAllAnonymous(std::string const& message,
GitLastError()),
true /*fatal*/);
// cleanup resources
- git_signature_free(signature);
- git_index_free(index);
+ git_signature_free(signature_ptr);
return std::nullopt;
}
+ auto signature =
+ std::unique_ptr<git_signature, decltype(&signature_closer)>(
+ signature_ptr, signature_closer);
+
// get tree object
- git_tree* tree = nullptr;
- if (git_tree_lookup(&tree, repo_, &tree_oid) != 0) {
+ git_tree* tree_ptr = nullptr;
+ if (git_tree_lookup(&tree_ptr, repo_.get(), &tree_oid) != 0) {
(*logger)(
fmt::format("tree lookup in git repository {} failed with:\n{}",
GetGitCAS()->git_path_.string(),
GitLastError()),
true /*fatal*/);
// cleanup resources
- git_tree_free(tree);
- git_signature_free(signature);
- git_index_free(index);
+ git_tree_free(tree_ptr);
return std::nullopt;
}
+ auto tree = std::unique_ptr<git_tree, decltype(&tree_closer)>(
+ tree_ptr, tree_closer);
+
// commit the tree containing the staged files
- git_buf buffer{};
+ git_buf buffer = GIT_BUF_INIT_CONST(NULL, 0);
git_message_prettify(&buffer, message.c_str(), 0, '#');
+
git_oid commit_oid;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
if (git_commit_create_v(&commit_oid,
- repo_,
+ repo_.get(),
"HEAD",
- signature,
- signature,
+ signature.get(),
+ signature.get(),
nullptr,
buffer.ptr,
- tree,
+ tree.get(),
0) != 0) {
(*logger)(
fmt::format("git commit in repository {} failed with:\n{}",
GetGitCAS()->git_path_.string(),
GitLastError()),
true /*fatal*/);
- // cleanup resources
git_buf_dispose(&buffer);
- git_tree_free(tree);
- git_signature_free(signature);
- git_index_free(index);
return std::nullopt;
}
std::string commit_hash{git_oid_tostr_s(&commit_oid)};
- // release resources
git_buf_dispose(&buffer);
- git_tree_free(tree);
- git_signature_free(signature);
- git_index_free(index);
return commit_hash; // success!
} catch (std::exception const& ex) {
Logger::Log(LogLevel::Error,
@@ -621,20 +612,25 @@ auto GitRepo::KeepTag(std::string const& commit,
return false;
}
// get commit spec
- git_object* target = nullptr;
- if (git_revparse_single(&target, repo_, commit.c_str()) != 0) {
+ git_object* target_ptr{nullptr};
+ if (git_revparse_single(&target_ptr, repo_.get(), commit.c_str()) !=
+ 0) {
(*logger)(fmt::format("rev-parse commit {} in repository {} failed "
"with:\n{}",
commit,
- git_repository_path(repo_),
+ GetGitCAS()->git_path_.string(),
GitLastError()),
true /*fatal*/);
+ git_object_free(target_ptr);
return false;
}
+ auto target = std::unique_ptr<git_object, decltype(&object_closer)>(
+ target_ptr, object_closer);
+
// set tagger signature
- git_signature* tagger = nullptr;
- if (git_signature_new(&tagger, "Nobody", "nobody@example.org", 0, 0) !=
- 0) {
+ git_signature* tagger_ptr{nullptr};
+ if (git_signature_new(
+ &tagger_ptr, "Nobody", "nobody@example.org", 0, 0) != 0) {
(*logger)(
fmt::format("creating signature in git repository {} failed "
"with:\n{}",
@@ -642,10 +638,13 @@ auto GitRepo::KeepTag(std::string const& commit,
GitLastError()),
true /*fatal*/);
// cleanup resources
- git_signature_free(tagger);
- git_object_free(target);
+ git_signature_free(tagger_ptr);
return false;
}
+ auto tagger =
+ std::unique_ptr<git_signature, decltype(&signature_closer)>(
+ tagger_ptr, signature_closer);
+
// create tag
git_oid oid;
auto name = fmt::format("keep-{}", commit);
@@ -656,17 +655,18 @@ auto GitRepo::KeepTag(std::string const& commit,
while (max_attempts > 0) {
--max_attempts;
err = git_tag_create(&oid,
- repo_,
+ repo_.get(),
name.c_str(),
- target,
- tagger,
+ target.get(),
+ tagger.get(),
message.c_str(),
1 /*force*/);
if (err == 0) {
return true; // success!
}
// check if tag hasn't already been added by another process
- if (git_tag_list_match(&tag_names, name.c_str(), repo_) == 0 and
+ if (git_tag_list_match(&tag_names, name.c_str(), repo_.get()) ==
+ 0 and
tag_names.count > 0) {
git_strarray_dispose(&tag_names);
return true; // success!
@@ -699,7 +699,7 @@ auto GitRepo::GetHeadCommit(anon_logger_ptr const& logger) noexcept
}
// get root commit id
git_oid head_oid;
- if (git_reference_name_to_id(&head_oid, repo_, "HEAD") != 0) {
+ if (git_reference_name_to_id(&head_oid, repo_.get(), "HEAD") != 0) {
(*logger)(fmt::format("retrieving head commit in git repository {} "
"failed with:\n{}",
GetGitCAS()->git_path_.string(),
@@ -732,7 +732,8 @@ auto GitRepo::GetBranchLocalRefname(std::string const& branch,
// get local reference of branch
git_reference* local_ref = nullptr;
if (git_branch_lookup(
- &local_ref, repo_, branch.c_str(), GIT_BRANCH_LOCAL) != 0) {
+ &local_ref, repo_.get(), branch.c_str(), GIT_BRANCH_LOCAL) !=
+ 0) {
(*logger)(fmt::format("retrieving branch {} local reference in git "
"repository {} failed with:\n{}",
branch,
@@ -771,23 +772,29 @@ auto GitRepo::GetCommitFromRemote(std::string const& repo_url,
return std::nullopt;
}
// create remote
- git_remote* remote = nullptr;
- if (git_remote_create_anonymous(&remote, repo_, repo_url.c_str()) !=
- 0) {
+ 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);
- if (git_remote_connect(
- remote, GIT_DIRECTION_FETCH, &callbacks, nullptr, nullptr) !=
- 0) {
+ if (git_remote_connect(remote.get(),
+ GIT_DIRECTION_FETCH,
+ &callbacks,
+ nullptr,
+ nullptr) != 0) {
(*logger)(
fmt::format("connecting to remote {} for git repository {} "
"failed with:\n{}",
@@ -795,22 +802,18 @@ auto GitRepo::GetCommitFromRemote(std::string const& repo_url,
GetGitCAS()->git_path_.string(),
GitLastError()),
true /*fatal*/);
- // cleanup resources
- git_remote_free(remote);
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) != 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*/);
- // cleanup resources
- git_remote_free(remote);
return std::nullopt;
}
// figure out what remote branch the local one is tracking
@@ -821,8 +824,6 @@ auto GitRepo::GetCommitFromRemote(std::string const& repo_url,
// branch found!
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
std::string new_commit_hash{git_oid_tostr_s(&refs[i]->oid)};
- // cleanup resources
- git_remote_free(remote);
return new_commit_hash;
}
}
@@ -832,8 +833,6 @@ auto GitRepo::GetCommitFromRemote(std::string const& repo_url,
repo_url,
GitLastError()),
true /*fatal*/);
- // cleanup resources
- git_remote_free(remote);
return std::nullopt;
} catch (std::exception const& ex) {
Logger::Log(LogLevel::Error,
@@ -858,9 +857,9 @@ auto GitRepo::FetchFromRemote(std::string const& repo_url,
return false;
}
// create remote from repo
- git_remote* remote = nullptr;
- if (git_remote_create_anonymous(&remote, repo_, repo_url.c_str()) !=
- 0) {
+ 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,
@@ -868,19 +867,26 @@ auto GitRepo::FetchFromRemote(std::string const& repo_url,
GitLastError()),
true /*fatal*/);
// cleanup resources
- git_remote_free(remote);
+ git_remote_free(remote_ptr);
return false;
}
+ auto remote = std::unique_ptr<git_remote, decltype(&remote_closer)>(
+ remote_ptr, remote_closer);
+
// setup fetch refspecs array
- git_strarray refspecs_array{};
+ git_strarray refspecs_array_obj{};
if (not refspec.empty()) {
- PopulateStrarray(&refspecs_array, {refspec});
+ PopulateStrarray(&refspecs_array_obj, {refspec});
}
+ auto refspecs_array =
+ std::unique_ptr<git_strarray, decltype(&strarray_deleter)>(
+ &refspecs_array_obj, strarray_deleter);
+
// do the fetch
git_fetch_options fetch_opts{};
git_fetch_init_options(&fetch_opts, GIT_FETCH_OPTIONS_VERSION);
- if (git_remote_fetch(remote,
- refspec.empty() ? nullptr : &refspecs_array,
+ if (git_remote_fetch(remote.get(),
+ refspec.empty() ? nullptr : refspecs_array.get(),
&fetch_opts,
nullptr) != 0) {
(*logger)(
@@ -890,14 +896,8 @@ auto GitRepo::FetchFromRemote(std::string const& repo_url,
GetGitCAS()->git_path_.string(),
GitLastError()),
true /*fatal*/);
- // cleanup resources
- git_remote_free(remote);
- git_strarray_dispose(&refspecs_array);
return false;
}
- // cleanup resources
- git_remote_free(remote);
- git_strarray_dispose(&refspecs_array);
return true; // success!
} catch (std::exception const& ex) {
Logger::Log(
@@ -925,8 +925,9 @@ auto GitRepo::GetSubtreeFromCommit(std::string const& commit,
// get commit object
git_oid commit_oid;
git_oid_fromstr(&commit_oid, commit.c_str());
- git_commit* commit_obj = nullptr;
- if (git_commit_lookup(&commit_obj, repo_, &commit_oid) != 0) {
+
+ git_commit* commit_ptr{nullptr};
+ if (git_commit_lookup(&commit_ptr, repo_.get(), &commit_oid) != 0) {
(*logger)(fmt::format("retrieving commit {} in git repository {} "
"failed with:\n{}",
commit,
@@ -934,12 +935,15 @@ auto GitRepo::GetSubtreeFromCommit(std::string const& commit,
GitLastError()),
true /*fatal*/);
// cleanup resources
- git_commit_free(commit_obj);
+ git_commit_free(commit_ptr);
return std::nullopt;
}
+ auto commit_obj = std::unique_ptr<git_commit, decltype(&commit_closer)>(
+ commit_ptr, commit_closer);
+
// get tree of commit
- git_tree* tree = nullptr;
- if (git_commit_tree(&tree, commit_obj) != 0) {
+ git_tree* tree_ptr{nullptr};
+ if (git_commit_tree(&tree_ptr, commit_obj.get()) != 0) {
(*logger)(fmt::format(
"retrieving tree for commit {} in git repository {} "
"failed with:\n{}",
@@ -948,15 +952,17 @@ auto GitRepo::GetSubtreeFromCommit(std::string const& commit,
GitLastError()),
true /*fatal*/);
// cleanup resources
- git_tree_free(tree);
- git_commit_free(commit_obj);
+ git_tree_free(tree_ptr);
return std::nullopt;
}
+ auto tree = std::unique_ptr<git_tree, decltype(&tree_closer)>(
+ tree_ptr, tree_closer);
+
if (subdir != ".") {
// get hash for actual subdir
- git_tree_entry* subtree_entry = nullptr;
- if (git_tree_entry_bypath(&subtree_entry, tree, subdir.c_str()) !=
- 0) {
+ git_tree_entry* subtree_entry_ptr{nullptr};
+ if (git_tree_entry_bypath(
+ &subtree_entry_ptr, tree.get(), subdir.c_str()) != 0) {
(*logger)(
fmt::format("retrieving subtree at {} in git repository "
"{} failed with:\n{}",
@@ -965,24 +971,19 @@ auto GitRepo::GetSubtreeFromCommit(std::string const& commit,
GitLastError()),
true /*fatal*/);
// cleanup resources
- git_tree_entry_free(subtree_entry);
- git_tree_free(tree);
- git_commit_free(commit_obj);
+ git_tree_entry_free(subtree_entry_ptr);
return std::nullopt;
}
+ auto subtree_entry =
+ std::unique_ptr<git_tree_entry, decltype(&tree_entry_closer)>(
+ subtree_entry_ptr, tree_entry_closer);
+
std::string subtree_hash{
- git_oid_tostr_s(git_tree_entry_id(subtree_entry))};
- // cleanup resources
- git_tree_entry_free(subtree_entry);
- git_tree_free(tree);
- git_commit_free(commit_obj);
+ git_oid_tostr_s(git_tree_entry_id(subtree_entry.get()))};
return subtree_hash;
}
// if no subdir, get hash from tree
- std::string tree_hash{git_oid_tostr_s(git_tree_id(tree))};
- // cleanup resources
- git_tree_free(tree);
- git_commit_free(commit_obj);
+ std::string tree_hash{git_oid_tostr_s(git_tree_id(tree.get()))};
return tree_hash;
} catch (std::exception const& ex) {
Logger::Log(LogLevel::Error,
@@ -1013,8 +1014,9 @@ auto GitRepo::GetSubtreeFromTree(std::string const& tree_id,
// get tree object from tree id
git_oid tree_oid;
git_oid_fromstr(&tree_oid, tree_id.c_str());
- git_tree* tree = nullptr;
- if (git_tree_lookup(&tree, repo_, &tree_oid) != 0) {
+
+ git_tree* tree_ptr{nullptr};
+ if (git_tree_lookup(&tree_ptr, repo_.get(), &tree_oid) != 0) {
(*logger)(fmt::format(
"retrieving tree {} in git repository {} failed "
"with:\n{}",
@@ -1023,14 +1025,16 @@ auto GitRepo::GetSubtreeFromTree(std::string const& tree_id,
GitLastError()),
true /*fatal*/);
// cleanup resources
- git_tree_free(tree);
+ git_tree_free(tree_ptr);
return std::nullopt;
}
+ auto tree = std::unique_ptr<git_tree, decltype(&tree_closer)>(
+ tree_ptr, tree_closer);
// get hash for actual subdir
- git_tree_entry* subtree_entry = nullptr;
- if (git_tree_entry_bypath(&subtree_entry, tree, subdir.c_str()) !=
- 0) {
+ git_tree_entry* subtree_entry_ptr{nullptr};
+ if (git_tree_entry_bypath(
+ &subtree_entry_ptr, tree.get(), subdir.c_str()) != 0) {
(*logger)(
fmt::format("retrieving subtree at {} in git repository "
"{} failed with:\n{}",
@@ -1039,15 +1043,15 @@ auto GitRepo::GetSubtreeFromTree(std::string const& tree_id,
GitLastError()),
true /*fatal*/);
// cleanup resources
- git_tree_entry_free(subtree_entry);
- git_tree_free(tree);
+ git_tree_entry_free(subtree_entry_ptr);
return std::nullopt;
}
+ auto subtree_entry =
+ std::unique_ptr<git_tree_entry, decltype(&tree_entry_closer)>(
+ subtree_entry_ptr, tree_entry_closer);
+
std::string subtree_hash{
- git_oid_tostr_s(git_tree_entry_id(subtree_entry))};
- // cleanup resources
- git_tree_entry_free(subtree_entry);
- git_tree_free(tree);
+ git_oid_tostr_s(git_tree_entry_id(subtree_entry.get()))};
return subtree_hash;
}
// if no subdir, return given tree hash
@@ -1126,7 +1130,8 @@ auto GitRepo::CheckCommitExists(std::string const& commit,
git_oid commit_oid;
git_oid_fromstr(&commit_oid, commit.c_str());
git_commit* commit_obj = nullptr;
- auto lookup_res = git_commit_lookup(&commit_obj, repo_, &commit_oid);
+ auto lookup_res =
+ git_commit_lookup(&commit_obj, repo_.get(), &commit_oid);
if (lookup_res != 0) {
if (lookup_res == GIT_ENOTFOUND) {
// cleanup resources
@@ -1215,9 +1220,9 @@ auto GitRepo::FetchViaTmpRepo(std::filesystem::path const& tmp_repo_path,
return false;
}
// add backend, with max priority
- FetchIntoODBBackend b{kFetchIntoODBParent, git_cas_->odb_};
+ FetchIntoODBBackend b{kFetchIntoODBParent, git_cas_->odb_.get()};
if (git_odb_add_backend(
- tmp_repo->GetGitCAS()->odb_,
+ tmp_repo->GetGitCAS()->odb_.get(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
reinterpret_cast<git_odb_backend*>(&b),
std::numeric_limits<int>::max()) == 0) {
@@ -1251,9 +1256,9 @@ auto GitRepo::GetRepoRootFromPath(std::filesystem::path const& fpath,
git_buf buffer = GIT_BUF_INIT_CONST(NULL, 0);
auto res = git_repository_discover(&buffer, fpath.c_str(), 0, nullptr);
+
if (res != 0) {
if (res == GIT_ENOTFOUND) {
- // cleanup resources
git_buf_dispose(&buffer);
return std::filesystem::path{}; // empty path cause nothing
// found
@@ -1264,13 +1269,11 @@ auto GitRepo::GetRepoRootFromPath(std::filesystem::path const& fpath,
fpath.string(),
GitLastError()),
true /*fatal*/);
- // cleanup resources
git_buf_dispose(&buffer);
return std::nullopt;
}
// found root repo path
std::string result{buffer.ptr};
- // cleanup resources
git_buf_dispose(&buffer);
// normalize root result
auto actual_root =
@@ -1288,11 +1291,6 @@ auto GitRepo::GetRepoRootFromPath(std::filesystem::path const& fpath,
#endif // BOOTSTRAP_BUILD_TOOL
}
-GitRepo::~GitRepo() noexcept {
- // release resources
- git_repository_free(repo_);
-}
-
auto GitRepo::IsRepoFake() const noexcept -> bool {
return is_repo_fake_;
}
@@ -1310,14 +1308,14 @@ auto GitRepo::ReadTree(std::string const& id, bool is_hex_id) const noexcept
// lookup tree
git_tree* tree_ptr{nullptr};
- if (git_tree_lookup(&tree_ptr, repo_, &(*oid)) != 0) {
+ if (git_tree_lookup(&tree_ptr, repo_.get(), &(*oid)) != 0) {
Logger::Log(LogLevel::Debug,
"failed to lookup Git tree {}",
is_hex_id ? std::string{id} : ToHexString(id));
return std::nullopt;
}
- auto tree =
- std::unique_ptr<git_tree, decltype(tree_closer)>{tree_ptr, tree_closer};
+ auto tree = std::unique_ptr<git_tree, decltype(&tree_closer)>{tree_ptr,
+ tree_closer};
// walk tree (flat) and create entries
tree_entries_t entries{};
@@ -1348,12 +1346,12 @@ auto GitRepo::CreateTree(tree_entries_t const& entries) const noexcept
#endif // NDEBUG
git_treebuilder* builder_ptr{nullptr};
- if (git_treebuilder_new(&builder_ptr, repo_, nullptr) != 0) {
+ if (git_treebuilder_new(&builder_ptr, repo_.get(), nullptr) != 0) {
Logger::Log(LogLevel::Debug, "failed to create Git tree builder");
return std::nullopt;
}
auto builder =
- std::unique_ptr<git_treebuilder, decltype(treebuilder_closer)>{
+ std::unique_ptr<git_treebuilder, decltype(&treebuilder_closer)>{
builder_ptr, treebuilder_closer};
for (auto const& [raw_id, es] : entries) {
@@ -1401,11 +1399,13 @@ auto GitRepo::ReadTreeData(std::string const& data,
return std::nullopt;
}
// create a GitCAS from a special-purpose in-memory object database.
- if (git_odb_new(&cas->odb_) == 0 and
+ git_odb* odb_ptr{nullptr};
+ if (git_odb_new(&odb_ptr) == 0 and
git_odb_add_backend(
- cas->odb_,
+ odb_ptr,
reinterpret_cast<git_odb_backend*>(&b), // NOLINT
0) == 0) {
+ cas->odb_.reset(odb_ptr); // take ownership of odb
// wrap odb in "fake" repo
auto repo =
GitRepo(std::static_pointer_cast<GitCAS const>(cas));
@@ -1427,11 +1427,13 @@ auto GitRepo::CreateShallowTree(tree_entries_t const& entries) noexcept
InMemoryODBBackend b{kInMemoryODBParent, &entries};
auto cas = std::make_shared<GitCAS>();
// create a GitCAS from a special-purpose in-memory object database.
- if (git_odb_new(&cas->odb_) == 0 and
+ git_odb* odb_ptr{nullptr};
+ if (git_odb_new(&odb_ptr) == 0 and
git_odb_add_backend(
- cas->odb_,
+ odb_ptr,
reinterpret_cast<git_odb_backend*>(&b), // NOLINT
0) == 0) {
+ cas->odb_.reset(odb_ptr); // take ownership of odb
// wrap odb in "fake" repo
auto repo = GitRepo(std::static_pointer_cast<GitCAS const>(cas));
if (auto raw_id = repo.CreateTree(entries)) {
diff --git a/src/buildtool/file_system/git_repo.hpp b/src/buildtool/file_system/git_repo.hpp
index a9ef232a..ecd3c776 100644
--- a/src/buildtool/file_system/git_repo.hpp
+++ b/src/buildtool/file_system/git_repo.hpp
@@ -17,11 +17,6 @@
#include "src/buildtool/file_system/git_cas.hpp"
-extern "C" {
-using git_repository = struct git_repository;
-using git_strarray = struct git_strarray;
-}
-
/// \brief Git repository logic.
/// Models both a real repository, owning the underlying ODB
/// (non-thread-safe), as well as a "fake" repository, which only wraps an
@@ -48,6 +43,7 @@ class GitRepo {
std::unordered_map<std::string, std::vector<tree_entry_t>>;
GitRepo() = delete; // no default ctor
+ ~GitRepo() noexcept = default;
// allow only move, no copy
GitRepo(GitRepo const&) = delete;
@@ -246,11 +242,12 @@ class GitRepo {
anon_logger_ptr const& logger) noexcept
-> std::optional<std::filesystem::path>;
- ~GitRepo() noexcept;
-
private:
+ // IMPORTANT! The GitCAS object must be defined before the repo object to
+ // keep the GitContext alive until cleanup ends.
GitCASPtr git_cas_{nullptr};
- git_repository* repo_{nullptr};
+ std::unique_ptr<git_repository, decltype(&repo_closer)> repo_{nullptr,
+ repo_closer};
// default to real repo, as that is non-thread-safe
bool is_repo_fake_{false};
diff --git a/src/buildtool/file_system/git_utils.cpp b/src/buildtool/file_system/git_utils.cpp
new file mode 100644
index 00000000..ec3b5f6d
--- /dev/null
+++ b/src/buildtool/file_system/git_utils.cpp
@@ -0,0 +1,99 @@
+// Copyright 2022 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/buildtool/file_system/git_utils.hpp"
+
+extern "C" {
+#include <git2.h>
+}
+
+void repo_closer(gsl::owner<git_repository*> repo) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_repository_free(repo);
+#endif
+}
+
+void odb_closer(gsl::owner<git_odb*> odb) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_odb_free(odb);
+#endif
+}
+
+void tree_closer(gsl::owner<git_tree*> tree) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_tree_free(tree);
+#endif
+}
+
+void treebuilder_closer(gsl::owner<git_treebuilder*> builder) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_treebuilder_free(builder);
+#endif
+}
+
+void index_closer(gsl::owner<git_index*> index) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_index_free(index);
+#endif
+}
+
+void strarray_closer(gsl::owner<git_strarray*> strarray) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_strarray_dispose(strarray);
+#endif
+}
+
+void strarray_deleter(gsl::owner<git_strarray*> strarray) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ if (strarray->strings != nullptr) {
+ for (size_t i = 0; i < strarray->count; ++i) {
+ // NOLINTNEXTLINE(cppcoreguidelines-owning-memory,cppcoreguidelines-pro-bounds-pointer-arithmetic)
+ delete[] strarray->strings[i];
+ }
+ delete[] strarray->strings;
+ strarray->strings = nullptr;
+ strarray->count = 0;
+ }
+#endif
+}
+
+void signature_closer(gsl::owner<git_signature*> signature) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_signature_free(signature);
+#endif
+}
+
+void object_closer(gsl::owner<git_object*> object) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_object_free(object);
+#endif
+}
+
+void remote_closer(gsl::owner<git_remote*> remote) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_remote_free(remote);
+#endif
+}
+
+void commit_closer(gsl::owner<git_commit*> commit) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_commit_free(commit);
+#endif
+}
+
+void tree_entry_closer(gsl::owner<git_tree_entry*> tree_entry) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ git_tree_entry_free(tree_entry);
+#endif
+}
diff --git a/src/buildtool/file_system/git_utils.hpp b/src/buildtool/file_system/git_utils.hpp
new file mode 100644
index 00000000..74f21500
--- /dev/null
+++ b/src/buildtool/file_system/git_utils.hpp
@@ -0,0 +1,60 @@
+// Copyright 2022 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_BUILDTOOL_FILE_SYSTEM_GIT_UTILS_HPP
+#define INCLUDED_SRC_BUILDTOOL_FILE_SYSTEM_GIT_UTILS_HPP
+
+#include "gsl-lite/gsl-lite.hpp"
+
+extern "C" {
+struct git_odb;
+struct git_repository;
+struct git_tree;
+struct git_treebuilder;
+struct git_index;
+struct git_strarray;
+struct git_signature;
+struct git_object;
+struct git_remote;
+struct git_commit;
+struct git_tree_entry;
+}
+
+void repo_closer(gsl::owner<git_repository*> repo);
+
+void odb_closer(gsl::owner<git_odb*> odb);
+
+void tree_closer(gsl::owner<git_tree*> tree);
+
+void treebuilder_closer(gsl::owner<git_treebuilder*> builder);
+
+void index_closer(gsl::owner<git_index*> index);
+
+// to be used for strarrays allocated by libgit2
+void strarray_closer(gsl::owner<git_strarray*> strarray);
+
+// to be used for strarrays allocated manually
+void strarray_deleter(gsl::owner<git_strarray*> strarray);
+
+void signature_closer(gsl::owner<git_signature*> signature);
+
+void object_closer(gsl::owner<git_object*> object);
+
+void remote_closer(gsl::owner<git_remote*> remote);
+
+void commit_closer(gsl::owner<git_commit*> commit);
+
+void tree_entry_closer(gsl::owner<git_tree_entry*> tree_entry);
+
+#endif // INCLUDED_SRC_BUILDTOOL_FILE_SYSTEM_GIT_UTILS_HPP