AI code review: choose the right Git diff before paying
TL;DR: For a feature-branch review, compare its pinned commit against its merge base with the target branch. Review uncommitted work separately. A clean working directory does not mean the branch has no changes, and comparing branch tips can spend your review budget on unrelated upstream work.
What you’ll be able to do:
- Select the committed changes that belong to a feature branch.
- Save the exact base and head identities alongside a readable patch.
- Bound the review packet before sending it to a paid or local model.
Honest take: Fix the review input before buying a larger context window or another review subscription.
Which Git diff represents your AI review task?
A feature-branch review needs a different diff from an uncommitted-edit review. The official Git diff reference, checked November 7, 2026, documents comparisons between working files, the index, commits, and a merge base. Choose the comparison that matches the question you want answered.
| Review question | Input to inspect | What it leaves out |
|---|---|---|
| What did I edit since staging? | git diff | Staged and already committed changes |
| What would the next commit include? | git diff --cached | Unstaged and untracked files |
| What differs between these branch tips? | git diff origin/main HEAD | Why those histories diverged |
| What did this branch change since a shared base? | Merge-base-to-head diff | Uncommitted local work and future merge results |
The last row is the default for the committed branch packet in this tutorial. It is not a promise that a pull request interface or a server-side merge simulation will always show identical results. Repository hosting settings, merge queues, and rebases can change the actual integration candidate.
If your task is reviewing the next local commit, use the staged-diff review workflow instead. Sending both packets without explaining their relationship can cause a reviewer to count the same edit twice.
Why can comparing branch tips include work you never wrote?
A target branch can advance after you create your feature branch. A direct comparison between the two tips describes their present file differences, including changes that exist only on the target. To a reviewer lacking history, those differences can look like your branch removed or reversed upstream work.
The merge-base manual explains how Git identifies common ancestors. Comparing the selected common base to your feature commit asks a narrower question: what changed along this branch relative to that shared starting point?
Consider a small constructed repository. Both branches begin with app.py. The target then adds upstream.txt, while the feature changes app.py. In our local fixture using Git 2.47.3, a direct tip comparison listed both paths; the merge-base comparison listed only app.py. That is a local Git behavior test, not a measurement of an AI model’s review accuracy.
A packet containing the extra upstream path could consume attention on an irrelevant apparent deletion. We have not measured how often a model misreads that input. The deterministic part is that the input file set differs, so the reviewer needs the right comparison and an explicit explanation of its scope.
How do you pin the review’s base and head?
Resolve commit identities once, then use those identities for every file in the packet. A moving reference name alone is an incomplete description of an artifact you plan to discuss tomorrow.
First update the target reference through your normal authenticated Git workflow:
git fetch origin main
git status --short
The fetch reference describes how fetched references are updated. If fetching fails, stop and state that the remote baseline was not refreshed. Do not label a packet as based on current upstream when the only available reference is an older local copy.
Save the following POSIX shell script as make-review-packet.sh and run it from the repository. It creates a new output directory and refuses to reuse one. Choose an output path outside the repository so the artifacts do not become accidental changes under review.
#!/usr/bin/env bash
set -euo pipefail
target_ref=${1:-origin/main}
out=${2:?Usage: bash make-review-packet.sh [target] NEW_OUTPUT_DIR}
head_sha=$(git rev-parse --verify 'HEAD^{commit}')
target_sha=$(git rev-parse --verify "${target_ref}^{commit}")
bases=$(git merge-base --all "$target_sha" "$head_sha")
if [[ -z "$bases" || "$bases" == *$'\n'* ]]; then
echo 'Expected one merge base; resolve history before review.' >&2
exit 1
fi
base_sha=$bases
mkdir -- "$out"
printf 'target=%s\nbase=%s\nhead=%s\n' \
"$target_sha" "$base_sha" "$head_sha" > "$out/commits.txt"
git diff --no-ext-diff --no-textconv --no-renames --stat \
"$base_sha" "$head_sha" -- > "$out/stat.txt"
git diff --no-ext-diff --no-textconv --no-renames --name-status \
"$base_sha" "$head_sha" -- > "$out/paths.txt"
git diff --no-ext-diff --no-textconv --no-renames \
"$base_sha" "$head_sha" -- > "$out/changes.patch"
git log --format='%h %s' "$base_sha..$head_sha" \
> "$out/commits.log"
git status --short > "$out/local-status.txt"
wc -c < "$out/changes.patch" > "$out/patch-bytes.txt"
printf 'REVIEW_PACKET_READY %s\n' "$out"
Example invocation:
bash make-review-packet.sh origin/main /tmp/my-feature-review-01
A successful run ends with REVIEW_PACKET_READY /tmp/my-feature-review-01. The script does not fetch automatically, stage files, modify branch history, run an AI tool, or send anything to a provider. It only writes local review artifacts.
The revision parser documentation supports verifying references as commits. The explicit commit identities also prevent a later background fetch from changing the endpoints halfway through this packet’s generation.
Why stop when Git finds multiple merge bases?
Some histories have more than one best common ancestor. The merge-base manual documents this possibility and the --all option. Choosing the first line silently would hide a decision that can affect the comparison.
This script takes the conservative route: a branch with multiple bases needs a deliberate review baseline. The script also stops when Git cannot find a common base. In a shallow clone, missing history can be the cause; in another repository, the histories may actually be unrelated. Those conditions need different investigation.
Inspect the clone’s history and fetch the necessary ancestry through the normal repository workflow. Do not create an empty patch to make the review automation pass. A packet that failed to establish its baseline is not a successful review with zero findings.
The shell script’s output directory may remain partially populated if an individual command fails after directory creation. Preserve it as failed evidence or choose a new directory for the next attempt. REVIEW_PACKET_READY is printed only after all packet commands succeed.
What should you inspect before sending the packet to a model?
Read the path list and summary first. They reveal whether the comparison matches your intended task before you spend model context on the full patch. An unexpected directory or a large generated file deserves an explanation.
The script disables external diff helpers and text conversion so the packet does not depend on locally configured presentation programs. It also disables rename detection: a renamed file may appear as a deletion and addition. That is an explicit trade-off for a straightforward artifact, not a claim that renames do not exist.
A normal text patch is not a complete review representation for binary assets. The patch may only indicate that binary files differ. Inspect those files with suitable tools, and record that limitation instead of asking a text model to infer their contents.
Check the packet for sensitive data before sharing it. A tracked credential accidentally included in a commit can appear in a patch. A path list does not establish that the content is safe to upload, and a private provider setting does not remove your organization’s rules for sharing source code.
How can you put a real bound on the review budget?
Measure the patch artifact before choosing a review mode. patch-bytes.txt contains a byte count, not a token estimate. There is no universal fixed conversion from bytes to billed tokens: text, identifiers, formatting, and the provider’s tokenizer all matter.
A practical local policy might require human triage above a chosen patch size. Label that threshold as your team’s rule rather than a model’s documented maximum. Smaller does not mean safer; a short authentication change can deserve more scrutiny than a long generated file.
For cost planning, use a symbolic calculation with your actual provider’s verified rates:
review cost = input_tokens * input_rate_per_million / 1_000_000
+ output_tokens * output_rate_per_million / 1_000_000
As a hypothetical workload, resending an unchanged 20,000-token packet in four uncached review requests sends 80,000 packet tokens before instructions and output. That is arithmetic, not an observed bill, and it excludes caching discounts, tool context, and other pricing rules. Read your provider’s current billing terms before attaching a currency amount.
For local inference, repeated packets still use context capacity and machine time even when there is no per-token API bill. This packet builder itself needs only Git, Bash, and local disk space; it has no GPU requirement or paid service dependency. Do not buy hardware merely to generate diffs.
The accepted-change cost ledger can help connect review spending to useful outcomes. Avoid claiming savings merely because a packet became smaller; record the actual billed usage and accepted review findings if you want to evaluate the policy.
How should you handle uncommitted and untracked files?
Treat local changes as a separate scope. The committed packet is frozen at head_sha. A later workspace edit cannot enter it, which is useful for reproducibility but easy to forget when an agent is still working.
local-status.txt is a human-readable reminder, not a machine-safe manifest for arbitrary filenames. Use Git’s NUL-delimited formats for automation that must handle unusual path characters. The tracked and untracked file reference documents the relevant file-listing commands.
Do not stage every file just to make it visible to the reviewer. Decide which changes belong in the task, inspect them, and build the appropriate additional packet. If you are running independent tasks, the two-worktree workflow keeps their working changes easier to separate.
What happened in the local reproduction?
We created a temporary repository with a common initial commit, an upstream-only file, and a feature-only code edit. The direct tip diff contained app.py and upstream.txt; the merge-base packet’s path list contained app.py alone. We then modified a tracked file without committing and added an untracked file. Both appeared in local status while the committed patch remained unchanged.
We also ran the packet script twice against the same output directory. The second invocation failed at directory creation, preserving the first packet. These checks validate baseline selection, uncommitted-work exclusion, and artifact preservation. They do not test an AI review product, prove defect detection, or establish any subscription’s value.
Honest verdict: when should you use another review input?
Use the merge-base packet for a committed feature branch whose changes you want reviewed relative to its shared ancestry. Use a staged packet for the next local commit. Use a tested merge candidate when the actual question is whether the feature integrates with current upstream.
A merge-base diff does not execute tests, establish that the feature still applies cleanly, or predict production behavior. Once target changes matter to the feature, integration validation becomes necessary even if the branch-only patch is small. Keep the packet as evidence of what the reviewer saw, then attach test results and the final commit identity before acting on its conclusions.
Sources
- Diff endpoints, merge-base comparison, and output options — Git
- Common ancestors and multiple merge bases — Git
- Resolve and verify commit identities — Git
- Fetch remote references — Git
- Commit history and revision ranges — Git
- Tracked and untracked file enumeration — Git
Last updated November 7, 2026. Official Git references checked today; local Git fixtures tested with version 2.47.3. No paid AI review, productivity improvement, or monetary saving is claimed. Verify provider billing separately before budgeting.
Was this article helpful?
Thanks for the feedback — it helps improve future articles.
Need hands-on help?
I offer 1-on-1 technical consulting for local AI setup, GPU selection, and AI coding tool configuration — same topics covered on this site.
Book a session — $49 / hour →Know which coding tool is worth paying for
Hands-on comparisons of AI coding assistants and what each one costs to run — including the local-model path. Sent only when something changes. Unsubscribe anytime.