@@ -170,3 +170,110 @@ def test_checkout_refuses_overwrite(
170170 branch_cmd = [git2cpp_path , "branch" ]
171171 p_branch = subprocess .run (branch_cmd , capture_output = True , cwd = tmp_path , text = True )
172172 assert "* newbranch" in p_branch .stdout
173+
174+
175+ def test_checkout_file_restores_modified_file (repo_init_with_commit , git2cpp_path , tmp_path ):
176+ """Test that checkout -- <file> discards working tree changes"""
177+ initial_file = tmp_path / "initial.txt"
178+ original_content = initial_file .read_text ()
179+
180+ # Modify the file (unstaged)
181+ initial_file .write_text ("Modified content" )
182+ assert initial_file .read_text () == "Modified content"
183+
184+ # Restore it via checkout -- <file>
185+ checkout_cmd = [git2cpp_path , "checkout" , "--" , "initial.txt" ]
186+ p = subprocess .run (checkout_cmd , capture_output = True , cwd = tmp_path , text = True )
187+
188+ assert p .returncode == 0
189+ assert initial_file .read_text () == original_content
190+
191+
192+ def test_checkout_file_restores_multiple_files (repo_init_with_commit , git2cpp_path , tmp_path ):
193+ """Test that checkout -- <file1> <file2> restores multiple files at once"""
194+ initial_file = tmp_path / "initial.txt"
195+
196+ # Create and commit a second file first
197+ second_file = tmp_path / "second.txt"
198+ second_file .write_text ("second content" )
199+
200+ add_cmd = [git2cpp_path , "add" , "second.txt" ]
201+ subprocess .run (add_cmd , cwd = tmp_path , text = True )
202+ commit_cmd = [git2cpp_path , "commit" , "-m" , "Add second file" ]
203+ subprocess .run (commit_cmd , cwd = tmp_path , text = True )
204+
205+ original_initial = initial_file .read_text ()
206+ original_second = second_file .read_text ()
207+
208+ # Modify both files
209+ initial_file .write_text ("dirty initial" )
210+ second_file .write_text ("dirty second" )
211+
212+ checkout_cmd = [git2cpp_path , "checkout" , "--" , "initial.txt" , "second.txt" ]
213+ p = subprocess .run (checkout_cmd , capture_output = True , cwd = tmp_path , text = True )
214+
215+ assert p .returncode == 0
216+ assert initial_file .read_text () == original_initial
217+ assert second_file .read_text () == original_second
218+
219+
220+ def test_checkout_file_does_not_affect_other_files (repo_init_with_commit , git2cpp_path , tmp_path ):
221+ """Test that checkout -- <file> only touches the specified file"""
222+ initial_file = tmp_path / "initial.txt"
223+ original_initial = initial_file .read_text ()
224+
225+ # Create and commit a second file
226+ second_file = tmp_path / "second.txt"
227+ second_file .write_text ("second content" )
228+
229+ add_cmd = [git2cpp_path , "add" , "second.txt" ]
230+ subprocess .run (add_cmd , cwd = tmp_path , text = True )
231+ commit_cmd = [git2cpp_path , "commit" , "-m" , "Add second file" ]
232+ subprocess .run (commit_cmd , cwd = tmp_path , text = True )
233+
234+ # Modify both files
235+ initial_file .write_text ("dirty initial" )
236+ second_file .write_text ("dirty second" )
237+
238+ # Only restore initial.txt
239+ checkout_cmd = [git2cpp_path , "checkout" , "--" , "initial.txt" ]
240+ p = subprocess .run (checkout_cmd , capture_output = True , cwd = tmp_path , text = True )
241+
242+ assert p .returncode == 0
243+ assert initial_file .read_text () == original_initial
244+ assert second_file .read_text () == "dirty second"
245+
246+
247+ def test_checkout_file_does_not_change_branch (repo_init_with_commit , git2cpp_path , tmp_path ):
248+ """Test that checkout -- <file> does not move HEAD or change the current branch"""
249+ initial_file = tmp_path / "initial.txt"
250+ original_initial = initial_file .read_text ()
251+
252+ initial_file .write_text ("dirty" )
253+
254+ checkout_cmd = [git2cpp_path , "checkout" , "--" , "initial.txt" ]
255+ p = subprocess .run (checkout_cmd , capture_output = True , cwd = tmp_path , text = True )
256+ assert p .returncode == 0
257+ assert initial_file .read_text () == original_initial
258+
259+ branch_cmd = [git2cpp_path , "branch" ]
260+ p_branch = subprocess .run (branch_cmd , capture_output = True , cwd = tmp_path , text = True )
261+ assert p_branch .returncode == 0
262+ assert "* main" in p_branch .stdout
263+
264+
265+ def test_checkout_file_nonexistent_path_fails (repo_init_with_commit , git2cpp_path , tmp_path ):
266+ """Test that checkout -- <nonexistent> fails with a non-zero exit code"""
267+ checkout_cmd = [git2cpp_path , "checkout" , "--" , "doesnotexist.txt" ]
268+ p = subprocess .run (checkout_cmd , capture_output = True , cwd = tmp_path , text = True )
269+
270+ assert p .returncode != 0
271+
272+
273+ def test_checkout_file_no_paths_fails (repo_init_with_commit , git2cpp_path , tmp_path ):
274+ """Test that checkout -- with no file arguments fails"""
275+ checkout_cmd = [git2cpp_path , "checkout" , "--" ]
276+ p = subprocess .run (checkout_cmd , capture_output = True , cwd = tmp_path , text = True )
277+
278+ assert p .returncode != 0
279+ assert "no branch or file specified" in p .stderr
0 commit comments