Your pull request has commits called wip, fix typo and asdf. Before anyone reviews it, let's turn that mess into clean, meaningful commits with git rebase -i.

Every command and hash below came from a real repository I ran this in.

Video version:

Before: a messy branch

$ git log --oneline 2c27ca8 Add login tests fc5d50d asdf 4ffd51f fix typo 3e5db2d wip 3d66ed5 Add login form 6b96e5f Initial commit 

There's one real commit for the login form, three junk commits that are really part of it, and then the tests. We want two clean commits: "Add login form" and "Add login tests".

Step 1: start the interactive rebase

$ git rebase -i HEAD~5 

Git opens your editor with one line per commit, oldest at the top:

pick 3d66ed5 Add login form pick 3e5db2d wip pick 4ffd51f fix typo pick fc5d50d asdf pick 2c27ca8 Add login tests 

pick means "keep this commit as it is".

Step 2: change the commands, save and close

pick 3d66ed5 Add login form squash 3e5db2d wip squash 4ffd51f fix typo squash fc5d50d asdf pick 2c27ca8 Add login tests 
Command What it does
squash (s) merge into the commit above, and edit the combined message
fixup (f) merge into the commit above, and discard this message
reword (r) keep the commit, change its message
drop (d) delete the commit
(move a line) reorder commits

Save and close the editor. Git replays the commits and asks you for the final message of the squashed commit.

After: clean history

Successfully rebased and updated refs/heads/main. $ git log --oneline 65399a2 Add login tests ae3841d Add login form 6b96e5f Initial commit 

There are two clean commits now. Notice that the hashes changed: rebase creates brand-new commits, which is why the safety rules below matter.

Pro tip: --fixup + --autosquash

Found a bug in an older commit? Don't make a "fix typo" commit and clean it up later by hand. Tell Git which commit the fix belongs to:

$ git commit -a --fixup=ae3841d $ git log --oneline 13275fc fixup! Add login form 65399a2 Add login tests ae3841d Add login form 

Later, rebase with --autosquash. Git moves the fix to the right place and marks it as fixup automatically:

$ git rebase -i --autosquash HEAD~3 pick ae3841d Add login form fixup 13275fc fixup! Add login form pick 65399a2 Add login tests 

Just save and close. You don't have to edit anything by hand. Set git config --global rebase.autoSquash true to make it the default.

Safety rules

  • Only rebase commits that aren't on a shared branch. Never rewrite main after others have pulled it.
  • Already pushed your own feature branch? Use git push --force-with-lease, not --force. It refuses to overwrite commits someone else pushed in the meantime.
  • Conflict or panic mid-rebase? git rebase --abort puts everything back.
  • Regret it after it's finished? git reflog still has your old commits.

Cheat sheet

  • git rebase -i HEAD~N: edit the last N commits
  • squash / fixup: merge into the commit above
  • reword: change a message · drop: delete a commit
  • git commit --fixup=<hash> + git rebase -i --autosquash
  • --force-with-lease, --abort, reflog

Does your team squash commits, or keep them all? 👇

I post one tested coding tip every day on YouTube at Refactorly. The 30-second version of this one is here.