Git is super mid. It’s a shame that Git and GitHub are so dominant that VCS tooling has stagnated. It could be so so so much better!
Git is super mid. It’s a shame that Git and GitHub are so dominant that VCS tooling has stagnated. It could be so so so much better!
Rebase does not make sense in Mercurial because it has the concept of fixed branches. A commit is permanently linked to the branch on which it was made. So you are supposed to use merges.
Same with force-pushing.
Believe me, I tried to have an open mind about it. Then one day I was getting ready to go on a work trip with a half-finished feature on my work laptop, and realised there was simply no in-model way for backing that wip up to the repo. If I lost my laptop, I lost the progress. mercurial-scm fails at SCM.
That is because you have this notion of a "clean history", (which IIUC prevented you from making this permanent wip commit) which in reality does not have a lot of use. For most project, "useful history" or "real history" is better than a "clean" history.
That is what mercurial caters to.
This is your opinion, so I'm compelled to point out it's not the consensus opinion.
not sure what you mean to say, but for thoroughness' sake, no: git and mercurial concepts are not interchangeable, with git having mostly an inferior model.
To give examples: git has no concept of branching (in the way every VCS but Git uses the term). A branch in git is merely a tag on the tip of a series meant to signify that all ancestors belong to the same lineage. This comes with the implication that this lineage information is totally lost when two branches merge (you can't tell which side of the merge corresponded to which lineage). The ugly and generalised workaround is to abuse commit message (e.g. "merge feat-ABC into main") to store an essential piece of the repository history that the VCS cannot take.
Another example is phasing: mercurial records at commit level whether it was exchanged with others or not. That draws a clean line between the history that's always safe to rewrite, and which that is subject to conflicting merges if the person you shared those commits with also happened to rewrite them on their end.
> Then one day I was getting ready to go on a work trip with a half-finished feature on my work laptop, and realised there was simply no in-model way for backing that wip up to the repo. If I lost my laptop, I lost the progress. mercurial-scm fails at SCM.
Sorry to be blunt, but that's a skill issue: hg is no different than every other VCS in that regard. If you want your WIP changes to leave your laptop, you've got to push them somewhere, just like you would in git.
Permanently, to a single branch in un-buildable form. So useful.
- rebasing in Mercurial simply means chopping a subtree off of the history and re-attaching it to a different parent commit. In that sense, rebasing is a very useful and common history-rewriting operation. In fact, it's even simpler and more powerful/versatile than in git, because mercurial couldn't care less if the sub-tree you are rebasing belongs to a branch or not: it's just a DAG. It gets transplanted from A to B. A may or may not be your checked commit, or be the tip of a branch, doesn't matter.
- that mercurial requires a configuration toggle before rebasing can be used (i.e. that the user need to enable the extension explicitly) is a way to encourage interested users to learn their tool, and grow its capabilities together with their knowledge. It's opinionated, it may be too much hand-holding for some, but there is an elegant simplicity in keeping the help pages and autocomplete commands just as complex as the user can take it.
Sure, but since commits have a branch attribute attached to them, "rebasing" does not appear to be "first class". It is something that has to be bolted on with an extension.
> because mercurial couldn't care less if the sub-tree you are rebasing belongs to a branch or not
IIUC Git also does not care much about the rebase target being a "branch".
I agree that Mercurial provides more value out of the box than git because it preserves branch info in commits.
I can live with Git because Git is "enough" if used carefully and after coming to terms with the non-intutive UI.
Again, that's orthogonal: you may or may not use "named branches" (the kind of which persists at commit level), rebasing works either way consistently and predictably.
> It is something that has to be bolted on with an extension.
The extension ships in core, UX is why it's not enabled by default.
> IIUC Git also does not care much about the rebase target being a "branch".
Indeed, it's just that things likely get weird (for no good reason) when you don't (detached head, "unreachable" commits)
> I can live with Git because Git is "enough" if used carefully and after coming to terms with the non-intutive UI.
That's our sad state of affairs. JJ helps a bit, though.
IOW, what do you know that nobody else does?
Of course, products also can win market dominance for reasons external to the product's quality itself (marketing, monopoly lock-in, other network effects, consumer preferences on something other than product quality itself, etc).
I personally went from .latest.latest.latest.use.this (naming versions as latest) to tortoise SVN (which I struggled with) to Git (which I also was one of those "walk around with a few memorised commands" people that don't actually know how to use it) to reading the fine manual (well 2.5 chapters of it) to being an evangalist.
I've tried Mercurial, and, frankly, it was just as black magic as Git was to me.
That's network effects.
But my counter is - I've not found Mercurial to be any better, not at all.
I have made multiple attempts to use it, but it's just not doing what I want.
And that's why I'm asking, is it any better, or not.
But what I will point out, for better or worse, people are now looking at LLMs as Git masters, which is effectively making the LLM the UI which is going to have the effect of removing any assumed advantage of whichever is the "superior" UX
I do wish to make absolutely clear that I personally am not yet ready to completely delegate VCS work to LLMs - as I have pointed out I have what I like to think of as an advanced understanding of the tools, which affords me the luxury of not having an LLM shoot me in the foot, that is soley reserved as my own doing :)
The thing is, to understand which one is actually better, you would have to give the same amount of investment in the second tool, which is not something most people are willing to do if the first tool is "good enough". That's how Python became the default programming language; people don't miss features they do not understand.
So at least for me, git was clearly better.
Google and Meta don’t use Git and GitHub. Sapling and Phabricator much much better (when supported by a massive internal team)
You can visit any resource about git and branches will have a prominent role. Git is very good at branches. Mercurial fans will counter by explaining one of the several different branching options it has available and how it is better than the one git has. They may very well be right. It also doesn't matter, because the fact that there's a discussion about what branching method to use really just means Mercurial doesn't solve branches. For close to 20 years the Mercurial website contained a guide that explained only how to have "branches" by having multiple copies of the repository on your system. It looks like the website has now been updated: it doesn't have any explanation about branches at all that I can find. Instead it links to several different external resources that don't focus on branches either. One of them mentions "topic", introduced in 2015. Maybe that's the answer to Git's branching model. I don't care enough to look into it. By 2015 Git had long since won.
Mercurial is a cool toolbox of stuff. Some of them are almost certainly better than git. It's not a better product.
I think there are (or perhaps were) some product issues regarding the specifics of various workflows. But at least some of that is simply the inertia of entrenched workflows and where there are actual downsides the (IMO substantial) advantages need to be properly weighed against them.
Personally I think it just comes down to the status quo. Git is popular because it's popular, not because it's noticably superior.
I love jumping in discussions about git branching, because that's a very objective and practical area where git made the playing field worse. Less and less people feel it, because people old-enough to have used branch-powered VCSes have long forgotten about them, and those who didn't forget are under-represented in comparison to the newcomers who never have experienced anything else since git became a monopoly.
Anyhow, let's pick django as a project that was using a VCS with branches before moving to git/github, and have a look at the repo history: https://github.com/django/django/commits/stable/6.0.x
Yes, every commit is prefixed with the branch name. Because, unlike mercurial, git is incapable of storing this in its commit metadata. That's ridiculous, that's obscene, but that's the easiest way to do it with git.
You can just not move branches. But once you can do it, you will like it. And you are going to use
git branch --contains COMMIT
which will tell you ALL the branches a commit is part of.Git's model is clean and simple, and makes a whole lot of sense. IMHO.
I'm old enough to have used SVN (and some CVS) and let me tell you branching was no fun, so much that we didn't really do it.
Maybe branching was an important reason to adopt git but now we'd probably be ok with a vcs that doesn't even support them.
A middle ground is small PRs where people are constantly rebasing to the tip of main to keep conflicts to a minimum
Git does not have such concept. That is a trade off and that trade off works great for projects managed like Linux kernel. But for smaller projects where there is a limited number of people working, the information preserved by mercurial could be very valuable.
It also had some really interesting ideas like change set evolution, which enabled history re-writing after a branch has been published. Don't know its current status and how well it turned out to be..
If you rebase the feature branch into the main branch THEN follow it up with the merge commit that records the branch name you store the branches (that have been made a part of main) and can see where they are in your log
Mercurial's notes can become cumbersome if there are a large number in the repository, but, obviously, humans can sort that out if it gets out of hand
That’s it. That’s why git won, you could put up open source libs with one for free and not the other.
Which is extra funny as the centralized service was the most important part of decentralized version control.
I've often thought this about github
So git /did/ have something better than Mercurial after all, it was a 3rd party, but it still meant that it was massively better than Mercurial.
Are we back to "programming language X is slow" assertions? I thought those had died long ago.
Better algorithms win over 'better' programming languages every single time. Git is really simple and efficient. You could reimplement it in Python and I doubt it would see any significant slowness. Heck, git was originally implemented as a handful of low level binaries stitched together with shell scripts.
It’s amusing you call Git fast. It’s notoriously problematic for large repos such that virtually every BigTech company has made a custom rewrite at some point or another!
For everything I've ever done, git was practically instant (except network IO of course). It's one of the fastest and most reliable tools I know. If it isn't fast for you, chances are you are on a slow Windows filesysrem additionally impeded by a Virus scanner.
The mere fact that Git is unable to handle large binary files makes it an unusable tool for literally every project I have ever worked on in my entire career.
Takes 21 seconds on my work laptop, indeed a corporate Windows laptop with antivirus installed. Majority of that time is simply network I/O. The cloned repository is 276 MB large.
Actually checking the kernel out takes 90 seconds. This amounts to creating 99195 individual files, totaling 2 GB of data. Expect this to be ~10 times faster on a Linux file system.
So what's your problem?
If you’d like to argue that version control should be centralized, shallow, and sparse by default then I agree.
I get your sentiment, but I know how working with e.g. SVN feels. Just doing "svn log" was a pain when I had to do it. The "distributed" aspect of DVCS doesn't prevent you from keeping central what you need central. E.g. you can have github or your own hosting server that your team is exchanging through.
The main point of distributed is speed and self-sufficiency which is a huge plus. E.g. occasional network outages and general lack of bandwidth are still a thing in 2026 (and remain so to some extent for the foreseeable future).
Now, could git improve and allow some things to be staged/tiered/transparently cached better? Probably, and that's where some things like LFS come in. I don't have a large amount of experience in this field though, because what I work with is adequately served by the out-of-the-box git experience.
Later on I also changed some of the algorithms to faster ones, but their impact was much lower than the language change.
Which is only to say: that rewrite away from python story can also work to show python doing its job. Risk reduction, scaffolding, MVP validation.
A bunch of low level binaries stitched together with shell scripts is a lot faster than python, so not really sure what the point of this comparison is.
Python is an extremely versatile language, but if what you're doing is computing hashes and diffs, and generally doing entirely CPU-bound work, then it's objectively the wrong tool, unless you can delegate that to a fast, native kernel, in which case you're not actually using Python anymore.
One of the reason mercurial lost the dvcs battle is because of its performance - even the mercurial folks admitted that was at least in part because of python
No, it's always been true. It's just that at some point people got bored and tired of pointing it out.
Python is absurdly slow - every method call is a string dict lookup (slots are way underused), everything is all dicts all the time, the bytecode doesn't specialize at all to observed types, it is a uniquely horrible slow language.
I love it, but python is almost uniquely a slow language.
Algorithms matter, but if you have good algorithms, or you're already linear time and just have a ton of data, rewriting something from a single-threaded Python program to a multithreaded rust program I've seen 500x speedups, where the algorithms were not improved at all.
It's the difference between a program running overnight vs. in 30 seconds. And if there are problems, the iteration speed from that is huge.
Doesn't the Python VM have inline caches? [0]
It didn't used to.
EDIT: python 3.11+: https://peps.python.org/pep-0659/
To be fair, Python as implement today is horribly slow. You could leave the language the same but apply all the tricks and heroic efforts they used to make JavaScript fast. The language would be the same, but the implementations would be faster.
Of course, in practice the available implementations are very much part of the language and its ecosystems; especially for a language like Python which is so defined by its dominant implementation of CPython.
But a lot of the monkey-patching kind of things and dynamism of python also means a lot of those sorts of things have to be re-checked often for correctness, so it does take a ton of optimizations off the table. (Of course, those are rare corner cases, so compilers like pypy have been able to optimize for the "happy case" and have a slow fall-back path - but pypy had a ton of incompatibility issues and now seems to be dying).
The real reason is that it is a deliberate choice by the CPython project to prefer extensibility and maintainability to performance. The result is that python is a much more hackable language, with much better C interop than V8 or JVM.
That's often true, but not "every single time".
The reason that some more modern tools, like jj, really blow git out of the water in terms of performance is because they make good choices, such as doing a lot of transformations entirely in memory rather than via the filesystem. It's also because it's written in a language that can execute efficiently. Luckily, it's clear that modern tools like jj are heavily inspired by mercurial so we're not doomed to the ux and performance git binds us with.
Apparently I belong to the same club -- when I'm writing AWK scripts. (Arrays are hashmaps in a trenchcoat there.) Using hashmaps is not necessarily an indictment you apparently think it is, if the access pattern fits the problem and other constraints are not in play.
> It's amazing how much we've brainwashed folks to focus on algorithms and lose sight of how to actually properly optimize code. Being aware of how your code interacts with cache is incredibly important.
By the time you start worrying about cache locality you have left general algorithmic concerns far behind. Yes, it's important to recognize the problem, but for most programs, most of the time, that kind of problem simply doesn't appear.
It also doesn't pay to be dogmatic about rules, which is probably the core of your complaint, although unstated. You need to know them, and then you need to know when to break them.
I'll take it back a little bit, because there _is_ in fact a lot of algorithmically inefficient code out there, which slows down everything a lot. But after getting the most obvious algorithmic problems out of the way -- even a log-n algorithm isn't much of an improvement to a linear scan, if n < 1000. It's much more important to get that 100+x speedup by implementing the algorithm in a straightforward and cache friendly way.
I doubt it wouldn't be significantly slower. I can't disprove it's possible to do this but it's totally possible for you to prove your claim, so I'd argue that the ball is in your court.
Yes we are? The slow paths of mercurial have been rewritten in C (and more recently in Rust) and improved the perf story substantially, without taking away from the wild modularity and extensibility hg always had.