diff --git a/packages/server/src/utils/checkout-git.test.ts b/packages/server/src/utils/checkout-git.test.ts index a5a8db146..d8042d29b 100644 --- a/packages/server/src/utils/checkout-git.test.ts +++ b/packages/server/src/utils/checkout-git.test.ts @@ -493,6 +493,53 @@ const x = 1; expect(status.aheadOfOrigin).toBeNull(); }); + it("does not report full history as unpushed for fresh no-track Paseo worktrees", async () => { + setupRemoteTrackingMain(repoDir, tempDir); + commitFile(repoDir, "second.txt", "second\n", "second commit"); + execFileSync("git", ["push"], { cwd: repoDir }); + + const worktree = await createLegacyWorktreeForTest({ + branchName: "fresh-feature", + cwd: repoDir, + baseBranch: "main", + worktreeSlug: "fresh-feature", + paseoHome, + }); + + const status = await getCheckoutStatus(worktree.worktreePath, { paseoHome }); + expect(status).toMatchObject({ + isGit: true, + isPaseoOwnedWorktree: true, + baseRef: "main", + aheadBehind: { ahead: 0, behind: 0 }, + aheadOfOrigin: 0, + }); + }); + + it("reports local-only worktree commits as unpushed relative to base", async () => { + setupRemoteTrackingMain(repoDir, tempDir); + commitFile(repoDir, "second.txt", "second\n", "second commit"); + execFileSync("git", ["push"], { cwd: repoDir }); + + const worktree = await createLegacyWorktreeForTest({ + branchName: "fresh-feature", + cwd: repoDir, + baseBranch: "main", + worktreeSlug: "fresh-feature", + paseoHome, + }); + commitFile(worktree.worktreePath, "feature.txt", "feature\n", "feature commit"); + + const status = await getCheckoutStatus(worktree.worktreePath, { paseoHome }); + expect(status).toMatchObject({ + isGit: true, + isPaseoOwnedWorktree: true, + baseRef: "main", + aheadBehind: { ahead: 1, behind: 0 }, + aheadOfOrigin: 1, + }); + }); + it("does not report incoming additions when the base branch is behind its remote", async () => { const { cloneDir } = setupRemoteTrackingMain(repoDir, tempDir); commitFile(cloneDir, "file.txt", "remote one\nremote two\n", "remote update"); diff --git a/packages/server/src/utils/checkout-git.ts b/packages/server/src/utils/checkout-git.ts index ba928da3d..4e4dee915 100644 --- a/packages/server/src/utils/checkout-git.ts +++ b/packages/server/src/utils/checkout-git.ts @@ -1268,6 +1268,7 @@ async function getAheadBehind( async function getAheadOfOrigin( cwd: string, currentBranch: string, + baseRef: string | null, context?: CheckoutContext, ): Promise { if (!currentBranch) { @@ -1286,12 +1287,15 @@ async function getAheadOfOrigin( if (trackedOriginBranch) { return null; } + if (!baseRef || normalizeLocalBranchRefName(baseRef) === currentBranch) { + return null; + } try { - const { stdout } = await runGitCommand(["rev-list", "--count", currentBranch], { - cwd, - envOverlay: READ_ONLY_GIT_ENV, - logger: context?.logger, - }); + const comparisonBaseRef = await resolveBestComparisonBaseRef(cwd, baseRef, context); + const { stdout } = await runGitCommand( + ["rev-list", "--count", `${comparisonBaseRef}..${currentBranch}`], + { cwd, envOverlay: READ_ONLY_GIT_ENV, logger: context?.logger }, + ); const count = Number.parseInt(stdout.trim(), 10); return Number.isNaN(count) ? null : count; } catch { @@ -1503,7 +1507,7 @@ export async function getCheckoutStatus( ? getAheadBehind(cwd, baseRef, currentBranch, context) : Promise.resolve(null), hasRemote && currentBranch - ? getAheadOfOrigin(cwd, currentBranch, context) + ? getAheadOfOrigin(cwd, currentBranch, baseRef, context) : Promise.resolve(null), hasRemote && currentBranch ? getBehindOfOrigin(cwd, currentBranch, context)