Git Said My Objects Were Corrupt - How I Recovered Without Losing Anything
Running git log broke my repository.
I was on WSL (Windows Subsystem for Linux) working on my blog project like any other day. Ran git log to check my recent commits and got this:
error: object file .git/objects/b9/2de209f45b133daeeb97a46ea9ed354a2b9664 is empty
error: object file .git/objects/b9/2de209f45b133daeeb97a46ea9ed354a2b9664 is empty
fatal: loose object b92de209f45b133daeeb97a46ea9ed354a2b9664 (stored in .git/objects/b9/2de209f45b133daeeb97a46ea9ed354a2b9664) is corrupt
Three corrupt objects. Empty files. Admittedly I have somewhat limited Git experience, but I had never seen this before.
Was I about to lose commits?
The Diagnosis
First, I needed to figure out how bad this was. I navigated to my repository root (where the .git folder is) and ran:
$ git fsck --full
This command scans every object in your repository for corruption. The --full flag makes it check everything, not just what's reachable from current branches.
The output was worse than I thought:
error: object file .git/objects/28/7186e66df4cf7057d2897bc1da0027affbfbe5 is empty
error: object file .git/objects/28/7186e66df4cf7057d2897bc1da0027affbfbe5 is empty
fatal: loose object 287186e66df4cf7057d2897bc1da0027affbfbe5 (stored in .git/objects/28/7186e66df4cf7057d2897bc1da0027affbfbe5) is corrupt
error: object file .git/objects/b9/2de209f45b133daeeb97a46ea9ed354a2b9664 is empty
error: object file .git/objects/b9/2de209f45b133daeeb97a46ea9ed354a2b9664 is empty
fatal: loose object b92de209f45b133daeeb97a46ea9ed354a2b9664 (stored in .git/objects/b9/2de209f45b133daeeb97a46ea9ed354a2b9664) is corrupt
error: object file .git/objects/e2/ec99bf1bb260a461c1b7670ad2e891ab0d7f88 is empty
error: object file .git/objects/e2/ec99bf1bb260a461c1b7670ad2e891ab0d7f88 is empty
fatal: loose object e2ec99bf1bb260a461c1b7670ad2e891ab0d7f88 (stored in .git/objects/e2/ec99bf1bb260a461c1b7670ad2e891ab0d7f88) is corrupt
Three corrupt object files. All empty (0 bytes).
I didn't know what these objects were exactly - could have been commits, could have been file contents, could have been something else. That was the scary part. Were these unreferenced objects I wouldn't miss, or was I about to lose actual work?
Git splits object hashes into two-character directories (b9/) to avoid having millions of files in a single folder. That's normal Git internals.
The Safety-First Question
The initial solution I got was:
rm .git/objects/28/7186e66df4cf7057d2897bc1da0027affbfbe5 \
.git/objects/b9/2de209f45b133daeeb97a46ea9ed354a2b9664 \
.git/objects/e2/ec99bf1bb260a461c1b7670ad2e891ab0d7f88
But I stopped. Because here's the thing: what if this doesn't work?
I asked: "Do we have to rm? Can we mv in case this doesn't work? Or am I screwed either way?"
This turned out to be the right question. Even though these files were corrupt and empty, having a backup before doing anything destructive felt smarter. Even if it was arguably pointless. Sometimes you need a safety net, you know?
The Actual Fix
Here's what I did instead:
Step 1: Create a backup location
mkdir -p .git/corrupt-backup
Will this work? I wasn't sure, but at least I'd have a backup if things went sideways.
Step 2: Move the corrupt files (don't delete)
mv .git/objects/28/7186e66df4cf7057d2897bc1da0027affbfbe5 \
.git/objects/b9/2de209f45b133daeeb97a46ea9ed354a2b9664 \
.git/objects/e2/ec99bf1bb260a461c1b7670ad2e891ab0d7f88 \
.git/corrupt-backup/
Now the corrupt objects are gone from Git's perspective, but I still have them if I need to investigate later.
I did try running git fetch origin before moving the files, but Git still threw the same corruption error. The empty files were blocking everything - they had to go.
Step 3: Recover from the remote
This was the scary part. From the same repository root, I ran:
git fetch origin
Git compared my local repo to GitHub, saw the missing objects, and re-downloaded them to .git/objects/. Because these commits existed on GitHub, I got back the exact object files I'd lost.
origin is the default name Git gives to your main remote. You can check your remotes with git remote -v if you're curious.
Step 4: Verify everything is fixed
That output looked good. But I needed to be sure. So I ran fsck again:
$ git fsck --full
Checking object directories: 100% (256/256), done.
Checking objects: 100% (257/257), done.
Step 5: Confirm Git commands work
$ git log --oneline -10
b92de20 Adding +2 reviews for setup blog post
626e596 refactor(content): apply multi-agent review fixes to setting-up-velite-nextjs-revised
42f41d7 Token optimization plan improvements
db5c595 refactor(content): apply multi-agent review fixes to setting-up-velite-nextjs-revised
4bf342a refactor(orchestrators): use VOICE_GUIDE.md instead of reading recent posts
...
Everything works. Repository fully recovered.
What If You Don't Have a Remote?
Honestly, I got lucky. I had pushed everything to GitHub, so git fetch origin worked immediately.
If I hadn't had a remote, I would have been in trouble. I could have tried git reflog to see if Git still had references locally, but if the commits weren't pushed and weren't in reflog, they'd be gone.
This is why regular pushes matter - they're not just for sharing, they're for disaster recovery.
What I Learned
Object corruption happens. System crashes, disk issues, power loss, process killed at the wrong moment - any of these can leave you with empty 0-byte files in .git/objects/. I don't know which one hit me.
Remote repositories are your safety net. Because I'd pushed these commits to GitHub, git fetch could restore them. The objects weren't actually lost, they just needed to be re-downloaded. If I hadn't pushed recently, this would've been scarier.
Always have a rollback plan. Moving files instead of deleting them was the right call. Even though they were corrupt and useless, having them in .git/corrupt-backup/ meant I could investigate or restore if something went wrong. I asked myself: "before any destructive operation, do I have an escape hatch?" That question saved me stress, even though I didn't end up needing the backup.
git fsck saved me time. When I first ran git fsck --full, I was overwhelmed by how verbose the output was. But once I realized it was showing me EXACTLY which three files were corrupt (with full paths and hashes), I knew what I needed to fix. Before this, I was guessing. After running fsck, I had precision.
Git object corruption sounded terrifying when it first happened. But because I had GitHub and had pushed my commits, recovery was straightforward. The remote wasn't just for sharing code, it was my disaster recovery system.
And that mv instead of rm? Saved me from unnecessary stress. Even though the files were genuinely corrupt and I never needed them again, having that backup gave me confidence to proceed.