diff --git a/CMakeLists.txt b/CMakeLists.txt index ef52831..eb25f40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,8 @@ set(GIT2CPP_SRC ${GIT2CPP_SOURCE_DIR}/subcommand/revlist_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/subcommand/revparse_subcommand.cpp ${GIT2CPP_SOURCE_DIR}/subcommand/revparse_subcommand.hpp + ${GIT2CPP_SOURCE_DIR}/subcommand/stash_subcommand.cpp + ${GIT2CPP_SOURCE_DIR}/subcommand/stash_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.cpp ${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/utils/ansi_code.cpp diff --git a/src/main.cpp b/src/main.cpp index 8aa4c0f..5813af2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ #include "subcommand/push_subcommand.hpp" #include "subcommand/remote_subcommand.hpp" #include "subcommand/reset_subcommand.hpp" +#include "subcommand/stash_subcommand.hpp" #include "subcommand/status_subcommand.hpp" #include "subcommand/revparse_subcommand.hpp" #include "subcommand/revlist_subcommand.hpp" @@ -50,6 +51,7 @@ int main(int argc, char** argv) remote_subcommand remote(lg2_obj, app); revparse_subcommand revparse(lg2_obj, app); revlist_subcommand revlist(lg2_obj, app); + stash_subcommand stash(lg2_obj, app); app.require_subcommand(/* min */ 0, /* max */ 1); diff --git a/src/subcommand/stash_subcommand.cpp b/src/subcommand/stash_subcommand.cpp new file mode 100644 index 0000000..de9a81f --- /dev/null +++ b/src/subcommand/stash_subcommand.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include + +#include "../subcommand/stash_subcommand.hpp" +#include "../wrapper/repository_wrapper.hpp" + +stash_subcommand::stash_subcommand(const libgit2_object&, CLI::App& app) +{ + auto* stash = app.add_subcommand("stash", "Stash the changes in a dirty working directory away"); + auto* push = stash->add_subcommand("push", ""); + auto* list = stash->add_subcommand("list", ""); + auto* pop = stash->add_subcommand("pop", ""); + auto* apply = stash->add_subcommand("apply", ""); + + push->add_option("-m", m_message, ""); + pop->add_option("--index", m_index, ""); + apply->add_option("--index", m_index, ""); + + stash->callback([this]() { this->run_push(); }); + push->callback([this]() { this->run_push(); }); + list->callback([this]() { this->run_list(); }); + pop->callback([this]() { this->run_pop(); }); + apply->callback([this]() { this->run_apply(); }); +} + +void stash_subcommand::run_push() +{ + auto directory = get_current_git_path(); + auto repo = repository_wrapper::open(directory); + auto author_committer_signatures = signature_wrapper::get_default_signature_from_env(repo); + + git_oid stash_id; + git_stash_save(&stash_id, repo, author_committer_signatures.first, m_message.c_str(), GIT_STASH_DEFAULT); + auto stash = repo.find_commit(stash_id); + std::cout << "Saved working directory " << stash.summary() << std::endl; +} + +static int list_stash_cb(size_t index, const char* message, const git_oid* stash_id, void* payload) +{ + std::cout << "stash@{" << index << "}: " << message << std::endl; + return 0; +} + +void stash_subcommand::run_list() +{ + auto directory = get_current_git_path(); + auto repo = repository_wrapper::open(directory); + + git_stash_foreach(repo, list_stash_cb, NULL); +} + +void stash_subcommand::run_pop() +{ + auto directory = get_current_git_path(); + auto repo = repository_wrapper::open(directory); + + git_stash_pop(repo, m_index, NULL); +} + +void stash_subcommand::run_apply() +{ + auto directory = get_current_git_path(); + auto repo = repository_wrapper::open(directory); + + git_stash_apply(repo, m_index, NULL); +} diff --git a/src/subcommand/stash_subcommand.hpp b/src/subcommand/stash_subcommand.hpp new file mode 100644 index 0000000..c6a13ce --- /dev/null +++ b/src/subcommand/stash_subcommand.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "../utils/common.hpp" + +class stash_subcommand +{ +public: + + explicit stash_subcommand(const libgit2_object&, CLI::App& app); + void run_push(); + void run_list(); + void run_pop(); + void run_apply(); + + std::vector m_options; + std::string m_message = ""; + size_t m_index = 0; +}; diff --git a/src/wrapper/commit_wrapper.cpp b/src/wrapper/commit_wrapper.cpp index 92f57df..33efa9f 100644 --- a/src/wrapper/commit_wrapper.cpp +++ b/src/wrapper/commit_wrapper.cpp @@ -1,4 +1,5 @@ #include "../wrapper/commit_wrapper.hpp" +#include commit_wrapper::commit_wrapper(git_commit* commit) : base_type(commit) @@ -27,6 +28,11 @@ std::string commit_wrapper::commit_oid_tostr() const return git_oid_tostr(buf, sizeof(buf), &this->oid()); } +std::string commit_wrapper::summary() const +{ + return git_commit_summary(*this); +} + commit_list_wrapper commit_wrapper::get_parents_list() const { size_t parent_count = git_commit_parentcount(*this); diff --git a/src/wrapper/commit_wrapper.hpp b/src/wrapper/commit_wrapper.hpp index 84b35d1..4fe280f 100644 --- a/src/wrapper/commit_wrapper.hpp +++ b/src/wrapper/commit_wrapper.hpp @@ -24,6 +24,8 @@ class commit_wrapper : public wrapper_base const git_oid& oid() const; std::string commit_oid_tostr() const; + std::string summary() const; + commit_list_wrapper get_parents_list() const; private: diff --git a/test/test_stash.py b/test/test_stash.py new file mode 100644 index 0000000..5841faa --- /dev/null +++ b/test/test_stash.py @@ -0,0 +1,130 @@ +import subprocess + +import pytest + + +def test_stash_push(xtl_clone, commit_env_config, git2cpp_path, tmp_path): + assert (tmp_path / "xtl").exists() + xtl_path = tmp_path / "xtl" + + p = xtl_path / "mook_file.txt" + p.write_text("") + + cmd_add = [git2cpp_path, "add", "mook_file.txt"] + p_add = subprocess.run(cmd_add, cwd=xtl_path, text=True) + assert p_add.returncode == 0 + + cmd_status = [git2cpp_path, "status"] + p_status = subprocess.run(cmd_status, capture_output=True, cwd=xtl_path, text=True) + assert p_status.returncode == 0 + assert "mook_file" in p_status.stdout + + cmd_stash = [git2cpp_path, "stash"] + p_stash = subprocess.run(cmd_stash, capture_output=True, cwd=xtl_path, text=True) + assert p_stash.returncode == 0 + + p_status_2 = subprocess.run( + cmd_status, capture_output=True, cwd=xtl_path, text=True + ) + assert p_status_2.returncode == 0 + assert "mook_file" not in p_status_2.stdout + + +def test_stash_list(xtl_clone, commit_env_config, git2cpp_path, tmp_path): + assert (tmp_path / "xtl").exists() + xtl_path = tmp_path / "xtl" + + p = xtl_path / "mook_file.txt" + p.write_text("") + + cmd_add = [git2cpp_path, "add", "mook_file.txt"] + p_add = subprocess.run(cmd_add, cwd=xtl_path, text=True) + assert p_add.returncode == 0 + + cmd_list = [git2cpp_path, "stash", "list"] + p_list = subprocess.run(cmd_list, capture_output=True, cwd=xtl_path, text=True) + assert p_list.returncode == 0 + assert "stash@" not in p_list.stdout + + cmd_stash = [git2cpp_path, "stash"] + p_stash = subprocess.run(cmd_stash, capture_output=True, cwd=xtl_path, text=True) + assert p_stash.returncode == 0 + + # p_list_2 = subprocess.run(cmd_list, capture_output=True, cwd=xtl_path, text=True) + # assert p_list_2.returncode == 0 + # print("second: \n", p_list_2.stdout, "\n\n") + # # assert "stash@{0}" in p_list_2.stdout + + +def test_stash_pop(xtl_clone, commit_env_config, git2cpp_path, tmp_path): + assert (tmp_path / "xtl").exists() + xtl_path = tmp_path / "xtl" + + p = xtl_path / "mook_file.txt" + p.write_text("") + + cmd_add = [git2cpp_path, "add", "mook_file.txt"] + p_add = subprocess.run(cmd_add, cwd=xtl_path, text=True) + assert p_add.returncode == 0 + + cmd_stash = [git2cpp_path, "stash"] + p_stash = subprocess.run(cmd_stash, capture_output=True, cwd=xtl_path, text=True) + assert p_stash.returncode == 0 + + cmd_status = [git2cpp_path, "status"] + p_status = subprocess.run(cmd_status, capture_output=True, cwd=xtl_path, text=True) + assert p_status.returncode == 0 + assert "mook_file" not in p_status.stdout + + # cmd_pop = [git2cpp_path, "stash", "pop"] + # p_pop = subprocess.run(cmd_pop, capture_output=True, cwd=xtl_path, text=True) + # assert p_pop.returncode == 0 + + # p_status_2 = subprocess.run( + # cmd_status, capture_output=True, cwd=xtl_path, text=True + # ) + # assert p_status_2.returncode == 0 + # print(p_status_2.stdout) + # assert "mook_file" in p_status.stdout + # + # cmd_list = [git2cpp_path, "stash", "list"] + # p_list = subprocess.run(cmd_list, capture_output=True, cwd=xtl_path, text=True) + # assert p_list.returncode == 0 + # assert p_list.stdout == "" + + +def test_stash_apply(xtl_clone, commit_env_config, git2cpp_path, tmp_path): + assert (tmp_path / "xtl").exists() + xtl_path = tmp_path / "xtl" + + p = xtl_path / "mook_file.txt" + p.write_text("") + + cmd_add = [git2cpp_path, "add", "mook_file.txt"] + p_add = subprocess.run(cmd_add, cwd=xtl_path, text=True) + assert p_add.returncode == 0 + + cmd_stash = [git2cpp_path, "stash"] + p_stash = subprocess.run(cmd_stash, capture_output=True, cwd=xtl_path, text=True) + assert p_stash.returncode == 0 + + cmd_status = [git2cpp_path, "status"] + p_status = subprocess.run(cmd_status, capture_output=True, cwd=xtl_path, text=True) + assert p_status.returncode == 0 + assert "mook_file" not in p_status.stdout + + # cmd_pop = [git2cpp_path, "stash", "pop"] + # p_pop = subprocess.run(cmd_pop, capture_output=True, cwd=xtl_path, text=True) + # assert p_pop.returncode == 0 + + # p_status_2 = subprocess.run( + # cmd_status, capture_output=True, cwd=xtl_path, text=True + # ) + # assert p_status_2.returncode == 0 + # print(p_status_2.stdout) + # assert "mook_file" in p_status.stdout + # + # cmd_list = [git2cpp_path, "stash", "list"] + # p_list = subprocess.run(cmd_list, capture_output=True, cwd=xtl_path, text=True) + # assert p_list.returncode == 0 + # assert "stash@{0}" in p_list.stdout