
The Real Difference Between Git and Mercurial - xentac
http://xentac.net/2012/01/19/the-real-difference-between-git-and-mercurial.html
======
rbehrends
In a way, the Git vs. Mercurial debate feels like vi vs. emacs all over again.
Hardcore emacs users never quite understood the appeal of a modal editor;
hardcore vi users couldn't quite understand how people lived with an input
model that constantly moved their fingers off home row.

Usability preferences can differ greatly by person. Another example is
information retrieval in a personal database or help system. Some people
prefer going through a search engine; others find a directory-based approach
more intuitive (and forcing one approach on the other group does not improve
productivity).

Similarly, having a unified set of tools for all kinds of version control
problems is not necessarily an unalloyed good for everyone. Dealing with
persistent history does not necessarily warrant the same approach as dealing
with work-in-progress changes. For some users, it may be easier and better to
consolidate both problems, for others, it can be more productive to keep them
separate.

The Mercurial designers, for better or worse, have decided to separate
persistent history changes from local work-in-progress changes, the latter
being done via Mercurial Queues. That has the disadvantage that you need to
learn a separate set of tools; it has the advantage that unfinished stuff does
not pollute your repository's history and that you can tailor both sets of
tools to their respective needs (whether they are in fact properly tailored is
a different debate).

I suspect that, similarly, Bzr's one-directory-per-branch approach (inherited
from Arch) is an easier mental model for some users to deal with than either
git's or Mercurial's DAG-based approach, despite many git and Mercurial users
not understanding the appeal.

Incidentally, both examples that you list (git stash and git commit --amend)
are easily handled by Mercurial Queues. You don't need several separate
extensions. (Obviously, some people do prefer special extensions because they
reflect their mental models better or optimize common workflows.)

~~~
gcp
_Incidentally, both examples that you list (git stash and git commit --amend)
are easily handled by Mercurial Queues._

No, no, no. Let's get this myth out of the way that Mercurial Queues can
somehow be part of a _modern_ revision control system. Mercurial Queues are a
simple stack of _DUMB_ patches. The only reason why they are used is that
often that is all you really need.

But the moment you need any kind of advanced functionality, such as say,
something unimaginable like rebasing a Mercurial Queue against an upstream
change that conflicts, you are suddenly dealing with tools that are no more
advanced than diff and patch, and most certainly don't support any advanced
operation on the patches that they juggle. I heard some modern tools like CVS
support an advanced operation they call "merge". I'm still waiting until
someone implements a usable version for Mercurial Queues.

So please, the moment you use Mercurial Queues in an argument, you've lost.
Mercurial Queues are diff and patch in a fancy packaging, but with all the
limitations of those. If your revision control system needs them to have the
same functionality as another, you've lost.

The dirty secret is this: Mercurial users use Mercurial Queues because
Mercurial got branching wrong. It's as simple as that.

 _In a way, the Git vs. Mercurial debate feels like vi vs. emacs all over
again. Hardcore emacs users never quite understood the appeal of a modal
editor; hardcore vi users couldn't quite understand how people lived with an
input model that constantly moved their fingers off home row._

Emacs and vi both have their strengths and weaknesses. I've used both over the
years sometimes trending to one or the other depending on the circumstances.
As I've gotten to know both better, I understand the relative strengths better
and know when it makes sense to use either. They're both good tools, and will
both keep big user-bases for the foreseeable future.

Can I say the same about Mercurial and Git? That's a resounding NO.

I can accept that some holy wars can't have winners. But we should also be
willing to accept that some tools can be just _better_ than others.

~~~
azakai
I disagree (and we have already had this argument I think ;). Mercurial Queues
are different from git branches, not just "worse".

With MQ, and especially with a versioned MQ directory, you have a great
workflow for review-ready patches. And review-ready patches are what you are
creating, after all, in most large projects: You do some work, you refactor
that work N times, then you submit it for review, then you repeat that M times
until it passes review and you push it to trunk.

With MQ, you can see the entire history of your set of patches. If you have a
queue and you refactor two patches into one, you can go back in history in the
versioned MQ dir to see what things were like before that. That is useful,
because again, the "product" you are creating is a set of review-ready
patches. So versioning the patches makes sense.

