From 5d1292c7adb7336a630df0d333537cae904db52c Mon Sep 17 00:00:00 2001 From: Lukas May Date: Fri, 6 Mar 2026 12:34:21 +0100 Subject: [PATCH] fix: Use update-ref for fast-forward to avoid dirty working tree failures fastForwardBranch used git merge --ff-only which fails when the clone has uncommitted files. This caused the ff to be silently skipped, the merge to proceed on stale main, and the push to fail (non-fast-forward). Switched to update-ref which only moves the branch pointer without touching the working tree. --- apps/server/git/simple-git-branch-manager.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/server/git/simple-git-branch-manager.ts b/apps/server/git/simple-git-branch-manager.ts index b8147d0..5b46640 100644 --- a/apps/server/git/simple-git-branch-manager.ts +++ b/apps/server/git/simple-git-branch-manager.ts @@ -208,7 +208,18 @@ export class SimpleGitBranchManager implements BranchManager { async fastForwardBranch(repoPath: string, branch: string, remote = 'origin'): Promise { const git = simpleGit(repoPath); const remoteBranch = `${remote}/${branch}`; - await git.raw(['merge', '--ff-only', remoteBranch, branch]); + + // Verify it's a genuine fast-forward (branch is ancestor of remote) + try { + await git.raw(['merge-base', '--is-ancestor', branch, remoteBranch]); + } catch { + throw new Error(`Cannot fast-forward ${branch}: it has diverged from ${remoteBranch}`); + } + + // Use update-ref instead of git merge so dirty working trees don't block it. + // The clone may have uncommitted agent work; we only need to advance the ref. + const targetCommit = (await git.raw(['rev-parse', remoteBranch])).trim(); + await git.raw(['update-ref', `refs/heads/${branch}`, targetCommit]); log.info({ repoPath, branch, remoteBranch }, 'fast-forwarded branch'); }