Introduction
A shallow clone downloads only the most recent N commits of a branch instead of the full history. The result is much smaller and faster to fetch, at the cost of an incomplete history.
Creating
git clone --depth 1 https://github.com/example/widget.git
git clone --depth 50 https://github.com/example/widget.git
git clone --depth 1 --branch v1.2.3 --single-branch <url>
The .git directory contains a shallow file listing commits whose parents have been omitted.
Why shallow
- CI builds: only need source for HEAD.
- Containers and Dockerfiles: smaller images.
- One-shot tooling that does not need history.
Limitations
- You cannot push from a shallow clone if your push depends on commits the server does not have (rare, but possible).
git logstops at the shallow boundary.- Some operations (rebase across the boundary, blame on old lines) cannot work.
- Until Git 2.11 cloning shallow over HTTP was unsupported; modern Git handles it.
Deepening and unshallowing
git fetch --depth 100 # extend depth
git fetch --deepen 50 # add 50 more commits
git fetch --shallow-since=2024-01-01
git fetch --unshallow # download full history
After --unshallow the repo behaves like a normal clone.
Single-branch and shallow
--single-branch + --depth 1 is the smallest possible useful clone. To later track other branches:
git remote set-branches --add origin main feature
git fetch origin
Partial clones (a different optimization)
If you need full history but not all blobs, use a partial clone instead:
git clone --filter=blob:none <url>
git clone --filter=tree:0 <url>
Partial clones download history but defer blobs (or trees) until needed. They are usually a better choice than shallow when the server supports them.
Detecting shallow state
git rev-parse --is-shallow-repository
cat .git/shallow
CI patterns
Many CI providers default to shallow clones (depth 1 or 50). If your build runs git describe or needs blame across versions, request a full clone:
# GitHub Actions
- uses: actions/checkout@v4
with:
fetch-depth: 0
Shallow files and grafts
The shallow file in .git/ lists commit SHAs whose parents are intentionally missing. Git treats them as roots during graph traversal. The related but deprecated info/grafts mechanism let you fake parent relationships; use git replace --graft instead, which is safer and replicates over fetch:
cat .git/shallow
git replace --graft <commit> <new-parent>
git replace -l
git replace -d <commit>
This is occasionally useful for stitching together imported histories without rewriting commits.
Common mistakes
Running git describe in a depth-1 clone and getting an error because no annotated tag is reachable; either fetch tags (--tags) or use a deeper clone. Trying to rebase a feature branch in a shallow clone where the merge base is beyond the shallow boundary; deepen first. Pushing from a shallow clone and being surprised by "remote rejected"; the server may need missing parent objects. Finally, treating shallow clones as a substitute for partial clones; they solve different problems and partial is usually friendlier.