Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ set(GIT2CPP_SRC
${GIT2CPP_SOURCE_DIR}/subcommand/init_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/log_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/log_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/merge_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/merge_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/reset_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/reset_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.cpp
Expand Down
3 changes: 3 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <CLI/CLI.hpp>
#include <cmath>
#include <git2.h> // For version number only
#include <iostream>

Expand All @@ -11,6 +12,7 @@
#include "subcommand/commit_subcommand.hpp"
#include "subcommand/init_subcommand.hpp"
#include "subcommand/log_subcommand.hpp"
#include "subcommand/merge_subcommand.hpp"
#include "subcommand/reset_subcommand.hpp"
#include "subcommand/status_subcommand.hpp"

Expand All @@ -35,6 +37,7 @@ int main(int argc, char** argv)
commit_subcommand commit(lg2_obj, app);
reset_subcommand reset(lg2_obj, app);
log_subcommand log(lg2_obj, app);
merge_subcommand merge(lg2_obj, app);

app.require_subcommand(/* min */ 0, /* max */ 1);

Expand Down
2 changes: 1 addition & 1 deletion src/subcommand/checkout_subcommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void checkout_subcommand::run()
}
else
{
auto optional_commit = resolve_local_ref(repo, m_branch_name);
auto optional_commit = repo.resolve_local_ref(m_branch_name);
if (!optional_commit)
{
// TODO: handle remote refs
Expand Down
2 changes: 1 addition & 1 deletion src/subcommand/commit_subcommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ void commit_subcommand::run()
}
}

repo.create_commit(author_committer_signatures, m_commit_message);
repo.create_commit(author_committer_signatures, m_commit_message, std::nullopt);
}
182 changes: 182 additions & 0 deletions src/subcommand/merge_subcommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#include <cassert>
#include <git2/types.h>

#include "merge_subcommand.hpp"
#include "../wrapper/repository_wrapper.hpp"


merge_subcommand::merge_subcommand(const libgit2_object&, CLI::App& app)
{
auto *sub = app.add_subcommand("merge", "Join two or more development histories together");

sub->add_option("<branch>", m_branches_to_merge, "Branch(es) to merge");

sub->callback([this]() { this->run(); });
}

annotated_commit_list_wrapper resolve_heads(const repository_wrapper& repo, std::vector<std::string> m_branches_to_merge)
{
std::vector<annotated_commit_wrapper> commits_to_merge;
commits_to_merge.reserve(m_branches_to_merge.size());

for (const auto branch_name:m_branches_to_merge)
{
std::optional<annotated_commit_wrapper> commit = repo.resolve_local_ref(branch_name);
if (commit.has_value())
{
commits_to_merge.push_back(std::move(commit).value());
}
}
return annotated_commit_list_wrapper(std::move(commits_to_merge));
}

void perform_fastforward(repository_wrapper& repo, const git_oid* target_oid, int is_unborn)
{
const git_checkout_options ff_checkout_options = GIT_CHECKOUT_OPTIONS_INIT;

auto lambda_get_target_ref = [] (auto repo, auto is_unborn)
{
if (!is_unborn)
{
return repo->head();
}
else
{
return repo->find_reference("HEAD");
}
};
auto target_ref = lambda_get_target_ref(&repo, is_unborn);

auto target = repo.find_object(target_oid, GIT_OBJECT_COMMIT);

repo.checkout_tree(target, &ff_checkout_options);

auto new_target_ref = target_ref.new_ref();
}

