rebase is dead long live the squabaserge
Alongside tabs vs spaces, tailwind vs vanilla css, emacs vs vim, pepsi vs coke, xbox vs playstation, the age old debate of git rebase vs git merge continues to rage on. It's still one of Whiskey, Web, and What Not's 🥃Hot Take questions...
Let me start by saying, git rebase is an oft misunderstood construct of git. It's complicated, hard to understand, and can be a cause for confusion. However, it has so many benefits over just merging things.
Git commit history is another form of documentation and it should be treated as such! It's such a useful tool as a developer to understand the history of how and why you're codebase changed.
No holy wars here
Instead of getting deeper into a holy war, let's just talk about how git rebase
when used correctly, can really be so much nicer than just git merge
all the time.
First of all, I'm not saying that you should never ever merge stuff. Far from it.
If you're a user working on a feature, you'll often have a flow where you work off of your main
branch.
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'mainBranchOrder': '3' }} }%% gitGraph commit id:"A" commit id:"B" branch my-new-feature checkout my-new-feature commit id:"C1" commit id:"C2" checkout main commit id:"D" checkout my-new-feature merge main checkout main merge my-new-feature id:"merge commit" commit id:"F" commit id:"G"
Here you have a developer which created a my-new-feature
branch, and while they were working, there were other commits introduced into main
. This means that the developer now has a choice.

A lot of developers fear the rebase because of the complexity of possible git conflicts, and the idea that they're re-writing history.
And to be fair it is a total PITA when you do rebase your branch, you have multiple commits in your branch history and you do happen to have a git conflict (which can happen in a merge scenario too BTW).
What we propose here today is...
squabaserge
The "squabaserge" is a combination of 2 git commands.
- git rebase -i :commmitId:
- git merge
The squash part gives you as the developer the opportunity to clean up the git history of your own branch, before you do the rebase. This allows you to remove extraneous git commits, or too many git commits that might make resolving git conflicts tedious.
Look at the git history for your branch with git log --oneline
pick the commit before you started your branch, grab it's ID, and do git rebase -i :commit:
This will pop open a VIM window... don't forget how to save/exit with vim :wq

You simply replace the pick
with squash
on all the commits you want, leaving behind just one commit with pick
.

Now you can edit your commit messages since you'll only have one commit.


Or a cool VSCode #protip is you can do everything we just did in VIM above, which is even cooler if you have Git Lens installed. You can do is set your
EDITOR=vscode --wait
environment variable to open it in VSCode.

Once the rebase is complete, you now have your branch behind D
, you have a nice clean git history in front of your shiny clean branch with just C1
.
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'mainBranchOrder': '3' }} }%% gitGraph commit id: "A" commit id: "B" branch feature commit id: "C1" commit id: "C2" checkout main commit id: "D" %% Rebased history branch feature-rebased commit id: "C1'" checkout main merge feature-rebased tag: "after rebase"
Now when you merge your branch with main
you'll have just the single merge commit into main
, and won't have all those merge commits in your git history.
Many teams in the world enforce squashing when merging into main
anyways, so by making use of the squabaserge
, you're actually doing yourself a favor because you'll have an opportunity to clean up the git commit history of your branch prior to that squash happening.
Conclusion
Instead of just git merge
all the time to get this history of main
into your branch, especially right when your feature is ready to get put into main
, opt for the slightly slower, but totally worth it flow of the squabaserge
.
Before
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'mainBranchOrder': '3' }} }%% gitGraph commit id:"A" commit id:"B" branch my-new-feature checkout my-new-feature commit id:"C1" commit id:"C2" checkout main commit id:"D" checkout my-new-feature merge main checkout main merge my-new-feature id:"merge commit" commit id:"F" commit id:"G"
After
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'mainBranchOrder': '3' }} }%% gitGraph commit id: "A" commit id: "B" commit id: "D" %% Rebased history branch my-new-feature checkout my-new-feature commit id: "C1'" checkout main merge my-new-feature tag: "after rebase"