parsers: a C implementation of the new ancestors algorithm
The performance of both the old and new Python ancestor algorithms
depends on the number of revs they need to traverse. Although the
new algorithm performs far better than the old when revs are
numerically and topologically close, both algorithms become slow
under other circumstances, taking up to 1.8 seconds to give answers
in a Linux kernel repo.
This C implementation of the new algorithm is a fairly straightforward
transliteration. The only corner case of interest is that it raises
an OverflowError if the number of GCA candidates found during the
first pass is greater than 24, to avoid the dual perils of fixnum
overflow and trying to allocate too much memory. (If this exception
is raised, the Python implementation is used instead.)
Performance numbers are good: in a Linux kernel repo, time for "hg
debugancestors" on two distant revs (
24bf01de7537 and
c2a8808f5943)
is as follows:
Old Python: 0.36 sec
New Python: 0.42 sec
New C: 0.02 sec
For a case where the new algorithm should perform well:
Old Python: 1.84 sec
New Python: 0.07 sec
New C: measures as zero when using --time
(This commit includes a paranoid cross-check to ensure that the
Python and C implementations give identical answers. The above
performance numbers were measured with that check disabled.)
revlog: choose a consistent ancestor when there's a tie
Previously, we chose a rev based on numeric ordering, which could
cause "the same merge" in topologically identical but numerically
different repos to choose different merge bases.
We now choose the lexically least node; this is stable across
different revlog orderings.
ancestor: a new algorithm that is faster for nodes near tip
Instead of walking all the way to the root of the DAG, we generate
a set of candidate GCA revs, then figure out which ones will win
the race to the root (usually without needing to traverse all the
way to the root).
In the common case of nodes that are close to each other in both
revision number and topology, this is usually a big win: it makes
"hg --time debugancestors" up to 9 times faster than the more general
ancestor function when measured on heads of the linux-2.6 hg repo.
Victory is not assured, however. The older function can still win
by a large margin if one node is much closer to the root than the
other, or by a much smaller amount if one is an ancestor of the
other.
For now, we've also got a small paranoid harness function that calls
both ancestor functions on every input and ensures that they give
equivalent answers.
Even without the checker function, the old ancestor function needs
to stay alive for the time being, as its generality is used by
context.filectx.merge.
update: allow dirty update to foreground (successors)
Update to "foreground" are no longer seen as cross branch update. "Foreground"
are descendants or successors (or successors of descendants (or descendant of
successors (etc))). This allows to update with uncommited changes that get
automatically merged.
This changeset is a small step forward. We want to allow dirty update to
"background" (precursors) and takes obsolescence in account when finding the
default update destination. But those requires deeper changes and will comes in
later changesets.
obsolete: extract foreground computation from bookmark.validdest
This foreground logic will be reused by update logic.
destroyed: invalidate phraserevs cache in all case (
issue3858)
When revisions are destroyed, the `phaserevs` cache becomes invalid in most case.
This cache hold a `{rev => phase}` mapping and revision number most likely
changed.
Since
1c8e0d6ac3b0, we filter unknown phases' roots after changesets
destruction. When some roots are filtered the `phaserevs` cache is invalidated.
But not if none root where destroyed.
We now invalidate the cache in all case filtered root or not.
This bug was a bit tricky to reproduce as in most case we either:
* rebase a set a draft changeset including root (phaserev invalidated)
* strip tip-most changesets (no re-numbering of revision)
Note that the invalidation of `phaserevs` are not strictly needed when only
tip-most part of the history have been destroyed. But I do not expect the
overhead to be significant.
largefiles: deprecate --all-largefiles for pull
The same can be achieved with --lfrev pulled() and we shouldn't advertise
unnecessary command line options.