mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-30 13:13:25 +00:00
fix(server): keep Git observation and worktree IO off the event loop (#7943)
* fix(server): keep Git observation and worktree IO off the event loop Resolve subscription path identities before callbacks and serialize async worktree metadata mutations by canonical ownership. Git command tracing must not probe the working directory before spawning. * test(server): verify Git trace records without filesystem probes * fix(server): establish Git identities at observation setup Read-only checkout refreshes should not resolve watcher namespaces. Observation tests await setup and compare cross-workspace fan-out without depending on completion order. * fix(server): retain literal symlink targets in Git event identity Canonical realpaths can erase a symlink target spelling when its parent is also a symlink, such as /var on macOS. Resolve these aliases once during setup so deleted and ignored event paths still match without per-event IO. * fix(server): preserve Windows worktree path spelling * fix(server): retain native aliases in worktree ownership * test(server): write Git IO trace markers asynchronously
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
import { MAX_RELAY_WEBSOCKET_MESSAGE_BYTES } from "@getpaseo/protocol/websocket-message-limit";
|
||||
import { resolve, dirname, basename } from "path";
|
||||
import { existsSync, realpathSync } from "fs";
|
||||
import { open as openFile, readFile, stat as statFile, mkdtemp, copyFile, rm } from "fs/promises";
|
||||
import {
|
||||
open as openFile,
|
||||
readFile,
|
||||
stat as statFile,
|
||||
mkdtemp,
|
||||
copyFile,
|
||||
realpath,
|
||||
access,
|
||||
rm,
|
||||
} from "fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { setImmediate } from "node:timers/promises";
|
||||
import { TTLCache } from "@isaacs/ttlcache";
|
||||
@@ -1104,7 +1112,7 @@ async function getMainRepoRootFromCommonDir(
|
||||
if (!commonDir) {
|
||||
throw new Error("Not in a git repository");
|
||||
}
|
||||
const normalized = realpathSync(commonDir);
|
||||
const normalized = await realpath(commonDir);
|
||||
|
||||
if (basename(normalized) === ".git") {
|
||||
return dirname(normalized);
|
||||
@@ -1229,7 +1237,7 @@ export async function renameCurrentBranch(
|
||||
|
||||
const currentBranch = await getCurrentBranch(cwd);
|
||||
if (currentBranch) {
|
||||
rebindPaseoWorktreeChangeRequestHint(worktreeRoot, previousBranch, currentBranch);
|
||||
await rebindPaseoWorktreeChangeRequestHint(worktreeRoot, previousBranch, currentBranch);
|
||||
}
|
||||
return { previousBranch, currentBranch };
|
||||
}
|
||||
@@ -1274,8 +1282,8 @@ function storedBaseRefFromMetadata(metadata: PaseoWorktreeMetadata | null): stri
|
||||
return metadata?.baseRef ?? metadata?.baseRefName ?? null;
|
||||
}
|
||||
|
||||
function readPaseoWorktreeBaseRef(worktreeRoot: string): string | null {
|
||||
return storedBaseRefFromMetadata(readPaseoWorktreeMetadata(worktreeRoot));
|
||||
async function readPaseoWorktreeBaseRef(worktreeRoot: string): Promise<string | null> {
|
||||
return storedBaseRefFromMetadata(await readPaseoWorktreeMetadata(worktreeRoot));
|
||||
}
|
||||
|
||||
async function getStoredBaseRefForCwd(
|
||||
@@ -1549,7 +1557,12 @@ async function abortGitPullConflictState(cwd: string): Promise<void> {
|
||||
const rebaseMergePath = resolve(gitDir, "rebase-merge");
|
||||
const rebaseApplyPath = resolve(gitDir, "rebase-apply");
|
||||
|
||||
if (existsSync(mergeHeadPath)) {
|
||||
if (
|
||||
await access(mergeHeadPath).then(
|
||||
() => true,
|
||||
() => false,
|
||||
)
|
||||
) {
|
||||
try {
|
||||
await runGitCommand(["merge", "--abort"], { cwd, timeout: 120_000 });
|
||||
} catch {
|
||||
@@ -1557,7 +1570,16 @@ async function abortGitPullConflictState(cwd: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
if (existsSync(rebaseMergePath) || existsSync(rebaseApplyPath)) {
|
||||
if (
|
||||
(await access(rebaseMergePath).then(
|
||||
() => true,
|
||||
() => false,
|
||||
)) ||
|
||||
(await access(rebaseApplyPath).then(
|
||||
() => true,
|
||||
() => false,
|
||||
))
|
||||
) {
|
||||
try {
|
||||
await runGitCommand(["rebase", "--abort"], { cwd, timeout: 120_000 });
|
||||
} catch {
|
||||
@@ -2078,7 +2100,7 @@ export async function getCheckoutSnapshotFacts(
|
||||
}
|
||||
|
||||
const paseoWorktreeMetadata = inspected.paseoWorktree.isPaseoOwnedWorktree
|
||||
? readPaseoWorktreeMetadata(inspected.paseoWorktree.worktreeRoot)
|
||||
? await readPaseoWorktreeMetadata(inspected.paseoWorktree.worktreeRoot)
|
||||
: null;
|
||||
const storedBaseRef = storedBaseRefFromMetadata(paseoWorktreeMetadata);
|
||||
const resolvedBaseRef = storedBaseRef ?? (await resolveBaseRef(cwd, context));
|
||||
|
||||
Reference in New Issue
Block a user