static void create_merge_commit(repository_wrapper repo, index_wrapper index, std::vector<std::string> m_branches_to_merge,
std::vector<annotated_commit_wrapper> commits_to_merge)
{
auto head_ref = repo.head();
auto merge_ref = repo.find_reference_dwim(m_branches_to_merge.front());
// if (ref)
// {
// auto merge_ref = std::move(ref).value();
// }
auto merge_commit = repo.resolve_local_ref(m_branches_to_merge.front()).value();

size_t annotated_count = commits_to_merge.size();
std::vector<commit_wrapper> parents_list;
parents_list.reserve(annotated_count + 1);
parents_list.push_back(std::move(head_ref.peel<commit_wrapper>()));
for (size_t i=0; i<annotated_count; ++i)
{
parents_list.push_back(repo.find_commit(commits_to_merge[i].oid()));
}
auto parents = commit_list_wrapper(std::move(parents_list));

auto author_committer_sign = signature_wrapper::get_default_signature_from_env(repo);
std::string author_name;
author_name = author_committer_sign.first.name();
std::string author_email;
author_email = author_committer_sign.first.email();
auto author_committer_sign_now = signature_wrapper::signature_now(author_name, author_email, author_name, author_email);

std::string msg_target = NULL;
if (merge_ref)
{
msg_target = merge_ref->short_name();
}
else
{
msg_target = git_oid_tostr_s(&(merge_commit.oid()));
}

std::string msg;
msg = "Merge ";
if (merge_ref)
{
msg.append("branch ");
}
else
{
msg.append("commit ");
}
msg.append(msg_target);
std::cout << msg << std::endl;

repo.create_commit(author_committer_sign_now, msg, std::optional<commit_list_wrapper>(std::move(parents)));
}

void merge_subcommand::run()
{
auto directory = get_current_git_path();
auto bare = false;
auto repo = repository_wrapper::open(directory);

auto state = repo.state();
if (state != GIT_REPOSITORY_STATE_NONE)
{
std::cout << "repository is in unexpected state " << state <<std::endl;
}

git_merge_analysis_t analysis;
git_merge_preference_t preference;
annotated_commit_list_wrapper commits_to_merge = resolve_heads(repo, m_branches_to_merge);
size_t num_commits_to_merge = commits_to_merge.size();
git_annotated_commit** c_commits_to_merge = commits_to_merge;
auto commits_to_merge_const = const_cast<const git_annotated_commit**>(c_commits_to_merge);

git_merge_analysis(&analysis, &preference, repo, commits_to_merge_const, num_commits_to_merge);

if (analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
{
std::cout << "Already up-to-date" << std::endl;
}
else if (analysis & GIT_MERGE_ANALYSIS_UNBORN ||
(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD &&
!(preference & GIT_MERGE_PREFERENCE_NO_FASTFORWARD)))
{
const git_oid* target_oid;
if (analysis & GIT_MERGE_ANALYSIS_UNBORN)
{
std::cout << "Unborn" << std::endl;
}
else
{
std::cout << "Fast-forward" << std::endl;
}
const annotated_commit_wrapper& commit = commits_to_merge.front();
target_oid = &commit.oid();
assert(num_commits_to_merge == 1);
perform_fastforward(repo, target_oid, (analysis & GIT_MERGE_ANALYSIS_UNBORN));
}
else if (analysis & GIT_MERGE_ANALYSIS_NORMAL)
{
git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;

merge_opts.flags = 0;
merge_opts.file_flags = GIT_MERGE_FILE_STYLE_DIFF3;

checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE|GIT_CHECKOUT_ALLOW_CONFLICTS;

if (preference & GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY)
{
std::cout << "Fast-forward is preferred, but only a merge is possible\n" << std::endl;
// how to break ?
}

// git_merge(repo,
// (const git_annotated_commit **)opts.annotated, opts.annotated_count,
// &merge_opts, &checkout_opts);
}

// if (git_index_has_conflicts(index)) {
// /* Handle conflicts */
// output_conflicts(index);
// } else if (!opts.no_commit) {
// create_merge_commit(repo, index, &opts);
// printf("Merge made\n");
// }
}
16 changes: 16 additions & 0 deletions src/subcommand/merge_subcommand.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <CLI/CLI.hpp>

#include "../utils/common.hpp"

class merge_subcommand
{
public:

explicit merge_subcommand(const libgit2_object&, CLI::App& app);
void run();

private:
std::vector<std::string> m_branches_to_merge;
};
26 changes: 26 additions & 0 deletions src/wrapper/annotated_commit_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,29 @@ std::string_view annotated_commit_wrapper::reference_name() const
const char* res = git_annotated_commit_ref(*this);
return res ? res : std::string_view{};
}

