For me, git becomes unintelligible when there's a crazy train-track map of branching and merging.
So I usually do whatever I can to keep a single straight-line master history.
I branch off for a task, then after a while my branch isn't joined at the tip of master. So I rebase locally until it is. Then when the PR happens, master gets my changes added to the top, with no extra noise from merge commits.
Even if the local-rebase workflow is slightly more complicated, the payoff is a really clean history, making future reasoning about branches much easier. Not to mention merge conflicts are easier to solve when you rebase early & often.
In my observation, the problem comes when there is a "merge main to the feature branch before merging"
This step is when newbies get confused because the diffs are the wrong way, and I've seen people often losing merge hunks from master when there are conflicts which can be disastrous (not a git issue, I've seen people do the same in SVN).
The proper solution IMO would be for git to have a "merge --reintegrate" which would do the opposite merge: take the main branch and merge the current feature branch to it... after success, you have a new feature branch.
This is why I also prefer rebase and cleaner history (but a common mistake here is to squash after PR approval... that should be done before, not after).
> For me, git becomes unintelligible when there's a crazy train-track map of branching and merging.
Consider finding peace by inverting your perspective: if your organization’s development process (for whatever reason, many of them legitimate) involves a crazy train-track of features being developed in parallel, isn’t it great that you're all at least using something that can keep track of it?
For a small team that should be unnecessary.
Frequent rebasing when you’re on a side branch of development is smart, but doesn’t conflict with my point.
Also, really, who looks back into the depths of history? There’s a reason a lot of backup schemes rotate a set of tapes over 30 or even 14 days. For that reason I am not a fan of rewriting history for “clarity”: I consider it wasted effort.
For the same reason I don’t care about branches for explorations that turned out to go nowhere — just mark the head abandoned and stop worrying about it.
I think this is a pretty sensible approach. Git feels like one of those tools where you are given a lot of power but it's your responsibility to use it in a sensible way. A bit like with Excel or spreadsheets. You can do a lot but you can also make a big mess pretty quickly.
So I usually do whatever I can to keep a single straight-line master history.
I branch off for a task, then after a while my branch isn't joined at the tip of master. So I rebase locally until it is. Then when the PR happens, master gets my changes added to the top, with no extra noise from merge commits.
Even if the local-rebase workflow is slightly more complicated, the payoff is a really clean history, making future reasoning about branches much easier. Not to mention merge conflicts are easier to solve when you rebase early & often.