Live data from Hacker News

Fortunately, I don't squash my commits

blog.ploeh.dk

131–140 of 333 posts

Re: Fortunately, I don't squash my commits

#131
I think much of the difference here depends on your workflow. Where do you run tests? How do you manage build infrastructure? What is your process for code review? What is your process for validation and testing of shared branches? How do you manage releases?

In my opinion, squash merges make the most sense when the following are true:

1. Developers cannot push directly to shared branches; all commits into shared branches are done via pull requests with mandatory code review.

2. Shared branches must build and pass tests at every commit.

3. The team builds features incrementally and uses the "Branch by Abstraction" approach (feature flags/experiments) to ensure functionality can be merged before it is ready to be enabled in production.

4. Changes that merge into the shared ("trunk") branch are released frequently.

Now, you may argue against some of those practices, and if you end up in a different place on one of those, squashes may not make sense to you. But then the actual difference of opinion is elsewhere. It's not really about squashing, it's about how much code is a reasonable granularity to code review at one time.

If you can keep the code review cycle tight, then a pull request branch basically becomes almost like a virtual pair-programming session. There may be some back and forth as the author and reviewer settle on a final form of the commit. But I don't see the commits that happen along the way as valuable in the least. (In my experience, the vast majority of intermediate commits are things like "fix typo", "make linter happy", "rename this function because code reviewer pointed out it was named inconsistently", etc.) Instead, my mental model is that a pull request is like a mutable commit: you keep mutating it until it's the commit you want, and then you merge it. Since my mental model is that a pull request is a kind of commit, it makes sense that when it merges in, it does so as a single commit.

Again, I'm not saying that this is the only valid way to work. But neither is it an invalid way to work.

As a meta-comment: When you see someone else making a technical decision that doesn't make any sense to you, rather than instantly assuming that they are a clueless incompetent, it's usually more instructive to assume that the decision does make sense to them, and to try to figure out what about their circumstance is different from yours to make that the case.

Re: Fortunately, I don't squash my commits

#132
post #17
post #6

Earlier quoted context omitted.

Merge commits are noise, a clean history is has no merge commits

git log --no-merges

See also

    git log --no-merges --first-parent
I don't get the "no merge commits" argument - git log has dozens of options allowing you to bend it to your use case.

To address the grand parent that "a clean history is has no merge commits" I would argue that a clean history is also a lie if you're working in a branching workflow (hint: you should be working in that most of the time).

If you want to avoid (note: not eliminate) merge commits then make sure to rebase against master before merging, and then ensure merges are fast-forward merges.

Re: Fortunately, I don't squash my commits

#133
post #82

Earlier quoted context omitted.

When you have to find a subtle one line breakage that happened years ago, you’ll be happy that you had a single commit with 30 lines of code instead of a squashed commit with 3000. It makes it much easier to isolate the case of a problem and fix it in many cases. I’m asked “how long has this been broken and what caused it?” and answer it via git blame or bisect probably every few weeks. This is life on legacy project…

That's not a squash or not problem, that's a your tickets are too large problem. If you normally end up with 3000 line feature commits, you're trying to do too much with individual feature changes.

Are we pretending that we, as devs, have much say in this? If our management says they want a features that’s going to take 3000 lines of code, you don’t have a choice. It’s nice if you’re somewhere where you can roll out feature mvps, but in some environments you don’t get that luxury.

And some features are just monsters, either through the nature of the feature or architectural choices that were made before it was conceived.

Re: Fortunately, I don't squash my commits

#134
> Not all bugs can be reproduced by an automated test

While that may be true, I'd say in this case, order-dependent tests are evil. This case study pretty much fits the definition -- the test result changes depending on what other tests are run before it (due to shared state across tests). It certainly is possible to write automated tests for this -- reset that state to its default before each test!

One thing that Google recently (in the past year or so) added to its CI setup is a job that goes through periodically and randomizes the order in which tests are run, thereby increasing the chance order-dependent tests are caught. (Early on, I remember being annoyed by all the triggers due to floating point error accumulation in a monitoring library -- do I really care about an error of 1e-10 when my noise is >10?)

That's a long-winded way of saying that it's also possible to catch order-dependent tests by shuffling the order in which they run, although built-in support may vary depending on your testing framework. (from a brief search, it looks like rspec and junit both support randomization; xunit forces it).

That said, I absolutely agree with the other conclusions, especially that binary search for debugging is immensely useful (even when dealing with Google-scale tens of thousands of unrelated commits due to monorepo), and definitely empathize with debugging often taking much longer than the bugfix.

Re: Fortunately, I don't squash my commits

#135
post #110
post #55

Earlier quoted context omitted.

Programmers can have the best of both worlds. Use granular commits on a local branch and squash merge into shared branches. That way one gets clean shared history while preserving local work history.

The problem with this approach is the local branch is no longer represented in the shared branch. So if I'm working on a larger feature and want to PR an intermediate part and continue working, I'm in for a bad merge. If I want to merge my hotfix topic branch into both the release and the master branch, their commits won't match so I can't check if it's present in both automatically. If a topic branch is left up inst…

But doesn't that assume that you based your larger feature on the local branch, instead of the squashed public version? If you wanted to build on the previous commit, why wouldn't you build on the squashed version?

Re: Fortunately, I don't squash my commits

#136
post #82

Earlier quoted context omitted.

