Skip to content
Merged
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
22 changes: 1 addition & 21 deletions 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 All @@ -56,26 +56,6 @@ void checkout_subcommand::run()
}
}

std::optional<annotated_commit_wrapper> checkout_subcommand::resolve_local_ref
(
const repository_wrapper& repo,
const std::string& target_name
)
{
if (auto ref = repo.find_reference_dwim(target_name))
{
return repo.find_annotated_commit(*ref);
}
else if (auto obj = repo.revparse_single(target_name))
{
return repo.find_annotated_commit(obj->oid());
}
else
{
return std::nullopt;
}
}

annotated_commit_wrapper checkout_subcommand::create_local_branch
(
repository_wrapper& repo,
Expand Down
6 changes: 0 additions & 6 deletions src/subcommand/checkout_subcommand.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ class checkout_subcommand

private:

std::optional<annotated_commit_wrapper> resolve_local_ref
(
const repository_wrapper& repo,
const std::string& target_name
);

annotated_commit_wrapper create_local_branch
(
repository_wrapper& repo,
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);
}
100 changes: 100 additions & 0 deletions src/subcommand/merge_subcommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#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 merge_subcommand::resolve_heads(const repository_wrapper& repo)
{
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");
}
};
reference_wrapper target_ref = lambda_get_target_ref(&repo, is_unborn);

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

repo.checkout_tree(target, ff_checkout_options);

target_ref.write_new_ref(target_oid);
}

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);
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);

throw_if_error(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)))
{
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();
const git_oid target_oid = commit.oid();
// Since this is a fast-forward, there can be only one merge head.
assert(num_commits_to_merge == 1);
perform_fastforward(repo, target_oid, (analysis & GIT_MERGE_ANALYSIS_UNBORN));
}
}
20 changes: 20 additions & 0 deletions src/subcommand/merge_subcommand.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <CLI/CLI.hpp>

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

class merge_subcommand
{
public:

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

private:

annotated_commit_list_wrapper resolve_heads(const repository_wrapper& repo);

std::vector<std::string> m_branches_to_merge;
};
1 change: 1 addition & 0 deletions src/wrapper/annotated_commit_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ class annotated_commit_wrapper : public wrapper_base<git_annotated_commit>
friend class repository_wrapper;
};

using annotated_commit_list_wrapper = list_wrapper<annotated_commit_wrapper>;
5 changes: 4 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 @@ -28,3 +29,5 @@ class commit_wrapper : public wrapper_base<git_commit>
friend class repository_wrapper;
friend class reference_wrapper;
};

using commit_list_wrapper = list_wrapper<commit_wrapper>;
15 changes: 15 additions & 0 deletions src/wrapper/refs_wrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include "../utils/git_exception.hpp"
#include "object_wrapper.hpp"
#include <git2/refs.h>
#include <git2/types.h>
#include "../wrapper/refs_wrapper.hpp"

reference_wrapper::reference_wrapper(git_reference* ref)
Expand All @@ -21,3 +24,15 @@ bool reference_wrapper::is_remote() const
{
return git_reference_is_remote(*this);
}

const git_oid* reference_wrapper::target() const
{
return git_reference_target(p_resource);
}

reference_wrapper reference_wrapper::write_new_ref(const git_oid target_oid)
{
git_reference* new_ref;
throw_if_error(git_reference_set_target(&new_ref, p_resource, &target_oid, NULL));
return reference_wrapper(new_ref);
}
4 changes: 4 additions & 0 deletions src/wrapper/refs_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

#include <git2.h>

#include "../utils/git_exception.hpp"
#include "../wrapper/wrapper_base.hpp"
#include "../wrapper/object_wrapper.hpp"

class reference_wrapper : public wrapper_base<git_reference>
{
Expand All @@ -20,6 +22,8 @@ class reference_wrapper : public wrapper_base<git_reference>

std::string short_name() const;
bool is_remote() const;
const git_oid* target() const;
reference_wrapper write_new_ref(const git_oid target_oid);

template <class W>
W peel() const;
Expand Down
Loading