From 36af779896e821dd1bc888ccd7d7675b5a26c470 Mon Sep 17 00:00:00 2001 From: Sandrine Pataut Date: Thu, 9 Apr 2026 16:37:13 +0200 Subject: [PATCH 1/2] Add show-ref subcommand --- CMakeLists.txt | 2 ++ src/main.cpp | 2 ++ src/subcommand/showref_subcommand.cpp | 31 +++++++++++++++++ src/subcommand/showref_subcommand.hpp | 17 ++++++++++ src/utils/common.cpp | 8 +++++ src/utils/common.hpp | 2 ++ src/wrapper/repository_wrapper.cpp | 21 ++++++++++++ src/wrapper/repository_wrapper.hpp | 3 +- test/test_fetch.py | 3 +- test/test_showref.py | 48 +++++++++++++++++++++++++++ 10 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 src/subcommand/showref_subcommand.cpp create mode 100644 src/subcommand/showref_subcommand.hpp create mode 100644 test/test_showref.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 940e418..ef6fb9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,8 @@ set(GIT2CPP_SRC ${GIT2CPP_SOURCE_DIR}/subcommand/revparse_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/subcommand/rm_subcommand.cpp ${GIT2CPP_SOURCE_DIR}/subcommand/rm_subcommand.hpp + ${GIT2CPP_SOURCE_DIR}/subcommand/showref_subcommand.cpp + ${GIT2CPP_SOURCE_DIR}/subcommand/showref_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/subcommand/stash_subcommand.cpp ${GIT2CPP_SOURCE_DIR}/subcommand/stash_subcommand.hpp ${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.cpp diff --git a/src/main.cpp b/src/main.cpp index 712885e..c1ce04d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ #include "subcommand/revlist_subcommand.hpp" #include "subcommand/revparse_subcommand.hpp" #include "subcommand/rm_subcommand.hpp" +#include "subcommand/showref_subcommand.hpp" #include "subcommand/stash_subcommand.hpp" #include "subcommand/status_subcommand.hpp" #include "subcommand/tag_subcommand.hpp" @@ -63,6 +64,7 @@ int main(int argc, char** argv) rm_subcommand rm(lg2_obj, app); stash_subcommand stash(lg2_obj, app); tag_subcommand tag(lg2_obj, app); + showref_subcommand showref(lg2_obj, app); app.require_subcommand(/* min */ 0, /* max */ 1); diff --git a/src/subcommand/showref_subcommand.cpp b/src/subcommand/showref_subcommand.cpp new file mode 100644 index 0000000..3ab15ca --- /dev/null +++ b/src/subcommand/showref_subcommand.cpp @@ -0,0 +1,31 @@ +#include "showref_subcommand.hpp" + +#include + +#include "../wrapper/repository_wrapper.hpp" + +showref_subcommand::showref_subcommand(const libgit2_object&, CLI::App& app) +{ + auto* sub = app.add_subcommand("show-ref", "List references in a local repository"); + + sub->callback( + [this]() + { + this->run(); + } + ); +}; + +void showref_subcommand::run() +{ + auto directory = get_current_git_path(); + auto repo = repository_wrapper::open(directory); + + auto repo_refs = repo.reference_list(); + + for (auto r:repo_refs) + { + git_oid oid = repo.ref_name_to_id(r); + std::cout << oid_to_hex(oid) << " " << r << std::endl; + } +} diff --git a/src/subcommand/showref_subcommand.hpp b/src/subcommand/showref_subcommand.hpp new file mode 100644 index 0000000..ad1b386 --- /dev/null +++ b/src/subcommand/showref_subcommand.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include "../utils/common.hpp" + +class showref_subcommand +{ +public: + + explicit showref_subcommand(const libgit2_object&, CLI::App& app); + void run(); + +private: + + +}; diff --git a/src/utils/common.cpp b/src/utils/common.cpp index 9bf7f84..4bb7d32 100644 --- a/src/utils/common.cpp +++ b/src/utils/common.cpp @@ -142,3 +142,11 @@ std::string trim(const std::string& str) auto s = std::regex_replace(str, std::regex("^\\s+"), ""); return std::regex_replace(s, std::regex("\\s+$"), ""); } + +std::string oid_to_hex(const git_oid& oid) +{ + char oid_str[GIT_OID_SHA1_HEXSIZE + 1]; + git_oid_fmt(oid_str, &oid); + oid_str[GIT_OID_SHA1_HEXSIZE] = '\0'; + return std::string(oid_str); +} diff --git a/src/utils/common.hpp b/src/utils/common.hpp index d9059f2..afb0d88 100644 --- a/src/utils/common.hpp +++ b/src/utils/common.hpp @@ -79,3 +79,5 @@ std::vector split_input_at_newlines(std::string_view str); // Remove whitespace from start and end of a string. std::string trim(const std::string& str); + +std::string oid_to_hex(const git_oid& oid); diff --git a/src/wrapper/repository_wrapper.cpp b/src/wrapper/repository_wrapper.cpp index 08bd7ec..465856f 100644 --- a/src/wrapper/repository_wrapper.cpp +++ b/src/wrapper/repository_wrapper.cpp @@ -134,6 +134,27 @@ std::optional repository_wrapper::find_reference_dwim(std::st return rc == 0 ? std::make_optional(reference_wrapper(ref)) : std::nullopt; } + +std::vector repository_wrapper::reference_list() const +{ + git_strarray refs = {0}; + throw_if_error(git_reference_list(&refs, *this)); + std::vector result; + for (size_t i = 0; i < refs.count; ++i) + { + result.push_back(refs.strings[i]); + } + git_strarray_free(&refs); + return result; +} + +const git_oid repository_wrapper::ref_name_to_id(std::string ref_name) const +{ + git_oid ref_id; + throw_if_error(git_reference_name_to_id(&ref_id, *this, ref_name.c_str())); + return ref_id; +} + // Index index_wrapper repository_wrapper::make_index() diff --git a/src/wrapper/repository_wrapper.hpp b/src/wrapper/repository_wrapper.hpp index d630343..06173af 100644 --- a/src/wrapper/repository_wrapper.hpp +++ b/src/wrapper/repository_wrapper.hpp @@ -6,7 +6,6 @@ #include -#include "../utils/common.hpp" #include "../utils/git_exception.hpp" #include "../wrapper/annotated_commit_wrapper.hpp" #include "../wrapper/branch_wrapper.hpp" @@ -63,6 +62,8 @@ class repository_wrapper : public wrapper_base // References reference_wrapper find_reference(std::string_view ref_name) const; std::optional find_reference_dwim(std::string_view ref_name) const; + std::vector reference_list() const; + const git_oid ref_name_to_id(std::string ref_name) const; // Index index_wrapper make_index(); diff --git a/test/test_fetch.py b/test/test_fetch.py index 4510875..f59ad7e 100644 --- a/test/test_fetch.py +++ b/test/test_fetch.py @@ -1,6 +1,7 @@ -import pytest import subprocess +import pytest + @pytest.mark.parametrize("protocol", ["http", "https"]) def test_fetch_private_repo(git2cpp_path, tmp_path, run_in_tmp_path, private_test_repo, protocol): diff --git a/test/test_showref.py b/test/test_showref.py new file mode 100644 index 0000000..9e08b84 --- /dev/null +++ b/test/test_showref.py @@ -0,0 +1,48 @@ +import re +import subprocess + + +def test_showref_list(repo_init_with_commit, git2cpp_path, tmp_path): + """`show-ref` lists the repository references (heads present after init+commit).""" + cmd = [git2cpp_path, "show-ref"] + p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True) + assert p.returncode == 0 + # repo_init_with_commit in conftest creates the branch "main" + assert "refs/heads/main" in p.stdout + + +def test_showref_includes_tag(repo_init_with_commit, git2cpp_path, tmp_path): + """A created tag appears in show-ref output as refs/tags/.""" + # create a lightweight tag using the CLI under test + subprocess.run([git2cpp_path, "tag", "v1.0"], cwd=tmp_path, check=True) + + p = subprocess.run([git2cpp_path, "show-ref"], capture_output=True, cwd=tmp_path, text=True) + assert p.returncode == 0 + assert "refs/tags/v1.0" in p.stdout + + +def test_showref_line_format(repo_init_with_commit, git2cpp_path, tmp_path): + """Each line of show-ref is: <40-hex-oid> .""" + p = subprocess.run([git2cpp_path, "show-ref"], capture_output=True, cwd=tmp_path, text=True) + assert p.returncode == 0 + print(p.stdout) + + hex_re = re.compile(r"^[0-9a-f]{40}$") + for line in p.stdout.splitlines(): + line = line.strip() + if not line: + continue + parts = line.split() + # Expect at least two tokens: oid and refname + assert len(parts) >= 2 + oid, refname = parts[0], parts[1] + assert hex_re.match(oid), f"OID not a 40-char hex: {oid!r}" + assert refname.startswith("refs/"), f"Refname does not start with refs/: {refname!r}" + + +def test_showref_nogit(git2cpp_path, tmp_path): + """Running show-ref outside a repository returns an error and non-zero exit.""" + cmd = [git2cpp_path, "show-ref"] + p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True) + assert p.returncode != 0 + assert "error: could not find repository at" in p.stderr From 123dbaf9e3d0acea35090e7fc866c0fb8842d9f5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 15:11:51 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/subcommand/showref_subcommand.cpp | 2 +- src/subcommand/showref_subcommand.hpp | 2 -- src/wrapper/repository_wrapper.cpp | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/subcommand/showref_subcommand.cpp b/src/subcommand/showref_subcommand.cpp index 3ab15ca..e0835d8 100644 --- a/src/subcommand/showref_subcommand.cpp +++ b/src/subcommand/showref_subcommand.cpp @@ -23,7 +23,7 @@ void showref_subcommand::run() auto repo_refs = repo.reference_list(); - for (auto r:repo_refs) + for (auto r : repo_refs) { git_oid oid = repo.ref_name_to_id(r); std::cout << oid_to_hex(oid) << " " << r << std::endl; diff --git a/src/subcommand/showref_subcommand.hpp b/src/subcommand/showref_subcommand.hpp index ad1b386..20af21e 100644 --- a/src/subcommand/showref_subcommand.hpp +++ b/src/subcommand/showref_subcommand.hpp @@ -12,6 +12,4 @@ class showref_subcommand void run(); private: - - }; diff --git a/src/wrapper/repository_wrapper.cpp b/src/wrapper/repository_wrapper.cpp index 465856f..df5dc61 100644 --- a/src/wrapper/repository_wrapper.cpp +++ b/src/wrapper/repository_wrapper.cpp @@ -134,7 +134,6 @@ std::optional repository_wrapper::find_reference_dwim(std::st return rc == 0 ? std::make_optional(reference_wrapper(ref)) : std::nullopt; } - std::vector repository_wrapper::reference_list() const { git_strarray refs = {0};