Skip to content

Comment on Resistance Against Git Merge Hell (2015)

Comments

This is a very short advert for rebasing your personal branch.

People seem to have very strong opinions about this. I don't much, either way; I have to use the gerrit flow at work, which mandates a lot of rebasing, and in general I prefer not having a commit which only records a merge. Some people seem to want highly detailed tracking of what an individual developer has done in their personal checkout. I will only note that the act of observing changes what is observed.

I'm an ADHD weirdo who will mix six issues in my personal branch, and use rebase, cherry-pick, and in a pinch, difftool to carve out clean patches when something is good and ready. If I were to give people an honest history of what was actually going on in my repo, and I used merge and revert to really keep it all when I cut a patch, it would be a forensic nightmare.

There is some benefit to my tendency to do experimental work in a branch where I've started to work on something else. In that mudpit, I find serendipitous solutions to multiple problems, and I find incompatibilities between desired changes. And when multiple issues have overlapping changes, there's usually an optimal ordering of which to address first -- and that's not always obvious.

I greatly prefer to do all the forensic work up-front, to make a clean history with a cogent story in the commit message. When I see other people's messy histories with every merge/revert, I need to do that forensic work every time I go back in history; without the benefit of recent first-person experience.

Same.

But there's a difference between a few separate things here:

- assembling your own chaotic actual work into cogent commits using interactive rebase and friends (but not moving the base, e.g. git rebase -i --keep-base): good and necessary. people doing reviews should also review commit history, not just a megadiff of all changes.

- rebasing in the sense of "moving the base upon which your work is based", which for unfortunate historical reasons uses the same command: this is rarely ever useful, despite how much people seem to enjoy doing it. replace this with a no-op in nearly all cases; and in the few cases where you might want to (the upstream you want to integrate with changed incompatibly) merging a recent version tag into your topic is better.

- rebasing in the sense of "as a maintainer adding completed work to an integration branch like master, rebasing the commits atop the integration branch rather than simply merging": this is actively harmful (as is "squash merging", which is this _plus_ destroying the commits crafted in point 1) and is also unfortunately what most people seem to mean.

why is merging upstream changes into my branch better than rebasing my branch on top of those changes?

and for the last one, how is the maintainers rebasing my branch onto master any different from me doing it before submitting my changes? and why is it actively harmful? (i agree with squash merge, that's not useful. in a project that does that i have resorted to carefully crafting my commits such that each commit can be a separate PR so they can't be squashed)

The dichotomy isn't between merging upstream changes into your branch vs. rebasing your branch on top of them. It's between doing one of those things and doing nothing, and doing nothing is the better (and easier!) option here.

(When I mention it above, it's for a case like "the branch is several months old, two versions have been released in that time, and I'm revisiting it to finish it up now", and in that case you just merge the latest version tag (tag, not branch) before continuing. It's a very specific thing.)

The main reason I see in practice that people do something, rather than nothing, is because someone is insisting on clicking the button on Github's web interface to merge a topic, which insists the merge be trivial. Faced with this sort of (bad) maintainership, the topic author has to choose between rebasing the topic, losing some useful graph properties (independence of unrelated branches) in the process, or merging upstream just before upstream merges them, which is incredibly stupid-looking (and very annoying when reading history, nothing is in the place you look for it).

Usually they choose the former, or have it chosen for them. And now the branch, topologically, includes a bunch of unrelated things, and if you have any sort of triaging integration, QA, maintenance versions, or just want to read the graph to see what topic depends on what other topic, this is broken on this no-longer-independent topic. (In very bad cases, you might need to acquire a lock on a human, precisely what the whole system is meant to avoid.)

The best thing is just to do nothing. If you're using Git as originally intended, your branch became patches anyway, it doesn't have a history. (As the maintainer receiving them, you just apply the patches to the latest version tag, pretty much, unless it's in series with something else, or it's a bugfix (always apply a bugfix commit directly onto the commit that introduced the bug. it can then be merged into any maintenance branch or other branch containing the bug. the word "cherry-pick" need never cross your keyboard.)) But a bad webapp broke things for a lot of people, and it was worked around in various ways.

i don't understand why the difference between tag and branch matters? why should i merge a tag and not the branch head?

as a maintainer, why should i have to make the effort of merging a contribution myself, instead of asking the contributor to make sure that their changes can be cleanly merged?

i agree that merging upstream before they merge me seems odd, but that suggests that rebasing is the only way to go. or how else can i ensure that my code can be merged cleanly without creating extra work for the maintainer?

Integrating things is 100% of a maintainer's job. As someone who wrote one topic, you don't have any insight into the arbitrarily many other topics that coincidentally were written at a similar time to yours; as the maintainer who integrates all submitted topics, you have read and are familiar with all of them.

As a topic author, you have no such responsibility to deal with arbitrary other topics you had nothing to do with; this holds in an opportunistic open source contribution, in a workplace, anywhere. There's someone who reads all the patches and has a working knowledge of them: the maintainer. And if they ask you to do their job for them (which you will likely do poorly, not being the maintainer), they are a _bad_ maintainer. (And if software encourages this it's bad software; I'm not attributing this to maintainer malice or something, most people are just taking their cues from common bad software like Github's website.)

