Taco Steemers

A personal blog.

Git: how can we squash (flatten) commits

Earlier I wrote about when (not) to squash commits . To squash commits means to flatten, or combine, a series of consecutive commits in to one commit. Here I explain two ways we can combine commits.

Squashing consecutive commits on your feature branch

The first way is interactive rebasing. With this tool we can choose to combine several commits that only exist on this branch in to one commit. In my opinion, interactive rebasing is only a good idea in these two situations:

  • you rebase right before merging the feature branch into the main branch, after this branch has been reviewed, and you know no-one has branched off of this branch
  • you know you are the only one working on this branch, and as a result there is no possibility that you will rebase commits that someone else is basing their own commits on

The basic example is as follows. We have finalized part of our work on this feature branch. This part is spread out over 5 commits. We would like to flatten this in to one commit for this part of the work, rather than keeping the 5 separate intermediate commits. This can be done with the following command:

git rebase -i HEAD~5

Note that this sign before the 5 is the tilde, ~, not the minus sign. This command will fail if there are only 5 commits on the branch.

We will be presented with the text editor. It shows a list of commits, ordered from the oldest on top to the newest at the bottom. Here we will indicate which commits we want to pick (use as a basis to flatten on) and which commits we want to squash away. The text editor window will contain an explanation of all possible commands. We will pick the first and squash the rest.

After we save the file and exit the editor we will receive instructions on how to proceed.

How does this look in a bigger process?

Let's look at the situation where we are manually merging a feature branch into the main branch. Our feature branch has received 5 commits since it was branched off of the main branch. These commits have passed the review phase. Now we want to combine these commits in to one commit and add that to the main branch. This one commit will be easier to backport than several commits, and easier to understand for future review than 5 separate commits would be.

First we update the main branch:

git checkout main
git pull

Then we squash our 5 commits on the feature branch:

git checkout feature
git rebase -i HEAD~5

The next step is where we rebase our feature branch with the current state of the main branch. In other words, we put the current state of the main branch below the additional commits that we added to the feature branch. It is possible that there are conflicts that we need to resolve by hand. We don't commit the changes that follow from the conflicts. We only stage the changed files.

git rebase main

If we are resolving conflicts, we can get a hint on how to proceed from git, by asking for the status.

git status

When we are done resolving conflicts, we finalize the rebase:

git rebase --continue

If we feel that something is going wrong, we can abort the rebase:

git rebase --abort

After finalizing the rebase we should perform our testing again. Once we are satisfied that everything is as it should be, we can merge the feature branch to the main branch.

Squash everything, while merging to the main branch

A second way we can combine commits is the actual git merge --squash command.

For conversation about this workflow: https://stackoverflow.com/questions/5308816/how-to-use-git-merge-squash

Personally I don't use this workflow by hand, as it is automated away where I work.

First we update the main branch.

git checkout main
git pull

Then we rebase the feature branch with the current state of the main branch, and resolve conflicts by hand.

git checkout feature
git pull
git rebase main

After we finish rebasing and resolving conflicts we should perform our testing again.

The next step is to check out the main branch and merge --squash the feature branch onto it:

git checkout main
git merge --squash feature

Finally, the changes need to be staged and committed with git commit . You might want to write a new message for this, using git commit -m .