annotated_commit_list_wrapper::annotated_commit_list_wrapper(std::vector<annotated_commit_wrapper> annotated_commit_list)
: m_annotated_commit_list(std::move(annotated_commit_list))
{
git_annotated_commit** p_resource = new git_annotated_commit*[m_annotated_commit_list.size()];
for (size_t i=0; i<m_annotated_commit_list.size(); ++i)
{
p_resource[i] = m_annotated_commit_list[i];
}
}

annotated_commit_list_wrapper::~annotated_commit_list_wrapper()
{
delete[] p_resource;
p_resource = nullptr;
}

size_t annotated_commit_list_wrapper::size() const
{
return m_annotated_commit_list.size();
}

annotated_commit_wrapper annotated_commit_list_wrapper::front()
{
return annotated_commit_wrapper(std::move(m_annotated_commit_list.front()));
}
22 changes: 22 additions & 0 deletions src/wrapper/annotated_commit_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <string_view>
#include <vector>

#include <git2.h>

Expand All @@ -27,3 +28,24 @@ class annotated_commit_wrapper : public wrapper_base<git_annotated_commit>
friend class repository_wrapper;
};

class annotated_commit_list_wrapper : public wrapper_base<git_annotated_commit*>
{
public:

using base_type = wrapper_base<git_annotated_commit*>;

explicit annotated_commit_list_wrapper(std::vector<annotated_commit_wrapper> annotated_commit_list);

~annotated_commit_list_wrapper();

annotated_commit_list_wrapper(annotated_commit_list_wrapper&&) noexcept = default;
annotated_commit_list_wrapper& operator=(annotated_commit_list_wrapper&&) noexcept = default;

size_t size() const;
annotated_commit_wrapper front();

private:

std::vector<annotated_commit_wrapper> m_annotated_commit_list;

};
20 changes: 20 additions & 0 deletions src/wrapper/commit_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,23 @@ std::string commit_wrapper::commit_oid_tostr() const
char buf[GIT_OID_SHA1_HEXSIZE + 1];
return git_oid_tostr(buf, sizeof(buf), &this->oid());
}

commit_list_wrapper::commit_list_wrapper(std::vector<commit_wrapper> commit_list)
{
git_commit** p_resource = new git_commit*[m_commit_list.size()];
for (size_t i=0; i<m_commit_list.size(); ++i)
{
p_resource[i] = m_commit_list[i];
}
}

commit_list_wrapper::~commit_list_wrapper()
{
delete[] p_resource;
p_resource = nullptr;
}

size_t commit_list_wrapper::size() const
{
return m_commit_list.size();
}
25 changes: 24 additions & 1 deletion src/wrapper/commit_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#pragma once

#include <git2.h>
#include <vector>
#include <string>

#include "../wrapper/repository_wrapper.hpp"
#include "../wrapper/wrapper_base.hpp"

class commit_wrapper : public wrapper_base<git_commit>
Expand All @@ -26,4 +27,26 @@ class commit_wrapper : public wrapper_base<git_commit>
commit_wrapper(git_commit* commit);

friend class repository_wrapper;
friend class reference_wrapper;
};

class commit_list_wrapper : public wrapper_base<git_commit*>
{
public:

using base_type = wrapper_base<git_commit*>;

explicit commit_list_wrapper(std::vector<commit_wrapper> commit_list);

~commit_list_wrapper();

commit_list_wrapper(commit_list_wrapper&&) noexcept = default;
commit_list_wrapper& operator=(commit_list_wrapper&&) noexcept = default;

size_t size() const;

private:

std::vector<commit_wrapper> m_commit_list;

};
1 change: 1 addition & 0 deletions src/wrapper/object_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ class object_wrapper : public wrapper_base<git_object>
object_wrapper(git_object* obj);

friend class repository_wrapper;
friend class reference_wrapper;
};
Loading
Loading