Skip to content

Commit a68463b

Browse files
authored
feat(review): add "All files" diff type (#629)
* feat(review): add "All files" diff type for reviewing entire codebases Adds an "all" diff type that diffs the empty tree against HEAD, showing every tracked file as an addition. Solves the case where a repo has only committed changes and no working tree diffs — previously there was no way to launch a review at all. For provenance purposes, this commit was AI assisted. * fix(review): add "All files" to Settings panel diff type options The Settings panel (gear icon) had its own DEFAULT_DIFF_TYPE_OPTIONS list that was missing the new "all" diff type, so users couldn't select or see it as the default from the settings UI. For provenance purposes, this commit was AI assisted. * fix(review): rename "All files" to "All files (HEAD)" for clarity Makes it clear this mode shows committed state at HEAD, not the working tree. Updated tooltip to distinguish from "Committed changes" (which diffs against a base branch). For provenance purposes, this commit was AI assisted.
1 parent 117c7c8 commit a68463b

11 files changed

Lines changed: 53 additions & 7 deletions

File tree

packages/review-editor/App.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,7 @@ const ReviewApp: React.FC = () => {
951951
const lastColon = rest.lastIndexOf(':');
952952
if (lastColon !== -1) {
953953
const sub = rest.slice(lastColon + 1);
954-
if (['uncommitted', 'staged', 'unstaged', 'last-commit', 'branch', 'merge-base'].includes(sub)) {
954+
if (['uncommitted', 'staged', 'unstaged', 'last-commit', 'branch', 'merge-base', 'all'].includes(sub)) {
955955
return { activeWorktreePath: rest.slice(0, lastColon), activeDiffBase: sub };
956956
}
957957
}
@@ -1977,6 +1977,7 @@ const ReviewApp: React.FC = () => {
19771977
{activeDiffBase === 'last-commit' && `No changes in the last commit${activeWorktreePath ? ' in this worktree' : ''}.`}
19781978
{activeDiffBase === 'branch' && `No changes vs ${selectedBase || gitContext?.defaultBranch || 'main'}${activeWorktreePath ? ' in this worktree' : ''}.`}
19791979
{activeDiffBase === 'merge-base' && `No changes vs ${selectedBase || gitContext?.defaultBranch || 'main'}${activeWorktreePath ? ' in this worktree' : ''}.`}
1980+
{activeDiffBase === 'all' && `No tracked files${activeWorktreePath ? ' in this worktree' : ' in this repository'}.`}
19801981
</p>
19811982
</>
19821983
)}

packages/review-editor/components/DiffTypePicker.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const OPTION_HINTS: Record<string, string> = {
2222
'last-commit': "Just your most recent commit.",
2323
branch: "Straight compare against the base branch (picked below). Can show commits that aren't yours if the base has new commits.",
2424
'merge-base': "Only what you've added on top of the base branch (picked below). Same as GitHub's PR view.",
25+
all: "Every tracked file at HEAD, shown as additions. Unlike Committed, which shows what changed vs a base branch, this shows the entire codebase.",
2526
};
2627

2728
export const DiffTypePicker: React.FC<DiffTypePickerProps> = ({

packages/review-editor/utils/exportFeedback.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ function describeDiff(ctx: FeedbackDiffContext): string {
3838
case "last-commit": label = "Last commit"; break;
3939
case "branch": label = base ? `Branch diff vs \`${base}\`` : "Branch diff"; break;
4040
case "merge-base": label = base ? `Committed changes vs \`${base}\`` : "Committed changes"; break;
41+
case "all": label = "All files"; break;
4142
default: label = mode; // p4-* or anything else — show raw
4243
}
4344
return worktreePath ? `${label} _(worktree: ${worktreePath})_` : label;

packages/server/codex-review.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ export function buildCodexReviewUserMessage(
223223
return `Review the PR-style diff against base '${base}'. First find the common ancestor with \`git merge-base ${base} HEAD\`, then run \`git diff <merge-base>..HEAD\` using that commit to inspect only the changes introduced on this branch (matches GitHub's PR view). Provide prioritized, actionable findings.`;
224224
}
225225

226+
case "all":
227+
return "Review every file in the repository (all files shown as additions, diffed against an empty tree). Provide prioritized, actionable findings.";
228+
226229
default:
227230
// p4 or unknown — fall back to generic with inlined diff
228231
return [

packages/server/tour/tour-review.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,8 @@ function buildTourUserMessage(
338338
const base = options?.defaultBranch || "main";
339339
return `Walk the reviewer through the PR-style diff against base '${base}' as a guided tour. First find the common ancestor with \`git merge-base ${base} HEAD\`, then run \`git diff <merge-base>..HEAD\` using that commit to inspect only the changes introduced on this branch (matches GitHub's PR view).`;
340340
}
341+
case "all":
342+
return "Walk the reviewer through every file in the repository as a guided tour. All files are shown as additions (diffed against an empty tree).";
341343
default:
342344
return [
343345
"Walk the reviewer through the following code changes as a guided tour.",

packages/server/vcs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export interface VcsProvider {
7272

7373
// --- Git provider ---
7474

75-
const GIT_DIFF_TYPES = new Set(["uncommitted", "staged", "unstaged", "last-commit", "branch", "merge-base"]);
75+
const GIT_DIFF_TYPES = new Set(["uncommitted", "staged", "unstaged", "last-commit", "branch", "merge-base", "all"]);
7676

7777
const gitProvider: VcsProvider = {
7878
id: "git",

packages/shared/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { join } from "path";
1010
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
1111
import { execSync } from "child_process";
1212

13-
export type DefaultDiffType = 'uncommitted' | 'unstaged' | 'staged' | 'merge-base';
13+
export type DefaultDiffType = 'uncommitted' | 'unstaged' | 'staged' | 'merge-base' | 'all';
1414

1515
export interface DiffOptions {
1616
diffStyle?: 'split' | 'unified';
@@ -198,7 +198,7 @@ export function getServerConfig(gitUser: string | null): {
198198
export function resolveDefaultDiffType(cfg?: PlannotatorConfig): DefaultDiffType {
199199
const v = cfg?.diffOptions?.defaultDiffType as string | undefined;
200200
if (v === 'branch') return 'merge-base';
201-
return v === 'uncommitted' || v === 'unstaged' || v === 'staged' || v === 'merge-base' ? v : 'unstaged';
201+
return v === 'uncommitted' || v === 'unstaged' || v === 'staged' || v === 'merge-base' || v === 'all' ? v : 'unstaged';
202202
}
203203

204204
/**

packages/shared/review-core.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type DiffType =
1414
| "last-commit"
1515
| "branch"
1616
| "merge-base"
17+
| "all"
1718
| `worktree:${string}`
1819
| "p4-default"
1920
| `p4-changelist:${string}`;
@@ -275,6 +276,8 @@ export async function getGitContext(
275276
diffOptions.push({ id: "merge-base", label: "Committed changes" });
276277
}
277278

279+
diffOptions.push({ id: "all", label: "All files (HEAD)" });
280+
278281
const [worktrees, currentTreePathResult] = await Promise.all([
279282
getWorktrees(runtime, cwd),
280283
runtime.runGit(["rev-parse", "--show-toplevel"], { cwd }),
@@ -368,6 +371,7 @@ const WORKTREE_SUB_TYPES = new Set([
368371
"last-commit",
369372
"branch",
370373
"merge-base",
374+
"all",
371375
]);
372376

373377
export function parseWorktreeDiffType(
@@ -539,6 +543,29 @@ export async function runGitDiff(
539543
break;
540544
}
541545

546+
case "all": {
547+
// Diff from the empty tree to HEAD — shows every tracked file as an addition.
548+
const emptyTreeResult = await runtime.runGit(["hash-object", "-t", "tree", "/dev/null"], { cwd });
549+
const emptyTree = emptyTreeResult.exitCode === 0
550+
? emptyTreeResult.stdout.trim()
551+
: "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
552+
const allDiffArgs = [
553+
"diff",
554+
"--no-ext-diff",
555+
"--src-prefix=a/",
556+
"--dst-prefix=b/",
557+
"--end-of-options",
558+
`${emptyTree}..HEAD`,
559+
];
560+
const allDiff = assertGitSuccess(
561+
await runtime.runGit(allDiffArgs, { cwd }),
562+
allDiffArgs,
563+
);
564+
patch = allDiff.stdout;
565+
label = "All files";
566+
break;
567+
}
568+
542569
default:
543570
return { patch: "", label: "Unknown diff type" };
544571
}
@@ -637,6 +664,11 @@ export async function getFileContentsForDiff(
637664
newContent: await gitShow("HEAD", filePath),
638665
};
639666
}
667+
case "all":
668+
return {
669+
oldContent: null,
670+
newContent: await gitShow("HEAD", filePath),
671+
};
640672
default:
641673
return { oldContent: null, newContent: null };
642674
}

packages/ui/components/DiffTypeSetupDialog.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ const OPTIONS: { value: DefaultDiffType; label: string; description: string }[]
2626
label: 'Committed',
2727
description: "Everything you've committed on this branch",
2828
},
29+
{
30+
value: 'all',
31+
label: 'All Files (HEAD)',
32+
description: "Every tracked file at HEAD, shown as additions",
33+
},
2934
];
3035

3136
interface DiffTypeSetupDialogProps {

packages/ui/components/Settings.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ const DEFAULT_DIFF_TYPE_OPTIONS = [
126126
{ value: 'unstaged' as const, label: 'Unstaged', description: "Only changes you haven't staged yet" },
127127
{ value: 'staged' as const, label: 'Staged', description: "Only changes you've staged for commit" },
128128
{ value: 'merge-base' as const, label: 'Committed', description: "Everything you've committed on this branch" },
129+
{ value: 'all' as const, label: 'All Files (HEAD)', description: "Every tracked file at HEAD, shown as additions" },
129130
];
130131

131132
function SegmentedControl<T extends string>({ options, value, onChange }: {

0 commit comments

Comments
 (0)