Combine two commits during an interactive Git rebase

Use squash or fixup to combine adjacent commits, adjust a pending rebase plan, and recover safely when the relevant commits have already been replayed.

Best practices
Illustration of Git branches and version history

In an interactive rebase, change the newer commit's action from pick to squash to combine it with the preceding commit. Use fixup when you want to discard the newer commit's message. If the rebase is already running, the right approach depends on which commits remain in its todo list.

Combine the last two of three commits

Start with a clean working tree and at least three commits after the chosen base:

git rebase -i HEAD~3

The todo list is ordered oldest first. Suppose it contains:

pick a1b2c3d Add login endpoint
pick e4f5a6b Fix validation message
pick c7d8e9f Correct another validation message

To combine only the last two, leave the first two actions as pick and change the third:

pick a1b2c3d Add login endpoint
pick e4f5a6b Fix validation message
squash c7d8e9f Correct another validation message

Git replays the first commit unchanged in purpose, then combines the second and third. When prompted, edit the combined message and save it. Changing both the second and third lines to squash would combine all three commits, which is a different operation.

Choose squash or fixup

  • squash combines the changes and opens a message editor containing the contributing messages.
  • Plain fixup combines the changes while discarding that commit's message, keeping the preceding commit's message.

For a small follow-up correction, change the third line to fixup instead. More advanced fixup -C and fixup -c forms have different message behavior; do not assume every fixup variant discards its message.

Change a rebase that has already started

Inspect the current state:

git status
git rebase --edit-todo

--edit-todo lets you change the remaining plan. If the two commits are still pending, arrange them next to each other and mark the later one squash or fixup. It does not automatically undo commits that have already been replayed.

If you are paused on a conflict, resolve it, stage the affected files, and use git rebase --continue. If the relevant commits have already passed or the plan is unclear, save any conflict-resolution work you want separately, then restart deliberately:

git rebase --abort
git rebase -i HEAD~3

Aborting returns the branch to its pre-rebase state; it can discard work done while resolving that rebase.

FAQ

Can I combine commits that have already been pushed?

Yes, but the rewritten history normally needs a non-fast-forward update. Coordinate with anyone using the branch, review the remote tip, and use a scoped --force-with-lease push if the server permits it. Shared protected branches usually need a different workflow.

How do I avoid keeping several fix-typo messages?

Use plain fixup, or edit the squash message to describe the final change clearly. Commit messages should explain the combined result.

Can I combine nonadjacent commits?

You can reorder lines in the interactive todo list and then squash adjacent entries. Reordering changes when each patch is applied, so dependencies can cause conflicts or change the result. Review and test the final code rather than treating a clean rebase as proof of correctness.

See git-rebase and git-push.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting