Usually you have a lot of “intermediate” commits while developing on a feature branch like
Fix jenkins. These commits are neither atomic nor does it help to read them in the history. They purely serve the purpose to persist the current work, trigger another build on your buildmachine or doing some fixes you discovered while testing. So before I rebase my changes to master I’d like to squash all commits that i’ve done to a single one. Usually I use interactive rebase for this purpose but since I need to actively tell every commit that I want to squash it I found it tedious. Also its easier, most of the time, to rebase onto master since you only apply conflicting changes to the end-result of your work without the intermediate steps you had to take. So I came up with my own git alias that solves this for me. Introducing:
git squash <Commit/Branch> <Message>
<Commit/Branch> is the parent from where you started your branch and
<Message> is the commit message for your squashed commit.
Let’s first demonstrate what we want to achieve. Imagine we are in the following state in our development:
Usually I would simply
rebase the feature branch onto master (
rebase because in larger teams you want to avoid a merge hell), but when we do so we end up with the following:
The two commits from our feature branch are not atomic so we now have “broken” commits on our master. This can make it harder to use tools like
git bisect to figure out where bugs were introduced. Also this forces you to write more or less useful commit messages on your feature branch, even when you don’t have a working version and just want to persist your current state before leaving into the weekend. So I prefer to squash all my commits before I rebase the feature onto master.
Unfortunately it’s not possible to (easily) use
git rebase --interactive and squash all commits. But thinking about what we want to achieve it’s actually easy with other commands. We want to get all changes until the commit we branched of, reset to it and commit all changes in a single commit. So basically the following is sufficient:
As you might noticed we also need to figure out the
<base commit>. This can be done by using
git merge-base <current branch> <base branch>.
git merge-base will figure out the best common ancestor of two branches. But now we need to figure out the
<current branch>. There are multiple ways to do that. In the most recent git version
git branch --show-current does exactly what you expect, but since this is a rather new feature I decided to rely on an older approach which is
git rev-parse --abbrev-ref HEAD. So we finally have our logic to do a squash with minimal input:
Now we add a couple of safety checks and some nice output and we end up with a script like this:
We are now able to use the script like
./git-squash master "Implement new feature" and it will squash all commits of the branch we are currently working on and put it into a new commit with the message “Implement new feature”.
The last piece missing now is that we also want to have it as a proper git alias. To do that we add the following entry into our
Finally we are able to use
git squash master "Implement new feature".
Of course you can also put the bash script directly into your git alias with some escape magic. So if you want to add it as a “one-liner” you can use the command in the TL;DR; in the beginning of this post.
I updated the script a bit to not redirect the error message when
git commit fails. This is handy because you might have pre-commit hooks that cause it to fail and you want to see the actual error message.