diff --git a/packages/server/src/server/diff/git.test.ts b/packages/server/src/server/diff/git.test.ts index 73f2975c0b..ddcee872ee 100644 --- a/packages/server/src/server/diff/git.test.ts +++ b/packages/server/src/server/diff/git.test.ts @@ -628,3 +628,40 @@ test("a batched oversized tracked patch does not hide its small sibling", async await rm(cwd, { recursive: true, force: true }); } }); + +test("replacing a submodule with a regular file preserves its replacement body", async () => { + const cwd = await mkdtemp(join(tmpdir(), "diff-gitlink-replacement-")); + const git = (...args: string[]) => execFileSync("git", args, { cwd, encoding: "utf8" }); + const { mkdir } = await import("node:fs/promises"); + try { + git("init", "-b", "main"); + git("config", "user.name", "Test"); + git("config", "user.email", "test@example.com"); + await mkdir(join(cwd, "sub")); + git("-C", "sub", "init", "-b", "main"); + git("-C", "sub", "config", "user.name", "Test"); + git("-C", "sub", "config", "user.email", "test@example.com"); + await writeFile(join(cwd, "sub", "a.txt"), "base\n"); + git("-C", "sub", "add", "."); + git("-C", "sub", "-c", "commit.gpgsign=false", "commit", "-m", "base"); + git("add", "."); + git("-c", "commit.gpgsign=false", "commit", "-m", "record submodule"); + await rm(join(cwd, "sub"), { recursive: true, force: true }); + await writeFile(join(cwd, "sub"), "replacement contents\n"); + const comparison = await listComparison({ + cwd, + base: { kind: "rev", rev: "HEAD" }, + head: { kind: "worktree" }, + }); + try { + const record = await readBody(comparison); + expect(record.body.hunks.flatMap((hunk) => hunk.lines)).toContainEqual( + expect.objectContaining({ type: "add", content: "replacement contents" }), + ); + } finally { + await comparison.close(); + } + } finally { + await rm(cwd, { recursive: true, force: true }); + } +}); diff --git a/packages/server/src/server/diff/git.ts b/packages/server/src/server/diff/git.ts index b655df33e1..f38bd53b35 100644 --- a/packages/server/src/server/diff/git.ts +++ b/packages/server/src/server/diff/git.ts @@ -325,7 +325,7 @@ async function readWorktreeIdentity( ): Promise { const value = await lstat(resolve(options.cwd, path), { bigint: true }); let identity = `${value.size}:${value.mtimeNs}`; - if (header[0] === ":160000" || header[1] === "160000") { + if (value.isDirectory() && (header[0] === ":160000" || header[1] === "160000")) { const submodule = await runGitCommand( ["status", "--porcelain=v2", "--branch", "--untracked-files=normal"], { cwd: resolve(options.cwd, path), envOverlay: { GIT_OPTIONAL_LOCKS: "0" } },