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:

  1. Detect current branch: Uses git rev-parse --abbrev-ref HEAD
  2. Auto-detect default branch: Reads origin/HEAD or falls back to DEFAULT_BRANCH_CANDIDATES (defined in src/constants.ts as ["main", "master", "develop", "dev"])
  3. Verify explicit base: If a branch name is provided, validates it with git rev-parse --verify
  4. 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

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 false if getDiffInfo failed 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 like main or master)
  • 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() in src/cli.ts normalizes the --diff flag and handles the --full override
  • getDiffInfo() in src/utils/get-diff-files.ts executes Git commands to find the merge-base and list changed files
  • Base branch auto-detection prefers origin/HEAD, falling back to main, master, develop, or dev
  • SOURCE_FILE_PATTERN filters Git output to include only relevant source files
  • The scan() function receives the filtered changedFiles array, 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →