Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 5 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@
#include <git2.h> // For version number only
#include <iostream>

#include "git_exception.hpp"
#include "src/utils/git_exception.hpp"
#include "version.hpp"
#include "subcommand/init_subcommand.hpp"
#include "subcommand/status_subcommand.hpp"

int main(int argc, char** argv)
{
int exitCode = 0;
try
{
const libgit2_object lg2_obj;
CLI::App app{"Git using C++ wrapper of libgit2"};

// Top-level command options.
auto version = app.add_flag("-v,--version", "Show version");

// Sub commands
InitSubcommand init(app);
init_subcommand init(lg2_obj, app);
status_subcommand status(lg2_obj, app);

app.parse(argc, argv);

Expand Down
4 changes: 2 additions & 2 deletions src/meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
subdir('subcommand')
subdir('utils')
subdir('wrapper')

src_files = files([
'git_exception.cpp',
'main.cpp'
]) + subcommand_files + wrapper_files
]) + subcommand_files + utils_files + wrapper_files
16 changes: 0 additions & 16 deletions src/subcommand/base_subcommand.hpp

This file was deleted.

13 changes: 6 additions & 7 deletions src/subcommand/init_subcommand.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <filesystem>
// #include <filesystem>
#include "init_subcommand.hpp"
#include "../wrapper/repository_wrapper.hpp"
#include "src/wrapper/repository_wrapper.hpp"