I understand that you can use git branches very effectively - I use git
myself, not mercurial. However, if you use git to rewrite the history of your
branch as part of your development process, AFAIK there is no way to go back
in time and instantly see what your branch looked like at time K in the past,
because history has been rewritten.

Rewriting history is great - you don't want to submit for review a huge list
of patches that include bugfixes as you were working. You want to submit the
final patches. But what if you do need to look at the state your work was in
the past, before you rewrote history?

~~~
gcp
_Mercurial Queues are different from git branches, not just "worse"._

Mercurial Queues are bolted onto Mercurial and you can often see the cracks.
Merging is my pet peeve which I've already mentioned. I think it's not
controversial to say that it's a pain? I think that, in true Mercurial
tradition, there's an extension in development for it now...

So why do we need to deal with this bolt-on and the resulting pain? Because
Mercurial branches aren't good enough, or at least can't adequately support
the workflow people need. Compare the amount of Mercurial users using mq to
the amount of git users using quilt or equivalents.

I don't disagree with the abstract principle of operation of Mercurial Queues.
I disagree with Mercurial as it is.

 _However, if you use git to rewrite the history of your branch as part of
your development process, AFAIK there is no way to go back in time and
instantly see what your branch looked like at time K in the past, because
history has been rewritten._

1) It may be possible to do this with reflog. I don't know for sure because I
haven't needed to do this yet. (I think the main problem is that by default
reflog purges old history at some point)

2) You can simply create more branches as you go. i.e. replace qcommit by git
checkout -b etc. Then push the final ones.

~~~
azakai
I agree that MQ has some pain points. Merging, as you say.

But git branches also have pain points. The only way to save history from
before you rewrite history is to save branches, as you mentioned - but doing
that manually every time you rewrite history is a pain.

~~~
sofal
Actually the history is kept by reflog for 90 days by default I believe (and
you can change this). This covers the large majority of cases where you have
completely screwed up your history rewriting and need to go back to a state
you remembered you were in.

~~~
azakai
90 days, or any other finite amount, isn't a solution for what I want. I don't
need a quick undo if I erred, I want to see the development process that went
into creating some code that later was committed into trunk (and perhaps much
much later turned out to have a bug).

~~~
sofal
This probably comes down to discipline, meaning that when you rewrite history
in git you may want to take care that you're keeping the useful bits of it and
not just squashing everything down to get fat feature-rich commits. Do you
think that the history-rewriting tools in git are too often abused by the
average git user such that the use of git in general has the problems you've
described?

~~~
azakai
Not sure if I agree as to what is normal and what is abuse here. If you write
some feature in a branch, and have various commits for bugfixes as you go, you
might want to merge those little commits away for review as well as for
bisection purposes. This seems both normal and desirable to me - no one wants
to review a set of 100 patches for a feature if they contain a lot of
redundancy. It's useful to do small commits as you work to get a feature
stable, but it isn't useful to ask someone to review all those commits,
especially if they fix a previous bug introduced by another such small commit.

However, that history can still be very useful: You might hit a variation on
those bugs later, and seeing the original unrewritten history can help a lot
in refreshing your memory on what you did there (and it can be even more
helpful for someone else getting up to speed on the code trying to fix that
bug). Losing the history after history rewriting isn't a good thing.

~~~
sofal
In my experience what is being reviewed by your peers are the feature diffs,
not the individual commit diffs. What the actual history looks like is
entirely up to you as the coder.

------
stevelosh
I wrote an article with the exact same title but very different content almost
exactly one year ago: [http://stevelosh.com/blog/2010/01/the-real-difference-
betwee...](http://stevelosh.com/blog/2010/01/the-real-difference-between-
mercurial-and-git/)

------
CJefferson
This article seemed to start off well, but at the end turned into a 'Git is
better because tools can edit the DAG storing state. QED'.

It is possible that that is true, but I'd want a stronger backup. Making no
negative comments about git at all sets off very strong alarm bells for me.

~~~
OmarIsmail
I think the author does say something negative about git, just doesn't say it
in a negative way. "When a git user runs into a problem, they look at the
tools they have on hand and ask, “how can I combine these ideas to solve my
problem?”"

The negative here is that A) git user has to know about all the tools, which
takes a long time, and then solving the issue requires quite a bit of thought.

That's one of the problems about git is that as a result of the massive
flexibility there doesn't seem to a "standard" way to do things, even basic
things. Each org has to develop their own processes which is annoying and
difficult for new people in the org.

