How to Squash Commits in Git
-
Using Interactive
git rebase
Tool to Squash Git Commits -
Using
git merge -squash
to Squash Git Commits
We will learn Git squashing in this tutorial. The basic idea is to take multiple continuous commits and squash them into one.
The main intention is to condense many commits to a few relevant commits. Thus, doing this makes the git history look concise and clear.
Another way of looking at it is that we do multiple commits related to some task. After a while, when we reach a satisfactory state, the many commit messages clutter up the git history.
At this point, we may want to combine the different commits into one so that the git history looks clear and best reflects the task done.
Another use case is to do squashing while doing branch merging. Usually, we create a feature branch from the main branch for some feature development.
After feature completion, we merge the feature branch into the main branch. Here too, we may want to squash the various commit messages done in the feature branch into one when merging into the main branch.
Please note that there is no git squash
command.
There are two ways to achieve Git squashing:
git rebase -i
as an interactive tool used to squash commitsgit merge -squash
using the-squash
option while merging
Using Interactive git rebase
Tool to Squash Git Commits
Consider the following git log excerpt, which shows the last four commits from HEAD
that we are interested in squashing.
25c38c4 remove .class files
da66e6a Delete version.ini
f4e3f09 Delete .log
b0e6655 Delete .lock
da66e6a github git notes
We can see in the log the first four commit messages signifying the operations of deleting different irrelevant files. Now, we will squash these four commits into one.
Following is the syntax of the command to squash the last X commits using the interactive rebase tool.
git rebase -i HEAD~[X]
Thus, to squash the four commits, we would do as below.
$ git rebase -i HEAD~4
After issuing this command, Git will invoke the default editor with details of commits to squash, as shown below.
pick b0e6655 Delete .lock
pick f4e3f09 Delete .log
pick da66e6a Delete version.ini
pick 25c38c4 remove .class files
# Rebase 652d2fe..25c38c4 onto 652d2fe (4 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
The editor shows the various commits with the pick
command. It also shows information about the available commands. We will be using the squash
(or s
) command.
As shown below, we will keep the first commit with the pick
command and change from pick
to s
(for squash) command for the remaining three commits.
pick b0e6655 Delete .lock
s f4e3f09 Delete .log
s da66e6a Delete version.ini
s 25c38c4 remove .class files
# Rebase 652d2fe..25c38c4 onto 652d2fe (4 command(s))
#
...
The commits marked with squash
( or s
) will be merged to the main commit viz. the one marked with pick
.
Now, we will save the changes in the editor and exit. After this, the rebase -i
tool will open another editor to enter the commit message, as below:
# This is a combination of 4 commits. The first commit's message is:
Delete .lock
# This is the 2nd commit message:
Delete .log
# This is the 3rd commit message:
Delete version.ini
# This is the 4th commit message:
remove .class files
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Sun Jan 3 16:39:23 2021 +0530
#
# interactive rebase in progress; onto 652d2fe
# Last commands done (4 commands done):
# pick b0e6655 Delete .lock
# s f4e3f09 Delete .log
# s da66e6a Delete version.ini
# s 25c38c4 remove .class files
# No commands remaining.
# You are currently editing a commit while rebasing branch 'master' on '652d2fe'.
#
# Changes to be committed:
# new file: github-git-notes.txt
#
Now, we will add the new commit message at the top of the first commit message.
Deleted irrelevant files
# This is a combination of 4 commits. The first commit's message is:
Delete .lock
# This is the 2nd commit message:
Delete .log
...
After saving and exiting the editor, the rebase -i
tool will print the following message.
HEAD~2
Rebasing (2/2)
[detached HEAD caab6e8] Deleted irrelevant files
Date: Sun Jan 3 16:39:23 2021 +0530
1 file changed, 54 insertions(+)
create mode 100644 github-git-notes.txt
Successfully rebased and updated refs/heads/master.
Now, we will check the git log
and see the squashed
commit (i.e.) single commit message instead of the four commit messages.
$ git log --oneline
25c38c4 Deleted irrelevant files
da66e6a github git notes
...
Using git merge -squash
to Squash Git Commits
Following is the command’s syntax to merge a branch with the current branch (usually main
) and squash the commits of the source branch.
git merge --squash <source_branch_name_to_squash>
We will now merge the feature branch viz. feature1
with squashing with the main
branch.
First, we will checkout to the main
branch.
$ git checkout main
Switched to branch 'main'
Then, we will do a git merge
with the squash
option as follows.
$ git merge --squash feature1
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested
When we do a merge
with --squash
option, Git will not create a merge commit in the destination branch, as it does in a normal merge. Instead, Git takes all the changes in the source branch viz. feature1
and puts it as local changes in the working copy of the destination branch viz. main
.
Please see below.
$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: config.ini
Here, the file config.ini
has the changes done in the feature1
branch.
Now, all that remains is to commit the changes to the main
branch as below.
$ git commit -am 'Merged and squashed the feature1 branch changes'
[main 573b923] Squashed and merged the feature1 branch
1 file changed, 4 insertions(+)
Thus, we have now merged the changes in the feature1
branch into the main
branch, along with squashing the commit messages of the feature1
branch. We now only have a single commit message in the main
branch.
Related Article - Git Rebase
- How to Rebase to a Specific Commit in Git
- How to Git Rebase Origin/Branch vs. Git Rebase Origin Branch
- How to Use Git Rebase on the Command Line
- How to Rebase Git Branch
- How to Rebase Local Branch When Pulling Changes From the Remote Repository Branch in Git
- How to Undo Rebase in Git