The Error
You (or your CI runner) cloned with a depth flag, and now git push fails cold:
fatal: shallow update not allowed
This hits most often during release steps. The runner grabs a shallow clone to save bandwidth, then a later job tries to push a tag, a version bump, or a build-artifact branch โ and Git refuses flat out.
Why This Happens
Shallow clones are intentionally incomplete. Git tracks the cut-off point in a file called .git/shallow, which lists the boundary commits โ the ones whose parents were never downloaded.
When the remote receives your push, it walks the commit ancestry to verify everything fits together. Hit a boundary it has never seen, and it bails out rather than risk corrupting the history graph. Three situations trigger this:
- You used
git clone --depth Nlocally and are pushing to a full remote. - Your CI runner checks out with
fetch-depth: 1(theactions/checkoutdefault) and a later step pushes tags or commits. - You're pushing from one shallow clone into another โ both sides have incomplete history and neither can fill the gap.
Diagnose First
One command tells you whether your copy is shallow:
git rev-parse --is-shallow-repository
true means yes. You can also inspect the boundary directly:
cat .git/shallow
If the file exists and has content, you're dealing with a shallow clone. Each line is a commit SHA marking the edge of what was downloaded.
Fix 1 โ Unshallow Before Pushing (Recommended)
Fetch the complete history, then push:
git fetch --unshallow
git push origin HEAD
This pulls every missing commit and deletes .git/shallow, converting the repo into a full clone. From this point, pushes work normally โ no further workarounds needed.
On older Git versions where --unshallow isn't available, use this instead:
git fetch --depth=2147483647
git push origin HEAD
The number 2147483647 is INT_MAX. Git treats it as "give me everything," which achieves the same result.
Fix 2 โ Change the CI Checkout Depth
For CI pipelines, the better answer is to stop shallow-cloning for jobs that push. Set depth to 0 on release and deploy jobs only โ your test and lint jobs can stay shallow.
GitHub Actions
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 0 = full history
GitLab CI
variables:
GIT_DEPTH: 0
Jenkins (pipeline script)
checkout([
$class: 'GitSCM',
extensions: [[$class: 'CloneOption', shallow: false]],
// ... other options
])
Fix 3 โ Targeted Fetch for Tags Only
Need to push a single tag but don't want to download gigabytes of history? Fetch just enough for the tag's commit:
git fetch --depth=1 origin main
git tag v1.2.3
git push origin v1.2.3
This works only when the tag points to a commit the remote already knows about. If you're pushing new commits the remote has never seen, go back to Fix 1 or Fix 2.
Fix 4 โ If the Remote Repository Is the Shallow One
Rare, but it happens when someone created a bare repo with git clone --depth N --bare. In that case, unshallow the server side:
# Run on the server
cd /path/to/remote.git
git fetch --unshallow
No server access? Recreate the remote from a full clone instead.
Verify the Fix
Confirm the repo is no longer shallow:
git rev-parse --is-shallow-repository
# Should print: false
Then retry the push:
git push origin HEAD
# or for a tag:
git push origin v1.2.3
No fatal: output means you're clear.
Prevent It in CI/CD
Shallow clones exist for one reason: speed. A depth-1 clone on a 50,000-commit repo can cut checkout time from 30 seconds to under 2. That trade-off makes sense for compile and test jobs that never touch history.
It breaks the moment a job needs to reason about the past โ pushing commits, creating tags, running git describe, generating changelogs, or diffing across releases.
Practical split: fetch-depth: 1 for test and lint, fetch-depth: 0 for release and deploy. Most CI systems let you set this per-job, so you keep speed where it matters and correctness where it's required.
On pipelines with many jobs, consider a dedicated checkout job that fetches full history and caches the .git directory. Downstream jobs restore from cache instead of cloning again.
Quick Reference
- Local shallow clone โ normal remote:
git fetch --unshallow && git push - CI pipeline with depth: 1: set
fetch-depth: 0in the checkout step - Pushing a tag only:
git fetch --depth=1 origin <branch> && git push origin <tag> - Remote is shallow: unshallow or recreate the remote