~~~
gcp
I wouldn't mind some of the big users of git summarizing the "best practices"
they arrived at in a nice presentation.

~~~
dmoney
For large projects, there is git-flow: <https://www.google.com/search?q=git-
flow>

I remember someone posting a recommended workflow for smaller projects, but I
don't remember what it was called.

~~~
lysium
github uses a different flow. This article mentions the issues with git-flow
and describes the flow used at github:

<http://scottchacon.com/2011/08/31/github-flow.html>

------
__david__
A small nit about git stashes: they aren't stored in a special linear branch.
Each stash is a little branch off of wherever you were when you created the
stash. The only trick is that the stash commits are named something weird so
that "git stash list" can find them and list them.

You can check this yourself:

    
    
        git show stash@{0}
    

Look where it says "WIP on {branch-name}: {hash}". That {hash} will be one of
the hashes in the "Merge" header near the top of the commit.

You can also see this visually with "gitk --all".

~~~
xentac
You are correct. They aren't a linear history. In fact, the key is the @{#}
syntax (man git-rev-parse). Each stash commit is a single commit pointed to by
a ref/stash reference. Because of the reflog, we can see what ref/stash used
to point at, that is @{1}, @{2}, etc.

~~~
__david__
It's actually (potentially) more than a single commit. If you have a dirty
index when you stash then that will also be committed along with your working
directory changes. If you use the new(ish) --include-untracked option to
stash, a 3rd commit will be added that has the untracked files in it. The main
stash commit (that holds your working directory changes) is a fake merge of
all the commits, just so there are pointers back to the other 2 commits.

------
yason
I sometimes wonder that BitKeeper had allowed kernel developers to use it
freely, none of git would have happened. Conversely, does anyone know what's
going on with BitKeeper these days? Assumedly they can sell their commercial
product to some markets but practically I haven't heard of BK once since git
was announced. Potentially a big blunder on their part.

------
mml
imho, as biased as a huge fan of hg can be, and someone who lives in git on a
day to day basis, is that git has an actively hostile UI, mercurial has a
great one.

all the other technical gizwangs can be debated by propellerheads till the
cows come home, but the user experience is quite plainly better in hg.

flamewar goes here ->

~~~
gcp
I think the point that the UI of the default tools is better in Mercurial is
entirely uncontroversial.

However, equating that to the user experience is short-sighted. The user
experience is also how well the tool works and fills the needs of the user. It
think it's very controversial to state hg is better there (this is a polite
way to say you're completely wrong).

------
skrebbel
Wait, Mercurial users start _coding_ when they run into a problem with the
tool?

I always start googling and hoping there's a stackoverflow question about it
that has an answer that I don't get.

~~~
gcp
That certainly reflects my experience: I have a problem...

Mercurial: there's an extension for that!

Git: you can fix that just by adding --foo bar:buzz --derp herp to the
commandline lol RTFM

~~~
mariusmg
Maybe somebody can add a extension to Mercurial that let's you use empty
folders.....

~~~
xentac
Git and Mercurial both suffer from this "problem". Because manifests/trees
must have contents (file hashes) to exist, you can't track an empty folder.

I suppose git's tree objects could point to the empty tree to record an empty
folder, but most of the git code is comparing file blobs not trees.

~~~
pavel_lishin
In git, we solve this problem by sticking a .gitignore file in any directories
we want to be part of the repo, but without the contents (cache folders, user-
generated data destinations, etc.)

How do you get around this in mercurial?

~~~
getsat
It's usually .gitkeep from what I've seen. .gitignore usually lives in the
repo root.

------
mrb
(I know Mercurial very well. I evaluated 5 or 6 distributed VCS tools and
decided to migrate my employer's repository from CVS to Mercurial in 2008: 2GB
of history, 150k files, 20k changesets, dozens of developers.)

I don't understand the poster's insistence about wanting to perform an
operation similar to git stash in Mercurial "without creating a commit".
Creating an ephemeral commit _is precisely the perfect solution_ in Mercurial!
Do it, then all regular Mercurial commands (log, diff, etc) will work to
manage it. To get rid of the temporary commit later, simply "hg strip" it. It
will remove the changeset (and all its children, if any) from the history (a
backup will be made in .hg/strip-backup). hg strip is part of the built-in mq
extension.

