Skip to content

Instantly share code, notes, and snippets.

@mitchellh
Last active December 9, 2024 12:54
Show Gist options
  • Save mitchellh/319019b1b8aac9110fcfb1862e0c97fb to your computer and use it in GitHub Desktop.
Save mitchellh/319019b1b8aac9110fcfb1862e0c97fb to your computer and use it in GitHub Desktop.
Merge vs. Rebase vs. Squash

I get asked pretty regularly what my opinion is on merge commits vs rebasing vs squashing. I've typed up this response so many times that I've decided to just put it in a gist so I can reference it whenever it comes up again.

I use merge, squash, rebase all situationally. I believe they all have their merits but their usage depends on the context. I think anyone who says any particular strategy is the right answer 100% of the time is wrong, but I think there is considerable acceptable leeway in when you use each. What follows is my personal and professional opinion:

I prefer merge and creating a merge commit because I think it best represents true history. You can see the merge point, you can see all the WIP commits the developer went through. You can revert the whole merge easily (git revert -mN ). I create merge commits more than 9 out of every 10 PRs.

I also believe having more commits makes git bisect better, as long as every commit builds. I hate hate hate when I bisect a project only to land on a single squashed commit from a single PR that is like +2000/-500. That is... not helpful at all. I want to bisect and land on a commit thats at worst like +500/-500. At worst. Ideally I land on a commit thats more like +50/-50. Then I can say "ah hah,the bug is there." Squashing destroys this information. I'll take a merge with 1000 +50/-50 commits over 1 squash every. single. day.

This strategy depends on good hygiene by the developer keeping every commit building. I follow this rule 99% of the time (I make mistakes, but I try very hard not to). In OSS, you can't really control this and I'll sometimes end up fixing up commits for people (using interactive rebase prior to making a merge commit). In a professional environment when I was an engineering leader, I would generally expect engineers I worked with to keep every commit buildable.

I do squash though when a PR has a bajillion tiny "WIP" "WIP" "WIP" commits but is really aiming towards one goal with a relatively small diff. That's my squash use case. I'm careful when squashing to rewrite the commit message so it is descriptive. The default squash commit message created by Git and GitHub is not good (it just concatenates all the squashed commit messages, usually a series of "WIP").

If you have a big diff AND a lot of "WIP", then I rebase (interactively), and selectively squash and reorder commits where it makes sense. I tend to expect developers to do this and care about their commit hygiene, but unfortunately a lot of developers aren't that comfortable with Git. In the OSS world, I do it for them. When I was an engineering manager back in the day, I'd expect engineers I worked with to have this knowledge.

On this last point, I also tend to use a Git GUI client for large interactive rebases. I'm extremely comfortable with the Git CLI but when I'm interactively rebasing a very large PR (say, 50+ commits) with a large number of changed lines, I find using a GUI to be helpful. I'm on macOS so I use Tower. This is the only situation I actually use a GUI, though.

@iamandrewluca
Copy link

iamandrewluca commented Dec 3, 2023

That's a great opinion on Merge vs. Rebase vs. Squash. 💬

I'm mostly a Squash guy, but after reading your thoughts on this, I reflect that the environment forced me to use Squash over other methods. Commit hygiene is something that seems hard to enforce in the environment, and I default to Squash by default.

I would love also to have a Merge Commit with a nice and clear list of commits that reflects the real history, but when I see a lot of noise commits, I just give up and use Squash because of the lack of time.

Thanks for your opinion on this topic. 🙌

@BTruer
Copy link

BTruer commented Dec 4, 2023

Rebase also has its place: let's say you're on a feature branch and a ton new commits have been made to the main branch or the branch that you're going into. The purpose of rebase simply put is: let me get the changes that are in the branch l'm going merge into on my own branch. then if I test the feature/test my code in my branch, I can confidently say it will not break anything whenever I re-base/merge into the target branch. It also keeps the commit history linear and so it's easy to follow changes because every feature or every request with a rebate squash would just be one commit when you're moving fast, it's especially, easy to do this. Getting the hang of rebasing is not easy though.

@phillco
Copy link

phillco commented Mar 26, 2024

The default squash commit message created by Git and GitHub is not good (it just concatenates all the squashed commit messages, usually a series of "WIP"

GitHub now has an optional setting per-repository that lets you make the commit message when squashing a PR default to the PR's title and description, rather than the list of commits. This helps a bit, since people tend to put more effort into the PR description. https://github.blog/changelog/2022-08-23-new-options-for-controlling-the-default-commit-message-when-merging-a-pull-request/

(Wouldn't help on the command line although you could probably mimic the behavior with gh pr)

@klmcwhirter
Copy link

Rebase also has its place: let's say you're on a feature branch and a ton new commits have been made to the main branch or the branch that you're going into. The purpose of rebase simply put is: ...

You should pull from the target branch before push-ing to avoid missing changes. That keeps the history accurate - which was his original point.

The purpose of rebase-ing is to re-write history; not maintain it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment