Skip to content

Commit 638fcba

Browse files
committed
Add checkout messages
1 parent e06080f commit 638fcba

File tree

3 files changed

+129
-79
lines changed

3 files changed

+129
-79
lines changed

src/subcommand/checkout_subcommand.cpp

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,34 +20,6 @@ checkout_subcommand::checkout_subcommand(const libgit2_object&, CLI::App& app)
2020
sub->callback([this]() { this->run(); });
2121
}
2222

23-
void checkout_subcommand::print_message(repository_wrapper& repo)
24-
{
25-
auto sl = status_list_wrapper::status_list(repo);
26-
if (sl.has_tobecommited_header())
27-
{
28-
bool is_long, is_coloured = false;
29-
std::set<std::string> tracked_dir_set{};
30-
print_tobecommited(sl, tracked_dir_set, is_long, is_coloured);
31-
}
32-
else if (sl.has_notstagged_header())
33-
{
34-
std::cout << "Your local changes to the following files would be overwritten by checkout:" << std::endl;
35-
36-
for (const auto* entry : sl.get_entry_list(GIT_STATUS_WT_MODIFIED))
37-
{
38-
std::cout << "\t" << entry->head_to_index->new_file.path << std::endl;
39-
}
40-
for (const auto* entry : sl.get_entry_list(GIT_STATUS_WT_DELETED))
41-
{
42-
std::cout << "\t" << entry->head_to_index->new_file.path << std::endl;
43-
}
44-
45-
std::cout << "Please commit your changes or stash them before you switch branches.\nAborting" << std::endl;
46-
}
47-
std::cout << "Switched to branch '" << m_branch_name << "'" << std::endl;
48-
print_tracking_info(repo, sl, true);
49-
}
50-
5123
void checkout_subcommand::run()
5224
{
5325
auto directory = get_current_git_path();
@@ -65,14 +37,18 @@ void checkout_subcommand::run()
6537
{
6638
options.checkout_strategy = GIT_CHECKOUT_FORCE;
6739
}
40+
// else
41+
// {
42+
// options.checkout_strategy = GIT_CHECKOUT_SAFE;
43+
// }
6844

6945
if (m_create_flag || m_force_create_flag)
7046
{
7147
auto annotated_commit = create_local_branch(repo, m_branch_name, m_force_create_flag);
7248
checkout_tree(repo, annotated_commit, m_branch_name, options);
7349
update_head(repo, annotated_commit, m_branch_name);
7450

75-
print_message(repo);
51+
std::cout << "Switched to a new branch '" << m_branch_name << "'" << std::endl;
7652
}
7753
else
7854
{
@@ -84,10 +60,54 @@ void checkout_subcommand::run()
8460
buffer << "error: could not resolve pathspec '" << m_branch_name << "'" << std::endl;
8561
throw std::runtime_error(buffer.str());
8662
}
87-
checkout_tree(repo, *optional_commit, m_branch_name, options);
88-
update_head(repo, *optional_commit, m_branch_name);
8963

90-
print_message(repo);
64+
auto sl = status_list_wrapper::status_list(repo);
65+
try
66+
{
67+
checkout_tree(repo, *optional_commit, m_branch_name, options);
68+
update_head(repo, *optional_commit, m_branch_name);
69+
}
70+
catch (const git_exception& e)
71+
{
72+
if (sl.has_notstagged_header())
73+
{
74+
std::cout << "Your local changes to the following files would be overwritten by checkout:" << std::endl;
75+
76+
for (const auto* entry : sl.get_entry_list(GIT_STATUS_WT_MODIFIED))
77+
{
78+
std::cout << "\t" << entry->index_to_workdir->new_file.path << std::endl;
79+
}
80+
for (const auto* entry : sl.get_entry_list(GIT_STATUS_WT_DELETED))
81+
{
82+
std::cout << "\t" << entry->index_to_workdir->old_file.path << std::endl;
83+
}
84+
85+
std::cout << "Please commit your changes or stash them before you switch branches.\nAborting" << std::endl;
86+
return;
87+
}
88+
else
89+
{
90+
throw e;
91+
}
92+
return;
93+
}
94+
95+
if (sl.has_notstagged_header())
96+
{
97+
bool is_long = false;
98+
bool is_coloured = false;
99+
std::set<std::string> tracked_dir_set{};
100+
print_notstagged(sl, tracked_dir_set, is_long, is_coloured);
101+
}
102+
if (sl.has_tobecommited_header())
103+
{
104+
bool is_long = false;
105+
bool is_coloured = false;
106+
std::set<std::string> tracked_dir_set{};
107+
print_tobecommited(sl, tracked_dir_set, is_long, is_coloured);
108+
}
109+
std::cout << "Switched to branch '" << m_branch_name << "'" << std::endl;
110+
print_tracking_info(repo, sl, true);
91111
}
92112
}
93113

