Skip to content

Commit 83d13f8

Browse files
committed
Add --show-current flag to the branch subcommand
1 parent 1ec65aa commit 83d13f8

File tree

3 files changed

+193
-5
lines changed

3 files changed

+193
-5
lines changed

src/subcommand/branch_subcommand.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ branch_subcommand::branch_subcommand(const libgit2_object&, CLI::App& app)
1414
sub->add_flag("-r,--remotes", m_remote_flag, "List or delete (if used with -d) the remote-tracking branches");
1515
sub->add_flag("-l,--list", m_list_flag, "List branches");
1616
sub->add_flag("-f,--force", m_force_flag, "Skips confirmation");
17+
sub->add_flag("--show-current", m_show_current_flag, "Print the name of the current branch. In detached HEAD state, nothing is printed.");
1718

1819
sub->callback([this]() { this->run(); });
1920
}
@@ -23,7 +24,12 @@ void branch_subcommand::run()
2324
auto directory = get_current_git_path();
2425
auto repo = repository_wrapper::open(directory);
2526

26-
if (m_list_flag || m_branch_name.empty())
27+
if (m_show_current_flag)
28+
{
29+
// TODO: if another flag, return usage/Generic options/Specific git-branch actions
30+
run_show_current(repo);
31+
}
32+
else if (m_list_flag || m_branch_name.empty())
2733
{
2834
run_list(repo);
2935
}
@@ -64,9 +70,20 @@ void branch_subcommand::run_deletion(repository_wrapper& repo)
6470
delete_branch(std::move(branch));
6571
}
6672

67-
6873
void branch_subcommand::run_creation(repository_wrapper& repo)
6974
{
7075
// TODO: handle specification of starting commit
7176
repo.create_branch(m_branch_name, m_force_flag);
7277
}
78+
79+
void branch_subcommand::run_show_current(const repository_wrapper& repo)
80+
{
81+
auto name = repo.head_short_name();
82+
83+
if (name == "HEAD")
84+
{
85+
return;
86+
}
87+
88+
std::cout << name << std::endl;
89+
}

src/subcommand/branch_subcommand.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ class branch_subcommand
1919
void run_list(const repository_wrapper& repo);
2020
void run_deletion(repository_wrapper& repo);
2121
void run_creation(repository_wrapper& repo);
22+
void run_show_current(const repository_wrapper& repo);
2223

2324
std::string m_branch_name = {};
2425
bool m_deletion_flag = false;
2526
bool m_all_flag = false;
2627
bool m_remote_flag = false;
2728
bool m_list_flag = false;
2829
bool m_force_flag = false;
30+
bool m_show_current_flag = false;
2931
};

test/test_branch.py

Lines changed: 172 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def test_branch_list(repo_init_with_commit, git2cpp_path, tmp_path):
99
cmd = [git2cpp_path, "branch"]
1010
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
1111
assert p.returncode == 0
12-
assert "* ma" in p.stdout
12+
assert "* main" in p.stdout
1313

1414

1515
def test_branch_create_delete(repo_init_with_commit, git2cpp_path, tmp_path):
@@ -22,15 +22,15 @@ def test_branch_create_delete(repo_init_with_commit, git2cpp_path, tmp_path):
2222
list_cmd = [git2cpp_path, "branch"]
2323
p_list = subprocess.run(list_cmd, capture_output=True, cwd=tmp_path, text=True)
2424
assert p_list.returncode == 0
25-
assert " foregone\n* ma" in p_list.stdout
25+
assert " foregone\n* main" in p_list.stdout
2626

2727
del_cmd = [git2cpp_path, "branch", "-d", "foregone"]
2828
p_del = subprocess.run(del_cmd, capture_output=True, cwd=tmp_path, text=True)
2929
assert p_del.returncode == 0
3030

