fix: prevent worktree sessions showing source branch

Desktop git-info preserved the launch branch before checking the active worktree cwd, so materialized isolated worktree sessions could keep showing the source branch instead of the branch Git had checked out in the worktree. The API now switches to the real cwd branch only once the session has actually entered its planned worktree, while direct branch launches still keep their stable launch branch.

Constraint: Direct branch launches still need stable launch metadata when the source checkout later changes.
Rejected: Always prefer git rev-parse output | would regress direct launch sessions that intentionally show the selected branch.
Confidence: high
Scope-risk: narrow
Tested: bun test src/server/__tests__/sessions.test.ts -t "git-info"
Tested: /tmp/cc-haha-issue-539-verify API reproduction returned the worktree branch
Tested: bun run check:server
This commit is contained in:
程序员阿江(Relakkes) 2026-05-21 17:21:02 +08:00
parent a159ee3511
commit 09c7167a49
2 changed files with 17 additions and 4 deletions

View File

@ -2022,7 +2022,7 @@ describe('Sessions API', () => {
branch: string | null
} | null
}
expect(body.branch).toBe('main')
expect(body.branch).toBe(repository!.worktreeBranch)
expect(body.workDir).toBe(activeWorktree)
expect(body.worktree).toEqual({
enabled: true,
@ -2067,7 +2067,7 @@ describe('Sessions API', () => {
branch: string | null
} | null
}
expect(body.branch).toBe('main')
expect(body.branch).toBe('worktree-desktop-main-12345678')
expect(body.workDir).toBe(activeWorktree)
expect(body.worktree).toEqual({
enabled: true,
@ -2121,7 +2121,7 @@ describe('Sessions API', () => {
branch: string | null
} | null
}
expect(body.branch).toBe('main')
expect(body.branch).toBe('worktree-desktop-main-12345678')
expect(body.worktree).toMatchObject({
path: activeWorktree,
plannedPath: activeWorktree,

View File

@ -15,6 +15,7 @@
* PATCH /api/sessions/:id
*/
import * as path from 'node:path'
import { sessionService } from '../services/sessionService.js'
import { conversationService } from '../services/conversationService.js'
import { ApiError, errorResponse } from '../middleware/errorHandler.js'
@ -622,6 +623,11 @@ function chooseRicherUsage(
: currentUsage
}
function sameResolvedPath(left: string | null | undefined, right: string | null | undefined): boolean {
if (!left || !right) return false
return path.resolve(left) === path.resolve(right)
}
async function getGitInfo(sessionId: string): Promise<Response> {
const workDir = conversationService.getSessionWorkDir(sessionId) || await sessionService.getSessionWorkDir(sessionId)
if (!workDir) {
@ -653,7 +659,14 @@ async function getGitInfo(sessionId: string): Promise<Response> {
stderr: 'pipe',
})
const branchText = await new Response(branchProc.stdout).text()
const branch = sessionBranch || branchText.trim()
const gitBranch = branchText.trim() || null
const materializedWorktree = !!worktree && (
sameResolvedPath(workDir, worktree.path) ||
sameResolvedPath(workDir, worktree.plannedPath)
)
const branch = materializedWorktree
? (gitBranch || worktree.branch || sessionBranch)
: (sessionBranch || gitBranch)
// Get repo name from remote or directory
let repoName = ''