src/subcommand/status_subcommand.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ class status_subcommand
2424
};
2525

2626
void print_tobecommited(status_list_wrapper& sl, std::set<std::string> tracked_dir_set, bool is_long, bool is_coloured);
27+
void print_notstagged(status_list_wrapper& sl, std::set<std::string> tracked_dir_set, bool is_long, bool is_coloured);
2728
void print_tracking_info(repository_wrapper& repo, status_list_wrapper& sl, bool is_long);
2829
void status_run(status_subcommand_options fl = {});

test/test_checkout.py

Lines changed: 76 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,8 @@ def test_checkout_invalid_branch(xtl_clone, git2cpp_path, tmp_path):
9898
assert "error: could not resolve pathspec 'nonexistent'" in p_checkout.stderr
9999

100100

101-
# @pytest.mark.skip(reason="Waiting for print_tobecommited and print_tracking_info implementations")
102101
def test_checkout_with_unstaged_changes(xtl_clone, git2cpp_path, tmp_path):
103-
"""Test that checkout warns about unstaged changes"""
102+
"""Test that checkout shows unstaged changes when switching branches"""
104103
assert (tmp_path / "xtl").exists()
105104
xtl_path = tmp_path / "xtl"
106105

@@ -113,52 +112,82 @@ def test_checkout_with_unstaged_changes(xtl_clone, git2cpp_path, tmp_path):
113112
readme_path = xtl_path / "README.md"
114113
readme_path.write_text("Modified content")
115114

