How to squash commits using an interactive rebase

Reginald LynchOctober 31st 2020, 3:02

To get started with this example, we need a new branch, namely rebaseExample3, which tracks origin/stable-3.1.

Step 1 : Create the branch with the following command:

git checkout -b rebaseExample3 --track origin/stable-3.1

Step 2 : Find a commit that is between origin/stable-3.1 and origin/stable-3.2, and list the commits in reverse order. Alternatively, you can scroll down to the bottom of the output and find the commit we will use, as shown in the following snippet:

git log origin/stable-3.1..origin/stable-3.2 --oneline --reverse

Step 3 : Reset the rebaseExample3 branch to the 5218f7b commit; this will simulate having six commits on top of the origin/stable-3.1 branch. This can be tested by running the status of Git as follows:

git reset --hard 5218f7b

git status

Step 4 : Now, we have these six commits on top of the origin/stable-3.1 branch, and we want to squash these commits into two different commits. This can be done by simply running git rebase --interactive. Note that we are not specifying which branch we want to rebase to, since we have already set up a tracking branch when we created the branch using --track. To continue, let's execute the rebase command as follows:

git rebase --interactive

The editor will open, and you will see four commits, not six as you would expect. This is because the rebase, in general, refuses to take merged commits as part of the rebase scenario

Step 5 : Edit the file so that it looks as follows:

pick 8a51c4453 Do not close ArchiveOutputStream on error
squash f045a68a7 Added the git-describe implementation
pick 7995d8771 Prepare 3.2.0-SNAPSHOT builds
squash 5218f7b33 Propagate IOException where possible when getting refs.

Step 6 : Remember that commits are listed in reverse order compared to the Git log. So, when squashing commits, we squash up into the commits we have marked with the pick. When you close the editor, Git will start the rebase from top to bottom. First, apply 8a51c44 and then squash f045a68 into the commit 8a51c44. This will open the commit message editor, which contains both of the commit messages. You can edit the commit messages, but for now, let's just close the editor to finish with the rebase and the squashing of these two commits. The editor will open one more time to complete the squashing of 5218f7b into 7995d87.

Step 7 : Use gitk to verify the result.

gitk

Step 8 : If you check the commit message of the HEAD commit, you will see that it has the information of two commits, as shown in the following command. This is because we decided not to change the commit message when we made the change:

git log -1

Step 9 : Now we have squashed two commits, but we could have used other keywords when editing the rebase's to-do list. We will try the fixup functionality, which works like the squash functionality, by performing the following steps; the exception is that Git will select the commit message of the commits using the pick keyword. Start by resetting back to our starting point:

git reset --hard 5218f7b

Step 10 : As you can see, we are back at the starting point, that is, we're six commits ahead of the origin/stable-3.1 branch. Now we can try the fixup functionality. Start the interactive rebase and change the file according to the following output. Notice that you can use f instead of fixup:

git rebase --interactive

pick 8a51c4453 Do not close ArchiveOutputStream on error
f f045a68a7 Added the git-describe implementation
pick 7995d8771 Prepare 3.2.0-SNAPSHOT builds
f 5218f7b33 Propagate IOException where possible when getting refs.

Step 11 : Once you close the editor, you will see rebase's progress through Git. As predicted, the commit message editor will not open. Git will just rebase the changes into two commits on top of the origin/stable-3.1 branch. Using git status, you can confirm that you have just two commits:

git status

Step 12 : Another difference is that the commit message from the two commits we marked with fixup has disappeared. So, if you compare this with the previous example, it's very clear what the difference is; it's shown in the following command:

git log -1

Step 13 : Finally, we can also confirm that we still have the same source code, but with different commits. This can be done by comparing this commit with the commit we created via 5218f7b, using the following command:

git diff 5218f7b

As predicted, there is no output from git diff, so we still have the same source code.