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).
This commit is contained in:
Jinjing
2026-09-02 21:44:17 -07:00
parent 861f304ee3
commit 66d77b2600
+14 -10
View File
@@ -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<T>(
signal: AbortSignal | undefined,
run: () => Promise<T>
): Promise<T> {
// 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)
})
}