By admin , 29 April 2026

What you will achieve

You will take a feature branch with messy work-in-progress commits ("wip", "more wip", "fix lint", "address feedback") and rewrite it into a clean, atomic commit series ready for review.

Set up a sandbox

mkdir cleanup-tutorial && cd cleanup-tutorial
git init
echo "main" > app.txt && git add . && git commit -m "Initial"
git checkout -b feature
echo "1" > one.txt && git add . && git commit -m "wip"
echo "2" > two.txt && git add . && git commit -m "more wip"
echo "3" > three.txt && git add . && git commit -m "fix"
echo "fixed" >> one.txt && git commit -am "actually fix one"
echo "lint" >> three.txt && git commit -am "lint"

Step 1: review

git log --oneline
# 5 commits with terrible messages

Step 2: interactive rebase

git rebase -i main

Editor opens:

pick aaa1111 wip
pick bbb2222 more wip
pick ccc3333 fix
pick ddd4444 actually fix one
pick eee5555 lint

Step 3: rewrite the script

Reorder, squash, and reword:

pick aaa1111 wip                # will become "Add one"
fixup ddd4444 actually fix one  # folds into above
pick bbb2222 more wip           # will become "Add two"
pick ccc3333 fix                # will become "Add three"
fixup eee5555 lint              # folds into above

Save and exit. Git replays commits according to your script.

Step 4: reword each commit

Wait - we wanted to reword too. Mark the picks as reword instead, or do a second pass:

git rebase -i main
reword AAA111 wip
reword BBB222 more wip
reword CCC333 fix

Git pauses on each reword commit; type a proper message:

Add file one with initial content

The first piece of the feature, providing the base data structure.

Step 5: verify

git log --oneline
# Clean commits with proper messages
git log
# Verify each commit's message and content

Step 6: push

If the branch was previously pushed:

git push --force-with-lease

The fixup workflow during development

The cleanup we just did manually can be partially automated by using --fixup as you go:

# During development, when you find a fix to an earlier commit:
git commit --fixup <target-sha>

# When ready to clean up:
git rebase -i --autosquash main

Autosquash automatically reorders fixup commits next to their targets and marks them as fixups - no manual editing of the rebase script.

Splitting a too-large commit

The opposite problem: one commit contains two unrelated changes. Mark it as edit:

git rebase -i main
# mark the commit as edit
# rebase pauses
git reset HEAD^                # un-stage the changes
git add -p                      # cherry-pick hunks
git commit -m "First atomic piece"
git add -p
git commit -m "Second atomic piece"
git rebase --continue

Pitfalls

  • Never rebase a branch others have based work on.
  • Always --force-with-lease, never plain --force on shared branches.
  • Test after rebase - the squashed result might behave differently from the sum of parts.

Recovery

Botched rebase? The reflog has the original tip:

git reflog
git reset --hard ORIG_HEAD

The result

Your reviewer sees a series of atomic, well-titled commits telling a story: setup, feature, polish. The diff is the same as your messy version, but the narrative invites careful review. Spend ten minutes cleaning up; save your reviewer thirty minutes of confusion.