How react-doctor --diff Mode Detects and Scans Changed Files Against a Base Branch
The --diff flag uses Git commands to identify files changed between your current branch and a base branch (or uncommitted changes), then restricts the diagnostic scan to only those modified source files.
react-doctor (from the Million ecosystem) provides a targeted scanning mode that avoids analyzing the entire codebase. When activated, the tool executes a precise Git-based detection pipeline to minimize feedback loops during development and CI workflows.
The Four-Step Detection Pipeline
The diff detection process is implemented across src/cli.ts and src/utils/get-diff-files.ts. It follows a strict resolution order to ensure reliability in various Git states.
Step 1: Resolve the Effective Diff Flag
The CLI entry point first normalizes user input through resolveEffectiveDiff(). This function handles boolean flags, explicit branch names, and the --full escape hatch.
If the user passes --full, diff mode is immediately disabled regardless of other flags. Otherwise, the function prepares the diff configuration for the next stage.
Step 2: Gather Diff Information via Git
The core logic resides in getDiffInfo() within src/utils/get-diff-files.ts. This utility executes a sequence of Git commands to determine the file delta:
- Detect current branch: Uses
git rev-parse --abbrev-ref HEAD - Auto-detect default branch: Reads
origin/HEADor falls back toDEFAULT_BRANCH_CANDIDATES(defined insrc/constants.tsas["main", "master", "develop", "dev"]) - Verify explicit base: If a branch name is provided, validates it with
git rev-parse --verify - Calculate changes:
- If on the base branch itself: captures uncommitted changes via
git diff HEAD - If on a feature branch: finds the merge-base and runs
git diff <merge-base>..HEAD
- If on the base branch itself: captures uncommitted changes via
The function returns file paths null-separated (-z flag) for safe handling of special characters, then filters them against SOURCE_FILE_PATTERN to exclude non-source files.
Step 3: Prompt and Mode Resolution
The resolveDiffMode() function in src/cli.ts makes the final go/no-go decision:
- Returns
falseifgetDiffInfofailed or no changes were detected - When running interactively (TTY), prompts the user: "Only scan changed files?"
- Falls back to a full scan with a warning if the diff set is empty
Step 4: Restrict the Scan Scope
For each project directory, the CLI builds an includePaths array containing only the changed source files (processed through filterSourceFiles(diffInfo.changedFiles)). The scan() function receives this whitelist, ensuring that linting, dead-code detection, and rule execution operate solely on the modified subset.
How the Base Branch Is Determined
When --diff is passed without an explicit branch name, detectDefaultBranch() attempts resolution in this order:
- Primary:
origin/HEAD(resolves to the remote default likemainormaster) - Fallback: The first existing ref among
DEFAULT_BRANCH_CANDIDATES(main,master,develop,dev) - Failure: Disables diff mode and runs a full scan
If the user specifies --diff <branch>, that string is used directly after verification.
File Filtering and Pattern Matching
Not all changed files warrant analysis. The system applies SOURCE_FILE_PATTERN (defined in src/constants.ts) to the raw Git output to exclude Markdown files, generated assets, and configuration files. Only matching source files enter the changedFiles array passed to the scanner.
Usage Examples
Scan uncommitted changes on the default branch
npx -y react-doctor@latest . --verbose --diff
On main, this scans only unstaged and staged modifications in your working directory.
Diff against a specific base branch
npx -y react-doctor@latest . --diff develop
The tool verifies that develop exists, finds the merge-base between develop and HEAD, and reports files changed since that divergence point.
Programmatic integration
import { getDiffInfo } from "react-doctor/src/utils/get-diff-files.js";
const diffInfo = getDiffInfo(process.cwd(), "main");
if (diffInfo) {
console.log("Changed files:", diffInfo.changedFiles);
// Returns: ['src/components/Button.tsx', 'src/utils/helpers.ts']
}
The changedFiles array contains repository-relative paths already filtered to source extensions.
Mode Interactions and Edge Cases
--full override: Takes precedence over --diff, forcing a complete codebase scan regardless of Git state.
--staged exclusivity: Mutually exclusive with --diff. When using --staged, the tool utilizes src/utils/get-staged-files.ts to materialize the Git index into a temporary snapshot and runs a full analysis on that staged content only.
Summary
resolveEffectiveDiff()insrc/cli.tsnormalizes the--diffflag and handles the--fulloverridegetDiffInfo()insrc/utils/get-diff-files.tsexecutes Git commands to find the merge-base and list changed files- Base branch auto-detection prefers
origin/HEAD, falling back tomain,master,develop, ordev SOURCE_FILE_PATTERNfilters Git output to include only relevant source files- The
scan()function receives the filteredchangedFilesarray, limiting analysis to the diff set
Frequently Asked Questions
What happens if I run --diff while on the base branch itself?
The tool detects that the current branch IS the base branch and switches behavior to scan uncommitted changes only (equivalent to git diff HEAD). It will not attempt to find a merge-base or compare against the remote.
Can I use --diff with --staged simultaneously?
No, these flags are mutually exclusive. --staged triggers a separate code path (get-staged-files.ts) that analyzes only staged changes through a temporary file snapshot, while --diff analyzes the working directory against a base branch.
How does react-doctor handle file paths with spaces or special characters?
The getDiffInfo() function uses Git's -z flag for null-terminated path output, ensuring that filenames containing spaces, quotes, or unicode characters are parsed correctly without shell interpolation issues.
What file extensions are considered "source files"?
The scan respects the SOURCE_FILE_PATTERN regular expression defined in src/constants.ts. This typically includes JavaScript, TypeScript, JSX, TSX, and other React-relevant extensions, while excluding documentation, images, and build artifacts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →