How to squash all commits in a branch?
You have been working for a while on a feature branch, but before merging it to main you want to squash all commits into a single one. How do you do that?
Let’s suppose you are on a branch b1
off the main
branch and you have 5 commits on that feature branch:
The commits have modified the file file.txt
:
alina
alina git:( b1 ) cat file.txt
Wed Jun 14 07:35:37 PDT 2023
1
2
3
4
5
alina git:( b1 ) █
You have multiple ways to squash those commits, for instance, by using a git rebase: git rebase -i HEAD~5
With an interactive git rebase, you need to know the number of commits you want to squash, which may not be convenient if you have a lot of commits.
Another solution is to perform a git reset --soft
to the first commit of the branch, and then commit again.
The question then becomes: how do you find the first commit of the branch?
The answer is to use the git merge-base
command:
alina
alina git:( b1 ) git merge-base main HEAD
c6bb5647a2db709e1d5dde845c1d35a60217f348
alina git:( b1 ) █
This command returns the hash of the first commit of the branch. However, you still need to remember that the branch was created from the main
branch.
The git reset --soft
will reset the branch to the given commit but will keep the changes in the working directory and the staging area.
alina
alina git:( b1 ) git reset --soft $(git merge-base main HEAD)
alina git:( b1 ) █
The git status
command will show you all the changes that are already staged for commit:
alina
alina git:( b1 ) git status
On branch b1
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: file.txt
alina git:( b1 ) █
Your working directory contains all the changes that were in the 5 commits:
alina
alina git:( b1 ) cat file.txt
Wed Jun 14 07:35:37 PDT 2023
1
2
3
4
5
alina git:( b1 ) █
If you compare the staging area with the HEAD
branch, you’ll see that the changes of those 5 commits are already staged:
alina
alina git:( b1 ) git diff --staged
diff --git a/file.txt b/file.txt
index b1c2a1b..39fc041 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,6 @@
Wed Jun 14 07:35:37 PDT 2023
+1
+2
+3
+4
+5
alina git:( b1 ) █
You can now commit again, and you will have a single commit on your branch:
alina
alina git:( b1 ) git commit -m 'commits squased
[b1 7ca520c] commits squashed
1 file changed, 5 insertions(+)
alina git:( b1 ) █
And your commit tree will look like this:
The commit tree above still shows the 5 commits, but they are not part of the branch anymore. They are still in the git repository, but they are not reachable from any branch and they will be garbage collected at some point.