3131
p_list2 = subprocess.run(list_cmd, capture_output=True, cwd=tmp_path, text=True)
3232
assert p_list2.returncode == 0
33-
assert "* ma" in p_list2.stdout
33+
assert "* main" in p_list2.stdout
3434

3535

3636
def test_branch_nogit(git2cpp_path, tmp_path):
@@ -51,3 +51,172 @@ def test_branch_new_repo(git2cpp_path, tmp_path, run_in_tmp_path):
5151
p_branch = subprocess.run(branch_cmd, cwd=tmp_path)
5252

5353
assert p_branch.returncode == 0
54+
55+
56+
def test_branch_list_flag(repo_init_with_commit, git2cpp_path, tmp_path):
57+
"""Explicit -l/--list flag behaves the same as bare 'branch'."""
58+
assert (tmp_path / "initial.txt").exists()
59+
60+
subprocess.run([git2cpp_path, "branch", "feature-a"], cwd=tmp_path, check=True)
61+
62+
for flag in ["-l", "--list"]:
63+
cmd = [git2cpp_path, "branch", flag]
64+
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
65+
assert p.returncode == 0
66+
assert " feature-a" in p.stdout
67+
assert "* main" in p.stdout
68+
69+
70+
def test_branch_list_all(xtl_clone, git2cpp_path, tmp_path):
71+
"""The -a/--all flag lists both local and remote-tracking branches."""
72+
xtl_path = tmp_path / "xtl"
73+
74+
cmd = [git2cpp_path, "branch", "-a"]
75+
p = subprocess.run(cmd, capture_output=True, cwd=xtl_path, text=True)
76+
assert p.returncode == 0
77+
assert "* master" in p.stdout
78+
assert "origin/" in p.stdout
79+
80+
81+
def test_branch_list_remotes(xtl_clone, git2cpp_path, tmp_path):
82+
"""The -r/--remotes flag lists only remote-tracking branches."""
83+
xtl_path = tmp_path / "xtl"
84+
85+
cmd = [git2cpp_path, "branch", "-r"]
86+
p = subprocess.run(cmd, capture_output=True, cwd=xtl_path, text=True)
87+
assert p.returncode == 0
88+
assert "origin/" in p.stdout
89+
# Local branch should NOT appear with * prefix
90+
assert "* master" not in p.stdout
91+
92+
93+
def test_branch_create_already_exists(repo_init_with_commit, git2cpp_path, tmp_path):
94+
"""Creating a branch that already exists should fail without --force."""
95+
assert (tmp_path / "initial.txt").exists()
96+
97+
subprocess.run([git2cpp_path, "branch", "duplicate"], cwd=tmp_path, check=True)
98+
99+
cmd = [git2cpp_path, "branch", "duplicate"]
100+
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
101+
assert p.returncode != 0
102+
103+
104+
def test_branch_create_force_overwrite(
105+
repo_init_with_commit, commit_env_config, git2cpp_path, tmp_path
106+
):
107+
"""--force allows overwriting an existing branch."""
108+
assert (tmp_path / "initial.txt").exists()
109+
110+
subprocess.run([git2cpp_path, "branch", "my-branch"], cwd=tmp_path, check=True)
111+
112+
# Add a second commit so HEAD moves forward
113+
(tmp_path / "second.txt").write_text("second")
114+
subprocess.run([git2cpp_path, "add", "second.txt"], cwd=tmp_path, check=True)
115+
subprocess.run(
116+
[git2cpp_path, "commit", "-m", "Second commit"], cwd=tmp_path, check=True
117+
)
118+
119+
# Without --force this would fail; with -f it should reset the branch to current HEAD
120+
cmd = [git2cpp_path, "branch", "-f", "my-branch"]
121+
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
122+
assert p.returncode == 0
123+
124+
125+
def test_branch_delete_nonexistent(repo_init_with_commit, git2cpp_path, tmp_path):
126+
"""Deleting a branch that doesn't exist should fail."""
127+
assert (tmp_path / "initial.txt").exists()
128+
129+
cmd = [git2cpp_path, "branch", "-d", "no-such-branch"]
130+
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
131+
assert p.returncode != 0
132+
133+
134+
def test_branch_create_multiple(repo_init_with_commit, git2cpp_path, tmp_path):
135+
"""Creating multiple branches and verifying they all appear in the listing."""
136+
assert (tmp_path / "initial.txt").exists()
137+
138+
branches = ["alpha", "beta", "gamma"]
139+
for name in branches:
140+
p = subprocess.run(
141+
[git2cpp_path, "branch", name], capture_output=True, cwd=tmp_path, text=True
142+
)
143+
assert p.returncode == 0
144+
145+
cmd = [git2cpp_path, "branch"]
146+
p_list = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
147+
assert p_list.returncode == 0
148+
for name in branches:
149+
assert f" {name}" in p_list.stdout
150+
# Current branch is still starred
151+
assert "* main" in p_list.stdout
152+
153+
154+
def test_branch_show_current(repo_init_with_commit, git2cpp_path, tmp_path):
155+
"""--show-current prints the current branch name."""
156+
assert (tmp_path / "initial.txt").exists()
157+
158+
cmd = [git2cpp_path, "branch", "--show-current"]
159+
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
160+
assert p.returncode == 0
161+
print(p.stdout)
162+
# Default branch after init is "main" or "master" depending on git config
163+
assert p.stdout.strip() == "main"
164+
165+
166+
def test_branch_show_current_after_create_and_switch(
167+
repo_init_with_commit, git2cpp_path, tmp_path
168+
):
169+
"""--show-current reflects the branch we switched to."""
170+
assert (tmp_path / "initial.txt").exists()
171+
172+
subprocess.run(
173+
[git2cpp_path, "checkout", "-b", "new-feature"], cwd=tmp_path, check=True
174+
)
175+
176+
cmd = [git2cpp_path, "branch", "--show-current"]
177+
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
178+
assert p.returncode == 0
179+
assert p.stdout == "new-feature\n"
180+
181+
182+
def test_branch_show_current_detached_head(
183+
repo_init_with_commit, git2cpp_path, tmp_path
184+
):
185+
"""--show-current prints nothing when HEAD is detached."""
186+
assert (tmp_path / "initial.txt").exists()
187+
188+
result = subprocess.run(
189+
[git2cpp_path, "rev-parse", "HEAD"],
190+
capture_output=True,
191+
cwd=tmp_path,
192+
text=True,
193+
check=True,
194+
)
195+
head_sha = result.stdout.strip()
196+
subprocess.run([git2cpp_path, "checkout", head_sha], cwd=tmp_path, check=True)
197+
198+
cmd = [git2cpp_path, "branch", "--show-current"]
199+
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
200+
assert p.returncode == 0
201+
assert p.stdout == ""
202+
203+
204+
def test_branch_show_current_new_repo(git2cpp_path, tmp_path, run_in_tmp_path):
205+
"""--show-current prints the branch name even on a fresh repo with no commits (unborn HEAD)."""
206+
assert list(tmp_path.iterdir()) == []
207+
208+
subprocess.run([git2cpp_path, "init", "-b", "main"], cwd=tmp_path, check=True)
209+
210+
cmd = [git2cpp_path, "branch", "--show-current"]
211+
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
212+
assert p.returncode == 0
213+
# Default branch after init is "main" or "master" depending on git config
214+
assert p.stdout.strip() == "main"
215+
216+
217+
def test_branch_show_current_nogit(git2cpp_path, tmp_path):
218+
"""--show-current fails gracefully outside a git repository."""
219+
cmd = [git2cpp_path, "branch", "--show-current"]
220+
p = subprocess.run(cmd, capture_output=True, cwd=tmp_path, text=True)
221+
assert p.returncode != 0
222+
assert "error: could not find repository at" in p.stderr

0 commit comments

Comments
 (0)