Skip to content

Commit b527e7a

Browse files
ymansurozerclaude
andauthored
fix: diff PRs against the up-to-date base to avoid unrelated changes (#27)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 58eb63e commit b527e7a

2 files changed

Lines changed: 20 additions & 4 deletions

File tree

src/cli.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,18 @@ async function resolvePrRef(ref: string, root: string): Promise<{ head: string;
309309
if (!info.headRefName || !info.baseRefName)
310310
throw new Error(`PR "${ref}" did not resolve to a head/base branch.`);
311311
await gh(["pr", "checkout", ref], root);
312-
return { head: info.headRefName, base: info.baseRefName };
312+
// `gh pr checkout` updates the HEAD but never refreshes the base branch, so a merge-base against
313+
// a stale local base makes a long-lived PR show unrelated mainline commits (the reported "PR diff
314+
// is wrong"). Refresh the base ref and diff against the remote-tracking tip, matching GitHub's
315+
// three-dot "Files changed". Best-effort: offline / a fork base not on `origin` falls back to the
316+
// bare branch name (prior behavior). --quiet keeps rev-parse from printing on the miss path.
317+
await git(["fetch", "origin", info.baseRefName], root).catch(() => {});
318+
const remoteBase = `origin/${info.baseRefName}`;
319+
const base = await git(["rev-parse", "--verify", "--quiet", remoteBase], root).then(
320+
() => remoteBase,
321+
() => info.baseRefName!,
322+
);
323+
return { head: info.headRefName, base };
313324
}
314325

315326
// Launch a persistent desk in repo / file / pr mode.

src/git.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@ import type { DiffFile, DiffHunk, DiffLine } from "./types.js";
66

77
const execFileAsync = promisify(execFile);
88

9+
// Cap on a single git/gh stdout. Generous because a whole-PR `git diff` or a `git show` of a large
10+
// generated/vendored file can be big; exceeding it rejects (git()) or degrades a file to a spurious
11+
// full add/delete (fileAt swallows the error), so a too-small cap silently corrupts a large diff.
12+
const MAX_BUFFER = 256 * 1024 * 1024;
13+
914
export async function git(args: string[], cwd: string) {
10-
const { stdout } = await execFileAsync("git", args, { cwd, maxBuffer: 50 * 1024 * 1024 });
15+
const { stdout } = await execFileAsync("git", args, { cwd, maxBuffer: MAX_BUFFER });
1116
return String(stdout).trimEnd();
1217
}
1318

1419
// Thin wrapper around the GitHub CLI, used only to resolve a PR number/URL to its branch.
1520
// Kept optional: callers catch failures (gh missing or unauthenticated) and report them.
1621
export async function gh(args: string[], cwd: string) {
17-
const { stdout } = await execFileAsync("gh", args, { cwd, maxBuffer: 50 * 1024 * 1024 });
22+
const { stdout } = await execFileAsync("gh", args, { cwd, maxBuffer: MAX_BUFFER });
1823
return String(stdout).trimEnd();
1924
}
2025

@@ -126,7 +131,7 @@ export async function fileAt(root: string, rel: string | undefined, ref?: string
126131
if (ref) {
127132
const { stdout } = await execFileAsync("git", ["show", `${ref}:${rel}`], {
128133
cwd: root,
129-
maxBuffer: 50 * 1024 * 1024,
134+
maxBuffer: MAX_BUFFER,
130135
});
131136
return String(stdout);
132137
}

0 commit comments

Comments
 (0)