diff --git a/.gitignore b/.gitignore index 3d43339..e4f7ae8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ build/ __pycache__/ +.cache/ +compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index b97b9c2..824aad9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,8 @@ find_package(libgit2) # ===== set(GIT2CPP_SRC + ${GIT2CPP_SOURCE_DIR}/subcommand/add_subcommand.cpp + ${GIT2CPP_SOURCE_DIR}/subcommand/add_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/subcommand/init_subcommand.cpp ${GIT2CPP_SOURCE_DIR}/subcommand/init_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.cpp @@ -47,6 +49,8 @@ set(GIT2CPP_SRC ${GIT2CPP_SOURCE_DIR}/utils/common.hpp ${GIT2CPP_SOURCE_DIR}/utils/git_exception.cpp ${GIT2CPP_SOURCE_DIR}/utils/git_exception.hpp + ${GIT2CPP_SOURCE_DIR}/wrapper/index_wrapper.cpp + ${GIT2CPP_SOURCE_DIR}/wrapper/index_wrapper.hpp ${GIT2CPP_SOURCE_DIR}/wrapper/refs_wrapper.cpp ${GIT2CPP_SOURCE_DIR}/wrapper/refs_wrapper.hpp ${GIT2CPP_SOURCE_DIR}/wrapper/repository_wrapper.cpp @@ -59,4 +63,3 @@ set(GIT2CPP_SRC add_executable(git2cpp ${GIT2CPP_SRC}) target_link_libraries(git2cpp PRIVATE libgit2::libgit2package) - diff --git a/README.md b/README.md index a03afe0..64c6743 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Developer's workflow using `micromamba` to manage the dependencies: ```bash micromamba create -f dev-environment.yml micromamba activate git2cpp-dev -cmake -Bbuild $CMAKE_INSALL_PREFIX=$CONDA_PREFIX +cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX cd build make -j8 ``` diff --git a/dev-environment.yml b/dev-environment.yml index 80a86dd..04f465e 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -4,7 +4,7 @@ channels: dependencies: - cli11 - libgit2 - - meson + - cmake - pkg-config - python - pytest diff --git a/src/main.cpp b/src/main.cpp index f5ce04a..64d2b72 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ #include "utils/git_exception.hpp" #include "version.hpp" +#include "subcommand/add_subcommand.hpp" #include "subcommand/init_subcommand.hpp" #include "subcommand/status_subcommand.hpp" @@ -21,6 +22,7 @@ int main(int argc, char** argv) // Sub commands init_subcommand init(lg2_obj, app); status_subcommand status(lg2_obj, app); + add_subcommand add(lg2_obj, app); app.parse(argc, argv); diff --git a/src/subcommand/add_subcommand.cpp b/src/subcommand/add_subcommand.cpp new file mode 100644 index 0000000..cb2c609 --- /dev/null +++ b/src/subcommand/add_subcommand.cpp @@ -0,0 +1,39 @@ +#include + +#include "add_subcommand.hpp" +#include "../wrapper/index_wrapper.hpp" +#include "../wrapper/repository_wrapper.hpp" + + +add_subcommand::add_subcommand(const libgit2_object&, CLI::App& app) +{ + auto *sub = app.add_subcommand("add", "Add file contents to the index"); + + sub->add_option("files", m_add_files, "Files to add"); + + sub->add_flag("-A,--all,--no-ignore-removal", m_all_flag, ""); + // sub->add_flag("-n,--dryrun", dryrun_flag, ""); + // sub->add_flag("-u,--update", update_flag, ""); + // sub->add_flag("-v,--verbose", verbose_flag, ""); + + sub->callback([this]() { this->run(); }); +}; + + +void add_subcommand::run() +{ + auto directory = get_current_git_path(); + auto bare = false; + auto repo = repository_wrapper::init(directory, bare); + + index_wrapper index = repo.make_index(); + + if (m_all_flag) + { + index.add_all(); + } + else + { + index.add_entries(m_add_files); + } +} diff --git a/src/subcommand/add_subcommand.hpp b/src/subcommand/add_subcommand.hpp new file mode 100644 index 0000000..6f31dff --- /dev/null +++ b/src/subcommand/add_subcommand.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include "../utils/common.hpp" + +class add_subcommand +{ +public: + + explicit add_subcommand(const libgit2_object&, CLI::App& app); + void run(); + +private: + bool m_all_flag = false; + std::vector m_add_files; +}; diff --git a/src/subcommand/init_subcommand.cpp b/src/subcommand/init_subcommand.cpp index 79d6807..2a3a703 100644 --- a/src/subcommand/init_subcommand.cpp +++ b/src/subcommand/init_subcommand.cpp @@ -6,10 +6,10 @@ init_subcommand::init_subcommand(const libgit2_object&, CLI::App& app) { auto *sub = app.add_subcommand("init", "Explanation of init here"); - sub->add_flag("--bare", bare, "info about bare arg"); + sub->add_flag("--bare", m_bare, "info about bare arg"); // If directory not specified, uses cwd. - sub->add_option("directory", directory, "info about directory arg") + sub->add_option("directory", m_directory, "info about directory arg") ->check(CLI::ExistingDirectory | CLI::NonexistentPath) ->default_val(get_current_git_path()); @@ -18,5 +18,5 @@ init_subcommand::init_subcommand(const libgit2_object&, CLI::App& app) void init_subcommand::run() { - repository_wrapper::init(directory, bare); + repository_wrapper::init(m_directory, m_bare); } diff --git a/src/subcommand/init_subcommand.hpp b/src/subcommand/init_subcommand.hpp index ef39e5a..6086492 100644 --- a/src/subcommand/init_subcommand.hpp +++ b/src/subcommand/init_subcommand.hpp @@ -14,6 +14,6 @@ class init_subcommand void run(); private: - bool bare; - std::string directory; + bool m_bare; + std::string m_directory; }; diff --git a/src/subcommand/status_subcommand.cpp b/src/subcommand/status_subcommand.cpp index 0133893..2da0780 100644 --- a/src/subcommand/status_subcommand.cpp +++ b/src/subcommand/status_subcommand.cpp @@ -13,19 +13,14 @@ 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("-s,--short", m_short_flag, "Give the output in the short-format."); + sub->add_flag("--long", m_long_flag, "Give the output in the long-format. This is the default."); // sub->add_flag("--porcelain[=]", 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->add_flag("-b,--branch", branch_flag, "Show the branch and tracking info even in short-format."); + sub->add_flag("-b,--branch", m_branch_flag, "Show the branch and tracking info even in short-format."); sub->callback([this]() { this->run(); }); }; @@ -152,7 +147,7 @@ void print_entries(std::vector entries_to_print) } } -void print_not_tracked(std::vector entries_to_print, const std::set& tracked_dir_set, +void print_not_tracked(const std::vector& entries_to_print, const std::set& tracked_dir_set, std::set& untracked_dir_set) { std::vector not_tracked_entries_to_print{}; @@ -199,11 +194,11 @@ void status_subcommand::run() std::vector ignored_to_print{}; output_format of = output_format::DEFAULT; - if (short_flag) + if (m_short_flag) { of = output_format::SHORT; } - if (long_flag) + if (m_long_flag) { of = output_format::LONG; } @@ -220,7 +215,7 @@ void status_subcommand::run() } else { - if (branch_flag) + if (m_branch_flag) { std::cout << "## " << branch_name << std::endl; } diff --git a/src/subcommand/status_subcommand.hpp b/src/subcommand/status_subcommand.hpp index 08b2893..ae259a9 100644 --- a/src/subcommand/status_subcommand.hpp +++ b/src/subcommand/status_subcommand.hpp @@ -12,7 +12,7 @@ class status_subcommand void run(); private: - bool branch_flag = false; - bool long_flag = false; - bool short_flag = false; + bool m_branch_flag = false; + bool m_long_flag = false; + bool m_short_flag = false; }; diff --git a/src/utils/common.hpp b/src/utils/common.hpp index c19b1b3..afc388b 100644 --- a/src/utils/common.hpp +++ b/src/utils/common.hpp @@ -36,7 +36,7 @@ class wrapper_base wrapper_base& operator=(wrapper_base&& rhs) { std::swap(p_resource, rhs.p_resource); - return this; + return *this; } operator resource_type*() const noexcept diff --git a/src/utils/meson.build b/src/utils/meson.build deleted file mode 100644 index 5d728df..0000000 --- a/src/utils/meson.build +++ /dev/null @@ -1,4 +0,0 @@ -utils_files = files([ - 'common.cpp', - 'git_exception.cpp', -]) diff --git a/src/version.hpp b/src/version.hpp index 5a0c737..30c9998 100644 --- a/src/version.hpp +++ b/src/version.hpp @@ -2,7 +2,7 @@ #define GIT2CPP_VERSION_MAJOR 0 #define GIT2CPP_VERSION_MINOR 0 -#define GIT2CPP_VERSION_PATCH 1 +#define GIT2CPP_VERSION_PATCH 2 // e.g. ".rc0" #define GIT2CPP_VERSION_SUFFIX diff --git a/src/wrapper/index_wrapper.cpp b/src/wrapper/index_wrapper.cpp new file mode 100644 index 0000000..c7c02ec --- /dev/null +++ b/src/wrapper/index_wrapper.cpp @@ -0,0 +1,35 @@ +#include "index_wrapper.hpp" +#include "../utils/git_exception.hpp" +#include "../wrapper/repository_wrapper.hpp" + +#include + +index_wrapper::~index_wrapper() +{ + git_index_free(p_resource); + p_resource=nullptr; +} + +index_wrapper index_wrapper::init(repository_wrapper& rw) +{ + index_wrapper index; + throwIfError(git_repository_index(&(index.p_resource), rw)); + return index; +} + +void index_wrapper::add_entries(std::vector patterns) +{ + add_impl(std::move(patterns)); +} + +void index_wrapper::add_all() +{ + add_impl({{"."}}); +} + +void index_wrapper::add_impl(std::vector patterns) +{ + git_strarray_wrapper array{patterns}; + throwIfError(git_index_add_all(*this, array, 0, NULL, NULL)); + throwIfError(git_index_write(*this)); +} diff --git a/src/wrapper/index_wrapper.hpp b/src/wrapper/index_wrapper.hpp new file mode 100644 index 0000000..f478e48 --- /dev/null +++ b/src/wrapper/index_wrapper.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include + +#include "../utils/common.hpp" + +class repository_wrapper; + +class index_wrapper : public wrapper_base +{ +public: + + ~index_wrapper(); + + index_wrapper(index_wrapper&&) = default; + index_wrapper& operator=(index_wrapper&&) = default; + + static index_wrapper init(repository_wrapper& rw); + + void add_entries(std::vector patterns); + void add_all(); + + +private: + + index_wrapper() = default; + void add_impl(std::vector patterns); +}; diff --git a/src/wrapper/repository_wrapper.cpp b/src/wrapper/repository_wrapper.cpp index b27adc4..10dc0be 100644 --- a/src/wrapper/repository_wrapper.cpp +++ b/src/wrapper/repository_wrapper.cpp @@ -1,6 +1,5 @@ -#include "../utils/git_exception.hpp" #include "repository_wrapper.hpp" - +#include "../utils/git_exception.hpp" repository_wrapper::~repository_wrapper() { @@ -21,3 +20,9 @@ repository_wrapper repository_wrapper::init(const std::string& directory, bool b throwIfError(git_repository_init(&(rw.p_resource), directory.c_str(), bare)); return rw; } + +index_wrapper repository_wrapper::make_index() +{ + index_wrapper index = index_wrapper::init(*this); + return index; +} diff --git a/src/wrapper/repository_wrapper.hpp b/src/wrapper/repository_wrapper.hpp index 2836636..dd0b8f7 100644 --- a/src/wrapper/repository_wrapper.hpp +++ b/src/wrapper/repository_wrapper.hpp @@ -5,6 +5,7 @@ #include #include "../utils/common.hpp" +#include "../wrapper/index_wrapper.hpp" class repository_wrapper : public wrapper_base { @@ -17,6 +18,7 @@ class repository_wrapper : public wrapper_base static repository_wrapper init(const std::string& directory, bool bare); static repository_wrapper open(const std::string& directory); + index_wrapper make_index(); private: diff --git a/test/data/status_data/embeded_git/config b/test/data/status_data/embeded_git/config old mode 100644 new mode 100755 diff --git a/test/test_add.py b/test/test_add.py new file mode 100644 index 0000000..b762121 --- /dev/null +++ b/test/test_add.py @@ -0,0 +1,36 @@ +import os +import subprocess + +import pytest + + +@pytest.mark.parametrize("all_flag", ["", "-A", "--all", "--no-ignore-removal"]) +def test_add(git2cpp_path, all_flag): + with open("./test/mook_file.txt", "x") as f: + pass + + with open("./test/mook_file_2.txt", "x") as f: + pass + + cmd_add = [git2cpp_path, 'add'] + if all_flag != "": + cmd_add.append(all_flag) + else: + cmd_add.append("test/mook_file.txt") + subprocess.run(cmd_add, capture_output=True, text=True) + + cmd_status = [git2cpp_path, 'status', "--long"] + p_status = subprocess.run(cmd_status, capture_output=True, text=True) + + assert "Changes to be committed" in p_status.stdout + assert "new file" in p_status.stdout + if all_flag != "": + assert "Untracked files" not in p_status.stdout + else: + assert "Untracked files" in p_status.stdout + + os.remove("./test/mook_file.txt") + os.remove("./test/mook_file_2.txt") + + # undo the add, to leave the test directory at the end the same as it was at the start + subprocess.run(cmd_add, capture_output=True, text=True)