116-
# Try to checkout - should warn about unstaged changes
115+
# Checkout - should succeed and show the modified file status
117116
checkout_cmd = [git2cpp_path, "checkout", "newbranch"]
118117
p_checkout = subprocess.run(
119118
checkout_cmd, capture_output=True, cwd=xtl_path, text=True
120119
)
121-
print(p_checkout.stdout)
122-
# Should show warning message (once implemented)
123-
# assert (
124-
# "Your local changes to the following files would be overwritten by checkout:"
125-
# in p_checkout.stdout
126-
# )
127-
# assert "README.md" in p_checkout.stdout
128-
# assert (
129-
# "Please commit your changes or stash them before you switch branches"
130-
# in p_checkout.stdout
131-
# )
132-
133-
# Verify we stayed on master branch
134-
branch_cmd = [git2cpp_path, "branch"]
135-
p_branch = subprocess.run(branch_cmd, capture_output=True, cwd=xtl_path, text=True)
136-
assert p_branch.returncode == 0
137-
# print(p_branch.stdout)
138-
# assert "* master" in p_branch.stdout
139-
140-
141-
# def test_checkout_force_with_unstaged_changes(xtl_clone, git2cpp_path, tmp_path):
142-
# """Test that checkout -f forces checkout even with unstaged changes"""
143-
# assert (tmp_path / "xtl").exists()
144-
# xtl_path = tmp_path / "xtl"
145-
146-
# # Create a new branch
147-
# create_cmd = [git2cpp_path, 'branch', 'forcebranch']
148-
# p_create = subprocess.run(create_cmd, capture_output=True, cwd=xtl_path, text=True)
149-
# assert p_create.returncode == 0
150-
151-
# # Modify a file (unstaged change)
152-
# readme_path = xtl_path / "README.md"
153-
# readme_path.write_text("Modified content that will be lost")
154-
155-
# # Force checkout - should succeed and discard changes
156-
# checkout_cmd = [git2cpp_path, 'checkout', '-f', 'forcebranch']
157-
# p_checkout = subprocess.run(checkout_cmd, capture_output=True, cwd=xtl_path, text=True)
158-
# assert p_checkout.returncode == 0
159-
# assert "Switched to branch 'forcebranch'" in p_checkout.stdout
160-
161-
# # Verify we're on the branch
162-
# branch_cmd = [git2cpp_path, 'branch']
163-
# p_branch = subprocess.run(branch_cmd, capture_output=True, cwd=xtl_path, text=True)
164-
# assert '* forcebranch' in p_branch.stdout
120+
121+
# Should succeed and show status
122+
assert p_checkout.returncode == 0
123+
assert " M README.md" in p_checkout.stdout
124+
assert "Switched to branch 'newbranch'" in p_checkout.stdout
125+
126+
127+
@pytest.mark.parametrize("force_flag", ["", "-f", "--force"])
128+
def test_checkout_refuses_overwrite(xtl_clone, git2cpp_path, tmp_path, force_flag):
129+
"""Test that checkout refuses to switch when local changes would be overwritten, and switches when using --force"""
130+
assert (tmp_path / "xtl").exists()
131+
xtl_path = tmp_path / "xtl"
132+
133+
# Create a new branch and switch to it
134+
create_cmd = [git2cpp_path, "checkout", "-b", "newbranch"]
135+
p_create = subprocess.run(create_cmd, capture_output=True, cwd=xtl_path, text=True)
136+
assert p_create.returncode == 0
137+
138+
# Modify README.md and commit it on newbranch
139+
readme_path = xtl_path / "README.md"
140+
readme_path.write_text("Content on newbranch")
141+
142+
add_cmd = [git2cpp_path, "add", "README.md"]
143+
subprocess.run(add_cmd, cwd=xtl_path, text=True)
144+
145+
commit_cmd = [git2cpp_path, "commit", "-m", "Change on newbranch"]
146+
subprocess.run(commit_cmd, cwd=xtl_path, text=True)
147+
148+
# Switch back to master
149+
checkout_master_cmd = [git2cpp_path, "checkout", "master"]
150+
p_master = subprocess.run(
151+
checkout_master_cmd, capture_output=True, cwd=xtl_path, text=True
152+
)
153+
assert p_master.returncode == 0
154+
155+
# Now modify README.md locally (unstaged) on master
156+
readme_path.write_text("Local modification on master")
157+
158+
# Try to checkout newbranch
159+
checkout_cmd = [git2cpp_path, "checkout"]
160+
if force_flag != "":
161+
checkout_cmd.append(force_flag)
162+
checkout_cmd.append("newbranch")
163+
p_checkout = subprocess.run(
164+
checkout_cmd, capture_output=True, cwd=xtl_path, text=True
165+
)
166+
167+
if force_flag == "":
168+
assert p_checkout.returncode == 0
169+
assert (
170+
"Your local changes to the following files would be overwritten by checkout:"
171+
in p_checkout.stdout
172+
)
173+
assert "README.md" in p_checkout.stdout
174+
assert (
175+
"Please commit your changes or stash them before you switch branches"
176+
in p_checkout.stdout
177+
)
178+
179+
# Verify we're still on master (didn't switch)
180+
branch_cmd = [git2cpp_path, "branch"]
181+
p_branch = subprocess.run(
182+
branch_cmd, capture_output=True, cwd=xtl_path, text=True
183+
)
184+
assert "* master" in p_branch.stdout
185+
else:
186+
assert "Switched to branch 'newbranch'" in p_checkout.stdout
187+
188+
# Verify we switched to newbranch
189+
branch_cmd = [git2cpp_path, "branch"]
190+
p_branch = subprocess.run(
191+
branch_cmd, capture_output=True, cwd=xtl_path, text=True
192+
)
193+
assert "* newbranch" in p_branch.stdout

0 commit comments

Comments
 (0)