From 9bf36cdb56925e6591d332f7add0e40962976bf0 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 15 May 2026 15:08:41 +0700 Subject: [PATCH] Handle unborn repos in changes diff --- .../server/src/utils/checkout-git.test.ts | 35 +++++++++++++++++++ packages/server/src/utils/checkout-git.ts | 27 +++++++++++--- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/packages/server/src/utils/checkout-git.test.ts b/packages/server/src/utils/checkout-git.test.ts index 7cda4a9c6..9c8c87301 100644 --- a/packages/server/src/utils/checkout-git.test.ts +++ b/packages/server/src/utils/checkout-git.test.ts @@ -204,6 +204,41 @@ describe("checkout git utilities", () => { expect(branch).toBeNull(); }); + it("returns untracked files in an uncommitted diff before the first commit", async () => { + const unbornRepo = join(tempDir, "unborn-repo"); + mkdirSync(unbornRepo, { recursive: true }); + execFileSync("git", ["init", "-b", "main"], { cwd: unbornRepo }); + writeFileSync(join(unbornRepo, "greeting.txt"), "hello\n"); + + const diff = await getCheckoutDiff(unbornRepo, { + mode: "uncommitted", + includeStructured: true, + }); + + expect(diff.structured).toEqual([ + { + path: "greeting.txt", + isNew: true, + isDeleted: false, + additions: 1, + deletions: 0, + hunks: [ + { + oldStart: 0, + oldCount: 0, + newStart: 1, + newCount: 1, + lines: [ + { type: "header", content: "@@ -0,0 +1 @@" }, + { type: "add", content: "hello" }, + ], + }, + ], + status: "ok", + }, + ]); + }); + it("returns the branch being rebased when HEAD is detached during a rebase", async () => { execFileSync("git", ["checkout", "-b", "feature/rebase-test"], { cwd: repoDir }); writeFileSync(join(repoDir, "file.txt"), "feature\n"); diff --git a/packages/server/src/utils/checkout-git.ts b/packages/server/src/utils/checkout-git.ts index e242abe6d..ba928da3d 100644 --- a/packages/server/src/utils/checkout-git.ts +++ b/packages/server/src/utils/checkout-git.ts @@ -539,6 +539,15 @@ function buildGitDiffArgs(args: { ignoreWhitespace?: boolean; extra: string[] }) const TRACKED_DIFF_NUMSTAT_MAX_BYTES = 2 * 1024 * 1024; // 2MB const TRACKED_MAX_CHANGED_LINES = 40_000; +const EMPTY_TREE_OBJECT_ID = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"; + +function isUnbornHeadDiffError(error: unknown): boolean { + return ( + error instanceof Error && + error.message.includes("--name-status HEAD") && + error.message.includes("ambiguous argument 'HEAD'") + ); +} async function getTrackedNumstatByPath( cwd: string, @@ -1926,7 +1935,17 @@ export async function getCheckoutDiff( } const ignoreWhitespace = compare.ignoreWhitespace === true; - const changes = await listCheckoutFileChanges(cwd, refsForDiff, ignoreWhitespace); + let effectiveRefsForDiff = refsForDiff; + let changes: CheckoutFileChange[]; + try { + changes = await listCheckoutFileChanges(cwd, effectiveRefsForDiff, ignoreWhitespace); + } catch (error) { + if (!isUnbornHeadDiffError(error)) { + throw error; + } + effectiveRefsForDiff = { ...refsForDiff, baseRef: EMPTY_TREE_OBJECT_ID }; + changes = await listCheckoutFileChanges(cwd, effectiveRefsForDiff, ignoreWhitespace); + } changes.sort((a, b) => { if (a.path === b.path) return 0; return a.path < b.path ? -1 : 1; @@ -1957,7 +1976,7 @@ export async function getCheckoutDiff( const trackedNumstatByPath = trackedChanges.length > 0 - ? await getTrackedNumstatByPath(cwd, refsForDiff, ignoreWhitespace) + ? await getTrackedNumstatByPath(cwd, effectiveRefsForDiff, ignoreWhitespace) : new Map(); const trackedDiffPaths: string[] = []; const trackedPlaceholderByPath = new Map< @@ -1984,7 +2003,7 @@ export async function getCheckoutDiff( const trackedDiffResult = await runGitCommand( buildGitDiffArgs({ ignoreWhitespace, - extra: [...getCheckoutDiffRefArgs(refsForDiff), "--", ...trackedDiffPaths], + extra: [...getCheckoutDiffRefArgs(effectiveRefsForDiff), "--", ...trackedDiffPaths], }), { cwd, @@ -2020,7 +2039,7 @@ export async function getCheckoutDiff( trackedPlaceholderByPath, trackedDiffText, trackedDiffTruncated, - refsForDiff, + refsForDiff: effectiveRefsForDiff, ignoreWhitespace, structured, appendDiff,