Let me answer your points directly and clearly:
Our issue is with long-term sidebranches -- the kind where you've got a few people working a sidebranch that splits from master, we develop for a few months, and when we reach a milestone we sync the two up.
You usually do not want to let your branches unsynced for months.
Your feature branch has branched off of something depending on your workflow; let's just call it master
for the sake of simplicity. Now, whenever you commit to master, you can and should git checkout long_running_feature ; git rebase master
. This means that your branches are, by design, always in sync.
git rebase
is also the correct thing to do here. It is not a hack or something weird or dangerous, but completely natural. You lose one bit of information, which is the "birthday" of the feature branch, but that's it. If someobody finds that to be important, it could be provided by saving it somewhere else (in your ticket system, or, if the need is great, in a git tag
...).
Now, IMHO, the natural way to handle this is, squash the sidebranch into a single commit.
No, you absolutely do not want that, you want a merge commit. A merge commit also is a "single commit". It does not, somehow, insert all the individual branch commits "into" master. It is a single commit with two parents - the master
head and the branch head at the time of the merge.
Be sure to specify the --no-ff
option, of course; merging without --no-ff
should, in your scenario, strictly be forbidden. Unfortunately, --no-ff
is not the default; but I believe there is an option you can set that makes it so. See git help merge
for what --no-ff
does (in short: it activates the behaviour I described in the previous paragraph), it is crucial.
we're not retroactively dumping months of parallel development into master's history.
Absolutely not - you are never dumping something "into the history" of some branch, especially not with a merge commit.
And if anybody needs better resolution for the sidebranch's history, well, of course it's all still there -- it's just not in master, it's in the sidebranch.
With a merge commit, it is still there. Not in master, but in the sidebranch, clearly visible as one of the parents of the merge commit, and kept for eternity, as it should be.
See what I've done? All things you describe for your squash commit are right there with the merge --no-ff
commit.
Here's the problem: I work exclusively with the command line, but the rest of my team uses GUIS.
(Side remark: I almost exclusively work with the command line as well (well, that's a lie, I usually use emacs magit, but that's another story - if I am not in a convenient place with my individual emacs setup, I prefer the command line as well). But please do yourself a favour and try at least git gui
once. It is so much more efficient for picking lines, hunks etc. for adding/undoing adds.)
And I've discovered the GUIS don't have a reasonable option to display history from other branches.
That is because what you are trying to do is totally against the spirit of git
. git
builds from the core on a "directed acyclic graph", which means, a lot of information is in the parent-child-relationship of commits. And, for merges, that means true merge commits with two parents and one child. The GUIs of your colleagues will be just fine as soon as you use no-ff
merge commits.
So if you reach a squash commit, saying "this development squashed from branch XYZ", it's a huge pain to go see what's in XYZ.
Yes, but that is not a problem of the GUI, but of the squash commit. Using a squash means you leave the feature branch head dangling, and creating a whole new commit into master
. This breaks the structure on two levels, creating a big mess.
So they want these big, long development-sidebranches merged in, always with a merge commit.
And they are absolutely right. But they are not "merged in", they are just merged. A merge is a truly balanced thing, it has no preferred side that is merged "into" the other (git checkout A ; git merge B
is exactly the same as git checkout B ; git merge A
except for minor visual differences like the branches being swapped around in git log
etc.).
They don't want any history that isn't immediately accessible from the master branch.
Which is completely correct. At a time when there are no unmerged features, you would have a single branch master
with a rich history encapsulating all feature commit lines there ever were, going back to the git init
commit from the beginning of time (note that I specifically avoided to use the term "branches" in the latter part of that paragraph because the history at that time is not "branches" anymore, although the commit graph would be quite branchy).
I hate that idea;
Then you are in for a bit of pain, since you are working against the tool you are using. The git
approach is very elegant and powerful, especially in the branching/merging area; if you do it right (as alluded to above, especially with --no-ff
) it is by leaps and bounds superiour to other approaches (e.g., the subversion mess of having parallel directory structures for branches).
it means an endless, unnavigable tangle of parallel development history.
Endless, parallel - yes.
Unnavigable, tangle - no.
But I'm not seeing what alternative we have.
Why not work just like the inventor of git
, your colleagues and the rest of the world do, every day?
Do we have any option here besides constantly merging sidebranches into master with merge-commits? Or, is there a reason that constantly using merge-commits is not as bad as I fear?
No other options; not as bad.
git log master+bugfixDec10th
to be the breaking point :-/merge --no-ff
what you want? Onmaster
itself you have one commit that describes what changed in the branch, but all of the commits still exist and are parented to HEAD.