checking repository integrity and catching data corruption early
Git is generally considered robust because every object is identified by a hash of its content, but an object database built on SHA hashes is not immune to failing disks, killed processes, or accidentally deleted files. The built in git fsck command scans exactly that object database for inconsistencies and surfaces problems before they turn into real, irreversible data loss. This article explains what fsck actually checks, how to read its most common error messages, and which steps repair a damaged repository.
Table of Contents
- 1. What git fsck actually checks
- 2. Making sense of common error messages
- 3. How corruption ends up in the object database in the first place
- 4. Important options in detail
- 5. Repair strategies after a positive fsck finding
- 6. The reflog as a built in safety net
- 7. Automating integrity checks
- 8. Case study: a corrupted repository after a server crash
- 9. Prevention: backups, bare mirrors, and regular checks
- 10. Summary
- 11. FAQ
1. What git fsck actually checks
Every object in Git, whether blob, tree, commit, or tag, is identified by a hash of its content and stored in the object database under .git/objects. git fsck, short for file system check, reads these objects, recomputes their hash, and compares it to the filename the object is stored under. If the two diverge, that is a clear sign of corruption, because in an intact repository an object's content must exactly match its hash.
In addition, fsck checks the reference structure between objects: if a commit points at a tree that does not exist, or a tree entry references a missing blob, fsck reports that as a broken link. The command is strictly read only, it does not modify the repository, it only produces a diagnosis that can then be used to repair things in a targeted way.
# Basic integrity check
git fsck
# More thorough check including packed objects
git fsck --full
# Also report unreachable objects
git fsck --full --unreachable --dangling
2. Making sense of common error messages
fsck's output clearly distinguishes between real errors and mere informational notices. dangling commit means a commit exists that no branch, tag, or other reference points to anymore, usually the result of git reset --hard, a deleted branch, or an aborted rebase. Such commits are not broken, they are simply unreachable and get removed by the garbage collector after some time.
unreachable object describes the same situation for blobs and trees no longer referenced by any reachable commit. Considerably more serious is missing blob or missing tree, here an object is entirely absent even though another object references it, a clear sign of data loss. The most critical case is error: sha1 mismatch, meaning a stored object's content differs from what its filename implies, in other words genuine bit level corruption.
3. How corruption ends up in the object database in the first place
Sudden power loss or a hard process kill while Git is in the middle of writing objects into .git/objects is among the most common causes, especially on systems without a journaling filesystem or with failing SSDs. Equally risky is a full disk, when Git aborts partway through writing a pack file because there is no space left.
Manual intervention directly inside the .git directory, say a careless cleanup script moving or deleting individual object files, reliably produces inconsistencies too. On network drives or synced cloud folders that happen to hold a .git directory, a conflict between two processes writing at the same time can also corrupt individual object files, which is why Git repositories generally should never live inside automatically synced folders.
4. Important options in detail
Without extra parameters, fsck only checks loose objects, meaning ones not yet compressed into a pack file. The --full option includes packed objects in the check as well and should be set for any serious diagnostic run. --strict enables additional, stricter consistency rules, for example around correctly formatted tree entries, uncovering more subtle problems in the process.
--unreachable lists objects no branch or tag reaches anymore, while --dangling specifically shows objects with no incoming reference at all, often exactly the commits someone wants to recover after an accidental reset. With --lost-found, fsck additionally writes any dangling objects it finds into the .git/lost-found directory, making them easier to work with afterward.
# Stricter check, saving found dangling objects
git fsck --full --strict --lost-found
# Inspect a found dangling commit
git show <commit-hash>
# Rescue a found commit as a new branch
git branch recovered-state <commit-hash>
5. Repair strategies after a positive fsck finding
Plain dangling or unreachable objects usually need no action at all, they are a normal side effect of using Git and get cleaned up automatically sooner or later. Real missing or corrupt objects are a different matter: the first move is checking a backup or another clone of the same repository, because Git objects are content addressed, an identical object from another, intact repository can simply be copied back into .git/objects.
If no backup is available but a current remote exists, a fresh clone from there is often the most pragmatic path, since it transfers the entire object database new and guaranteed to be consistent. Only if the affected object existed exclusively on the local machine and nowhere else is it truly and permanently lost, one more reason to push important commits promptly.
6. The reflog as a built in safety net
Before reaching for fsck at all, it is worth checking the reflog for commits believed lost, since it locally logs where HEAD and every branch pointed over the past weeks. git reflog lists this history chronologically and surfaces commits that seemingly vanished through a reset, a deleted branch reference, or a failed rebase, but are actually still sitting in the database as dangling objects.
The reflog is purely local and by default gets pruned after 90 days for reachable entries and 30 days for unreachable ones, a narrow window compared to a real backup strategy, but perfectly sufficient for most accidental losses that get noticed within days or a few weeks.
# Search the local history of HEAD and branches
git reflog show HEAD
# Restore a lost state
git checkout -b restored HEAD@{5}
7. Automating integrity checks
For critical repositories, especially central bare repositories on a self hosted Git server, a regular, automated fsck run via a cron job pays off, combined with an alert whenever the output contains real errors instead of just harmless dangling notices. A simple script can distinguish the two cases by filtering specifically for lines containing missing, corrupt, or sha1 mismatch.
In larger infrastructures, such a check ideally complements an existing backup regime, since fsck detects problems but does not fix them automatically, actual recovery always needs an intact copy of the affected objects from somewhere.
#!/bin/bash
# Simple fsck check for cron jobs, only alerts on real errors
OUTPUT=$(git fsck --full 2>&1)
if echo "$OUTPUT" | grep -qE "missing|corrupt|sha1 mismatch"; then
echo "Critical fsck finding in repository:" >&2
echo "$OUTPUT" >&2
exit 1
fi
8. Case study: a corrupted repository after a server crash
After an unexpected power outage on a build server, git status suddenly reported fatal: loose object is corrupt. An initial git fsck --full confirmed exactly one broken blob object, every other object checked out fine. Since the repository was pushed regularly to a central GitLab server, a simple git fetch followed by copying the corresponding object file from a fresh clone into the local .git/objects directory was enough to get the build server running again.
Had no remote been available, only a full re-clone from an intact backup would have helped. The incident once again showed that a locally isolated repository without a remote offers no recovery option in a real emergency, no matter how carefully commits were made.
9. Prevention: backups, bare mirrors, and regular checks
The most effective protection against data loss is not fsck itself, but consistently pushing to at least one remote repository, ideally complemented by a separate bare mirror on a second server. A bare repository without a working tree can easily be kept up to date via a cron job running git remote update, serving as an additional, independent copy of the entire object database.
On developer machines, regularly pushing feature branches is part of prevention too, since local, never pushed commits are the only ones that can actually be lost in a corruption incident. A monthly fsck run on central servers rounds out the strategy and surfaces creeping problems before they become a real incident.
| Error type | Meaning | Typical cause | Action |
|---|---|---|---|
dangling commit |
Commit with no incoming reference | Reset, deleted branch, rebase | Usually harmless, rescue via branch if needed |
unreachable object |
Blob/tree with no reachable path | Amend, rebase, squash | Harmless, cleaned up later by gc |
missing blob/tree |
Referenced object entirely absent | Deleted object file, transfer error | Restore from backup or an intact clone |
sha1 mismatch |
Content does not match its hash | Bit corruption, failing hardware | Replace object from remote/backup, check hardware |
Mironsoft
Git workflows, branching strategies, and CI hooks
Chaotic Git history and unclear branching rules across the team?
We set up clean Git workflows, clarify branching strategies for the team, and automate quality checks via Git hooks and CI pipelines so the history stays traceable.
Workflow Audit
Review the existing branching strategy and merge practice for weak spots.
Hook Automation
Set up pre-commit and pre-push hooks for linting, tests, and commit conventions.
Team Training
Teach rebase, cherry-pick, and conflict resolution hands-on across the team.
10. Summary
git fsck at a Glance
Purpose
Checks whether stored objects match their hash and every reference resolves
Key option
git fsck --full also includes packed objects in the check
First stop
For seemingly lost commits, check git reflog first, not fsck directly
Prevention
Regular pushing and a separate bare mirror beat after the fact repair