Set actions/checkout fetch-depth: 0 vs 1 vs 2 in GitHub Actions
Speed up GitHub Actions checkout and reduce network transfer by letting `actions/checkout` fetch only the commit under test (its default `fetch-depth: 1`) instead of the whole git history. Reach for `fetch-depth: 0` only when a job genuinely needs history.
How StarSling worksAI agents open the PR
StarSling agents inspect your workflow, apply this optimization, and open a reviewable PR automatically.
Do this
actions/checkout runs at its default depth of 1, a single commit, for the vast majority of jobs. When a job needs the PR diff, fetch-depth: 2 is enough. Full history (fetch-depth: 0) is reserved for the specific jobs that actually need it: release-notes generation, git blame, or tools that walk tags. On a large monorepo where checkout itself is the bottleneck, depth is the first lever but not the last: a cone-mode sparse-checkout limits the working tree to the directories a job actually builds, and filter: blob:none makes a blobless clone that downloads a file's contents only when something reads it.123
# Most jobs: the default fetches only the commit under test
- uses: actions/checkout@v7
# Need the PR diff? Depth 2 is enough, not the whole history
- uses: actions/checkout@v7
with:
fetch-depth: 2
# Need the tags themselves (release naming, an exact-match tag lookup)? Fetch
# the tags, not the history. fetch-tags defaults to false, which is why tags go
# missing on a shallow clone. Note git describe is different: it walks ancestry
# to find the nearest reachable tag, so it still needs fetch-depth: 0.
- uses: actions/checkout@v7
with:
fetch-tags: true
# Huge monorepo and a job only touches a few directories? Narrow the working
# tree, not just the history. Cone-mode sparse-checkout materializes the paths
# you list (plus top-level files) and skips everything else, so a job that
# builds one app never pulls the other forty-nine packages.
- uses: actions/checkout@v7
with:
sparse-checkout: |
apps/web
packages/ui
# Want a blobless clone of the full tree (fetch commits and trees, download each
# file's contents only when something reads it)? Set filter: blob:none. It
# composes with sparse-checkout rather than disabling it: a sparse-checkout is
# already blobless under the hood, and an explicit filter only replaces the
# filter it applies for you, so you can set both.
- uses: actions/checkout@v7
with:
filter: 'blob:none'Avoid this
Every run downloads the entire history, even though the job only needs the latest commit.
- uses: actions/checkout@v7
with:
fetch-depth: 0 # clones the ENTIRE git history, slow on large reposSeen in the wild
Claude Code, browser-use, and Sentry limit checkout history with fetch-depth so CI does not clone more Git data than the job needs.
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 1 - uses: actions/checkout@v4
with:
# Force fresh checkout to avoid any caching issues
fetch-depth: 1 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 1Shipped by StarSling
On Mastra, StarSling kept the full commit graph for name-only diff jobs while making those checkouts blobless, so the workflows can compare commits and trees without downloading file contents they never read.
- Read the Mastra customer storyMerged 2026-08-12
Customer story
perf(ci): fetch full-history checkouts with --filter=blob:none
StarSling added a blobless partial-clone filter to full-history checkouts used for name-only change detection, preserving the commit and tree data those diffs need while deferring blob downloads.
Read the Mastra customer storyView PR #21298 in mastra-ai/mastra (opens in new tab)
Why it matters
On a repo with a long history, fetch-depth: 0 downloads far more than the job needs, and it pays that cost on every run. The default shallow fetch is often the single easiest checkout win. The fix is safe as long as you keep full history on the few jobs that require it. Past depth, the next levers narrow *what* you download rather than *how far back*, and their payoff scales with how much of the repo a job never touches. Size them honestly: a blobless clone (filter: blob:none) can be a wash when the job ultimately reads every file, because git then re-fetches those blobs on demand, so it pays off most paired with a sparse checkout where the excluded files are never fetched at all.
When to use
Use it when
Almost every job. Drop an explicit fetch-depth: 0 unless you can name why the job needs history. Find this automatically with /ci-speedup.
Be careful when
Keep full history when a job runs changelog/release tooling, git blame, git describe, or anything that walks the full commit graph. For those, fetch-depth: 0 is correct - git describe in particular names the nearest tag *reachable* from HEAD, and a shallow clone has no path back to it. A job that only needs the tags to exist (release naming, an exact-match lookup) does not need history at all: actions/checkout@v7 takes a fetch-tags: true input, which fetches tags at the shallow depth you already have.
Verify on your repo
Hand this prompt to your coding agent (Claude Code, Cursor, and the like) to audit and fix this practice in your own repo.
Inspect this repo's .github/workflows for `fetch-depth` on `actions/checkout`. Find every step that sets `fetch-depth: 0` (full history) and, for each, determine whether the job actually needs history: changelog or release tooling, `git blame`, `git describe`, or anything that walks tags. If nothing in the job needs history, remove the `fetch-depth: 0` (back to the default shallow depth of 1), or drop it to `fetch-depth: 2` when only a parent diff is needed. Leave jobs that genuinely need full history untouched. Show me the diff and open a PR rather than applying it blindly.Prefer to check by hand?
Search for the setting:
grep -rn 'fetch-depth' .github/workflows/.For each
fetch-depth: 0, ask what in the job needs history. If nothing does, remove it (back to the default 1) or drop to 2 for diff detection.A missing
fetch-depthis fine, the default is already 1.
Go further
One fix, all of them, or forever.
You have the prompt for this one practice. Here is how much further you can take it, each step doing more for you than the last.
Fix this one thing
Copy the prompt above
Hand OPT28 to your coding agent and fix it in your repo today.
Fix everything, once
Install the ci-score skill
One prompt grades your whole workflow config against all 11 CI Score checks, this one included, and hands your agent a ranked fix for every gap it finds. Open source, runs locally. It grades configuration, not speed.
Keep it fixed, forever
Install the StarSling GitHub App
Connect GitHub and the fixes stay applied as your CI evolves, with agents that keep inspecting your workflows and opening optimization PRs you review.
More best practices for GitHub Actions
Where to go next in the CI best-practices catalog.
- Caching & Setup
GitHub Actions cache: dependencies, keys, and cache hits
The GitHub Actions cache reuses package-manager downloads between workflow runs.
- Trigger Scope
Filter workflows with paths and paths-ignore in GitHub Actions
Add
pathsorpaths-ignoreto expensive workflows so a docs-only or unrelated change doesn't trigger the full test suite.
More ways to improve GitHub Actions
If you do not know why a run is slow yet, start with the diagnostic guide. Let an agent find which of these applies: /ci-speedup. If your workflows are already optimized but still slow, see our guide to fast GitHub Actions and GitHub Actions runner alternatives. Building containers in CI? The Docker workflow guide covers layer caching end to end. Want to see how your configuration measures up before changing anything? CI Score grades workflow config against a pass/fail rubric of these practices. It is not a speed measurement. To find exploitable workflow paths before attackers do, ci-secure checks the ten critical GitHub Actions attack vectors and lets you choose which fixes to apply.
- GitHub Actions too slow
- Fast GitHub Actions
- GitHub Actions alternatives
- GitHub Actions pricing
- Self-hosted GitHub Actions runners
- Docker CI on GitHub Actions
- Install the free /ci-score skill to improve your GitHub Actions setup
- Install the /ci-secure skill to close critical attack vectors in GitHub Actions
- Browse GitHub Actions agent skills
FAQ
fetch-depth 0 vs 1: what's the difference?
fetch-depth: 1, the GitHub Actions default, clones only the single commit being tested, while fetch-depth: 0 clones the complete history and every tag, which you need only for tooling that walks the full graph. 2 sits between them: it adds the parent commit, enough to diff the merge commit's two parents (the default checkout) or a single-commit PR, though a multi-commit PR checked out at its head ref needs the merge base instead.
My diff-based tool broke after I removed fetch-depth: 0. Why?
It needs more than one commit to compare against. Use fetch-depth: 2 for a simple parent diff, or fetch the specific base ref your tool diffs against, rather than the whole history.
Do I lose my tags if I drop fetch-depth: 0?
Yes, by default. actions/checkout fetches no tags on a shallow clone: its fetch-tags input defaults to false, and fetch-depth: 0 is what pulls in all history for all branches and tags. If a job only needs the tags to be present (release naming, an exact-match lookup), set fetch-tags: true and keep the shallow depth rather than cloning the entire history to get them. git describe is the exception: it reports the distance to the nearest tag reachable from HEAD, so it needs the history too.
How do I speed up checkout beyond fetch-depth on a huge repo?
Narrow what you clone, not just how far back. actions/checkout takes a sparse-checkout: input (cone mode by default) that limits the working tree to the directories you list plus top-level files, so a job that builds one app in a fifty-package monorepo never materializes the other forty-nine. For an even lighter clone, filter: blob:none makes a blobless partial clone that downloads a file's contents only when git needs them. These narrow the files, where fetch-depth narrows the history, and they stack with a shallow depth.
Can I use sparse-checkout and filter: blob:none together?
Yes. actions/checkout's docs say filter "overrides sparse-checkout," but that wording is misleading: an explicit filter only replaces the filter that sparse-checkout applies for you, not the path-narrowing itself, so the two compose. In fact a sparse-checkout is already blobless - the action sets filter: blob:none under the hood - so sparse-checkout alone gives you a blobless clone limited to the directories you list, which is the biggest win on a wide monorepo. Set an explicit filter when you want a different partial-clone filter (say tree:0) while keeping the sparse paths, or filter: blob:none on its own when a job needs the whole tree but you want to defer downloading file contents.
Does a blobless (filter: blob:none) clone always make CI faster?
No. A blobless clone skips file contents up front and fetches each blob on demand the first time git reads it. A build that ends up reading most of the tree re-downloads those blobs anyway, in extra round trips, so the clone can come out a wash or slightly slower. It pays off when the job touches only part of the tree, which is why it shines paired with a sparse checkout, and for history-only work where you never check out files at all.
Sources
1actions/checkout · fetch-depth (opens in new tab)
Last updated 2026-08-19