Combining Git commits with squash
Imagine you have done lots of commits, i.e. several commits for fixing a bug, but you don’t need all of them, and somehow you want to meld them and squash them into a single commit. You can use rebase. Git always squashes a newer commit into an older commit or “upward”. Let’s create some files:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
mkdir remote repos1 repos2 cd remote/ git init --bare cd ../repos1/ git clone ../remote/ . touch a git add . git commit -m "a added" touch b-1 git add . git commit -m "b-1 added" touch b-2 git add . git commit -m "b-2 added" touch b-3 git add . git commit -m "b-3 added" touch c git add . git commit -m "c added" touch d git add . git commit -m "d added" |
Your tree would be something like this:
But now you would like to squash b-1, b2 and b-3 into one commit. So copy the hash ID of b-3 and execute rebase:
1 2 |
git rebase -i <b-3 HASH ID>~3 git rebase -i 02f4da994c622f7db23a051b3cad3d92385b21c2~3 |
This will open your editor and the first few lines are:
1 2 3 4 5 |
pick 5833ab5 b-1 added pick b7d1e42 b-2 added pick 8d7dae1 b-3 added pick b2fa00f c added pick f1d7439 d added |
Git will melt new commits into older ones, and we want to squash b-3 and b-2 into b-1 so change it into:
1 2 3 4 5 |
pick 5833ab5 b-1 added squash b7d1e42 b-2 added squash 8d7dae1 b-3 added pick b2fa00f c added pick f1d7439 d added |
Close all instances of your editor and save them. This will open another editor to write a new commit message.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# This is a combination of 3 commits. # This is the 1st commit message: b-1 added # This is the commit message #2: b-2 added # This is the commit message #3: b-3 added # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Thu Nov 26 11:04:26 2020 +0100 # # interactive rebase in progress; onto ef211ef # Last commands done (3 commands done): # squash b54c2ca b-2 added # squash 30e735c b-3 added # Next commands to do (2 remaining commands): # pick 591529e c added # pick 7960530 d added # You are currently rebasing branch 'master' on 'ef211ef'. # # Changes to be committed: # new file: b-1 # new file: b-2 # new file: b-3 # |
Write your new commit message and save it. Your new tree should look like this:
Squashing commits in git after push
Squash the commits locally (for the last N commits) :
1 |
git rebase -i origin/<branch-name>~<number-of-commits-from-last> <branch-name> |
force push them with +
:
1 |
git push origin +master |
Refs: 1
Difference between --force
and +
:
Note that --force
applies to all the refs that are pushed, hence using it with push.default
set to matching
or with multiple push destinations configured with remote.*.push
may overwrite refs other than the current branch (including local refs that are strictly behind their remote counterparts). To force a push to only one branch, use a +
in front of the refspec to push (e.g git push origin +master
to force a push to the master
branch).
Refs: 1
Rebasing branches onto the base
When you create a feature branch and start working on that, your master might grow and also your branch. Let’s create a repository and add “a” and b:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mkdir remote repos1 repos2 cd remote/ git init --bare cd ../repos1/ git clone ../remote/ . touch a git add . git commit -m "a added" touch b git add . git commit -m "b added" |
Then lets create a feature branch for “c”
1 2 3 4 5 6 7 8 9 10 |
git checkout -b feature-c touch c-1 git add . git commit -m "c-1 added" touch c-2 git add . git commit -m "c-2 added" touch c-3 git add . git commit -m "c-3 added" |
Now lets get back to master and “d” and “e”:
1 2 3 4 5 6 7 |
git checkout master touch d git add . git commit -m "d added" touch e git add . git commit -m "e added" |
At a certain point, you will decide to merge with the master branch, your tree looks like this:
1 |
git merge feature-c -m "feature-c merged into master" |
By doing this you will the following tree, which in a big project might become very complicated and difficult to track.
As you can see, the branch feature-c is 2 commits behind of the master branch. You can rebase your feature branch onto the master branch, so you can make it as your feature branch has just branched from your master.
1 2 |
git checkout feature-c git rebase master |
1 2 |
git checkout master git merge feature-c |
By doing so, you finally end up with a “linear history”. You can also give tags to your commits so you make your tree more readable:
1 2 |
git tag <tage name> SHA1 ID git tag feature-c 392111d90a31a6bbbd0dc1c8ea15bd8dec7dd0d3 |
If you want git to do a rebase instead of a merge when pulling:
1 |
git pull --rebase |
or you can set it as the default action in your gitconfig file:
1 |
git config --global pull.rebase true |
Rebase with a merge conflict