Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions apps/web/src/components/GitActionsControl.logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { assert, describe, it } from "vite-plus/test";
import {
buildMenuItems,
requiresDefaultBranchConfirmation,
resolveChangesTotals,
resolveDefaultBranchActionDialogCopy,
resolveLiveThreadBranchUpdate,
resolveQuickAction,
Expand Down Expand Up @@ -247,3 +248,53 @@ describe("resolveAutoFeatureBranchName", () => {
assert.equal(ref, "feature/ticket-1-2");
});
});

describe("resolveChangesTotals", () => {
it("does not let an empty branchChanges hide a dirty working tree", () => {
assert.deepEqual(
resolveChangesTotals(
status({
hasWorkingTreeChanges: true,
branchChanges: { baseRef: "main", insertions: 0, deletions: 0 },
workingTree: {
files: [{ path: "a.ts", insertions: 5, deletions: 1 }],
insertions: 5,
deletions: 1,
},
}),
),
{ insertions: 5, deletions: 1 },
);
});

it("keeps non-zero branchChanges over the working tree", () => {
assert.deepEqual(
resolveChangesTotals(
status({
branchChanges: { baseRef: "main", insertions: 20, deletions: 3 },
workingTree: {
files: [{ path: "a.ts", insertions: 1, deletions: 0 }],
insertions: 1,
deletions: 0,
},
}),
),
{ insertions: 20, deletions: 3 },
);
});

it("falls back to the working tree when branchChanges is omitted", () => {
assert.deepEqual(
resolveChangesTotals(
status({
workingTree: {
files: [{ path: "a.ts", insertions: 2, deletions: 0 }],
insertions: 2,
deletions: 0,
},
}),
),
{ insertions: 2, deletions: 0 },
);
});
});
25 changes: 25 additions & 0 deletions apps/web/src/components/GitActionsControl.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,3 +425,28 @@ export function resolveLiveThreadBranchUpdate(input: {
branch: input.gitStatus.refName,
};
}

export type ChangesLineTotals = {
readonly insertions: number;
readonly deletions: number;
};

// `branchChanges ?? workingTree` treats `{0,0}` as present, so a zero branch total hid a dirty
// working tree and the Changes row read +0 while Diff still showed the work.
export function resolveChangesTotals(
status: VcsStatusResult | null | undefined,
): ChangesLineTotals | null {
if (!status) return null;
const branch = status.branchChanges;
const tree = status.workingTree;
if (branch && (branch.insertions > 0 || branch.deletions > 0)) {
return { insertions: branch.insertions, deletions: branch.deletions };
}
if (tree.insertions > 0 || tree.deletions > 0) {
return { insertions: tree.insertions, deletions: tree.deletions };
}
if (branch) {
return { insertions: branch.insertions, deletions: branch.deletions };
}
return { insertions: tree.insertions, deletions: tree.deletions };
}
20 changes: 11 additions & 9 deletions apps/web/src/components/GitActionsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
type GitQuickAction,
type DefaultBranchConfirmableAction,
requiresDefaultBranchConfirmation,
resolveChangesTotals,
resolveDefaultBranchActionDialogCopy,
resolveFeatureBranchUnavailableReason,
resolveGitActionProgressPresentation,
Expand Down Expand Up @@ -1048,7 +1049,10 @@ export default function GitActionsControl({
const isRepo = gitStatus?.isRepo ?? true;
const hasPrimaryRemote = gitStatus?.hasPrimaryRemote ?? false;
const gitStatusForActions = gitStatus;
const changesTotals = gitStatusForActions?.branchChanges ?? gitStatusForActions?.workingTree;
const changesTotals = resolveChangesTotals(gitStatusForActions) ?? {
insertions: 0,
deletions: 0,
};

const allFiles = gitStatusForActions?.workingTree.files ?? [];
// Changed-file paths are repository-relative, so they must resolve against
Expand Down Expand Up @@ -1118,8 +1122,8 @@ export default function GitActionsControl({
gitActionProgress === null &&
!gitStatusError &&
quickAction.disabled &&
!changesTotals?.insertions &&
!changesTotals?.deletions;
!changesTotals.insertions &&
!changesTotals.deletions;
const pendingDefaultBranchActionCopy = pendingDefaultBranchAction
? resolveDefaultBranchActionDialogCopy({
action: pendingDefaultBranchAction.action,
Expand Down Expand Up @@ -1636,19 +1640,17 @@ export default function GitActionsControl({
<span className="flex items-center gap-1.5 font-mono text-xs tabular-nums">
<span
className={
changesTotals?.insertions
? "text-diff-addition-foreground"
: "text-muted-foreground"
changesTotals.insertions ? "text-diff-addition-foreground" : "text-muted-foreground"
}
>
+{formatDiffCount(changesTotals?.insertions ?? 0)}
+{formatDiffCount(changesTotals.insertions)}
</span>
<span
className={
changesTotals?.deletions ? "text-diff-deletion-foreground" : "text-muted-foreground"
changesTotals.deletions ? "text-diff-deletion-foreground" : "text-muted-foreground"
}
>
-{formatDiffCount(changesTotals?.deletions ?? 0)}
-{formatDiffCount(changesTotals.deletions)}
</span>
</span>
</ThreadDetailsControl>
Expand Down
Loading