Git: Beginner to Advanced
Clean up commit history
Prepare a branch that others can understand.
Lesson 8 of 12
Reading: about 6 min
Lesson + practice: about 15 min
Difficulty: Intermediate
On this page5 sections
Prepare a branch that others can understand. Follow the animated example, then use the sections below to review each idea.
Visual walkthrough
Use the player controls or jump menu to inspect each step. The diagram changes as the example moves forward.
The player shows one focused example. The sections below explain the ideas behind each topic.
While you work, commits pile up as they happen: a first attempt, a “wip”, a typo fix, the tests. That history is honest but hard to review. Before you share a branch, you can reshape it into a few clear steps that each make sense on their own.
This lesson tidies a search branch with interactive rebase, as in the walkthrough above. Only rewrite commits that are still yours alone: rewriting replaces commits with new ones, which causes trouble for anyone who already has the old ones. Commit IDs will differ.
The messy branch
Four commits on top of main, in the order they were made. wip and fix typo are really part of Add search.
git log --oneline4bc237b fix typo
1e8ad5b Add search tests
d1402b1 wip
00af006 Add search
97009a9 Add READMEInteractive rebase
git rebase -i main takes the commits on your branch that are not on main and replays them one by one. Before replaying, it opens a todo list in your text editor so you can decide what happens to each commit. Saving and closing the editor starts the rebase.
pick 00af006 Add search
pick d1402b1 wip
pick 1e8ad5b Add search tests
pick 4bc237b fix typo
# Rebase 97009a9..4bc237b onto 97009a9 (4 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
# commit's log message, unless -C is used, in which case
# keep only this commit's message; -c is same as -C but
# opens the editor
# d, drop <commit> = remove commit
# ...| Word | Effect |
|---|---|
| pick | Keep the commit as it is |
| reword | Keep the changes, but edit the message |
| squash | Combine into the commit above and edit the combined message |
| fixup | Combine into the commit above and keep only that commit's message |
| drop | Remove the commit and its changes |
| edit | Stop after this commit so you can amend it |
Squash and fixup
Both fold a commit into the one above it in the todo list. The difference is the message: squash opens your editor with both messages so you can write a combined one, while fixup quietly keeps only the earlier message. Fixup is ideal for “wip” and “fix typo” commits whose messages add nothing.
Reorder commits
To move a commit, move its line in the todo list. Here fix typo belongs with Add search, so its line moves up beneath it. Each line now says what should happen:
pick 00af006 Add search
fixup d1402b1 wip
fixup 4bc237b fix typo
pick 1e8ad5b Add search testsSave the todo and check the result
Two clear commits remain. Add search now contains the final version of search.js, including the trim and lower-case fixes. The commit IDs are new because these are new commits.
git rebase -i main
git log --oneline$ git rebase -i main
Successfully rebased and updated refs/heads/search.
$ git log --oneline
731501f Add search tests
55b6724 Add search
97009a9 Add READMEIf you already know a new commit is a correction to an earlier one, let Git do the reordering. --fixup names the commit to fix, and --autosquash places and marks it for you:
Mark a fix while you work, fold it in later
The fixup! Add search commit is folded into Add search automatically. With --autosquash the todo list arrives already arranged, so you only need to save it.
git commit -a --fixup HEAD~1
git log --oneline
git rebase -i --autosquash main
git log --oneline$ git commit -a --fixup HEAD~1
[search d0b5f0e] fixup! Add search
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --oneline
d0b5f0e fixup! Add search
731501f Add search tests
55b6724 Add search
97009a9 Add README
$ git rebase -i --autosquash main
Successfully rebased and updated refs/heads/search.
$ git log --oneline
62898af Add search tests
c25ef16 Add search
97009a9 Add READMEWrite useful commit messages
A good message lets someone understand the change from git log --oneline alone, months later. A widely used format:
- A short summary line, about 50 characters, in the imperative: “Add search box”, “Fix crash on empty list”.
- A blank line, then a body if needed, explaining why the change was made and anything surprising. The diff already shows what changed.
- One logical change per commit, so each can be reviewed, reverted or cherry-picked on its own.
| Unclear | Clear |
|---|---|
| wip | Add search across lesson titles |
| fix | Fix search ignoring capital letters |
| changes | Rename settings page to Preferences |
| more stuff | Add tests for empty search terms |
Your todo list has pick A, then fixup B. What happens to B's commit message?
Reveal answer
It is discarded. Fixup combines B's changes into A and keeps only A's message. Use squash instead if you want to edit a combined message.
Halfway through an interactive rebase, a conflict appears and you do not want to deal with it now. What do you run?
Reveal answer
git rebase --abort. It stops the rebase and returns the branch to exactly how it was before you started.
Your progress
Reveal the check answer or reach the end to complete this lesson.