> Fundamentally any workflow where you have multiple long-lived branches and want to cross-merge between them (e.g. branches for several mostly-independent long-term features, per-client branches) is impossible with rebase

I absolutely agree with you, if you have long lived branches (for example if you have several supported versions of a product and you need to apply security backports etc) you should use merges. We both agree that long lived branches are to be avoided if possible, so although I further agree that merge is better if you want to integrate across branches, I place no value on that in the scenarios I normally have to deal with (no inherent necessity for long lived branches). I want feature branches to be short lived and integration to happen (predominantly) via master, because that offers significant benefits. For the cases where I really needed something from A in B, rebasing or cherry-picking has not been much of a hassle for me in practice. My feeling is that the majority of open source or commercial projects should not have long lived branches (although some undoubtedly need them).

> Merging rather than rebasing also makes bisect (especially automated bisect) much more effective

Not really: there is exactly one method I have found that works well for bisecting robustly, and this applies equally to merge and rebase based commits: you need to to record which commits have passed CI in some way that's trivial to use for bisect skip (e.g. by CI rewriting the head commit's message to indicate it was tested and passed). This is pretty easy to set up and very useful. If you have a merge based workflow you can also use merges commits to a blessed branch (like master) indicate that there are no broken commits (hence my previous question, how do you bisect just on master merges? What I was getting at is that git annoyingly makes it somewhat awkward to say "skip bisect everything that's not a merge into master"). The only other alternative to get robust bisect is to test all commits via CI, but that's typically not practical for anything but small projects.

> Why would that ever be what you want, and how does rebase make it any easier?

See above: in my experience feature branches that get merged (rather than rebased) will be still be full of intermediate broken commits. If you have set up CI sensibly, your merge commits to master should all be non-broken, so this is a good way to skip unreliable commits. Rebase does not make bisect harder or easier (well, other than that it offers a convenient way to tag CI passes in the commit message), this was just to back up my claim that bisect in git is unnecessarily awkward to use.

> if you rebase a branch that has a semantic conflict with master then it's very common for the rewritten commits to not even compile, so you end up with lots of non-compiling commits scattered through your history.

I'd really like to understand better how you or (more likely?) your co-workers end up in this situation and why you think it's related to a merge based vs rebase based workflow. Linu[sx] use a merge flow workflow but insists on a lot of rebasing to clean up local history, for example. I assume what's happening is something like this: someone has done work on a branch that lived long enough to accumulate a larg-ish number and also diverge enough from master to cause conflicts. When they then rebase master into their branch, they don't both to fix the problems at the commit they occurred, and instead just make the head of the branch work again. So you have a bunch of intermittent broken commits and they pollute history. Is that it? But you can have the exact same thing happen with a merge based workflow, firstly because you can never prevent someone from (badly) rebasing master into their private local branch and secondly because it is also quite likely that someone who couldn't be asked to rebased cleanly but uses merges exclusively would merge master multiplie times into their branch without making the merge commits necessarily pass tests, instead they'd probably also concentrate their efforts on making the head of the feature branch pass.

> But if you're happy with having foo deployed on version X and bar deployed on version Y then I'd say that foo and bar are separate systems and the VCS model should reflect that

Kinda. I think the correct way to handle this is make it painful and requite people to duplicate the code in repo. You sometimes need two versions of X in production in parallel for a longer amount of time (e.g. if you are transitioning to a new architecture, and you need to run it as a shadow system for some time first to gather confidence), but it should not be a common thing and I find having both in the working tree works well for these cases.

> In the case of an internal library there most likely isn't a single consistent version, because different components will depend on different versions of it

That's a big no-no in my book. Maybe there is some scale at which this is the lesser evil because otherwise you make it too painful to refactor stuff, but in general I think you should make it painful for people to do this: all versions should be in master and there should be strong pressure to avoid having more than one version of anything in master, certainly for anything but a short transition periods. There are massive downsides to allowing people to use random versions of internal libraries: security concerns, people ending up on some completely outdated version of a lib that then suddenly breaks completely for them and they don't have the time budget to rewrite all their crap, the massive cost of not being able to look into a single repo and know that if you see "import foolib" it's the foolib in the same source tree. I've seen this being accepted practice and it being not accepted practice, and I found the latter brought big benefits.

I don't think it's necessary for master to correspond 100% to production, but it should be quite close. If you land something in master and it's not shipped by the end of the day, I'd say that's generally a bad thing and you should consider reverting.

> I don't think presenting them as a single repository is helpful for that.

It is helpful because all the normal tooling works. You found a problem in production and would like to see where it came from: if production is either a monorepo or a "super-repo" composed of subrepos, you can just bisect. You can just git log the whole thing to understand recent history etc. You can git grep and it will show you all the uses of something, as opposed you have to figure out how to navigate dozens of different things done by different teams. I have worked on projects with monorepos from the start, multi-repos and multi-repo transitioning to mono-repo. Based on that I think monorepos are great, and wherever possible I would strongly encourage their use: everything being in one place and under a single tool is super-super useful. I think a good way to deal with subrepos should preserve a lot of the advantages of monorepos where monorepos are less applicable, so it's very annoying git has terrible support for this.