diff --git a/apps/server/git/simple-git-branch-manager.ts b/apps/server/git/simple-git-branch-manager.ts index 5b46640..47b690e 100644 --- a/apps/server/git/simple-git-branch-manager.ts +++ b/apps/server/git/simple-git-branch-manager.ts @@ -6,7 +6,7 @@ * on project clones without requiring a worktree. */ -import { join } from 'node:path'; +import { join, resolve } from 'node:path'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { simpleGit } from 'simple-git'; @@ -164,7 +164,26 @@ export class SimpleGitBranchManager implements BranchManager { async pushBranch(repoPath: string, branch: string, remote = 'origin'): Promise { const git = simpleGit(repoPath); - await git.push(remote, branch); + try { + await git.push(remote, branch); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + if (!msg.includes('branch is currently checked out')) throw err; + + // Local non-bare repo with the branch checked out — temporarily allow it. + // receive.denyCurrentBranch=updateInstead updates the remote's working tree + // and index to match, or rejects if the working tree is dirty. + const remoteUrl = (await git.remote(['get-url', remote]))?.trim(); + if (!remoteUrl) throw err; + const remotePath = resolve(repoPath, remoteUrl); + const remoteGit = simpleGit(remotePath); + await remoteGit.addConfig('receive.denyCurrentBranch', 'updateInstead'); + try { + await git.push(remote, branch); + } finally { + await remoteGit.raw(['config', '--unset', 'receive.denyCurrentBranch']); + } + } log.info({ repoPath, branch, remote }, 'branch pushed to remote'); }