This error occurs when Git cannot find the specified commit reference, usually because the repository has fewer commits than requested or you're working with a shallow clone that lacks sufficient history.
The "fatal: bad revision 'HEAD~10'" error means Git cannot locate the commit you're trying to reference. HEAD~10 refers to the commit that is 10 commits before your current HEAD position. This error typically occurs in these scenarios: 1. **Fewer commits than requested**: If your repository only has 5 commits but you reference HEAD~10, Git cannot find a commit that far back in history. 2. **Shallow clone**: CI/CD systems like GitHub Actions, GitLab CI, and Jenkins often perform shallow clones (--depth=1) by default to save bandwidth and time. This means only recent commits are available locally. 3. **Empty repository**: In a freshly initialized repository with no commits, HEAD doesn't exist yet, so any reference to HEAD~N will fail. 4. **Corrupted or incomplete history**: If commits are missing from your local repository due to corruption or incomplete fetch operations, Git cannot resolve the reference. This is an extremely common issue in CI/CD pipelines where shallow cloning is the default behavior for performance optimization.
First, verify the total number of commits in your repository:
git rev-list --count HEADIf this returns a number smaller than the N in HEAD~N you're trying to reference, that's the cause of the error. For example, if you have 5 commits but try to reference HEAD~10, it will fail.
Determine if your repository is a shallow clone:
git rev-parse --is-shallow-repositoryIf this returns "true", your repository has truncated history. You can also check:
cat .git/shallowIf this file exists, you're in a shallow clone with limited history.
If you're in a shallow clone, fetch more history:
# Fetch all history (removes shallow status)
git fetch --unshallow
# Or fetch a specific amount of additional history
git fetch --deepen=50
# Or fetch to a specific depth
git fetch --depth=100After fetching more history, your HEAD~N references should work.
For GitHub Actions, configure the checkout action to fetch more history:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all historyAlternatively, fetch only what you need:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 20 # Fetch last 20 commitsFor GitLab CI, adjust the clone depth in your .gitlab-ci.yml:
variables:
GIT_DEPTH: 0 # Fetch all history
# Or for specific jobs:
job_name:
variables:
GIT_DEPTH: 50 # Fetch last 50 commitsYou can also configure this globally in GitLab:
Settings > CI/CD > General Pipelines > Git shallow clone depth
When writing scripts that need to reference history, check available commits first:
#!/bin/bash
NEEDED_DEPTH=10
AVAILABLE=$(git rev-list --count HEAD)
if [ "$AVAILABLE" -lt "$NEEDED_DEPTH" ]; then
echo "Only $AVAILABLE commits available, need $NEEDED_DEPTH"
# Fetch more history if in shallow clone
if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then
git fetch --deepen=$NEEDED_DEPTH
else
# Use what's available
NEEDED_DEPTH=$AVAILABLE
fi
fi
git log HEAD~$NEEDED_DEPTH..HEADIf you need to reference commits beyond available history, use alternatives:
Reference the first commit:
# Get the first commit hash
git rev-list --max-parents=0 HEAD
# Use it in rebase
git rebase -i --rootReference by date instead of count:
git log --since="1 week ago"
git diff @{1.week.ago}Why CI/CD uses shallow clones: Shallow cloning significantly reduces clone time and bandwidth usage. For large repositories, fetching full history can take minutes, while a shallow clone completes in seconds. Most CI jobs only need the current commit to run tests.
Treeless clones as a middle ground: Git 2.19+ supports partial clones. You can fetch commit history without blob data using:
git clone --filter=tree:0 <url>This gives you full commit history for references while still saving bandwidth on file contents.
Understanding HEAD~N vs HEAD^N:
- HEAD~N follows the first parent N times (linear history)
- HEAD^N selects the Nth parent of a single commit (useful for merge commits)
- HEAD~3 = "three commits back"
- HEAD^2 = "second parent of HEAD" (in a merge commit)
Repository depth inspection:
# See the shallow boundary commits
cat .git/shallow
# Check repository status
git rev-parse --is-shallow-repository
# See actual depth
git rev-list --count HEADPerformance considerations: For GitHub Actions, using fetch-depth: 0 on very large repositories (100k+ commits) can significantly slow down your workflow. Consider using a specific depth or fetching only necessary history with git fetch --deepen=N.
warning: BOM detected in file, this may cause issues
UTF-8 Byte Order Mark (BOM) detected in file
fatal: Server does not support --shallow-exclude
Server does not support --shallow-exclude
warning: filtering out blobs larger than limit
Git partial clone filtering large blobs warning
fatal: Server does not support --shallow-since
Server does not support --shallow-since in Git
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server