-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathtest_checkout.py
More file actions
279 lines (207 loc) · 11.1 KB
/
test_checkout.py
File metadata and controls
279 lines (207 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
import subprocess
import pytest
from .conftest import strip_ansi_colours
def test_checkout(repo_init_with_commit, git2cpp_path, tmp_path):
assert (tmp_path / "initial.txt").exists()
create_cmd = [git2cpp_path, "branch", "foregone"]
p_create = subprocess.run(create_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_create.returncode == 0
checkout_cmd = [git2cpp_path, "checkout", "foregone"]
p_checkout = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_checkout.returncode == 0
assert "Switched to branch 'foregone'" in p_checkout.stdout
branch_cmd = [git2cpp_path, "branch"]
p_branch = subprocess.run(branch_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_branch.returncode == 0
assert p_branch.stdout == "* foregone\n main\n"
checkout_cmd[2] = "main"
p_checkout2 = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_checkout2.returncode == 0
assert "Switched to branch 'main'" in p_checkout2.stdout
def test_checkout_b(repo_init_with_commit, git2cpp_path, tmp_path):
assert (tmp_path / "initial.txt").exists()
checkout_cmd = [git2cpp_path, "checkout", "-b", "foregone"]
p_checkout = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_checkout.returncode == 0
assert "Switched to a new branch 'foregone'" in p_checkout.stdout
branch_cmd = [git2cpp_path, "branch"]
p_branch = subprocess.run(branch_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_branch.returncode == 0
assert p_branch.stdout == "* foregone\n main\n"
checkout_cmd.remove("-b")
checkout_cmd[2] = "main"
p_checkout2 = subprocess.run(checkout_cmd, cwd=tmp_path, text=True)
assert p_checkout2.returncode == 0
p_branch2 = subprocess.run(branch_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_branch2.returncode == 0
assert p_branch2.stdout == " foregone\n* main\n"
def test_checkout_B_force_create(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test checkout -B to force create or reset a branch"""
assert (tmp_path / "initial.txt").exists()
# Create a branch first
create_cmd = [git2cpp_path, "branch", "resetme"]
p_create = subprocess.run(create_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_create.returncode == 0
# Use -B to reset it (should not fail even if branch exists)
checkout_cmd = [git2cpp_path, "checkout", "-B", "resetme"]
p_checkout = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_checkout.returncode == 0
assert "Switched to a new branch 'resetme'" in p_checkout.stdout
# Verify we're on the branch
branch_cmd = [git2cpp_path, "branch"]
p_branch = subprocess.run(branch_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_branch.returncode == 0
assert "* resetme" in p_branch.stdout
def test_checkout_invalid_branch(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test that checkout fails gracefully with invalid branch name"""
assert (tmp_path / "initial.txt").exists()
# Try to checkout non-existent branch
checkout_cmd = [git2cpp_path, "checkout", "nonexistent"]
p_checkout = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
# Should fail with error message
assert p_checkout.returncode != 0
assert "error: could not resolve pathspec 'nonexistent'" in p_checkout.stderr
def test_checkout_with_unstaged_changes(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test that checkout shows unstaged changes when switching branches"""
initial_file = tmp_path / "initial.txt"
assert (initial_file).exists()
# Create a new branch
create_cmd = [git2cpp_path, "branch", "newbranch"]
p_create = subprocess.run(create_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_create.returncode == 0
# Modify a file (unstaged change)
initial_file.write_text("Modified content")
# Checkout - should succeed and show the modified file status
checkout_cmd = [git2cpp_path, "checkout", "newbranch"]
p_checkout = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
# Should succeed and show status
assert p_checkout.returncode == 0
p_checkout.stdout = strip_ansi_colours(p_checkout.stdout)
assert " M initial.txt" in p_checkout.stdout
assert "Switched to branch 'newbranch'" in p_checkout.stdout
@pytest.mark.parametrize("force_flag", ["", "-f", "--force"])
def test_checkout_refuses_overwrite(
repo_init_with_commit, commit_env_config, git2cpp_path, tmp_path, force_flag
):
"""Test that checkout refuses to switch when local changes would be overwritten, and switches when using --force"""
initial_file = tmp_path / "initial.txt"
assert (initial_file).exists()
# Create a new branch and switch to it
create_cmd = [git2cpp_path, "checkout", "-b", "newbranch"]
p_create = subprocess.run(create_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_create.returncode == 0
# Modify initial.txt and commit it on newbranch
initial_file.write_text("Content on newbranch")
add_cmd = [git2cpp_path, "add", "initial.txt"]
subprocess.run(add_cmd, cwd=tmp_path, text=True)
commit_cmd = [git2cpp_path, "commit", "-m", "Change on newbranch"]
subprocess.run(commit_cmd, cwd=tmp_path, text=True)
# Switch back to default branch
checkout_default_cmd = [git2cpp_path, "checkout", "main"]
p_default = subprocess.run(checkout_default_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_default.returncode == 0
# Now modify initial.txt locally (unstaged) on default branch
initial_file.write_text("Local modification on main")
# Try to checkout newbranch
checkout_cmd = [git2cpp_path, "checkout"]
if force_flag != "":
checkout_cmd.append(force_flag)
checkout_cmd.append("newbranch")
p_checkout = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
if force_flag == "":
assert p_checkout.returncode != 0
assert (
"Your local changes to the following files would be overwritten by checkout:"
in p_checkout.stdout
)
assert "initial.txt" in p_checkout.stdout
assert (
"Please commit your changes or stash them before you switch branches"
in p_checkout.stdout
)
# Verify we're still on default branch (didn't switch)
branch_cmd = [git2cpp_path, "branch"]
p_branch = subprocess.run(branch_cmd, capture_output=True, cwd=tmp_path, text=True)
assert "* main" in p_branch.stdout
else:
assert "Switched to branch 'newbranch'" in p_checkout.stdout
# Verify we switched to newbranch
branch_cmd = [git2cpp_path, "branch"]
p_branch = subprocess.run(branch_cmd, capture_output=True, cwd=tmp_path, text=True)
assert "* newbranch" in p_branch.stdout
def test_checkout_file_restores_modified_file(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test that checkout -- <file> discards working tree changes"""
initial_file = tmp_path / "initial.txt"
original_content = initial_file.read_text()
# Modify the file (unstaged)
initial_file.write_text("Modified content")
assert initial_file.read_text() == "Modified content"
# Restore it via checkout -- <file>
checkout_cmd = [git2cpp_path, "checkout", "--", "initial.txt"]
p = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p.returncode == 0
assert initial_file.read_text() == original_content
def test_checkout_file_restores_multiple_files(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test that checkout -- <file1> <file2> restores multiple files at once"""
initial_file = tmp_path / "initial.txt"
# Create and commit a second file first
second_file = tmp_path / "second.txt"
second_file.write_text("second content")
add_cmd = [git2cpp_path, "add", "second.txt"]
subprocess.run(add_cmd, cwd=tmp_path, text=True)
commit_cmd = [git2cpp_path, "commit", "-m", "Add second file"]
subprocess.run(commit_cmd, cwd=tmp_path, text=True)
original_initial = initial_file.read_text()
original_second = second_file.read_text()
# Modify both files
initial_file.write_text("dirty initial")
second_file.write_text("dirty second")
checkout_cmd = [git2cpp_path, "checkout", "--", "initial.txt", "second.txt"]
p = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p.returncode == 0
assert initial_file.read_text() == original_initial
assert second_file.read_text() == original_second
def test_checkout_file_does_not_affect_other_files(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test that checkout -- <file> only touches the specified file"""
initial_file = tmp_path / "initial.txt"
original_initial = initial_file.read_text()
# Create and commit a second file
second_file = tmp_path / "second.txt"
second_file.write_text("second content")
add_cmd = [git2cpp_path, "add", "second.txt"]
subprocess.run(add_cmd, cwd=tmp_path, text=True)
commit_cmd = [git2cpp_path, "commit", "-m", "Add second file"]
subprocess.run(commit_cmd, cwd=tmp_path, text=True)
# Modify both files
initial_file.write_text("dirty initial")
second_file.write_text("dirty second")
# Only restore initial.txt
checkout_cmd = [git2cpp_path, "checkout", "--", "initial.txt"]
p = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p.returncode == 0
assert initial_file.read_text() == original_initial
assert second_file.read_text() == "dirty second"
def test_checkout_file_does_not_change_branch(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test that checkout -- <file> does not move HEAD or change the current branch"""
initial_file = tmp_path / "initial.txt"
original_initial = initial_file.read_text()
initial_file.write_text("dirty")
checkout_cmd = [git2cpp_path, "checkout", "--", "initial.txt"]
p = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p.returncode == 0
assert initial_file.read_text() == original_initial
branch_cmd = [git2cpp_path, "branch"]
p_branch = subprocess.run(branch_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_branch.returncode == 0
assert "* main" in p_branch.stdout
def test_checkout_file_nonexistent_path_fails(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test that checkout -- <nonexistent> fails with a non-zero exit code"""
checkout_cmd = [git2cpp_path, "checkout", "--", "doesnotexist.txt"]
p = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p.returncode != 0
def test_checkout_file_no_paths_fails(repo_init_with_commit, git2cpp_path, tmp_path):
"""Test that checkout -- with no file arguments fails"""
checkout_cmd = [git2cpp_path, "checkout", "--"]
p = subprocess.run(checkout_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p.returncode != 0
assert "no branch or file specified" in p.stderr