why should i merge a tag and not the branch head?

Because a branch head is an arbitrary collection of things and a version is a specific supported collection of things.

i am going to have to disagree to an extent. while on the face of it this makes sense. realities are often different.

as a FOSS contributor, making the work of a maintainer easier can mean the difference between my submission being accepted or rejected. if i want to push a change upstream it is therefore in my interest to not rely on the maintainer to do that work. especially not if that maintainer is a volunteer. not every contributor is going to do that, nor should every contributor do that. only those that have sufficient experience to work at the level of the maintainer, but simply are not the designated maintainer because it is not their project.

the same holds true at work. i expect everyone in a team to be familiar with the whole project and be capable of doing the work of the maintainer, at least everyone at senior level. whether they end up doing it or not. having a single person be responsible for all merges is an idea that i do not agree with. among other reasons it causes the designated maintainer to become a gatekeeper preventing others from developing that skill and step up to do that kind of work. whether it is practical for the maintainer to do all the integration then is a matter of their workload.

the suggestion that only the designated maintainer can do a good job at merging is even somewhat patronizing.

on tag vs branch, this seems to me only makes sense if we are developing against a release branch. a development branch often does not have release tags. the "releases" in a development branch are the merges of new feature branches. if each feature is merged on completion then there should not be any work in progress commits anyways. even if rebasing is used, those commits will be pushed all at once, so HEAD is always at a completed feature.

i think that these are simply different approaches to development, and it can't be said that one is better than the other. it comes down to preference. i favor an environment where everyone can step up to contribute to the extent of their experience and knowledge over an environment where my capacity is limited by my role.

being told that i am not capable of doing something because it is not part of my role is not something i want to hear ever.

I think you're having a strong emotional reaction to something unclear, which is outside the scope of what I'm willing to discuss further. Thanks for the comment chain, however, I do appreciate it.

that's a pity because i think that is the area where i could learn something new. but thank you for bearing with me so far.

Yeah, I have a love/hate relationship with squash. On one hand, I think everybody misses an autocommit hook once and a while and in Python its not uncommon to have "black" commits interspersed throughout it all. Love because it's convenient to squash all that noise with a single click; hate because I want a surgical squash where tooling/typo commits are automatically squashed into the parent.

commit --fixup=HEAD~1 (or any form of reference to the commit to fix up); rebase -i --autosquash later

my "reroll" alias is "reroll = rebase --interactive --keep-base --rebase-merges"; but the --rebase-merges is less useful to most people (I do maintainer operations as well, and it's sometimes useful there)

autosquash is on in the config, though i should really add it to the alias for redundancy, it's an alias and length doesn't matter.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.