Safeguard
Developer Tools•

Why does my diff-based scan fail in CI after a shallow checkout?

The default checkout fetches one commit, so a diff against main has nothing to compare. Raise fetch depth or fetch the base to fix changed-file scans.

Safeguard Security Team
4 min read

Because the default checkout fetches a single commit. A diff against the base branch needs the base commit in the local history, and a shallow clone does not have it. Raise the fetch depth, fetch the base ref explicitly, or use the platform's changed file list instead.

What the default does

The documentation for the GitHub checkout action says fetch-depth defaults to 1, meaning only a single commit is fetched, for the ref or SHA that triggered the workflow. Setting it to 0 fetches all history for all branches and tags. The docs show fetching HEAD^ with a depth of 2.

Most tools that find changed packages run a git diff between two refs. With one commit in the clone, the base ref is missing, and the command fails or, worse, returns nothing.

Symptoms

SymptomLikely cause
Error about an unknown revisionBase commit not in the shallow clone
No packages detected as changedDiff failed quietly and returned empty
Everything scanned every timeTool fell back to a full run
Works locally, fails in CIYour local clone has full history

The quiet empty result is the dangerous one. A scan that finds nothing changed reports a pass and ships unreviewed dependency changes.

Three fixes

  1. Full history. Set fetch-depth to 0. Simple, but slow on very large repositories.
  2. Fetch the base. Keep the depth small and fetch the target branch explicitly before running the diff.
  3. Use the changed file list from the platform. Pull request events carry the base and head commit SHAs, and many tools accept them directly.
- uses: actions/checkout@v4
  with:
    fetch-depth: 0

Pin the action to the version you have reviewed in your own setup.

Guard against the silent pass

Add a check that fails the job if the diff command errors or if the base SHA is not found. Also log the list of changed files in the job output, so a reviewer can see what the scan believed had changed. For pull requests, remember that GitHub computes the changed file list using a three dot diff against where the topic branch last synced with its base.

Sparse checkout is a separate choice

The checkout action also offers sparse checkout, which fetches only the patterns you name. That helps speed but means files outside the pattern are absent, so a scan run there cannot see them. Sparse checkout is not access control either, see sparse checkout is not access control.

Keep a full scan anyway

Diff based scans save time but depend on correct history. Run a scheduled full scan so a bad diff on one day does not become a blind spot for weeks.

Safeguard can scan on pull requests and in the pipeline, with policies that gate on findings. The free scan at https://gold.safeguard.sh/scan needs no setup.

Try it yourself

In the gate demo, look at what information the check needs to decide pass or fail. Missing history means missing input.

Click through it at your own pace, or open it full screen.

Quick check

What is the default fetch depth in the checkout action?

1, which fetches a single commit. A value of 0 fetches all history.

Why is an empty changed-file list risky?

It can mean the diff failed quietly, so the scan skips packages that did change.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.