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:

bash
git merge main

You see:

bash
fatal: refusing to merge unrelated histories
fatal: shallow file has changed since last read

Or pushing:

bash
git push origin feature-branch
bash
error: remote unpack failed: error Shallow fetch not permitted
fatal: The remote end hung up unexpectedly

Or viewing history:

bash
git log --all

History 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:

bash
git fetch --unshallow

Fetches 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:

bash
git fetch --depth=50

Increases 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:

bash
fatal: refusing to merge unrelated histories

Unshallow 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:

bash
error: remote unpack failed: error Shallow fetch not permitted

Unshallow 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:

bash
fatal: shallow file has changed since last read

Refresh 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:

bash
# Single branch, full depth
git clone --single-branch --branch main https://github.com/user/repo.git

Or 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:

bash
git clone --filter=blob:none https://github.com/user/repo.git

Fetches 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:

bash
git submodule update --init

May 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