From f1a4be691cb167b3b57eef0e24f201fb764ae5f0 Mon Sep 17 00:00:00 2001 From: Dan Childers Date: Thu, 23 Jul 2026 16:10:28 -0400 Subject: [PATCH 1/2] fix: skip rebase when remote branch does not exist yet On first implementation run, workspace_setup creates the branch locally but never pushes it. pull_rebase then fails trying to rebase onto a non-existent remote ref, exhausting all 3 retries and blocking the workflow. Guard the rebase with the existing remote_branch_exists check. Co-Authored-By: Claude Opus 4.6 --- src/forge/workspace/git_ops.py | 5 +++ tests/unit/workspace/test_pull_rebase.py | 47 ++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/unit/workspace/test_pull_rebase.py diff --git a/src/forge/workspace/git_ops.py b/src/forge/workspace/git_ops.py index c78a1a78..e4f5248c 100644 --- a/src/forge/workspace/git_ops.py +++ b/src/forge/workspace/git_ops.py @@ -131,6 +131,11 @@ def pull_rebase(self, remote: str = "fork") -> None: branch = self.workspace.branch_name logger.info(f"Syncing with {remote}/{branch} before implementing changes") self._run_git("fetch", remote) + if not self.remote_branch_exists(branch, remote=remote): + logger.info( + f"Remote branch {remote}/{branch} does not exist yet; skipping rebase" + ) + return self._run_git("rebase", f"{remote}/{branch}") logger.info(f"Rebase onto {remote}/{branch} complete") diff --git a/tests/unit/workspace/test_pull_rebase.py b/tests/unit/workspace/test_pull_rebase.py new file mode 100644 index 00000000..26e78905 --- /dev/null +++ b/tests/unit/workspace/test_pull_rebase.py @@ -0,0 +1,47 @@ +"""Tests for GitOperations.pull_rebase behavior.""" + +from pathlib import Path +from unittest.mock import MagicMock, patch + +from forge.workspace.git_ops import GitOperations +from forge.workspace.manager import Workspace + + +def _git_ops(tmp_path: Path) -> GitOperations: + settings = MagicMock() + settings.github_token.get_secret_value.return_value = "ghp_fake" + workspace = Workspace( + path=tmp_path / "repo", + repo_name="org/repo", + branch_name="forge/test-123", + ticket_key="TEST-123", + ) + with patch("forge.workspace.git_ops.get_settings", return_value=settings): + return GitOperations(workspace) + + +def test_pull_rebase_skips_when_remote_branch_missing(tmp_path): + """First implementation run: remote branch doesn't exist yet, rebase must be skipped.""" + git = _git_ops(tmp_path) + + with ( + patch.object(git, "_run_git") as run_git, + patch.object(git, "remote_branch_exists", return_value=False), + ): + git.pull_rebase(remote="origin") + + run_git.assert_called_once_with("fetch", "origin") + + +def test_pull_rebase_rebases_when_remote_branch_exists(tmp_path): + """Subsequent runs: remote branch exists, rebase must happen.""" + git = _git_ops(tmp_path) + + with ( + patch.object(git, "_run_git") as run_git, + patch.object(git, "remote_branch_exists", return_value=True), + ): + git.pull_rebase(remote="origin") + + assert run_git.call_args_list[0].args == ("fetch", "origin") + assert run_git.call_args_list[1].args == ("rebase", "origin/forge/test-123") From 5e90027c8ffa953edb07e0b03af95a2ad2b82fe0 Mon Sep 17 00:00:00 2001 From: Dan Childers Date: Thu, 23 Jul 2026 16:19:32 -0400 Subject: [PATCH 2/2] fix: applied ruff format --- src/forge/workspace/git_ops.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/forge/workspace/git_ops.py b/src/forge/workspace/git_ops.py index e4f5248c..f5f03a28 100644 --- a/src/forge/workspace/git_ops.py +++ b/src/forge/workspace/git_ops.py @@ -132,9 +132,7 @@ def pull_rebase(self, remote: str = "fork") -> None: logger.info(f"Syncing with {remote}/{branch} before implementing changes") self._run_git("fetch", remote) if not self.remote_branch_exists(branch, remote=remote): - logger.info( - f"Remote branch {remote}/{branch} does not exist yet; skipping rebase" - ) + logger.info(f"Remote branch {remote}/{branch} does not exist yet; skipping rebase") return self._run_git("rebase", f"{remote}/{branch}") logger.info(f"Rebase onto {remote}/{branch} complete")