Squashing commits with Git’s rebase command is useful when you want to clean up your commit history. Here’s a step-by-step guide.
First, review the commit log to decide which commits you want to combine:
git log --onelineExample:
a1b2c3d Fix typo
d4e5f6g Add feature X
h7i8j9k WIP: implement feature X
...
Suppose you want to squash Add feature X and WIP: implement feature X.
Run git rebase -i using the commit before the ones you want to squash as the base:
git rebase -i <base_commit_id>In this case, if a1b2c3d is the commit before, you would run:
git rebase -i a1b2c3dYour editor will open with something like this:
pick d4e5f6g Add feature X
pick h7i8j9k WIP: implement feature X
Change the second pick to squash (or just s):
pick d4e5f6g Add feature X
squash h7i8j9k WIP: implement feature X
Next, Git will prompt you to combine commit messages:
# This is a combination of 2 commits.
# The first commit’s message is:
Add feature X
# The following commit message will also be included:
WIP: implement feature X
Clean it up so the final message is concise:
Add feature X
Save and close the editor. Git will apply the rebase and squash the commits into one.
-
If you hit conflicts: resolve them as usual, then run:
git add <fixed_files> git rebase --continue
-
If you want to cancel the rebase:
git rebase --abort
-
After rewriting history: if the branch is pushed to a remote you will need to force-push the rewritten history:
git push --force-with-lease
(Prefer
--force-with-leaseover--forcewhen collaborating.)
By default git rebase -i <base> rebases starting after the specified base commit. If you want to include the very first (root) commit in an interactive rebase — for example to edit or squash the initial commit together with later commits — use the --root option:
git rebase -i --rootWhat this does
- Lists the root (initial) commit as the first entry in the interactive todo.
- Lets you
pick,reword,edit,squash, orfixupthe root commit just like any other commit. - Rewrites the root and all descendant commits (so every commit ID in the branch will change).
Common uses & examples
-
Squash all commits into a single initial commit If you want a single commit that contains the whole history, keep the root as
pickand mark every other commit assquash(ors):pick a1b2c3d Initial commit squash d4e5f6g Add feature X squash h7i8j9k WIP: implement feature X ...After saving, Git will open the combined commit-message editor so you can craft the final message.
-
Edit or reword the root commit message Change the first line to
reword(orr) to update the initial commit message during the rebase:reword a1b2c3d Initial commit pick d4e5f6g Add feature X ...The rebase will stop and prompt you to edit the root commit message.
-
Amend the root content Use
editon the root entry if you need to change the actual tree of the root commit (add/remove files). Rebase will stop at that commit; then you can modify files,git addthem, and rungit commit --amendbefore continuing the rebase:# interactive todo: edit a1b2c3d Initial commit pick d4e5f6g Add feature X ... # when rebase stops: # make changes to the working tree git add <files> git commit --amend --no-edit # or edit message as needed git rebase --continue
Caveats
- Rewriting the root rewrites the entire branch history. This is disruptive if the branch is shared — coordinate with collaborators or avoid rewriting published branches.
- After a
--rootrebase you must force-push to update any remote branch (git push --force-with-lease). git rebase -i --rootbehaves like a normal interactive rebase in terms of conflict resolution and aborting (git rebase --abort).
- Use
git rebase -i <base>to interactively squash commits after<base>. - Mark commits to squash with
squash(ors). - Edit the commit message when prompted.
- Complete the rebase and resolve conflicts with
git rebase --continue. - To include the initial/root commit in your rebase (so you can squash or edit it), run
git rebase -i --root. Be aware this rewrites the whole branch history and requires a force-push for published branches.