Also, a simple solution to the poster's last complain (re-doing a commit while
keeping history of the original commit) is as follow: let's say you are at
revision A. You commit B. You realize you screwed up. So you go back to A: _hg
update A_. Then you revert your working directory to the state of B: _hg
revert --all --no-backup -r B_. Then you make the changes you forgot, and
commit again: _hg commit_.

~~~
gcp
_I don't understand the poster's insistence about wanting to perform an
operation similar to git stash in Mercurial "without creating a commit"._

Having it as a real commit is an issue when you want to pull upstream for
example. My solution would be hg qnew stashname, but I already ranted about
the limitations of MQ in another thread here.

~~~
mrb
Why is it an issue when you want to pull upstream? If you do it, the temp
commit will live aside from all the csets pulled from upstream. If you want to
resume work on it later, then just update to it. If you want to resume work on
it while effectively "merging" its changes with recent upstream changes, then
simply rebase the temp commit: hg rebase -s tempcommit -d new-head-pulled-
from-upstream. (rebase is a built-in extension.) That's the workflow policy I
implemented and it works well. It is also easy to keep track of temp commits:
running hg incoming/outgoing will show which changesets are present/absent in
one repo compared to whatever other repo you use as a reference.

------
reinhardt
Mercurial was my first DVCS so I'm perhaps biased but its API has always fit
my brain better. We use Git at my current job but after a few days of trying
to get used to it, adding shortcuts to avoid remembering the cryptic switches
required to do simple tasks and finally screwing up in some merge conflict, I
said the hell with it, installed hg-git and work happily ever after.

------
Rusky
The biggest advantage of Git's storage model is imo the reflog- I have
irretrievably lost work to Mercurial Queues, and that's virtually impossible
in Git.

~~~
azakai
If you version your MQ, you can never lose data in it.

In git, if you rewrite history, you lose information - you can no longer see
what things looked like before you rewrote it.

Disclaimer: I use git myself and prefer it. But the hg and MQ bashing in the
comments here is unfair.

~~~
Rusky
When I used MQ, it was to modify history in a way equivalent to some of git's
features. Versioning that defeats the purpose.

~~~
gcp
The MQ versioning repo is separate from the main versioning repo. So I don't
see how one follows from the other here. You can perfectly modify history in
the main repo and version that editing in the MQ repo. The main repo will look
clean because the history editing is versioned in the MQ repo.

~~~
Rusky
Ah, that's good to know. I was not aware of that.

------
alwillis
Even GitHub dude Scott Chacon (who does training on Git for GitHub and who
wrote the book Pro Git) says in this podcast some aspects of Mercurial are
better thought-out than Git:
[http://thechangelog.com/post/3445186374/episode-0-4-9-git-
sh...](http://thechangelog.com/post/3445186374/episode-0-4-9-git-showoff-and-
xbox-kinect-with-scott-cha)

I agree with Scott's assessment: it's really not that huge a deal if your
using Git or Mercurial; they're very similar--it's using a distributed version
control system that's a huge deal. Many many places are still locked into old
school, centralized systems that make collaboration much harder than it is
using Mercurial or Git.

------
k_bx
I think that the real difference between git and mercurial is allowing
multiple heads in the same branch. Everything else is just an API difference,
but from high level point of view -- they're just trees if commits, that's
all.

~~~
tonfa
The real difference is the terminology: mercurial named branches mean
something other than git branches (git branches are similar to mercurial
bookmarks). Steve Losh's article explains that very well.

~~~
k_bx
No, I know about bookmarks, but still, it's all about multiple heads. In
mercurial branches, if you merge someone's work on the same bookmark as you
did, you get new anonymous head. That head gets it's bookmark lost, since
bookmark can only point to one changeset, that's why this bookmarks mechanism
fits mercurial not too good.

While in mercurial native branching, you are allowed to get multiple heads in
one branch, which from one point of view is frustrating, but from another is
natural (since you really were both working on the same branch at the same
time).

In Git they tried to solve this problem by origins system, but now, when I
worked with both, I find mercurial branches to be the simplest concept that is
easier to explain than git's origins.

The only big problem with mercurial branches is their speed, but I think that
it can be solved (algorithms are not so complex to make it fast).

