@@ -269,6 +269,74 @@ def test_clean_error_outside_git_repo(self, tmp_path, monkeypatch, capsys):
269269 ), f"Unexpected error message: { out !r} "
270270
271271
272+ class TestCheckoutRemoteOnlyBranch :
273+ """#277: branch exists on a remote but was never checked out locally.
274+
275+ git's DWIM (`git checkout <branch>` → create local tracking branch) can
276+ fail to fire; the op must resolve it explicitly via the remote-tracking
277+ ref instead of erroring 'not found even after fetch'.
278+ """
279+
280+ def _make_clone_with_remote_branch (self , tmp_path : Path ) -> Path :
281+ """Clone with a remote-only branch 'feature/remote-only'.
282+
283+ Returns the clone repo. The branch exists on origin and as a
284+ remote-tracking ref, but has no local branch.
285+ """
286+ origin = self ._make_origin (tmp_path )
287+ clone = tmp_path / "clone"
288+ _git (tmp_path , "clone" , str (origin ), str (clone ), check = True )
289+ _git (clone , "config" , "user.email" , "test@test.com" , check = True )
290+ _git (clone , "config" , "user.name" , "Test" , check = True )
291+ # Tracking ref exists (from clone), but no local branch.
292+ assert _git (clone , "rev-parse" , "--verify" , "--quiet" ,
293+ "origin/feature/remote-only" ).returncode == 0
294+ assert _git (clone , "rev-parse" , "--verify" , "--quiet" ,
295+ "feature/remote-only" ).returncode != 0
296+ return clone
297+
298+ def _make_origin (self , tmp_path : Path ) -> Path :
299+ seed = _make_repo (tmp_path )
300+ _git (seed , "checkout" , "-b" , "feature/remote-only" , check = True )
301+ (seed / "feature.txt" ).write_text ("remote work\n " )
302+ _git (seed , "add" , "feature.txt" , check = True )
303+ _git (seed , "commit" , "-m" , "remote-only feature" , check = True )
304+ _git (seed , "checkout" , "master" , check = True )
305+ origin = tmp_path / "origin.git"
306+ _git (tmp_path , "clone" , "--bare" , str (seed ), str (origin ), check = True )
307+ return origin
308+
309+ def test_checks_out_remote_only_branch (self , tmp_path , monkeypatch , capsys ):
310+ clone = self ._make_clone_with_remote_branch (tmp_path )
311+
312+ rc , out = _run_checkout (clone , "feature/remote-only" , monkeypatch , capsys )
313+
314+ assert rc == 0 , f"Expected success, got rc={ rc } : { out !r} "
315+ branch = _git (clone , "rev-parse" , "--abbrev-ref" , "HEAD" ).stdout .strip ()
316+ assert branch == "feature/remote-only" , f"On wrong branch: { branch !r} "
317+ # Local tracking branch must now exist with the remote's content.
318+ assert (clone / "feature.txt" ).read_text () == "remote work\n "
319+
320+ def test_dwim_disabled_still_resolves (self , tmp_path , monkeypatch , capsys ):
321+ """checkout.guess=false disables git's DWIM — op must still resolve."""
322+ clone = self ._make_clone_with_remote_branch (tmp_path )
323+ _git (clone , "config" , "checkout.guess" , "false" , check = True )
324+
325+ rc , out = _run_checkout (clone , "feature/remote-only" , monkeypatch , capsys )
326+
327+ assert rc == 0 , f"Expected success with guess disabled, got rc={ rc } : { out !r} "
328+ branch = _git (clone , "rev-parse" , "--abbrev-ref" , "HEAD" ).stdout .strip ()
329+ assert branch == "feature/remote-only"
330+
331+ def test_genuinely_missing_branch_still_errors (self , tmp_path , monkeypatch , capsys ):
332+ clone = self ._make_clone_with_remote_branch (tmp_path )
333+
334+ rc , out = _run_checkout (clone , "no/such/branch" , monkeypatch , capsys )
335+
336+ assert rc != 0
337+ assert "not found" in out .lower (), f"Expected not-found error, got: { out !r} "
338+
339+
272340# ===========================================================================
273341# git-resolve tests
274342# ===========================================================================
0 commit comments