InitSubcommand::InitSubcommand(CLI::App& app)
init_subcommand::init_subcommand(const libgit2_object&, CLI::App& app)
{
auto *sub = app.add_subcommand("init", "Explanation of init here");

Expand All @@ -11,13 +11,12 @@ InitSubcommand::InitSubcommand(CLI::App& app)
// If directory not specified, uses cwd.
sub->add_option("directory", directory, "info about directory arg")
->check(CLI::ExistingDirectory | CLI::NonexistentPath)
->default_val(std::filesystem::current_path());
->default_val(get_current_git_path());

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

void InitSubcommand::run()
void init_subcommand::run()
{
RepositoryWrapper repo;
repo.init(directory, bare);
repository_wrapper::init(directory, bare);
}
10 changes: 7 additions & 3 deletions src/subcommand/init_subcommand.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
#pragma once

#include <string>
#include "base_subcommand.hpp"

class InitSubcommand : public BaseSubcommand
#include <CLI/CLI.hpp>

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

class init_subcommand
{
public:
InitSubcommand(CLI::App& app);

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

private:
Expand Down
1 change: 1 addition & 0 deletions src/subcommand/meson.build
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
subcommand_files = files([
'init_subcommand.cpp',
'status_subcommand.cpp',
])
170 changes: 170 additions & 0 deletions src/subcommand/status_subcommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#include <iostream>
#include <ostream>
#include <string>

#include <git2.h>

#include "status_subcommand.hpp"
#include "../wrapper/status_wrapper.hpp"
#include "git2/diff.h"

status_subcommand::status_subcommand(const libgit2_object&, CLI::App& app)
{
auto *sub = app.add_subcommand("status", "Show modified files in working directory, staged for your next commit");
// Displays paths that have differences between the index file and the current HEAD commit,
// paths that have differences between the working tree and the index file, and paths in the
// working tree that are not tracked by Git (and are not ignored by gitignore[5]).
// The first are what you would commit by running git commit;
// the second and third are what you could commit by running git add before running git commit.

sub->add_flag("-s,--short", short_flag, "Give the output in the short-format.");
sub->add_flag("--long", long_flag, "Give the output in the long-format. This is the default.");
// sub->add_flag("--porcelain[=<version>]", porcelain, "Give the output in an easy-to-parse format for scripts.
// This is similar to the short output, but will remain stable across Git versions and regardless of user configuration.
// See below for details. The version parameter is used to specify the format version. This is optional and defaults
// to the original version v1 format.");

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

std::string untracked_header = "Untracked files:\n";
// "Untracked files:\n (use \"git add <file>...\" to include in what will be committed)";
std::string tobecommited_header = "Changes to be committed:\n";
// "Changes to be committed:\n (use \"git reset HEAD <file>...\" to unstage)";
std::string ignored_header = "Ignored files:\n";
// "Ignored files:\n (use \"git add -f <file>...\" to include in what will be committed)"
std::string notstagged_header = "Changes not staged for commit:\n";
// "Changes not staged for commit:\n (use \"git add%s <file>...\" to update what will be committed)\n (use \"git checkout -- <file>...\" to discard changes in working directory)"
std::string nothingtocommit_message = "No changes added to commit";
// "No changes added to commit (use \"git add\" and/or \"git commit -a\")"

struct status_messages
{
std::string short_mod;
std::string long_mod;
};

const std::map<git_status_t, status_messages> status_msg_map = //TODO : check spaces in short_mod
{
{ GIT_STATUS_CURRENT, {"", ""} },
{ GIT_STATUS_INDEX_NEW, {"A ", "\t new file:"} },
{ GIT_STATUS_INDEX_MODIFIED, {"M ", "\t modified:"} },
{ GIT_STATUS_INDEX_DELETED, {"D ", "\t deleted:"} },
{ GIT_STATUS_INDEX_RENAMED, {"R ", "\t renamed:"} },
{ GIT_STATUS_INDEX_TYPECHANGE, {"T ", "\t typechange:"} },
{ GIT_STATUS_WT_NEW, {"?? ", ""} },
{ GIT_STATUS_WT_MODIFIED, {" M" , "\t modified:"} },
{ GIT_STATUS_WT_DELETED, {" D ", "\t deleted:"} },
{ GIT_STATUS_WT_TYPECHANGE, {" T ", "\t typechange:"} },
{ GIT_STATUS_WT_RENAMED, {" R ", "\t renamed:"} },
{ GIT_STATUS_WT_UNREADABLE, {"", ""} },
{ GIT_STATUS_IGNORED, {"!! ", ""} },
{ GIT_STATUS_CONFLICTED, {"", ""} },
};

void print_entries(git_status_t status, status_list_wrapper& sl, bool head_selector, size_t output_format) // TODO: add different mods
{
const auto& entry_list = sl.get_entry_list(status);
if (!entry_list.empty())
{
for (auto* entry : entry_list)
{
if ((output_format <= 1))
{
std::cout << status_msg_map.at(status).long_mod << "\t";
}
else if (output_format == 2)
{
std::cout << status_msg_map.at(status).short_mod;
}

git_diff_delta* diff_delta;
if (head_selector)
{
diff_delta = entry->head_to_index;
}
else
{
diff_delta = entry->index_to_workdir;
}
const char* old_path = diff_delta->old_file.path;
const char* new_path = diff_delta->new_file.path;
if (old_path && new_path && std::strcmp(old_path, new_path))
{
std::cout << old_path << " -> " << new_path << std::endl;
}
else
{
if (old_path)
{
std::cout << old_path << std::endl;
}
else
{
std::cout << new_path << std::endl;
}
}
}
}
else
{}
}

void status_subcommand::run()
{
auto directory = get_current_git_path();
auto bare = false;
auto repo = repository_wrapper::init(directory, bare);
auto sl = status_list_wrapper::status_list(repo);

// TODO: add branch info

size_t output_format = 0;
if (short_flag)
{
output_format = 2;
}
if (long_flag)
{
output_format = 1;
}
// else if (porcelain_format)
// {
// output_format = 3;
// }

if (sl.has_tobecommited_header())
{
std::cout << tobecommited_header << std::endl;
print_entries(GIT_STATUS_INDEX_NEW, sl, true, output_format);
print_entries(GIT_STATUS_INDEX_MODIFIED, sl, true, output_format);
print_entries(GIT_STATUS_INDEX_DELETED, sl, true, output_format);
print_entries(GIT_STATUS_INDEX_RENAMED, sl, true, output_format);
print_entries(GIT_STATUS_INDEX_TYPECHANGE, sl, true, output_format);
std::cout << std::endl;
}

if (sl.has_notstagged_header())
{
std::cout << notstagged_header << std::endl;
print_entries(GIT_STATUS_WT_MODIFIED, sl, false, output_format);
print_entries(GIT_STATUS_WT_DELETED, sl, false, output_format);
print_entries(GIT_STATUS_WT_TYPECHANGE, sl, false, output_format);
print_entries(GIT_STATUS_WT_RENAMED, sl, false, output_format);
std::cout << std::endl;
}

if (sl.has_untracked_header())
{
std::cout << untracked_header << std::endl;
print_entries(GIT_STATUS_WT_NEW, sl, false, output_format);
std::cout << std::endl;
}

if (sl.has_ignored_header())
{
std::cout << ignored_header << std::endl;
print_entries(GIT_STATUS_IGNORED, sl, false, output_format);
std::cout << std::endl;
}
}
17 changes: 17 additions & 0 deletions src/subcommand/status_subcommand.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <CLI/CLI.hpp>

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

class status_subcommand
{
public:

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

private:
bool short_flag = false;
bool long_flag = false;
};
25 changes: 25 additions & 0 deletions src/utils/common.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <filesystem>

#include <git2.h>

#include "common.hpp"

libgit2_object::libgit2_object()
{
git_libgit2_init();
}

libgit2_object::~libgit2_object()
{
git_libgit2_shutdown();
}

std::string get_current_git_path()
{
return std::filesystem::current_path(); // TODO: make sure that it goes to the root
}

// // If directory not specified, uses cwd.
// sub->add_option("directory", directory, "info about directory arg")
// ->check(CLI::ExistingDirectory | CLI::NonexistentPath)
// ->default_val(std::filesystem::current_path());
59 changes: 59 additions & 0 deletions src/utils/common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once

#include <string>
#include <utility>

class noncopiable_nonmovable
{
public:
noncopiable_nonmovable(const noncopiable_nonmovable&) = delete;
noncopiable_nonmovable& operator=(const noncopiable_nonmovable&) = delete;
noncopiable_nonmovable(noncopiable_nonmovable&&) = delete;
noncopiable_nonmovable& operator=(noncopiable_nonmovable&&) = delete;

protected:
noncopiable_nonmovable() = default;
~noncopiable_nonmovable() = default;
};

template <class T>
class wrapper_base
{
public:
using ressource_type = T;

wrapper_base(const wrapper_base&) = delete;
wrapper_base& operator=(const wrapper_base&) = delete;

wrapper_base(wrapper_base&& rhs)
: p_ressource(rhs.p_ressource)
{
rhs.p_ressource = nullptr;
}
wrapper_base& operator=(wrapper_base&& rhs)
{
std::swap(p_ressource, rhs.p_ressource);
return this;
}

operator ressource_type*() const noexcept
{
return p_ressource;
}

protected:
// Allocation and deletion of p_ressource must be handled by inheriting class.
wrapper_base() = default;
~wrapper_base() = default;
ressource_type* p_ressource = nullptr;
};

class libgit2_object : private noncopiable_nonmovable
{
public:

libgit2_object();
~libgit2_object();
};

std::string get_current_git_path();
File renamed without changes.
File renamed without changes.
4 changes: 4 additions & 0 deletions src/utils/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
utils_files = files([
'common.cpp',
'git_exception.cpp',
])
Loading