#include "../wrapper/repository_wrapper.hpp"
#include
#include
#include
#include "../utils/git_exception.hpp"
#include "../wrapper/commit_wrapper.hpp"
#include "../wrapper/index_wrapper.hpp"
#include "../wrapper/object_wrapper.hpp"
#include "../wrapper/remote_wrapper.hpp"
#include "config_wrapper.hpp"
#include "diff_wrapper.hpp"
repository_wrapper::~repository_wrapper()
{
git_repository_free(p_resource);
p_resource = nullptr;
}
repository_wrapper repository_wrapper::open(std::string_view directory)
{
repository_wrapper rw;
throw_if_error(git_repository_open(&(rw.p_resource), directory.data()));
return rw;
}
repository_wrapper repository_wrapper::init(std::string_view directory, bool bare)
{
repository_wrapper rw;
throw_if_error(git_repository_init(&(rw.p_resource), directory.data(), bare));
return rw;
}
repository_wrapper repository_wrapper::init_ext(std::string_view directory, git_repository_init_options* opts)
{
repository_wrapper rw;
throw_if_error(git_repository_init_ext(&(rw.p_resource), directory.data(), opts));
return rw;
}
repository_wrapper
repository_wrapper::clone(std::string_view url, std::string_view path, const git_clone_options& opts)
{
repository_wrapper rw;
throw_if_error(git_clone(&(rw.p_resource), url.data(), path.data(), &opts));
return rw;
}
std::string repository_wrapper::path() const
{
return git_repository_path(*this);
}
git_repository_state_t repository_wrapper::state() const
{
return git_repository_state_t(git_repository_state(*this));
}
void repository_wrapper::state_cleanup()
{
throw_if_error(git_repository_state_cleanup(*this));
}
bool repository_wrapper::is_bare() const
{
return git_repository_is_bare(*this);
}
bool repository_wrapper::is_shallow() const
{
return git_repository_is_shallow(*this);
}
revwalk_wrapper repository_wrapper::new_walker()
{
git_revwalk* walker;
throw_if_error(git_revwalk_new(&walker, *this));
return revwalk_wrapper(walker);
}
bool repository_wrapper::does_track(std::string_view path) const
{
unsigned int flags;
throw_if_error(git_status_file(&flags, *this, path.data()));
return !(flags & GIT_STATUS_WT_NEW) && !(flags & GIT_STATUS_IGNORED);
}
// Head
bool repository_wrapper::is_head_unborn() const
{
return git_repository_head_unborn(*this) == 1;
}
reference_wrapper repository_wrapper::head() const
{
git_reference* ref;
throw_if_error(git_repository_head(&ref, *this));
return reference_wrapper(ref);
}
std::string repository_wrapper::head_short_name() const
{
git_reference* ref;
std::string name;
throw_if_error(git_reference_lookup(&ref, *this, "HEAD"));
if (git_reference_type(ref) == GIT_REFERENCE_DIRECT)
{
name = git_reference_shorthand(ref);
}
else
{
name = git_reference_symbolic_target(ref);
name = name.substr(name.find_last_of('/') + 1);
}
git_reference_free(ref);
return name;
}
// References
reference_wrapper repository_wrapper::find_reference(std::string_view ref_name) const
{
git_reference* ref;
throw_if_error(git_reference_lookup(&ref, *this, ref_name.data()));
return reference_wrapper(ref);
}
std::optional repository_wrapper::find_reference_dwim(std::string_view ref_name) const
{
git_reference* ref;
int rc = git_reference_dwim(&ref, *this, ref_name.data());
return rc == 0 ? std::make_optional(reference_wrapper(ref)) : std::nullopt;
}
// Index
index_wrapper repository_wrapper::make_index()
{
index_wrapper index = index_wrapper::init(*this);
return index;
}
// Branches
branch_wrapper repository_wrapper::create_branch(std::string_view name, bool force)
{
return create_branch(name, find_commit(), force);
}
branch_wrapper
repository_wrapper::create_branch(std::string_view name, const commit_wrapper& commit, bool force)
{
git_reference* branch = nullptr;
throw_if_error(git_branch_create(&branch, *this, name.data(), commit, force));
return branch_wrapper(branch);
}
branch_wrapper
repository_wrapper::create_branch(std::string_view name, const annotated_commit_wrapper& commit, bool force)
{
git_reference* branch = nullptr;
throw_if_error(git_branch_create_from_annotated(&branch, *this, name.data(), commit, force));
return branch_wrapper(branch);
}
branch_wrapper repository_wrapper::find_branch(std::string_view name) const
{
git_reference* branch = nullptr;
throw_if_error(git_branch_lookup(&branch, *this, name.data(), GIT_BRANCH_LOCAL));
return branch_wrapper(branch);
}
branch_iterator repository_wrapper::iterate_branches(git_branch_t type) const
{
git_branch_iterator* iter = nullptr;
throw_if_error(git_branch_iterator_new(&iter, *this, type));
return branch_iterator(iter);
}
std::optional repository_wrapper::upstream() const
{
git_reference* ref;
reference_wrapper head = this->head();
int error = git_branch_upstream(&ref, head);
if (error == 0)
{
return reference_wrapper(ref);
}
else
{
return std::nullopt;
}
}
branch_tracking_info repository_wrapper::get_tracking_info() const
{
branch_tracking_info info;
info.has_upstream = false;
info.ahead = 0;
info.behind = 0;
info.upstream_name = "";
if (this->is_head_unborn())
{
return info;
}
reference_wrapper head = this->head();
std::optional upstream = this->upstream();
if (upstream)
{
info.has_upstream = true;
info.upstream_name = upstream.value().short_name();
auto local_oid = head.target();
auto upstream_oid = upstream.value().target();
if (local_oid && upstream_oid)
{
git_graph_ahead_behind(&info.ahead, &info.behind, *this, local_oid, upstream_oid);
}
}
return info;
}
// Commits
commit_wrapper repository_wrapper::find_commit(std::string_view ref_name) const
{
git_oid oid_parent_commit;
throw_if_error(git_reference_name_to_id(&oid_parent_commit, *this, ref_name.data()));
return find_commit(oid_parent_commit);
}
commit_wrapper repository_wrapper::find_commit(const git_oid& id) const
{
git_commit* commit;
throw_if_error(git_commit_lookup(&commit, *this, &id));
return commit_wrapper(commit);
}
void repository_wrapper::create_commit(
const signature_wrapper::author_committer_signatures& author_committer_signatures,
const std::string_view message,
const std::optional& parents_list
)
{
const char* message_encoding = "UTF-8";
git_oid commit_id;
std::string update_ref = "HEAD";
const git_commit* placeholder[1] = {nullptr};
auto [parents, parents_count] = [&]() -> std::pair
{
if (parents_list)
{
// TODO: write a "as_const" function to replace the following
auto pl_size = parents_list.value().size();
git_commit** pl_value = parents_list.value();
auto pl_value_const = const_cast(pl_value);
return {pl_value_const, pl_size};
}
else
{
auto parent = revparse_single(update_ref);
size_t parents_count = 0;
if (parent)
{
parents_count = 1;
placeholder[0] = *parent;
}
return {placeholder, parents_count};
}
}();
index_wrapper index = this->make_index();
git_oid tree_id = index.write_tree();
index.write();
auto tree = this->tree_lookup(&tree_id);
throw_if_error(git_commit_create(
&commit_id,
*this,
update_ref.c_str(),
author_committer_signatures.first,
author_committer_signatures.second,
message_encoding,
message.data(),
tree,
parents_count,
parents
));
}
std::optional
repository_wrapper::resolve_local_ref(const std::string_view target_name) const
{
if (auto ref = this->find_reference_dwim(target_name))
{
return this->find_annotated_commit(*ref);
}
else if (auto obj = this->revparse_single(target_name))
{
return this->find_annotated_commit(obj->oid());
}
else
{
return std::nullopt;
}
}
// Annotated commits
annotated_commit_wrapper repository_wrapper::find_annotated_commit(const git_oid& id) const
{
git_annotated_commit* commit;
throw_if_error(git_annotated_commit_lookup(&commit, *this, &id));
return annotated_commit_wrapper(commit);
}
// Objects
std::optional repository_wrapper::revparse_single(std::string_view spec) const
{
git_object* obj;
int rc = git_revparse_single(&obj, *this, spec.data());
return rc == 0 ? std::make_optional(object_wrapper(obj)) : std::nullopt;
}
object_wrapper repository_wrapper::find_object(const git_oid id, git_object_t type)
{
git_object* object;
git_object_lookup(&object, *this, &id, type);
return object_wrapper(object);
}
// Head manipulations
void repository_wrapper::set_head(std::string_view ref_name)
{
throw_if_error(git_repository_set_head(*this, ref_name.data()));
}
void repository_wrapper::set_head_detached(const annotated_commit_wrapper& commit)
{
throw_if_error(git_repository_set_head_detached_from_annotated(*this, commit));
}
void repository_wrapper::reset(
const object_wrapper& target,
git_reset_t reset_type,
const git_checkout_options& checkout_options
)
{
// TODO: gerer l'index
throw_if_error(git_reset(*this, target, reset_type, &checkout_options));
}
size_t repository_wrapper::shallow_depth_from_head() const
{
if (!this->is_shallow())
{
return 0u;
}
std::string path = this->path();
std::string shallow_path = path + "shallow";
std::vector boundaries_list;
std::ifstream f(shallow_path);
std::string line;
while (std::getline(f, line))
{
if (!line.empty())
{
git_oid commit_oid;
git_oid_fromstrp(&commit_oid, line.c_str());
boundaries_list.push_back(commit_oid);
}
}
if (boundaries_list.size() == 0u)
{
return 0u;
}
commit_wrapper head_commit = this->find_commit("HEAD");
commit_list_wrapper commits_list = head_commit.get_parents_list();
std::vector depth_list(commits_list.size(), 1u);
std::vector final_depths(boundaries_list.size(), 1u);
bool has_parent = commits_list.size() > 0u;
while (has_parent)
{
has_parent = false;
std::vector temp_commits_list;
std::vector temp_depth_list;
commit_list_wrapper parent_list({});
for (size_t i = 0u; i < commits_list.size(); i++)
{
const commit_wrapper& commit = commits_list[i];
size_t depth = depth_list[i];
const git_oid& oid = commit.oid();
bool is_boundary = std::find_if(
boundaries_list.cbegin(),
boundaries_list.cend(),
[oid](const git_oid& val)
{
return git_oid_equal(&oid, &val);
}
)
!= boundaries_list.cend();
if (is_boundary)
{
final_depths.push_back(depth + 1u);
}
else
{
parent_list = commit.get_parents_list();
if (parent_list.size() > 0u)
{
has_parent = true;
for (size_t j = 0u; j < parent_list.size(); ++j)
{
const commit_wrapper& c = parent_list[j];
temp_commits_list.push_back(std::move(const_cast(c)));
temp_depth_list.push_back(depth + 1u);
}
}
}
}
depth_list = temp_depth_list;
commits_list = commit_list_wrapper(std::move(temp_commits_list));
}
std::size_t depth = *std::max_element(final_depths.begin(), final_depths.end());
return depth;
}
// Trees
void repository_wrapper::checkout_tree(const object_wrapper& target, const git_checkout_options opts)
{
throw_if_error(git_checkout_tree(*this, target, &opts));
}
tree_wrapper repository_wrapper::tree_lookup(const git_oid* tree_id)
{
git_tree* tree;
throw_if_error(git_tree_lookup(&tree, *this, tree_id));
return tree_wrapper(tree);
}
tree_wrapper repository_wrapper::treeish_to_tree(const std::string& treeish)
{
auto obj = this->revparse_single(treeish.c_str());
git_tree* tree = nullptr;
throw_if_error(git_object_peel(reinterpret_cast(&tree), obj.value(), GIT_OBJECT_TREE));
return tree_wrapper(tree);
}
// Remotes
remote_wrapper repository_wrapper::find_remote(std::string_view name) const
{
git_remote* remote = nullptr;
throw_if_error(git_remote_lookup(&remote, *this, name.data()));
return remote_wrapper(remote);
}
remote_wrapper repository_wrapper::create_remote(std::string_view name, std::string_view url)
{
git_remote* remote = nullptr;
throw_if_error(git_remote_create(&remote, *this, name.data(), url.data()));
return remote_wrapper(remote);
}
void repository_wrapper::delete_remote(std::string_view name)
{
throw_if_error(git_remote_delete(*this, name.data()));
}
void repository_wrapper::rename_remote(std::string_view old_name, std::string_view new_name)
{
git_strarray problems = {0};
int error = git_remote_rename(&problems, *this, old_name.data(), new_name.data());
if (error != 0)
{
for (size_t i = 0; i < problems.count; ++i)
{
std::cerr << problems.strings[i] << std::endl;
}
git_strarray_dispose(&problems);
throw_if_error(error);
}
git_strarray_dispose(&problems);
}
void repository_wrapper::set_remote_url(std::string_view name, std::string_view url, bool push)
{
if (push)
{
throw_if_error(git_remote_set_pushurl(*this, name.data(), url.data()));
}
else
{
throw_if_error(git_remote_set_url(*this, name.data(), url.data()));
}
}
std::vector<:string> repository_wrapper::list_remotes() const
{
git_strarray remotes = {0};
throw_if_error(git_remote_list(&remotes, *this));
std::vector<:string> result;
for (size_t i = 0; i < remotes.count; ++i)
{
result.emplace_back(remotes.strings[i]);
}
git_strarray_dispose(&remotes);
return result;
}
// Config
config_wrapper repository_wrapper::get_config()
{
git_config* cfg;
throw_if_error(git_repository_config(&cfg, *this));
return config_wrapper(cfg);
}
// Diff
diff_wrapper repository_wrapper::diff_tree_to_index(
const tree_wrapper& old_tree,
std::optional index,
git_diff_options* diffopts
)
{
git_diff* diff;
git_index* idx = nullptr;
if (index)
{
idx = *index;
}
throw_if_error(git_diff_tree_to_index(&diff, *this, old_tree, idx, diffopts));
return diff_wrapper(diff);
}
diff_wrapper repository_wrapper::diff_tree_to_tree(
const tree_wrapper& old_tree,
const tree_wrapper& new_tree,
git_diff_options* diffopts
)
{
git_diff* diff;
throw_if_error(git_diff_tree_to_tree(&diff, *this, old_tree, new_tree, diffopts));
return diff_wrapper(diff);
}
diff_wrapper repository_wrapper::diff_tree_to_workdir(const tree_wrapper& old_tree, git_diff_options* diffopts)
{
git_diff* diff;
throw_if_error(git_diff_tree_to_workdir(&diff, *this, old_tree, diffopts));
return diff_wrapper(diff);
}
diff_wrapper
repository_wrapper::diff_tree_to_workdir_with_index(const tree_wrapper& old_tree, git_diff_options* diffopts)
{
git_diff* diff;
throw_if_error(git_diff_tree_to_workdir_with_index(&diff, *this, old_tree, diffopts));
return diff_wrapper(diff);
}
diff_wrapper
repository_wrapper::diff_index_to_workdir(std::optional index, git_diff_options* diffopts)
{
git_diff* diff;
git_index* idx = nullptr;
if (index)
{
idx = *index;
}
throw_if_error(git_diff_index_to_workdir(&diff, *this, idx, diffopts));
return diff_wrapper(diff);
}
// Tags
std::vector<:string> repository_wrapper::tag_list_match(std::string pattern)
{
git_strarray tag_names;
throw_if_error(git_tag_list_match(&tag_names, pattern.c_str(), *this));
std::vector<:string> result(tag_names.strings, tag_names.strings + tag_names.count);
git_strarray_dispose(&tag_names);
return result;
}