diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/buildtool/file_system/TARGETS | 16 | ||||
-rw-r--r-- | src/buildtool/file_system/git_cas.cpp | 23 | ||||
-rw-r--r-- | src/buildtool/file_system/git_cas.hpp | 9 | ||||
-rw-r--r-- | src/buildtool/file_system/git_repo.cpp | 320 | ||||
-rw-r--r-- | src/buildtool/file_system/git_repo.hpp | 13 | ||||
-rw-r--r-- | src/buildtool/file_system/git_utils.cpp | 99 | ||||
-rw-r--r-- | src/buildtool/file_system/git_utils.hpp | 60 |
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 |