From 66d77b2600a6bacd06ffab29b3b72cdca42b0de9 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Wed, 2 Sep 2026 21:44:17 -0700 Subject: [PATCH] Sequence git worktree operations to preserve stage/commit order Back-to-back operations (stage, then commit) must execute in call order. Use an order key to sequence them synchronously while keeping realpath async to avoid blocking on hung network paths (9P/UNC lookups). --- src/shared/git-worktree-operation-lock.ts | 24 +++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/shared/git-worktree-operation-lock.ts b/src/shared/git-worktree-operation-lock.ts index 1c3f6b05e27..273fa74f947 100644 --- a/src/shared/git-worktree-operation-lock.ts +++ b/src/shared/git-worktree-operation-lock.ts @@ -1,4 +1,4 @@ -import { realpathSync } from 'node:fs' +import { realpath } from 'node:fs/promises' import { resolve } from 'node:path' import { runWithGitOperationLock } from './git-operation-lock' @@ -8,13 +8,17 @@ export async function runWithGitWorktreeOperationLock( signal: AbortSignal | undefined, run: () => Promise ): Promise { - // Why: the key must resolve synchronously so back-to-back callers (stage, then commit) - // join the lane in call order. An async realpath is not ordered and can run a commit first. - let key = resolve(worktreePath) - try { - key = realpathSync.native(worktreePath) || key - } catch { - // A missing or temporarily unreachable worktree still gets serialized. - } - return runWithGitOperationLock(key, signal, run) + const fallbackKey = resolve(worktreePath) + // Why: back-to-back callers (stage, then commit) must join the canonical lane in call order. + // realpath is async and unordered, so sequence it under a lane keyed by the raw path, which + // needs no I/O. realpath stays async because a hung 9P/UNC lookup must not block the process. + return runWithGitOperationLock(`order\0${fallbackKey}`, signal, async () => { + let key = fallbackKey + try { + key = (await realpath(worktreePath)) || fallbackKey + } catch { + // A missing or temporarily unreachable worktree still gets serialized. + } + return runWithGitOperationLock(key, signal, run) + }) }