diff --git a/CMakeLists.txt b/CMakeLists.txt index 49f3295..90e3246 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,8 @@ set(GIT2CPP_SRC ${GIT2CPP_SOURCE_DIR}/subcommand/add_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/subcommand/branch_subcommand.cpp ${GIT2CPP_SOURCE_DIR}/subcommand/branch_subcommand.hpp + ${GIT2CPP_SOURCE_DIR}/subcommand/checkout_subcommand.cpp + ${GIT2CPP_SOURCE_DIR}/subcommand/checkout_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/subcommand/init_subcommand.cpp ${GIT2CPP_SOURCE_DIR}/subcommand/init_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.cpp @@ -51,12 +53,16 @@ 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/annotated_commit_wrapper.cpp + ${GIT2CPP_SOURCE_DIR}/wrapper/annotated_commit_wrapper.hpp ${GIT2CPP_SOURCE_DIR}/wrapper/branch_wrapper.cpp ${GIT2CPP_SOURCE_DIR}/wrapper/branch_wrapper.hpp ${GIT2CPP_SOURCE_DIR}/wrapper/commit_wrapper.cpp ${GIT2CPP_SOURCE_DIR}/wrapper/commit_wrapper.hpp ${GIT2CPP_SOURCE_DIR}/wrapper/index_wrapper.cpp ${GIT2CPP_SOURCE_DIR}/wrapper/index_wrapper.hpp + ${GIT2CPP_SOURCE_DIR}/wrapper/object_wrapper.cpp + ${GIT2CPP_SOURCE_DIR}/wrapper/object_wrapper.hpp ${GIT2CPP_SOURCE_DIR}/wrapper/refs_wrapper.cpp ${GIT2CPP_SOURCE_DIR}/wrapper/refs_wrapper.hpp ${GIT2CPP_SOURCE_DIR}/wrapper/repository_wrapper.cpp diff --git a/src/main.cpp b/src/main.cpp index a04444a..55e8a24 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "version.hpp" #include "subcommand/add_subcommand.hpp" #include "subcommand/branch_subcommand.hpp" +#include "subcommand/checkout_subcommand.hpp" #include "subcommand/init_subcommand.hpp" #include "subcommand/status_subcommand.hpp" @@ -25,6 +26,7 @@ int main(int argc, char** argv) status_subcommand status(lg2_obj, app); add_subcommand add(lg2_obj, app); branch_subcommand(lg2_obj, app); + checkout_subcommand(lg2_obj, app); app.parse(argc, argv); diff --git a/src/subcommand/checkout_subcommand.cpp b/src/subcommand/checkout_subcommand.cpp new file mode 100644 index 0000000..67b8644 --- /dev/null +++ b/src/subcommand/checkout_subcommand.cpp @@ -0,0 +1,127 @@ +#include +#include + +#include "../subcommand/checkout_subcommand.hpp" +#include "../utils/git_exception.hpp" +#include "../wrapper/repository_wrapper.hpp" + +checkout_subcommand::checkout_subcommand(const libgit2_object&, CLI::App& app) +{ + auto* sub = app.add_subcommand("checkout", "Switch branches or restore working tree files"); + + sub->add_option("", m_branch_name, "Branch to checkout"); + sub->add_flag("-b", m_create_flag, "Create a new branch before checking it out"); + sub->add_flag("-B", m_force_create_flag, "Create a new branch or reset it if it exists before checking it out"); + sub->add_flag("-f, --force", m_force_checkout_flag, "When switching branches, proceed even if the index or the working tree differs from HEAD, and even if there are untracked files in the way"); + + sub->callback([this]() { this->run(); }); +} + +void checkout_subcommand::run() +{ + auto directory = get_current_git_path(); + auto repo = repository_wrapper::open(directory); + + if (repo.state() != GIT_REPOSITORY_STATE_NONE) + { + throw std::runtime_error("Cannot checkout, repository is in unexpected state"); + } + + git_checkout_options options; + git_checkout_options_init(&options, GIT_CHECKOUT_OPTIONS_VERSION); + + if(m_force_checkout_flag) + { + options.checkout_strategy = GIT_CHECKOUT_FORCE; + } + + if (m_create_flag || m_force_create_flag) + { + auto annotated_commit = create_local_branch(repo, m_branch_name, m_force_create_flag); + checkout_tree(repo, annotated_commit, m_branch_name, options); + update_head(repo, annotated_commit, m_branch_name); + } + else + { + auto optional_commit = resolve_local_ref(repo, m_branch_name); + if (!optional_commit) + { + // TODO: handle remote refs + std::ostringstream buffer; + buffer << "error: could not resolve pathspec '" << m_branch_name << "'" << std::endl; + throw std::runtime_error(buffer.str()); + } + checkout_tree(repo, *optional_commit, m_branch_name, options); + update_head(repo, *optional_commit, m_branch_name); + } +} + +std::optional 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, + const std::string& target_name, + bool force +) +{ + auto branch = repo.create_branch(target_name, force); + return repo.find_annotated_commit(branch); +} + +void checkout_subcommand::checkout_tree +( + const repository_wrapper& repo, + const annotated_commit_wrapper& target_annotated_commit, + const std::string& target_name, + const git_checkout_options& options +) +{ + auto target_commit = repo.find_commit(target_annotated_commit.oid()); + throwIfError(git_checkout_tree(repo, target_commit, &options)); +} + +void checkout_subcommand::update_head +( + repository_wrapper& repo, + const annotated_commit_wrapper& target_annotated_commit, + const std::string& target_name +) +{ + std::string_view annotated_ref = target_annotated_commit.reference_name(); + if (!annotated_ref.empty()) + { + auto ref = repo.find_reference(annotated_ref); + if (ref.is_remote()) + { + auto branch = repo.create_branch(target_name, target_annotated_commit); + repo.set_head(branch.reference_name()); + } + else + { + repo.set_head(annotated_ref); + } + } + else + { + repo.set_head_detached(target_annotated_commit); + } +} diff --git a/src/subcommand/checkout_subcommand.hpp b/src/subcommand/checkout_subcommand.hpp new file mode 100644 index 0000000..533b46a --- /dev/null +++ b/src/subcommand/checkout_subcommand.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +#include + +#include "../utils/common.hpp" +#include "../wrapper/repository_wrapper.hpp" + +class checkout_subcommand +{ +public: + + explicit checkout_subcommand(const libgit2_object&, CLI::App& app); + void run(); + +private: + + std::optional resolve_local_ref + ( + const repository_wrapper& repo, + const std::string& target_name + ); + + annotated_commit_wrapper create_local_branch + ( + repository_wrapper& repo, + const std::string& target_name, + bool force + ); + + void checkout_tree + ( + const repository_wrapper& repo, + const annotated_commit_wrapper& target_annotated_commit, + const std::string& target_name, + const git_checkout_options& options + ); + + void update_head + ( + repository_wrapper& repo, + const annotated_commit_wrapper& target_annotated_commit, + const std::string& target_name + ); + + std::string m_branch_name = {}; + bool m_create_flag = false; + bool m_force_create_flag = false; + bool m_force_checkout_flag = false; +}; diff --git a/src/wrapper/annotated_commit_wrapper.cpp b/src/wrapper/annotated_commit_wrapper.cpp new file mode 100644 index 0000000..da38620 --- /dev/null +++ b/src/wrapper/annotated_commit_wrapper.cpp @@ -0,0 +1,23 @@ +#include "../wrapper/annotated_commit_wrapper.hpp" + +annotated_commit_wrapper::annotated_commit_wrapper(git_annotated_commit* commit) + : base_type(commit) +{ +} + +annotated_commit_wrapper::~annotated_commit_wrapper() +{ + git_annotated_commit_free(p_resource); + p_resource = nullptr; +} + +const git_oid& annotated_commit_wrapper::oid() const +{ + return *git_annotated_commit_id(p_resource); +} + +std::string_view annotated_commit_wrapper::reference_name() const +{ + const char* res = git_annotated_commit_ref(*this); + return res ? res : std::string_view{}; +} diff --git a/src/wrapper/annotated_commit_wrapper.hpp b/src/wrapper/annotated_commit_wrapper.hpp new file mode 100644 index 0000000..7bb5a4c --- /dev/null +++ b/src/wrapper/annotated_commit_wrapper.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include + +#include "../wrapper/wrapper_base.hpp" + +class annotated_commit_wrapper : public wrapper_base +{ +public: + + using base_type = wrapper_base; + + ~annotated_commit_wrapper(); + + annotated_commit_wrapper(annotated_commit_wrapper&&) noexcept = default; + annotated_commit_wrapper& operator=(annotated_commit_wrapper&&) noexcept = default; + + const git_oid& oid() const; + std::string_view reference_name() const; + +private: + + annotated_commit_wrapper(git_annotated_commit* commit); + + friend class repository_wrapper; +}; + diff --git a/src/wrapper/branch_wrapper.cpp b/src/wrapper/branch_wrapper.cpp index afc5d64..bc0af64 100644 --- a/src/wrapper/branch_wrapper.cpp +++ b/src/wrapper/branch_wrapper.cpp @@ -19,10 +19,16 @@ branch_wrapper::~branch_wrapper() std::string_view branch_wrapper::name() const { const char* out = nullptr; - throwIfError(git_branch_name(&out, p_resource)); + throwIfError(git_branch_name(&out, *this)); return std::string_view(out); } +std::string_view branch_wrapper::reference_name() const +{ + const char* out = git_reference_name(*this); + return out ? out : std::string_view(); +} + void delete_branch(branch_wrapper&& branch) { throwIfError(git_branch_delete(branch)); diff --git a/src/wrapper/branch_wrapper.hpp b/src/wrapper/branch_wrapper.hpp index 436c9e4..d73bad0 100644 --- a/src/wrapper/branch_wrapper.hpp +++ b/src/wrapper/branch_wrapper.hpp @@ -21,6 +21,7 @@ class branch_wrapper : public wrapper_base branch_wrapper& operator=(branch_wrapper&&) = default; std::string_view name() const; + std::string_view reference_name() const; private: diff --git a/src/wrapper/commit_wrapper.cpp b/src/wrapper/commit_wrapper.cpp index e8168e0..66fcd4f 100644 --- a/src/wrapper/commit_wrapper.cpp +++ b/src/wrapper/commit_wrapper.cpp @@ -1,6 +1,9 @@ -#include "../utils/git_exception.hpp" #include "../wrapper/commit_wrapper.hpp" -#include "../wrapper/repository_wrapper.hpp" + +commit_wrapper::commit_wrapper(git_commit* commit) + : base_type(commit) +{ +} commit_wrapper::~commit_wrapper() { @@ -8,13 +11,13 @@ commit_wrapper::~commit_wrapper() p_resource = nullptr; } - -commit_wrapper commit_wrapper::from_reference_name(const repository_wrapper& repo, const std::string& ref_name) +commit_wrapper::operator git_object*() const noexcept { - git_oid oid_parent_commit; - throwIfError(git_reference_name_to_id(&oid_parent_commit, repo, ref_name.c_str())); + return reinterpret_cast(p_resource); +} - commit_wrapper cw; - throwIfError(git_commit_lookup(&(cw.p_resource), repo, &oid_parent_commit)); - return cw; +const git_oid& commit_wrapper::oid() const +{ + return *git_commit_id(p_resource); } + diff --git a/src/wrapper/commit_wrapper.hpp b/src/wrapper/commit_wrapper.hpp index 379bd44..201cbce 100644 --- a/src/wrapper/commit_wrapper.hpp +++ b/src/wrapper/commit_wrapper.hpp @@ -1,26 +1,27 @@ #pragma once -#include - #include #include "../wrapper/wrapper_base.hpp" -class repository_wrapper; - class commit_wrapper : public wrapper_base { public: + using base_type = wrapper_base; + ~commit_wrapper(); commit_wrapper(commit_wrapper&&) noexcept = default; commit_wrapper& operator=(commit_wrapper&&) noexcept = default; - static commit_wrapper - from_reference_name(const repository_wrapper& repo, const std::string& ref_name = "HEAD"); + operator git_object*() const noexcept; + + const git_oid& oid() const; private: - commit_wrapper() = default; + commit_wrapper(git_commit* commit); + + friend class repository_wrapper; }; diff --git a/src/wrapper/object_wrapper.cpp b/src/wrapper/object_wrapper.cpp new file mode 100644 index 0000000..cea6175 --- /dev/null +++ b/src/wrapper/object_wrapper.cpp @@ -0,0 +1,17 @@ +#include "../wrapper/object_wrapper.hpp" + +object_wrapper::object_wrapper(git_object* obj) + : base_type(obj) +{ +} + +object_wrapper::~object_wrapper() +{ + git_object_free(p_resource); + p_resource = nullptr; +} + +const git_oid& object_wrapper::oid() const +{ + return *git_object_id(*this); +} diff --git a/src/wrapper/object_wrapper.hpp b/src/wrapper/object_wrapper.hpp new file mode 100644 index 0000000..b0b86bf --- /dev/null +++ b/src/wrapper/object_wrapper.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "../wrapper/wrapper_base.hpp" + +class object_wrapper : public wrapper_base +{ +public: + + using base_type = wrapper_base; + + ~object_wrapper(); + + object_wrapper(object_wrapper&&) noexcept = default; + object_wrapper& operator=(object_wrapper&&) noexcept = default; + + const git_oid& oid() const; + +private: + + object_wrapper(git_object* obj); + + friend class repository_wrapper; +}; diff --git a/src/wrapper/refs_wrapper.cpp b/src/wrapper/refs_wrapper.cpp index 21ef84b..571ca52 100644 --- a/src/wrapper/refs_wrapper.cpp +++ b/src/wrapper/refs_wrapper.cpp @@ -16,3 +16,8 @@ std::string reference_wrapper::short_name() const { return git_reference_shorthand(p_resource); } + +bool reference_wrapper::is_remote() const +{ + return git_reference_is_remote(*this); +} diff --git a/src/wrapper/refs_wrapper.hpp b/src/wrapper/refs_wrapper.hpp index dfe32a7..2ad4b21 100644 --- a/src/wrapper/refs_wrapper.hpp +++ b/src/wrapper/refs_wrapper.hpp @@ -1,6 +1,6 @@ #pragma once - #include +#include #include @@ -18,6 +18,7 @@ class reference_wrapper : public wrapper_base reference_wrapper& operator=(reference_wrapper&&) noexcept = default; std::string short_name() const; + bool is_remote() const; private: diff --git a/src/wrapper/repository_wrapper.cpp b/src/wrapper/repository_wrapper.cpp index c35eece..9c9a0e0 100644 --- a/src/wrapper/repository_wrapper.cpp +++ b/src/wrapper/repository_wrapper.cpp @@ -7,20 +7,25 @@ repository_wrapper::~repository_wrapper() p_resource=nullptr; } -repository_wrapper repository_wrapper::open(const std::string& directory) +repository_wrapper repository_wrapper::open(std::string_view directory) { repository_wrapper rw; - throwIfError(git_repository_open(&(rw.p_resource), directory.c_str())); + throwIfError(git_repository_open(&(rw.p_resource), directory.data())); return rw; } -repository_wrapper repository_wrapper::init(const std::string& directory, bool bare) +repository_wrapper repository_wrapper::init(std::string_view directory, bool bare) { repository_wrapper rw; - throwIfError(git_repository_init(&(rw.p_resource), directory.c_str(), bare)); + throwIfError(git_repository_init(&(rw.p_resource), directory.data(), bare)); return rw; } +git_repository_state_t repository_wrapper::state() const +{ + return git_repository_state_t(git_repository_state(*this)); +} + reference_wrapper repository_wrapper::head() const { git_reference* ref; @@ -28,28 +33,49 @@ reference_wrapper repository_wrapper::head() const return reference_wrapper(ref); } +reference_wrapper repository_wrapper::find_reference(std::string_view ref_name) const +{ + git_reference* ref; + throwIfError(git_reference_lookup(&ref, *this, ref_name.data())); + return reference_wrapper(ref); +} + +std::optional repository_wrapper::find_reference_dwim(std::string_view ref_name) const +{ + git_reference* ref; + int rc = git_reference_dwim(&ref, *this, ref_name.data()); + return rc == 0 ? std::make_optional(reference_wrapper(ref)) : std::nullopt; +} + index_wrapper repository_wrapper::make_index() { index_wrapper index = index_wrapper::init(*this); return index; } -branch_wrapper repository_wrapper::create_branch(const std::string& name, bool force) +branch_wrapper repository_wrapper::create_branch(std::string_view name, bool force) { - return create_branch(name, commit_wrapper::from_reference_name(*this), force); + return create_branch(name, find_commit(), force); } -branch_wrapper repository_wrapper::create_branch(const std::string& name, const commit_wrapper& commit, bool force) +branch_wrapper repository_wrapper::create_branch(std::string_view name, const commit_wrapper& commit, bool force) { git_reference* branch = nullptr; - throwIfError(git_branch_create(&branch, *this, name.c_str(), commit, force)); + throwIfError(git_branch_create(&branch, *this, name.data(), commit, force)); return branch_wrapper(branch); } -branch_wrapper repository_wrapper::find_branch(const std::string& name) +branch_wrapper repository_wrapper::create_branch(std::string_view name, const annotated_commit_wrapper& commit, bool force) { git_reference* branch = nullptr; - throwIfError(git_branch_lookup(&branch, *this, name.c_str(), GIT_BRANCH_LOCAL)); + throwIfError(git_branch_create_from_annotated(&branch, *this, name.data(), commit, force)); + return branch_wrapper(branch); +} + +branch_wrapper repository_wrapper::find_branch(std::string_view name) const +{ + git_reference* branch = nullptr; + throwIfError(git_branch_lookup(&branch, *this, name.data(), GIT_BRANCH_LOCAL)); return branch_wrapper(branch); } @@ -59,3 +85,41 @@ branch_iterator repository_wrapper::iterate_branches(git_branch_t type) const throwIfError(git_branch_iterator_new(&iter, *this, type)); return branch_iterator(iter); } + +commit_wrapper repository_wrapper::find_commit(std::string_view ref_name) const +{ + git_oid oid_parent_commit; + throwIfError(git_reference_name_to_id(&oid_parent_commit, *this, ref_name.data())); + return find_commit(oid_parent_commit); +} + +commit_wrapper repository_wrapper::find_commit(const git_oid& id) const +{ + git_commit* commit; + throwIfError(git_commit_lookup(&commit, *this, &id)); + return commit_wrapper(commit); +} + +annotated_commit_wrapper repository_wrapper::find_annotated_commit(const git_oid& id) const +{ + git_annotated_commit* commit; + throwIfError(git_annotated_commit_lookup(&commit, *this, &id)); + return annotated_commit_wrapper(commit); +} + +std::optional repository_wrapper::revparse_single(std::string_view spec) const +{ + git_object* obj; + int rc = git_revparse_single(&obj, *this, spec.data()); + return rc == 0 ? std::make_optional(object_wrapper(obj)) : std::nullopt; +} + +void repository_wrapper::set_head(std::string_view ref_name) +{ + throwIfError(git_repository_set_head(*this, ref_name.data())); +} + +void repository_wrapper::set_head_detached(const annotated_commit_wrapper& commit) +{ + throwIfError(git_repository_set_head_detached_from_annotated(*this, commit)); +} diff --git a/src/wrapper/repository_wrapper.hpp b/src/wrapper/repository_wrapper.hpp index cf07a20..a33c4a3 100644 --- a/src/wrapper/repository_wrapper.hpp +++ b/src/wrapper/repository_wrapper.hpp @@ -1,12 +1,17 @@ #pragma once -#include +#include +#include +#include #include +#include "../utils/git_exception.hpp" +#include "../wrapper/annotated_commit_wrapper.hpp" #include "../wrapper/branch_wrapper.hpp" #include "../wrapper/commit_wrapper.hpp" #include "../wrapper/index_wrapper.hpp" +#include "../wrapper/object_wrapper.hpp" #include "../wrapper/refs_wrapper.hpp" #include "../wrapper/wrapper_base.hpp" @@ -19,21 +24,54 @@ class repository_wrapper : public wrapper_base repository_wrapper(repository_wrapper&&) noexcept = default; repository_wrapper& operator=(repository_wrapper&&) noexcept = default; - static repository_wrapper init(const std::string& directory, bool bare); - static repository_wrapper open(const std::string& directory); + static repository_wrapper init(std::string_view directory, bool bare); + static repository_wrapper open(std::string_view directory); + git_repository_state_t state() const; + + // References reference_wrapper head() const; + reference_wrapper find_reference(std::string_view ref_name) const; + std::optional find_reference_dwim(std::string_view ref_name) const; + // Index index_wrapper make_index(); - branch_wrapper create_branch(const std::string& name, bool force); - branch_wrapper create_branch(const std::string& name, const commit_wrapper& commit, bool force); + // Branches + branch_wrapper create_branch(std::string_view name, bool force); + branch_wrapper create_branch(std::string_view name, const commit_wrapper& commit, bool force); + branch_wrapper create_branch(std::string_view name, const annotated_commit_wrapper& commit, bool force); - branch_wrapper find_branch(const std::string& name); + branch_wrapper find_branch(std::string_view name) const; branch_iterator iterate_branches(git_branch_t type) const; + // Commits + commit_wrapper find_commit(std::string_view ref_name = "HEAD") const; + commit_wrapper find_commit(const git_oid& id) const; + + // Annotated commits + annotated_commit_wrapper find_annotated_commit(const git_oid& id) const; + + template T> + annotated_commit_wrapper find_annotated_commit(const T& wrapper) const; + + // Objects + std::optional revparse_single(std::string_view spec) const; + + // Set head + void set_head(std::string_view ref_name); + void set_head_detached(const annotated_commit_wrapper& commit); + private: repository_wrapper() = default; }; + +template T> +annotated_commit_wrapper repository_wrapper::find_annotated_commit(const T& wrapper) const +{ + git_annotated_commit* commit; + throwIfError(git_annotated_commit_from_ref(&commit, *this, wrapper)); + return annotated_commit_wrapper(commit); +} diff --git a/src/wrapper/wrapper_base.hpp b/src/wrapper/wrapper_base.hpp index 424f274..08a5662 100644 --- a/src/wrapper/wrapper_base.hpp +++ b/src/wrapper/wrapper_base.hpp @@ -1,5 +1,6 @@ #pragma once +#include template class wrapper_base diff --git a/test/conftest.py b/test/conftest.py index 5f8435e..75c398e 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -2,7 +2,6 @@ from pathlib import Path import pytest - # Fixture to run test in current tmp_path @pytest.fixture def run_in_tmp_path(tmp_path): @@ -11,7 +10,12 @@ def run_in_tmp_path(tmp_path): yield os.chdir(original_cwd) - @pytest.fixture(scope='session') def git2cpp_path(): return Path(__file__).parent.parent / 'build' / 'git2cpp' + +@pytest.fixture +def rename_git(): + os.rename("test/data/status_data/embedded_git/", "test/data/status_data/.git/") + yield + os.rename("test/data/status_data/.git/", "test/data/status_data/embedded_git/") diff --git a/test/test_branch.py b/test/test_branch.py index c6d5882..5265a62 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -3,12 +3,6 @@ import pytest -@pytest.fixture -def rename_git(): - os.rename("test/data/status_data/embedded_git/", "test/data/status_data/.git/") - yield - os.rename("test/data/status_data/.git/", "test/data/status_data/embedded_git/") - def test_branch_list(rename_git, git2cpp_path): cmd = [git2cpp_path, 'branch'] p = subprocess.run(cmd, capture_output=True, cwd="test/data/status_data", text=True) diff --git a/test/test_checkout.py b/test/test_checkout.py new file mode 100644 index 0000000..857d5e3 --- /dev/null +++ b/test/test_checkout.py @@ -0,0 +1,39 @@ +import os +import subprocess + +import pytest + +def test_checkout(rename_git, git2cpp_path): + create_cmd = [git2cpp_path, 'branch', 'foregone'] + subprocess.run(create_cmd, capture_output=True, cwd="test/data/status_data", text=True) + + checkout_cmd = [git2cpp_path, 'checkout', 'foregone'] + p = subprocess.run(checkout_cmd, capture_output=True, cwd="test/data/status_data", text=True) + assert(p.stdout == ''); + + branch_cmd = [git2cpp_path, 'branch'] + p2 = subprocess.run(branch_cmd, capture_output=True, cwd="test/data/status_data", text=True) + assert(p2.stdout == '* foregone\n main\n') + + checkout_cmd[2] = 'main' + subprocess.run(checkout_cmd, capture_output=True, cwd="test/data/status_data", text=True) + + del_cmd = [git2cpp_path, 'branch', '-d', 'foregone'] + subprocess.run(del_cmd, cwd="test/data/status_data", text=True) + +def test_checkout_b(rename_git, git2cpp_path): + checkout_cmd = [git2cpp_path, 'checkout', '-b', 'foregone'] + p = subprocess.run(checkout_cmd, capture_output=True, cwd="test/data/status_data", text=True) + assert(p.stdout == ''); + + branch_cmd = [git2cpp_path, 'branch'] + p2 = subprocess.run(branch_cmd, capture_output=True, cwd="test/data/status_data", text=True) + assert(p2.stdout == '* foregone\n main\n') + + checkout_cmd.remove('-b') + checkout_cmd[2] = 'main' + subprocess.run(checkout_cmd, cwd="test/data/status_data", text=True) + + del_cmd = [git2cpp_path, 'branch', '-d', 'foregone'] + subprocess.run(del_cmd, cwd="test/data/status_data", text=True) +