Guide

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 prints
  • pick keeps the commit.
  • reword edits its message.
  • edit stops so you can amend the commit.
  • squash folds the commit into the previous one and combines the messages.
  • fixup folds it in and discards its message.
  • drop removes 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

Bash
git branch backup-before-rebase

This 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:

Bash
$ 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 reset

The commits were never deleted. Only the branch pointer moved. Point it back and they reappear.

Recovery table

SituationRecovery
Botched rebase or resetgit reflog, find the entry before it, then git reset --hard HEAD@{N}
Regret a merge, rebase or reset just donegit reset --hard ORIG_HEAD (Git saves the pre-operation tip there)
Deleted a branchFind its tip in git reflog, then git branch rescued <sha>
Committed on detached HEAD, then switched awaygit reflog shows the commit; git branch keep-this <sha>
Dropped a stashgit fsck --unreachable | grep commit, inspect, then git stash apply <sha>
Rebase in progress and confusedgit 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 --hard first. Try git reflog and git rebase --abort before 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 reflog and reset the branch to it, or use ORIG_HEAD if 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 --autosquash folds them in cleanly before you open the pull request.

Key takeaways

  • Rewriting your own unshared commits is safe and makes history readable.
  • fixup plus rebase -i --autosquash folds 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.