Git Lost Commits
Introduction
Have you ever made changes to your project, committed them, and then somehow lost those commits? Maybe you ran a Git command without fully understanding its consequences, or perhaps you did a hard reset and regretted it immediately. Don't panic! Git is designed with data safety in mind, and in most cases, your commits aren't truly gone—they're just no longer referenced.
This guide will teach you how to recover "lost" commits in Git. We'll explore various recovery techniques and best practices to ensure you can rescue your work even when things go wrong.
Understanding Git's Object Model
Before diving into recovery techniques, it helps to understand how Git stores your data. Git uses a content-addressable filesystem, which means:
- Every commit, file, and directory is stored as an object in Git's database
- Each object has a unique SHA-1 hash identifier
- Git maintains references (like branches and tags) that point to these objects
- When a commit appears to be "lost," the object still exists in Git's database—it's just no longer referenced by any branch or tag
Let's visualize this:
In this diagram, the commit with hash jkl012
is "lost" because no reference points to it, but it still exists in Git's database.
Common Scenarios for Lost Commits
Let's look at some common scenarios where commits might appear to be lost:
- Hard reset: Running
git reset --hard
to an earlier commit - Branch deletion: Deleting a branch with unique commits
- Rebasing gone wrong: Interrupting a rebase operation
- Detached HEAD commits: Making commits while in a detached HEAD state, then checking out another branch
- Force push: Overwriting remote history with a force push
Recovering Lost Commits with Git Reflog
Git maintains a log of all reference updates in your local repository called the "reflog." This is your first line of defense when recovering lost commits.
Using Git Reflog
The reflog shows a history of all the actions you've performed in your repository:
git reflog
Output example:
abc123 (HEAD -> main) HEAD@{0}: commit: Add new feature
def456 HEAD@{1}: checkout: moving from feature to main
jkl012 HEAD@{2}: commit: WIP: Feature implementation
ghi789 HEAD@{3}: reset --hard HEAD~1
In this example, the commit jkl012
might be considered "lost" if you abandoned the feature branch, but reflog still has a reference to it.
To recover this commit, you can create a new branch at that commit:
git branch recovered-branch jkl012
Or checkout directly to that commit:
git checkout jkl012
Time-Based Recovery
If you don't remember the specific commit hash, you can use time-based specifications:
git reflog show --since="2 days ago"
Finding Dangling Commits with Git FSCheck
If a commit is not in the reflog (perhaps it's older than the reflog's retention period), you can still find it using Git's filesystem checker:
git fsck --lost-found
This command will show you "dangling" commits and blobs. Look for lines like:
dangling commit abc123def456...
You can then examine the content of a dangling commit with:
git show abc123def456
And recover it by creating a branch:
git branch recovered-branch abc123def456
Practical Example: Recovering from a Hard Reset
Let's walk through a common scenario: recovering from an accidental hard reset.
Scenario
You've been working on a feature and made several commits. Then, by mistake, you run:
git reset --hard HEAD~3
This command moves your branch pointer back three commits, making it appear as if your last three commits are gone.
Recovery Steps
- First, check the reflog to see your recent actions:
git reflog
Output:
abc123 (HEAD -> main) HEAD@{0}: reset --hard HEAD~3: updating HEAD
def456 HEAD@{1}: commit: Add validation logic
ghi789 HEAD@{2}: commit: Implement error handling
jkl012 HEAD@{3}: commit: Create initial UI components
mno345 HEAD@{4}: commit: Base setup
-
Identify the last commit before the reset (
jkl012
in this case) -
Create a new branch at that commit:
git branch recovery jkl012
- Switch to the recovery branch:
git checkout recovery
Now you've recovered your lost commits!
Cherry-Picking Specific Lost Commits
Sometimes, you might not want to recover all lost commits but just specific ones. In this case, you can use cherry-pick:
git cherry-pick def456
This will apply the changes from the commit def456
to your current branch as a new commit.
Preventing Lost Commits
Prevention is better than cure. Here are some best practices to avoid losing commits:
- Create branches for experimental work: Always work in a separate branch for major changes
- Use
git stash
for temporary changes: Instead of hard resets, use stash to temporarily set aside changes - Understand destructive commands: Be careful with commands like
reset --hard
,push --force
, andrebase
- Push to remote regularly: Once your changes are pushed to a remote repository, they're much harder to lose
- Configure longer reflog expiration: You can extend the reflog retention period:
git config --global gc.reflogExpire "90 days"
Advanced Recovery: Git Bisect for Finding Lost Work
If you have a rough idea of when a specific piece of code existed in your repository but don't know which commit it was in, you can use git bisect
to help find it:
git bisect start
git bisect good <known-good-commit>
git bisect bad <known-bad-commit>
Then Git will help you navigate through the commit history to find when the code was present.
Summary
In this guide, we've covered:
- How Git's object model stores your commits even when they appear "lost"
- Using
git reflog
to find recently "lost" commits - Finding older dangling commits with
git fsck
- Practical recovery techniques for common scenarios
- Best practices to prevent losing commits in the first place
Remember, Git is designed to keep your data safe. In most cases, if you've committed something, it's still there somewhere—you just need to know how to find it.
Additional Resources
Exercises
-
Create a test repository and practice losing and recovering commits:
- Make several commits
- Perform a hard reset
- Use reflog to recover the lost commits
-
Experiment with the following scenario:
- Create a branch and make some commits
- Delete the branch without merging
- Recover the deleted branch's commits
-
Practice using
git fsck
to find dangling commits:- Create a repository with some commits
- Delete references to some commits
- Use
git fsck
to find and recover them
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)