diff --git a/acp.py b/acp.py index 0350b52..25e1878 100755 --- a/acp.py +++ b/acp.py @@ -503,18 +503,26 @@ def sync_fork(branch: str = "main", verbose: bool = False) -> None: if verbose: print(f"Syncing fork '{fork_repo}' branch '{branch}' with upstream...") - sync_result = subprocess.run( - ["gh", "repo", "sync", fork_repo, "-b", branch], - capture_output=True, - text=True, - check=False, - ) + if verbose: + sync_result = subprocess.run( + ["gh", "repo", "sync", fork_repo, "-b", branch], text=True, check=False + ) + else: + sync_result = subprocess.run( + ["gh", "repo", "sync", fork_repo, "-b", branch], + capture_output=True, + text=True, + check=False, + ) if sync_result.returncode != 0: - print( - f"Error: Failed to sync fork: {sync_result.stderr.strip()}", - file=sys.stderr, - ) + if verbose: + print("Error: Failed to sync fork", file=sys.stderr) + else: + print( + f"Error: Failed to sync fork: {sync_result.stderr.strip()}", + file=sys.stderr, + ) sys.exit(1) if verbose: @@ -522,20 +530,39 @@ def sync_fork(branch: str = "main", verbose: bool = False) -> None: current_branch = run(["git", "rev-parse", "--abbrev-ref", "HEAD"], quiet=True) - run(["git", "fetch", "origin", branch], quiet=True) + fetch_cmd = ["git", "fetch", "origin", branch] + if verbose: + fetch_cmd.append("--progress") + fetch_result = subprocess.run(fetch_cmd, check=False) + if fetch_result.returncode != 0: + print("Error: git fetch failed", file=sys.stderr) + sys.exit(1) + else: + run(fetch_cmd, quiet=True) if current_branch == branch: - merge_result = subprocess.run( - ["git", "merge", "--ff-only", f"origin/{branch}"], - capture_output=True, - text=True, - check=False, - ) - if merge_result.returncode != 0: - print( - f"Warning: Could not fast-forward local '{branch}': {merge_result.stderr.strip()}", - file=sys.stderr, + merge_cmd = ["git", "merge", "--ff-only", f"origin/{branch}"] + if verbose: + merge_result = subprocess.run(merge_cmd, text=True, check=False) + else: + merge_result = subprocess.run( + merge_cmd, + capture_output=True, + text=True, + check=False, ) + if merge_result.returncode != 0: + if verbose: + print( + f"Warning: Could not fast-forward local '{branch}'", + file=sys.stderr, + ) + else: + print( + f"Warning: Could not fast-forward local '{branch}': " + f"{merge_result.stderr.strip()}", + file=sys.stderr, + ) elif verbose: print(f"Local branch '{branch}' updated") elif verbose: diff --git a/test_acp.py b/test_acp.py index 6f451d1..eeb2b4c 100644 --- a/test_acp.py +++ b/test_acp.py @@ -1586,6 +1586,74 @@ def subprocess_side_effect(*args, **kwargs): assert "Could not fast-forward" in captured.err assert "Fork synced with upstream (main)" in captured.out + @mock.patch("subprocess.run") + @mock.patch("acp.run") + def test_sync_fork_verbose_streams_output(self, mock_run, mock_subprocess, capsys): + mock_run.side_effect = [ + "git@github.com:user/fork.git", # origin url + "main", # current branch + ] + + def subprocess_side_effect(*args, **kwargs): + cmd = args[0] + if "upstream" in str(cmd) and "get-url" in str(cmd): + return mock.Mock( + returncode=0, + stdout="git@github.com:upstream/repo.git", + stderr="", + ) + return mock.Mock(returncode=0, stdout="", stderr="") + + mock_subprocess.side_effect = subprocess_side_effect + + acp.sync_fork(branch="main", verbose=True) + + captured = capsys.readouterr() + assert "Fork synced with upstream (main)" in captured.out + + for c in mock_subprocess.call_args_list: + cmd = c[0][0] + if ( + ("gh" in str(cmd) and "sync" in str(cmd)) + or "fetch" in str(cmd) + or "merge" in str(cmd) + ): + assert "capture_output" not in c.kwargs + + fetch_calls = [ + c for c in mock_subprocess.call_args_list if "fetch" in str(c[0][0]) + ] + assert len(fetch_calls) == 1 + assert "--progress" in fetch_calls[0][0][0] + + @mock.patch("subprocess.run") + @mock.patch("acp.run") + def test_sync_fork_verbose_gh_sync_failure(self, mock_run, mock_subprocess, capsys): + mock_run.side_effect = [ + "git@github.com:user/fork.git", # origin url + ] + + def subprocess_side_effect(*args, **kwargs): + cmd = args[0] + if "upstream" in str(cmd) and "get-url" in str(cmd): + return mock.Mock( + returncode=0, + stdout="git@github.com:upstream/repo.git", + stderr="", + ) + if "gh" in str(cmd) and "sync" in str(cmd): + return mock.Mock(returncode=1, stdout="", stderr="") + return mock.Mock(returncode=0, stdout="", stderr="") + + mock_subprocess.side_effect = subprocess_side_effect + + with pytest.raises(SystemExit) as exc: + acp.sync_fork(branch="main", verbose=True) + assert exc.value.code == 1 + + captured = capsys.readouterr() + assert "Failed to sync fork" in captured.err + class TestSyncCommand: @mock.patch("acp.sync_fork")