How to Recover from a Corrupted Git Repository
Source: Dev.to
1. What Actually Broke?
Git stores everything as objects in .git/objects/:
- blobs – file contents
- trees – directories
- commits – snapshots
- tags – references
Each object is named by its SHA‑1 hash and compressed with zlib.
Common causes of corruption
- Disk failure or power loss interrupts a write to
.git/objects/ - A process is killed during
git gc,git repack, or a push/pull - Filesystem bugs (especially on networked/shared drives)
- Aggressive antivirus or backup software that locks or modifies files in
.git/
Because Git’s storage is content‑addressable, an object that still matches its hash is intact. Corruption is usually isolated to a handful of objects.
2. Identify the Damage
Never panic‑delete anything. First, figure out exactly what’s broken.
Check overall repository integrity
git fsck --full --no-danglingTypical output:
error: object file .git/objects/a1/b2c3... is empty
error: sha1 mismatch for .git/objects/d4/e5f6...
missing blob a1b2c3d4e5f6...
broken link from tree f7a8b9... to blob a1b2c3...Write down the broken SHA‑1 hashes – you’ll need them later.
Verify HEAD
cat .git/HEAD # should show: ref: refs/heads/main
cat .git/refs/heads/main
git cat-file -t $(cat .git/refs/heads/main) # should output: commitIf HEAD or the branch ref is corrupted, that’s your starting point. Otherwise the problem lies deeper (blobs, trees, etc.).
3. Use the Reflog – Your Best Friend
The reflog records where branch pointers have been and survives most corruption because it’s stored separately from the object database.
# Show recent reflog entries for all refs
git reflog show --all
# If HEAD is broken, read the reflog directly
cat .git/logs/HEADFind the last known‑good commit hash and reset to it:
git reset --hard <commit‑hash>The reflog keeps 90 days of history by default, so a recent corruption almost always has a valid commit hash you can recover.
4. Pull Missing Objects from a Remote
If you’ve ever pushed to a remote (GitHub, self‑hosted server, a colleague’s machine), you already have a backup.
# 1️⃣ Move the broken objects out of the way
mkdir -p .git/objects-broken
# Example for a broken object a1b2c3...
mv .git/objects/a1/b2c3d4e5f6* .git/objects-broken/
# 2️⃣ Fetch missing objects from the remote
git fetch origin
# 3️⃣ Verify again
git fsck --full --no-danglingBecause Git objects are immutable, the same commit on the remote has the exact same SHA‑1 and bytes. Fetching restores any missing objects.
5. Re‑create a Corrupted Blob (File)
If the corrupted object is a blob (a specific version of a file) and you still have the working‑tree version, you can rebuild it:
# Find which file the broken blob belongs to
git ls-tree -r HEAD | grep <blob‑sha>
# Example output:
# 100644 blob a1b2c3d4e5f6 path/to/file.js
# Re‑hash the current file into the object store
git hash-object -w path/to/file.jsOnly works if the working‑tree file matches the broken blob. Otherwise fetch the correct version from a remote or backup.
6. Start Fresh from a Remote (When Damage Is Extensive)
If the repository is heavily damaged but you have a remote copy:
# Rename the broken repo
mv my-project my-project-broken
# Clone a fresh copy
git clone <remote‑url> my-project
# Copy over any uncommitted work
diff -rq my-project-broken/ my-project/ --exclude='.git'
# (Manually copy any files that differ)You’re not losing anything that was pushed; the only risk is uncommitted local work, which the diff helps you locate.
7. Dealing with a Corrupted Packfile
Git periodically packs loose objects into .git/objects/pack/. A corrupted pack can hide hundreds of objects.
# Try to unpack what you can
git unpack-objects .pack
# Verify the packfile
git verify-pack -v .git/objects/pack/pack-*.idxgit unpack-objects will fail on corrupted objects but recover the intact ones. After extraction, fetch from a remote to fill the gaps.
8. Prevention – Make Corruption Rare
Push frequently – every remote is a full backup. Push even messy feature branches; you can squash later.
Never interrupt Git operations – let
git gc,git repack, etc., finish. Killing them mid‑run is the #1 cause of corruption I’ve seen.Avoid shared/networked filesystems – NFS, CIFS, Dropbox, OneDrive, etc., are notorious for causing repo corruption. If you must use them, create portable backups with
git bundle.Enable fsync for object files – protects against power loss:
git config --global core.fsyncObjectFiles trueRegularly run
git fsckon important repos to catch problems early.Back up the
.gitdirectory (or the whole repo) to an external drive or cloud storage on a regular schedule.
TL;DR
- Run
git fsck→ note broken SHA‑1s. - Check
HEADand branch refs. - Use the reflog to locate a good commit and
git reset --hard. - If you have a remote, move broken objects aside,
git fetch, and verify again. - Re‑hash any recoverable blobs, or clone fresh and copy uncommitted work.
- For packfile issues, unpack what you can, verify, then fetch missing objects.
With these steps, most “fatal: bad object HEAD” scenarios are fully recoverable. Happy coding!
Git (2.36+) – More Granular core.fsync
git config core.fsync objects,derived-metadata,referencePeriodic Integrity Checks
- Run
git fsckregularly. - Add it to a cron job or a pre‑push hook.
- Detecting corruption early gives you more recovery options.
Why Git’s Design Helps Recovery
- The content‑addressable object store means every clone is a full backup.
- Each object is self‑verifying.
- Corruption is usually localized, not catastrophic.
A Real‑World Example
The worst Git corruption I ever dealt with took about 20 minutes to fix once I understood the object model. The first time, I spent three hours panicking before I learned any of this. Hopefully this saves you those three hours.
Want to Dive Deeper?
- Read the Git Internals chapter in the official Git book.
- Understanding how
.git/objects/works turns “my repo is corrupted” from a crisis into a 15‑minute fix.