rebase is dead long live the squabaserge

git Aug 21, 2025

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.

rebase vs merge pressure

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"

Tags