Engineer's toolkit
Editing history and recovering work
How interactive rebase, fixup commits and backup branches make your own history clean, and the recovery steps for a botched reset, rebase or deleted branch.
Your own commits can be rewritten before anyone else builds on them. Editing them makes review easier. When an edit goes wrong, the old commits are usually still there, and you can point a branch back at them.
Concept
Before you start: a rebase replays commits onto a new base, producing new commits (see merge, rebase and squash). Rewriting is safe for your own commits, and not safe for shared ones.
Interactive rebase
git rebase -i main opens a todo list of your branch's commits. You reorder them and choose what to do with each one:
pick 1a2b3c4 Add retry policy to HTTP client
fixup 5d6e7f8 fix typo
reword 9a8b7c6 add jitter
drop 0f1e2d3 WIP debugging printspickkeeps the commit.rewordedits its message.editstops so you can amend the commit.squashfolds the commit into the previous one and combines the messages.fixupfolds it in and discards its message.dropremoves it.
A good workflow: while you work, make fix-up commits with git commit --fixup=<sha>. Before you open the pull request, run git rebase -i --autosquash main. Git arranges each fix-up next to the commit it fixes.
Make a backup before anything risky
git branch backup-before-rebaseThis costs about 41 bytes, and it gives you a name to return to if the rebase goes badly.
Walking through it: recovering from a bad reset
You ran git reset --hard and the last three commits vanished from git log. Here is the recovery:
$ git reflog
a91c2e0 HEAD@{0}: reset: moving to HEAD~3
f4d10b2 HEAD@{1}: commit: add retry tests
...
$ git reset --hard HEAD@{1} # back to the tip before the resetThe commits were never deleted. Only the branch pointer moved. Point it back and they reappear.
Recovery table
| Situation | Recovery |
|---|---|
| Botched rebase or reset | git reflog, find the entry before it, then git reset --hard HEAD@{N} |
| Regret a merge, rebase or reset just done | git reset --hard ORIG_HEAD (Git saves the pre-operation tip there) |
| Deleted a branch | Find its tip in git reflog, then git branch rescued <sha> |
| Committed on detached HEAD, then switched away | git reflog shows the commit; git branch keep-this <sha> |
| Dropped a stash | git fsck --unreachable | grep commit, inspect, then git stash apply <sha> |
| Rebase in progress and confused | git rebase --abort restores everything |
What Git cannot recover
Uncommitted changes that you overwrote with git reset --hard, git checkout -- file or git clean are often gone. If you had staged them with git add, their contents are in the object store, and git fsck --lost-found can find them. Otherwise, they were never stored.
So commit early, even as a work-in-progress commit, and squash it later.
Common mistakes
- Rebasing without a backup. Create the backup branch first. It costs nothing.
- Relying on uncommitted work. Overwritten working-directory changes usually cannot be recovered.
- Reaching for
reset --hardfirst. Trygit reflogandgit rebase --abortbefore anything destructive.
Check yourself
- You rebased by mistake and want the pre-rebase branch back. What do you do? Find the pre-rebase entry in
git reflogand reset the branch to it, or useORIG_HEADif it is still the last operation. - Why are fix-up commits safer than amending during review? They keep a clear record of each change, and
--autosquashfolds them in cleanly before you open the pull request.
Key takeaways
- Rewriting your own unshared commits is safe and makes history readable.
fixupplusrebase -i --autosquashfolds small fixes into their target commits.- Almost every mistake is a pointer move. The reflog holds the old tip for 30 to 90 days.
- Uncommitted changes are the one thing reflog cannot save, so commit early.
Further learning
Next, use the clean history you have built to find regressions, in bisect regressions.
Related
Refs, HEAD and the reflog
Branches, tags and HEAD are small files that point at commits, and the reflog records every move, which is why almost any committed work can be recovered.
Merge, rebase and squash
The three ways to combine a branch into main, what each one does to the commit graph, how to resolve conflicts without guessing, and the one rule about rewriting shared history.
Code review
Author small, reviewable PRs and give reviews that improve both the code and the author — design first, tone that teaches, turnaround that unblocks.