diff --git a/src/cli/land-fixer-repository.ts b/src/cli/land-fixer-repository.ts index 238b2f83..8113517b 100644 --- a/src/cli/land-fixer-repository.ts +++ b/src/cli/land-fixer-repository.ts @@ -41,6 +41,9 @@ export function importFixer( const objects: string[] = []; const walk = (path: string, relative = "") => { for (const name of readdirSync(path)) { + // Git lock files are transient: a background `git maintenance` or auto-gc can create and remove one + // while this walks (moq-dev/moq#6987 crashed on a vanished `objects/maintenance.lock`). None is imported. + if (name.endsWith(".lock")) continue; if (++files > 100_000) throw new Error("fixer repository has too many files"); const child = join(path, name); const rel = relative ? `${relative}/${name}` : name; diff --git a/src/cli/land-fixer.ts b/src/cli/land-fixer.ts index 0ce9e9e0..9597f4db 100644 --- a/src/cli/land-fixer.ts +++ b/src/cli/land-fixer.ts @@ -180,6 +180,9 @@ export async function startFixer( return; } git(checkout, ["init", "--quiet"]); + // No background gc or maintenance may rewrite objects while the daemon imports the fixer's result. + git(checkout, ["config", "gc.auto", "0"]); + git(checkout, ["config", "maintenance.auto", "false"]); git(checkout, [ "fetch", "--quiet", diff --git a/test/land.test.ts b/test/land.test.ts index f0995368..4ab6ed31 100644 --- a/test/land.test.ts +++ b/test/land.test.ts @@ -2074,6 +2074,8 @@ describe.skipIf(process.platform === "win32")("asynchronous land fixer", () => { mkdirSync(submodule, { recursive: true }); writeFileSync(join(submodule, `${large}.pack`), ""); truncateSync(join(submodule, `${large}.pack`), 101 * 1024 * 1024); + // A transient lock is skipped, never read: git maintenance may remove it mid-walk (moq-dev/moq#6987). + symlinkSync(join(checkout, "missing"), join(checkout, ".git", "objects", "maintenance.lock")); importFixer(root, tempDir(), checkout, 1, fixed, [...packs, `${large}.pack`]); expect(git(root, "show", `${fixed}:kept.txt`)).toBe("unchanged"); expect(git(root, "rev-parse", `${fixed}^`)).toBe(pinned);