Hg sure is weird!
Stephen J. Turnbull
stephen at xemacs.org
Sun Jan 27 03:42:37 EST 2008
mike.kupfer at xemacs.org writes:
> >>>>> "ST" == Stephen J Turnbull <stephen at xemacs.org> writes:
> ST> Aidan's patch, as he wrote it. No more, no less.
> hg export 01970033faa6
> is safest, though "hg export 4401" would work on a fresh clone of
Probably not; I think that xemacs-beta already fails to be a tail of
xemacs, so the numbering differs, and also differs between my repo and
the public one on alioth.
But suppose it was a largish change that was actually a sequence of
several commits? Now export is no help, is it? (Note I want a single
diff, not several of them.)
> ST> git gets this right.
> Having never used git, I'm curious what "gets this right" means. Is it
> that the revision identifiers are different, so it's easier to
> distinguish revisions on different branches?
This is true. In git, the revision identifiers most commonly used
count generations away from the tip of a named branch. (There is a
notation for the situation where there are merges, so that you can
count back through a merge, but I've never used it.)
> Something else?
Something else: there are no sequence numbers in git.
The big wart in Mercurial is that it imposes a sequential view on each
repository regardless of how many branches it contains, and exposes
those sequence numbers in several places. In a revision DAG, sequence
numbers are worse than a YAGNI. In Mercurial, you can't easily get
away from them, although you can certainly construct ways to get the
Here's an example I've constructed, based on the analogy that a
revision control system is Lisp (almost; actually it's Scheme-oid with
multivalued cdrs). In the following, branches are represented as
lists of tree objects (which has problems if you push it too far, but
works well enough here).
Suppose the upstream is
release = (tree1 tree2 ...)
mybranch = (tree3 . release)
and you have
yourbranch = (tree4 . release)
where your changes (tree4) are completely independent of mine (tree3),
ie no merge conflicts. Now in git I pull your branch (implicitly
merging and updating the workspace). I get
mybranch = ((merge tree3 tree4) tree3 . release)
<implicit-branch> = ((merge tree3 tree4) tree4 . release)
(To access revisions on <implicit-branch>, I'd need to use the SHA1
name of the commit, or the arcane "through-the-merge" notation
mentioned above.) Now, if I do "git diff mybranch^ mybranch" (^ is
the git way of expressing cdr), I get (diff tree3 (merge tree3 tree4))
== (diff release tree4), as you'd expect. What Mercurial does is very
different. Because its internal representation is an array of all
revisions, what we get is this:
mybranch = ((merge tree3 tree4) tree4 tree3 . release)
Now consider (~n is the git notation for nthcdr):
(diff mybranch^ mybranch) == (diff tree4 (merge tree3 tree4))
== (diff release tree3)
;; My changes, but I already knew that!
(diff mybranch~2 mybranch^) == (diff tree3 tree4)
== (diff release tree4) - (diff release tree3)
;; Hey! What did you do with *my* changes?!
(diff mybranch~2 mybranch) == (diff tree3 (merge tree3 tree4))
== (diff release tree4)
;; What we want! How intuitive ... not. :(
So what it comes down to is that by construction Mercurial diffs
between neighbors in the sequence are typically uninteresting. You
need to do something unnatural (like actually export a patch -- which
only works for a single patch -- or give explicit non-neighboring
revisions for what is conceptually a single patch).
"hg log" output is also filled with (usually) uninteresting merge
logs, and interposes fetched revisions in the line of development we
really want to see.
More information about the XEmacs-Beta