avatarDr. Derek Austin 🥳

Summary

The author, Dr. Derek Austin, argues for the use of regular merge commits over squash commits due to the loss of context and historical information that squashing causes, which hinders day-to-day work such as bug fixing.

Abstract

Dr. Derek Austin shares his experience working in an engineering organization that favored squash commits, which initially impressed him with their perceived professionalism and clean commit history. However, as he engaged in routine bug fixes, he found that squash commits lacked the necessary context for understanding code changes, as they consolidate all commits into a single commit. This flattening of history made it difficult to track why specific lines of code were altered, leading to significant time spent trying to deduce the reasons behind changes. Austin advocates for regular merge commits, which maintain the full history of code changes, including individual commit messages that provide valuable insights into the development process. He suggests that using Conventional Commits and atomic commits can enhance the usefulness of commit messages, making them more informative and preserving the rationale behind code modifications. Despite the appeal of a tidy commit history, Austin prioritizes the practical benefits of detailed commit logs for efficient debugging and maintenance.

Opinions

  • Squash commits create a clean Git commit history, but this cleanliness is superficial and does not justify the loss of historical context.
  • Regular merge commits are preferable as they provide a detailed account of the development process, which is crucial for understanding the evolution of the codebase.
  • The author values the information provided by individual commit messages, which are lost in squash commits, making it harder to retrace the steps of code changes.
  • Using Conventional Commits and atomic commits in conjunction with regular merge commits offers a more transparent and informative history of code changes.
  • The practice of squashing commits can lead to wasted time when trying to fix bugs, as it obscures the original intent and context of code modifications.
  • The author emphasizes the importance of preserving the "why" behind code changes, which is often captured in commit messages and is essential for effective code maintenance and troubleshooting.

Why I Prefer Regular Merge Commits Over Squash Commits

I used to think squash commits were so cool, and then I had to use them all day, every day. Here’s why you should avoid squash

I’m not actually allergic to squash, just to git squash. (Photo by Viviana Rishe on Unsplash)

“Wow, that’s so cool!”

I’ll always remember seeing my first squash commit.

When I first joined a large engineering organization with more than 200 members, the squash commits were the first thing I noticed.

To me, the consistent use of squash commits across the company screamed “professionalism” and “we know what we’re doing here!”

It took me about a month to realize that I really, really didn’t like squash commits, and I started longing for good, old-fashioned merge commits.

Comparing Squash vs. Merge Git Commits

As a refresher, the difference between a “squash commit” and a “merge commit” is that a regular “merge” includes all of the Git commits in the history of the target branch, while “squash” flattens them to one commit.

Confusingly, “squash” isn’t its own command. Instead, you can choose “squash and merge” or “squash and rebase” as an option when you’re running the git merge or git rebase commands, respectively.

By default, a pull request (PR) will be a merge commit, so after the PR is merged then the entire history of the working branch will be merged in, plus an additional commit (“Merge pull request #21 from branch…”).

On the other hand, you can set your repository to “squash and merge” by default, meaning merged PRs will result in only a single commit each, instead of bringing over all the commits from the working branch.

Why Would Anyone Use Squash Commits?

The benefit of squash commits is that you keep a “clean” Git commit history. Since we developers are famously… let’s say “precise”… then a tidy commit history sounds absolutely fantastic. It’s not.

You can see why I was impressed by the professionalism — the entire engineering organization was so dedicated to having a clean commit history that they had mandated “squash and merge” as the default!

Unfortunately, the benefits of having a clean commit history end exactly right there — the history may be clean, but my day-to-day work needed much more context than what a squashed commit could provide.

By definition, a squash commit loses information. I like to think of it as a black hole, where the information gets turned into “Hawking radiation,” which in this particular case is measured in units of developer frustration.

The Overwhelming Weakness of Squash Commits

I soon became disillusioned with squash commits for the simple reason that my job was 90% fixing bugs in old code. When I would inspect a line using Git Lens to run git blame, I would never learn everything.

Literally every git blame at the company read, “Merge pull request #237 by {my boss or coworker},” with no additional information. I couldn’t even find the topic of the PR without looking it up manually! How frustrating!

I would dutifully pull up the original PR and ticket, trying to get some sense of why this particular line of code changed, and I would have no idea. You know how a PR often changes hundreds of lines of code at once? Yeah…

I can count on one finger the number of times I needed to actually revert an entire squashed commit at once, and it was a dramatic rollback of a PR that pushed our entire roadmap back by an entire month.

The rest of the time, I’d be doing my normal work, trying to fix bugs with a deadline of “can you get this to QA by end of day?” And when I’d inspect a line, I’d have no idea why it was changed, because of the squash commits.

Why I Prefer To Not Squash My Merge Commits

Look, I would love a clean commit history, too, but the downsides are crippling to day-to-day work. While it’s great to know “this broke when fixing this other bug,” a squash commit doesn’t give me enough info.

Let’s say I know my boss changed a certain line of code 18 months ago by inspecting it with git blame. At this point, he surely has no idea why he changed it, even if he could find the time to investigate it for me.

With squash commits, I’m left with less information: I can only find out the bugfix or feature that resulted in the line being changed, not the actual reason the line was changed, and that’s just not enough to go on.

While it would be great if every line had a code comment or two, we all know that’s not how most developers work — I typically see less than 1% of code actually having any comments when I look at a new React codebase.

The reason I have no idea why the code changed is pretty straightforward; my boss wrote down the reason at the time (in the commit message), but then we decided to “squash” it away for no reason. That does not spark joy.

The Solution Is To Just Merge, Not Squash, Commits

There’s a super simple solution here, and it’s actually the default — just don’t squash. What I want to know isn’t much, but it makes all the difference:

  • refactor: Was my boss “refactoring” this code to be more readable or maintainable, hopefully without affecting the actual functionality?
  • chore: Was my boss doing some cleanup “chores” that shouldn’t have affected the production code, such as adding code comments or tests?
  • fix: Was my boss “fixing” an identified issue, and if so what was the specific issue that was broken with this piece of code?
  • feat: Was my boss implementing a new feature, such as creating a brand-new component with user-facing functionality?

Did you pick up on the fact that these are Conventional Commits (also called “semantic commits”), which are best paired with atomic commits?

The reason I became so disillusioned with the company is that there was a great culture around commit messages, but a terrible one around PRs.

Since we had super-long JIRA tickets, there wasn’t a great reason to write much in a PR, but then we threw away all of our context by squashing.

The result was we would spend literally hours trying to fix bugs because no one remembered what any given piece of code was supposed to do.

So, at my current company, I make dozens of commits a day, and I leave them all in the history when I merge my PRs. It may be a little messy, but it’s better for my sanity, as well as for my GitHub contribution graph.

Happy coding!

P.S. If you liked this hot take on Git best practices, then you’ll love me trying to convince you to write 200-character commit messages, right? Read this:

Dr. Derek Austin is the author of Career Programming: How You Can Become a Successful 6-Figure Programmer in 6 Months, now available on Amazon.

Programming
Software Engineering
Git
Coding
Web Development
Recommended from ReadMedium