When you have to find a subtle one line breakage that happened years ago, you’ll be happy that you had a single commit with 30 lines of code instead of a squashed commit with 3000. It makes it much easier to isolate the case of a problem and fix it in many cases. I’m asked “how long has this been broken and what caused it?” and answer it via git blame or bisect probably every few weeks. This is life on legacy project…

I think you just highlighted one source of the disagreement. Nobody on any team I work with would accept a PR with 3000 lines of change. Each team had a policy, whether formal or informal, to break large work items across PRs so they were more easily reviewable. I would say that 300 lines changed is getting towards the bigger end of what we would accept. If your choice is between 30 or 3000 lines changed per commit,…

Yeah, that’s nice in theory, but in practice the situation is often less ideal. Some features, partially implemented, would break existing functionality if not completed, and merging those upstream prior to total completion is therefore impossible. And on the other side of things, some environments and features require large, systemic changes. This is, surely, an organizational failing, but one that we must adapt to.

Re: Fortunately, I don't squash my commits

#137
`squash` is a tool and it's neither good nor bad; it needs to be applied where it makes sense. The title is indeed click-baity. It would have been more interesting to read that the bug/mistake was caused as a result of squashing. It's not the case and I take issue with the way the author describes his commits.

The problematic commit is described "Extract CreateTokenValidationParameters method", without an explanation of why the refactoring is necessary or what problem is it solving. It looks like it is improving code readability, but it doesn't go as far as fixing the more glaring issue with global variables. Other commit messages seem to follow the same pattern.

In other words, the commit:

- has minor code readability improvements

- contains no useful message for the future programmer (why is it needed?)

- provides no new functional feature/improvement/business value

- is later found to contain a bug

I find squash/rebase/cherry-pick useful when reviewing my work and deciding what should go in the current pull-request. For example, a refactoring might be postponed for later if it is deemed too time-consuming or irrelevant to the current PR. Or, one can squash logically related commits together, add a useful message and merge them separately. The resulting commit log will still be bisect-able.

For beginners: there's a very good article with tips for Git Commit Messages[0] that helped me have git histories I enjoy reading.

[0] https://chris.beams.io/posts/git-commit/#why-not-how

Re: Fortunately, I don't squash my commits

#138
post #98
post #89

Earlier quoted context omitted.

If git has a concept of branches like mercurial has it would be a lot easier as you can actually see what branch your commits attached to. (there are pros and cons of both approaches - this is a con of the git approach, I'm not knowledge about enough about esoteric details to comment on if git actually made a bad choice or just a compromise)

If you use the standard merge commit message in git, then you can still tell what branch things came from when being merged. As someone that has used both mercurial and git, the trouble with named branches (and having to remember to remove them when merging to master) is one of the reasons why I prefer git.

Knowing the name of the branch is not enough to find the commits. With Git, a branch is just a kind of moving tag on the last commit.

The problem mentioned in this thread is rolling back a feature that was merged. The only solution I know is navigating the log to find the first commit on the branch from which to revert. Don't forget there may have been several merges from and to master, as well as commits shared with other git branches that should not be reverted.

In a Mercurial branch, each commit is tagged with the branch name. Unamed branches, à la Git, are called "bookmarks".

Re: Fortunately, I don't squash my commits

#139

One obvious requirement for bisect to work is that your code builds on every commit - and so does everybody else participating in the git history. Straw poll - do you enforce this? If so, for literally every commit, or do you use partial squashing to maintain this property? While that's certainly a _desirable_ property, I've never really been concerned if, say, the penultimate commit on a PR failed CI. It feels like…

I don't "enforce" this but I do aim for it, and yes, I do partial squashing throughout my workday.

As I work on a feature branch, I'll check in WIP commits as checkpoints, especially at EOD. I don't expect these to pass the full CI suite.

But as the code starts to shape up, I'll unstage all those WIPs and start to group the changes into logical commits. As work progresses, I'll use `git add --patch` to split new lines of code into those existing logical commits. Sometimes I'll split one up, sometimes I'll group two together; it's still flexible and amorphous at this point.

By the time I'm ready to merge upstream, these commits tend to be neat, focused, and functional, and I do check to make sure they pass the relevant tests (though I don't enforce a full CI build here).

Then a rebase from master, push to CI, and then a no-ff merge commit into master to retain both the low-level commits and the ability to easily revert the whole lot.

It might seem like a ton of busywork, but I find that staging atomic commits like this doubles as an excellent line-by-line review of the code I've written. It also forces that review step to happen throughout the process rather than all the way at the end when I've forgotten all that deep context.

Re: Fortunately, I don't squash my commits

#140

Earlier quoted context omitted.

I think you just highlighted one source of the disagreement. Nobody on any team I work with would accept a PR with 3000 lines of change. Each team had a policy, whether formal or informal, to break large work items across PRs so they were more easily reviewable. I would say that 300 lines changed is getting towards the bigger end of what we would accept. If your choice is between 30 or 3000 lines changed per commit,…

Yeah, that’s nice in theory, but in practice the situation is often less ideal. Some features, partially implemented, would break existing functionality if not completed, and merging those upstream prior to total completion is therefore impossible. And on the other side of things, some environments and features require large, systemic changes. This is, surely, an organizational failing, but one that we must adapt to.

Then that's the other half of my point. If you can limit PRs to 300, then merging PRs into a single commit is better. If you can't, then maybe squashing isn't ideal.
Post reply on HN