87
Linus Torvalds on how and when to maintain a clean git history (2009)
(www.mail-archive.com)
Welcome to the main community in programming.dev! Feel free to post anything relating to programming here!
Cross posting is strongly encouraged in the instance. If you feel your post or another person's post makes sense in another community cross post into it.
Hope you enjoy the instance!
Rules
Follow the wormhole through a path of communities !webdev@programming.dev
I think this is dependent on context. Linus is working with a very public repository. Private repositories shared with a small team have different conditions.
What works in my smallish team at my company is:
In non-minimal changesets, I would miss information/documentation about individual logical changes that make up the changeset. Commit separation that is useful for review will also be useful for history.
I prefer a deliberate, rebase- and rewrite-heavy workflow with a semi-linear history. The linear history remains readable, while allowing sum-of-parts changesets/merges.
It's an investment, but I think it guides into good structuring and thoughts, and whenever you look at history, you have more than a squashed potential mess.
Squash-on-merge is simpler to implement and justify, of course. Certainly much better than "never rebase, never rewrite, always merge", which I am baffled some teams have no problem doing. The history tree quickly becomes unreadable.
What I like with squash on merge is I don’t need to worry about shit my coworkers make. My coworkers can have terrible git disciplines, and the master branch is still clean.
This is the reality. You'll spend most of your time working with people of varying SCM skill levels, and spread all the way across the spectrum. Squash commits combined with centralised auditing (GHE, GitLab, etc) add the necessary rail to keep a clean history on main and to make building-block change sets easily revert-able.
In my decades working on large teams of engineers, the need to identify changes by wip/interim commits has never been terribly useful for the reason you describe: everyone has different git hygiene procedures and most corps don't give a tiny little shit about maintaining that level of hygiene unless you're white room / highly regulated.
And if you do want that level of depth you can go find the PR/MR in the central source where the revision history of the dead branch is often sustained (unless you configure it not to)
But yeah, I call YAGNI a lot on git history purists to this day. It's a huge amount of effort and coordination to retain a tiny amount of value that is 50/50 gonna be useful depending on the git hygiene of the person who wrote it. Save your efforts and just read the damn code.
There are CI tools like Prow and Tide which make it possible to use squash by default while still giving control to developers who want to use a different merge strategy.
Also when using
git bisect
aka "The Alaskan Wolf Fence Method" on nasty bugs e.g. causing concurrency or UB issues.It is also a potential downside of rebasing that it can (sometimes) invalidate interim tests.
Not really because I've never seen a setup that requires every commit in a branch to compile and pass tests. Only the merge commit needs to.
Also if your PR is so big that it would be painful to bisect within it, then it should be broken into smaller PRs.
It's usually possible to find this by navigating back to the PR which you can find referenced in the squash commit.
I guess this might be a larger problem for codebases not following a trunk-based approach, where PRs grows to very large sizes before going into the mainline branch.
Review iterations mean messy comits there though. And full documented history in Git seems preferable because you don't have to switch tools, and for persistence and robustness too, in case of repo/review platform changes (switching platforms etc).
That sounds like pretty much exactly what we did at my last job, and it worked pretty well IMO. The individual commits in a PR didn't ever matter. I don't even think we used them for code review, except if it came up for review a second time after rework. In that case, we were able to just look at the new commit to see if the right changes were made.
And we definitely avoided basing off each other's branches. We had to do it a few times. The only times it went well was when the intent was to merge the child branch into the feature branch. If they were actually separate tickets (and the second relied on the first) it was generally chaotic. But sometimes, it was just necessary.
If ‘—first-parent’ was the default way that git log worked, I don’t think we’d even be having this argument over how to merge branches.
In my opinion, the best strategy is to always use a merge commit, and then when viewing master, always use —first-parent which will ONLY show commits on master. This gives you:
The problem is just the default log view of git and tools.