Say you have a few commits on a branch and want to squash them all
(done via git log --oneline
)
3cf1e45e Refactor
7bfc2e3b Cleanup
c203f2ba Did some fixes, adds tests
e28ebc36 Adds feature XYZ
d3ee6f4e Commit not to squash
You want to:
- backup branch
- find commit before yours to use for rebase (
d3ee6f4e
) - choose commits to squash using
git rebase --interactive <commit-sha>
and save - edit commit message and save
- confirm with
git log
- revert or remove temp branch
- push to remote
You can make a backup of your current branch, in case you do the rebase incorrectly with
git checkout -b tmp-branch
then git checkout -
to go back to your branch. Once you get the hang of squashing, you probably don't need to do this step.
git rebase -i <commit-sha>
takes a commit to start from, you'll want to do the commit before the one you want to sqaush to, or an earlier than that is fine too. Find a commit SHA via git log
or you can use the shortcuts like head^
, head^^^
, head~5
etc to go backwards that way.
You can squash these to one commit by using git rebase --interactive <commit-sha>
or git rebase -i <commit-sha>
interactive mode. You'll need to specify the SHA of the commit before, or a few before
git rebase --interactive d3ee6f4e
You will be presented in your editor the commits (sorted newest at the bottom) with pick
in front. To squash the commits up to the next pick
commit, change the pick
to squash
or s
for short. There should be instructions at the bottom as a comments to help. Save and quit the editor.
ex of git rebase -i
:
pick e28ebc36 Adds feature XYZ
pick c203f2ba Did some fixes, adds tests
pick 7bfc2e3b Cleanup
pick 3cf1e45e Refactor
# Rebase e28ebc36..e81b3049 onto e28ebc36 (3 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
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# create a merge commit using the original merge commit's
# message (or the oneline, if no original merge commit was
# specified); use -c <commit> to reword the commit message
# u, update-ref <ref> = track a placeholder for the <ref> to be updated
# to this position in the new commits. The <ref> is
# updated at the end of the rebase
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
you would want to switch the pick
s to squash
or s
except for the last commit of yours, keeping that as pick
:
pick e28ebc36 Adds feature XYZ
s c203f2ba Did some fixes, adds tests
s 7bfc2e3b Cleanup
s 3cf1e45e Refactor
Then save the result (:wq
if in the default vi
editor).
When you save the result, git will present an editor with a commit message similar to git commit
, except all squashed commit messages will be there too, edit the entire commit message how you want and save. Sometimes it's easy to just prepend the previous commit messages with -
or *
like a bulleted list, but you're free to do whatever you'd like.
Confirm it's what you wanted with git log
or however you'd like to see if it's how you wanted it.
If everything looks good, you can remove the backed up temp branch you created via:
git branch -D <name>
If it's not how you wanted, and you want to go back to your original commits, you can use your temp branch:
git checkout <tmp-branch-name>
git branch -D <orig-branch-name>
git branch -m <orig-branch-name>
with git branch -m
you will have renamed your backed up temp branch to the original branch name.
If you did the rebase and sqaushed the commits, we have changed git history, so if your old commits were on a remote
branch, the history has changed, so you'll need to force push, via git push --force
or git push -f
next time. And if you reverted your temp branch, you might have to re-setup the upstream link via git push -u <branch-name>
or git push --set-upstream <branch-name>
vi
shortcut for visual block mode, then delete, then insert mode:
CTRL-V
for Visual Block mode, select lines of similar textpick
for examplec
for deleting the selections, and staying in insert mode- then type whatever you want, like
s
orsquash
, thenESC