# HG changeset patch # User Gregory Szorc # Date 2018-08-17 23:11:35 # Node ID b95b48a55c36050852b46868f4e4f750a29d8754 # Parent b623c7b2369574bdf156c8867644ff07e4f126c6 # Parent f736fdbe546af28040f53b3bbed40af02fdf7a65 merge with stable diff --git a/contrib/perf.py b/contrib/perf.py --- a/contrib/perf.py +++ b/contrib/perf.py @@ -790,6 +790,63 @@ def perfphases(ui, repo, **opts): timer(d) fm.end() +@command('perfphasesremote', + [], "[DEST]") +def perfphasesremote(ui, repo, dest=None, **opts): + """benchmark time needed to analyse phases of the remote server""" + from mercurial.node import ( + bin, + ) + from mercurial import ( + exchange, + hg, + phases, + ) + timer, fm = gettimer(ui, opts) + + path = ui.paths.getpath(dest, default=('default-push', 'default')) + if not path: + raise error.abort(('default repository not configured!'), + hint=("see 'hg help config.paths'")) + dest = path.pushloc or path.loc + branches = (path.branch, opts.get('branch') or []) + ui.status(('analysing phase of %s\n') % util.hidepassword(dest)) + revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) + other = hg.peer(repo, opts, dest) + + # easier to perform discovery through the operation + op = exchange.pushoperation(repo, other) + exchange._pushdiscoverychangeset(op) + + remotesubset = op.fallbackheads + + with other.commandexecutor() as e: + remotephases = e.callcommand('listkeys', + {'namespace': 'phases'}).result() + del other + publishing = remotephases.get('publishing', False) + if publishing: + ui.status(('publishing: yes\n')) + else: + ui.status(('publishing: no\n')) + + nodemap = repo.changelog.nodemap + nonpublishroots = 0 + for nhex, phase in remotephases.iteritems(): + if nhex == 'publishing': # ignore data related to publish option + continue + node = bin(nhex) + if node in nodemap and int(phase): + nonpublishroots += 1 + ui.status(('number of roots: %d\n') % len(remotephases)) + ui.status(('number of known non public roots: %d\n') % nonpublishroots) + def d(): + phases.remotephasessummary(repo, + remotesubset, + remotephases) + timer(d) + fm.end() + @command('perfmanifest',[ ('m', 'manifest-rev', False, 'Look up a manifest node revision'), ('', 'clear-disk', False, 'clear on-disk caches too'), diff --git a/mercurial/phases.py b/mercurial/phases.py --- a/mercurial/phases.py +++ b/mercurial/phases.py @@ -664,9 +664,39 @@ def newheads(repo, heads, roots): * `heads`: define the first subset * `roots`: define the second we subtract from the first""" + # prevent an import cycle + # phases > dagop > patch > copies > scmutil > obsolete > obsutil > phases + from . import dagop + repo = repo.unfiltered() - revs = repo.revs('heads(::%ln - (%ln::%ln))', heads, roots, heads) - return pycompat.maplist(repo.changelog.node, revs) + cl = repo.changelog + rev = cl.nodemap.get + if not roots: + return heads + if not heads or heads == [nullrev]: + return [] + # The logic operated on revisions, convert arguments early for convenience + new_heads = set(rev(n) for n in heads if n != nullid) + roots = [rev(n) for n in roots] + if not heads or not roots: + return heads + # compute the area we need to remove + affected_zone = repo.revs("(%ld::%ld)", roots, new_heads) + # heads in the area are no longer heads + new_heads.difference_update(affected_zone) + # revisions in the area have children outside of it, + # They might be new heads + candidates = repo.revs("parents(%ld + (%ld and merge())) and not null", + roots, affected_zone) + candidates -= affected_zone + if new_heads or candidates: + # remove candidate that are ancestors of other heads + new_heads.update(candidates) + prunestart = repo.revs("parents(%ld) and not null", new_heads) + pruned = dagop.reachableroots(repo, candidates, prunestart) + new_heads.difference_update(pruned) + + return pycompat.maplist(cl.node, sorted(new_heads)) def newcommitphase(ui): """helper to get the target phase of new commit diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -263,13 +263,17 @@ def _trimchunk(revlog, revs, startidx, e if endidx is None: endidx = len(revs) - # Trim empty revs at the end, but never the very first revision of a chain - while endidx > 1 and endidx > startidx and length(revs[endidx - 1]) == 0: - endidx -= 1 + # If we have a non-emtpy delta candidate, there are nothing to trim + if revs[endidx - 1] < len(revlog): + # Trim empty revs at the end, except the very first revision of a chain + while (endidx > 1 + and endidx > startidx + and length(revs[endidx - 1]) == 0): + endidx -= 1 return revs[startidx:endidx] -def _segmentspan(revlog, revs): +def _segmentspan(revlog, revs, deltainfo=None): """Get the byte span of a segment of revisions revs is a sorted array of revision numbers @@ -295,7 +299,14 @@ def _segmentspan(revlog, revs): """ if not revs: return 0 - return revlog.end(revs[-1]) - revlog.start(revs[0]) + if deltainfo is not None and len(revlog) <= revs[-1]: + if len(revs) == 1: + return deltainfo.deltalen + offset = revlog.end(len(revlog) - 1) + end = deltainfo.deltalen + offset + else: + end = revlog.end(revs[-1]) + return end - revlog.start(revs[0]) def _slicechunk(revlog, revs, deltainfo=None, targetsize=None): """slice revs to reduce the amount of unrelated data to be read from disk. @@ -527,7 +538,7 @@ def _slicechunktodensity(revlog, revs, d yield revs return - if deltainfo is not None: + if deltainfo is not None and deltainfo.deltalen: revs = list(revs) revs.append(nextrev) @@ -2471,7 +2482,8 @@ class revlog(object): deltachain = [] chunks = _slicechunk(self, deltachain, deltainfo) - distance = max(map(lambda revs:_segmentspan(self, revs), chunks)) + all_span = [_segmentspan(self, revs, deltainfo) for revs in chunks] + distance = max(all_span) else: distance = deltainfo.distance diff --git a/tests/test-bookmarks-pushpull.t b/tests/test-bookmarks-pushpull.t --- a/tests/test-bookmarks-pushpull.t +++ b/tests/test-bookmarks-pushpull.t @@ -141,10 +141,10 @@ delete a remote bookmark bundle2-output: payload chunk size: 23 bundle2-output: closing payload chunk bundle2-output: bundle part: "check:phases" - bundle2-output-part: "check:phases" 48 bytes payload + bundle2-output-part: "check:phases" 24 bytes payload bundle2-output: part 2: "CHECK:PHASES" bundle2-output: header chunk size: 19 - bundle2-output: payload chunk size: 48 + bundle2-output: payload chunk size: 24 bundle2-output: closing payload chunk bundle2-output: bundle part: "pushkey" bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload @@ -180,9 +180,9 @@ delete a remote bookmark bundle2-input: part parameters: 0 bundle2-input: found a handler for part check:phases bundle2-input-part: "check:phases" supported - bundle2-input: payload chunk size: 48 + bundle2-input: payload chunk size: 24 bundle2-input: payload chunk size: 0 - bundle2-input-part: total payload size 48 + bundle2-input-part: total payload size 24 bundle2-input: part header size: 90 bundle2-input: part type: "PUSHKEY" bundle2-input: part id: "3" @@ -253,10 +253,10 @@ delete a remote bookmark bundle2-output: payload chunk size: 23 bundle2-output: closing payload chunk bundle2-output: bundle part: "check:phases" - bundle2-output-part: "check:phases" 48 bytes payload + bundle2-output-part: "check:phases" 24 bytes payload bundle2-output: part 2: "CHECK:PHASES" bundle2-output: header chunk size: 19 - bundle2-output: payload chunk size: 48 + bundle2-output: payload chunk size: 24 bundle2-output: closing payload chunk bundle2-output: bundle part: "bookmarks" bundle2-output-part: "bookmarks" 23 bytes payload @@ -293,9 +293,9 @@ delete a remote bookmark bundle2-input: part parameters: 0 bundle2-input: found a handler for part check:phases bundle2-input-part: "check:phases" supported - bundle2-input: payload chunk size: 48 + bundle2-input: payload chunk size: 24 bundle2-input: payload chunk size: 0 - bundle2-input-part: total payload size 48 + bundle2-input-part: total payload size 24 bundle2-input: part header size: 16 bundle2-input: part type: "BOOKMARKS" bundle2-input: part id: "3" diff --git a/tests/test-contrib-perf.t b/tests/test-contrib-perf.t --- a/tests/test-contrib-perf.t +++ b/tests/test-contrib-perf.t @@ -103,6 +103,8 @@ perfstatus perfpathcopies (no help text available) perfphases benchmark phasesets computation + perfphasesremote + benchmark time needed to analyse phases of the remote server perfrawfiles (no help text available) perfrevlogchunks Benchmark operations on revlog chunks. @@ -212,4 +214,7 @@ Check perf.py for historical portability contrib/perf.py:\d+: (re) > from mercurial import ( import newer module separately in try clause for early Mercurial + contrib/perf.py:\d+: (re) + > from mercurial import ( + import newer module separately in try clause for early Mercurial [1]