Shallow clones save space and time by fetching only recent history, but they create complications when you need full history, merge with other branches, or push changes.
The Error
Working with a shallow clone:
git merge mainYou see:
fatal: refusing to merge unrelated histories
fatal: shallow file has changed since last readOr pushing:
git push origin feature-brancherror: remote unpack failed: error Shallow fetch not permitted
fatal: The remote end hung up unexpectedlyOr viewing history:
git log --allHistory stops at the shallow point: ``` commit abc123 (HEAD -> main) Author: Developer <dev@example.com> Date: Today
Recent commit
commit def456 Author: Developer <dev@example.com> Date: Yesterday
Another commit ... stops here, no earlier history ```
Why Shallow Clones Have Issues
Shallow clones:
- Fetch only recent commits (--depth N)
- Lack ancestry beyond depth limit
- Cannot merge branches with different cutoff points
- Some servers reject shallow pushes
- Git treats shallow roots as unrelated histories
Diagnosis Steps
Check if repository is shallow:
``bash
cat .git/shallow
Shows shallow commit boundary hashes.
Check shallow depth:
``bash
git rev-list --count HEAD
Returns the number of commits available.
Check for shallow marker:
``bash
git config --get core.shallow
Returns true for shallow repos.
Verify fetch history:
``bash
git log --oneline --all --graph
Solution 1: Unshallow the Repository
Convert shallow clone to full clone:
git fetch --unshallowFetches all history from the remote.
For very large history, fetch incrementally:
``bash
git fetch --depth=100
git fetch --depth=200
git fetch --depth=500
git fetch --unshallow
Or specify deep enough:
``bash
git fetch --depth=1000
Solution 2: Fetch More History
When you need more but not all history:
git fetch --depth=50Increases depth by 50 more commits.
Deepen to specific commit:
``bash
git fetch --deepen=100
Adds 100 more commits to current depth.
Solution 3: Fix Shallow Merge Conflicts
When merging fails due to shallow history:
fatal: refusing to merge unrelated historiesUnshallow first:
``bash
git fetch --unshallow
git merge main
Or force merge with shallow history:
``bash
git merge main --allow-unrelated-histories
Warning: This creates a merge without proper ancestry. Use only when you understand the implications.
Solution 4: Handle Shallow Push Rejection
Server rejects shallow pushes:
error: remote unpack failed: error Shallow fetch not permittedUnshallow before pushing:
``bash
git fetch --unshallow
git push origin feature-branch
Check server configuration: Some servers (GitLab, GitHub Enterprise) have settings to allow or reject shallow pushes.
For Gerrit:
``bash
git push origin HEAD:refs/for/main
Gerrit handles shallow pushes differently.
Solution 5: Update Shallow File
If shallow file is stale:
fatal: shallow file has changed since last readRefresh shallow state:
``bash
git fetch --depth=$(cat .git/shallow | wc -l)
Or remove and re-fetch:
``bash
rm .git/shallow
git fetch origin
This creates a full clone.
Solution 6: Clone Specific Branch Deeply
Avoid shallow for specific needs:
# Single branch, full depth
git clone --single-branch --branch main https://github.com/user/repo.gitOr shallow single branch:
``bash
git clone --single-branch --depth 1 --branch main https://github.com/user/repo.git
Solution 7: Use Blobless Clone Alternative
For large repos where you need history but not files:
git clone --filter=blob:none https://github.com/user/repo.gitFetches all commits and trees, but blobs (file contents) are fetched on-demand.
Or treeless:
``bash
git clone --filter=tree:none https://github.com/user/repo.git
Solution 8: Handle Shallow Submodules
Submodules cloned shallowly cause issues:
git submodule update --initMay create shallow submodules if configured.
Update with full depth:
``bash
git submodule update --init --no-fetch
git submodule foreach 'git fetch --unshallow'
Or configure non-shallow:
``bash
git config --global submodule.fetchJobs 0
git submodule update --init --depth 99999999
Solution 9: Reclone as Full Repository
When shallow issues persist:
```bash # Note your branch and changes git branch --show-current git stash push -m "saving work"
# Clone fresh without shallow git clone https://github.com/user/repo.git repo-full cd repo-full
# Recreate branch and apply stash git checkout -b feature-branch git stash pop ```
Verification
Confirm unshallow succeeded:
``bash
test -f .git/shallow && echo "Still shallow" || echo "Full clone"
Verify full history:
``bash
git rev-list --count HEAD
git log --oneline | wc -l
Should show all commits.
Test merge capability:
``bash
git fetch origin
git merge origin/main --no-commit --no-ff
git merge --abort
Should not refuse with unrelated histories.
Check log goes back to root:
``bash
git log --reverse --oneline | head -1
Should show the initial commit.
Shallow Clone Options Reference
```bash # Create shallow clone with N commits git clone --depth N <url>
# Clone single branch shallowly git clone --depth N --single-branch --branch <name> <url>
# Fetch more history git fetch --depth N # Set depth to N git fetch --deepen N # Add N more commits
# Convert to full clone git fetch --unshallow
# Clone without blobs (partial clone) git clone --filter=blob:none <url> git clone --filter=tree:none <url> ```
When to Use Shallow vs Full Clone
Use shallow clone when: - CI/CD builds only need latest code - Quick inspection of repository - Disk space severely limited - Bandwidth constrained
Use full clone when: - Active development work - Need to merge branches - Need to browse history - Contributing changes back - Bisecting bugs
Prevention Strategies
Clone appropriately from the start: ```bash # For development git clone <url> # Full clone
# For CI/build git clone --depth 1 <url> # Shallow ```
Configure partial clone for large repos:
``bash
git clone --filter=blob:none <url>
This gets full history but lazy-loads file contents.
Document shallow limitations: Team should understand shallow clones can't merge properly.
Common Scenarios
CI pipeline shallow clone: ```bash # CI clones shallow by default git clone --depth 1 $REPO_URL
# If CI needs to merge git fetch --unshallow git merge main ```
Shallow submodule problems: ```bash # In .gitmodules [submodule "lib"] path = lib url = https://github.com/org/lib.git # Remove shallow = true if present
# Update submodules fully git submodule update --init --recursive --no-fetch git submodule foreach git fetch --unshallow ```
Gerrit shallow push:
``bash
# Gerrit often rejects shallow
git fetch --unshallow
git push origin HEAD:refs/for/main