Mercurial > evolve
changeset 2130:d784622dd5dc
stablerange: move the range class in the new module
Our ultimate goal is to remove this class for performance reason. however for
now, it contains most of the code we care about so we migrate it as a block
first.
author | Pierre-Yves David <pierre-yves.david@ens-lyon.org> |
---|---|
date | Sun, 19 Mar 2017 03:07:01 +0100 |
parents | d07bb7cbae2f |
children | 86dd39478638 |
files | hgext3rd/evolve/obsdiscovery.py hgext3rd/evolve/stablerange.py |
diffstat | 2 files changed, 153 insertions(+), 145 deletions(-) [+] |
line wrap: on
line diff
--- a/hgext3rd/evolve/obsdiscovery.py Sun Mar 19 03:06:53 2017 +0100 +++ b/hgext3rd/evolve/obsdiscovery.py Sun Mar 19 03:07:01 2017 +0100 @@ -24,7 +24,6 @@ import hashlib import heapq -import math import struct from mercurial import ( @@ -260,7 +259,7 @@ return True for h in heads: - entry = _range(local, h, 0) + entry = stablerange.stablerange(local, h, 0) addentry(entry) querycount = 0 @@ -362,7 +361,7 @@ n = data[:20] index = _unpack('>I', data[20:])[0] r = op.repo.changelog.rev(n) - rhash = _range(op.repo, r, index).obshash + rhash = stablerange.stablerange(op.repo, r, index).obshash replies.append(data + rhash) data = inpart.read(24) op.reply.newpart('reply:_donotusemeever_evoext_obshashrange_1', data=iter(replies)) @@ -395,7 +394,7 @@ # prewarm depth cache for r in repo.revs("::%ld", revs): repo.stablerange.depthrev(repo, r) - toproceed = [_range(repo, r, 0, ) for r in revs] + toproceed = [stablerange.stablerange(repo, r, 0, ) for r in revs] ranges = set(toproceed) while toproceed: entry = toproceed.pop() @@ -410,147 +409,6 @@ d = (r.head, s(r.node), r.index, len(r), r.depth, node.short(r.obshash)) ui.status('%3d %s %5d %4d %5d %s\n' % d) -def _hlp2(i): - """return highest power of two lower than 'i'""" - return 2 ** int(math.log(i - 1, 2)) - -class _range(object): - - def __init__(self, repo, head, index, revs=None): - self._repo = repo.unfiltered() - self.head = head - self.index = index - if revs is not None: - assert len(revs) == len(self) - self._revs = revs - assert index < self.depth, (head, index, self.depth, revs) - - def __repr__(self): - return '%s %d %d %s' % (node.short(self.node), self.depth, self.index, node.short(self.obshash)) - - def __hash__(self): - return self._id - - def __eq__(self, other): - if type(self) != type(other): - raise NotImplementedError() - return self.stablekey == other.stablekey - - @util.propertycache - def _id(self): - return hash(self.stablekey) - - @util.propertycache - def stablekey(self): - return (self.node, self.index) - - @util.propertycache - def node(self): - return self._repo.changelog.node(self.head) - - def __len__(self): - return self.depth - self.index - - @util.propertycache - def depth(self): - return self._repo.stablerange.depthrev(self._repo, self.head) - - @util.propertycache - def _revs(self): - r = stablerange.stablesort(self._repo, [self.head])[self.index:] - assert len(r) == len(self), (self.head, self.index, len(r), len(self)) - return r - - def _slicesat(self, globalindex): - localindex = globalindex - self.index - - cl = self._repo.changelog - - result = [] - bottom = self._revs[:localindex] - top = _range(self._repo, self.head, globalindex, self._revs[localindex:]) - # - toprootdepth = self._repo.stablerange.depthrev(self._repo, top._revs[0]) - if toprootdepth + len(top) == self.depth + 1: - bheads = [bottom[-1]] - else: - bheads = set(bottom) - parentrevs = cl.parentrevs - du = bheads.difference_update - for r in bottom: - du(parentrevs(r)) - # if len(bheads) == 1: - # assert 1 == len(self._repo.revs('roots(%ld)', top._revs)) - if len(bheads) == 1: - newhead = bottom[-1] - bottomdepth = self._repo.stablerange.depthrev(self._repo, newhead) - newstart = bottomdepth - len(bottom) - result.append(_range(self._repo, newhead, newstart, bottom)) - else: - # assert 1 < len(bheads), (toprootdepth, len(top), len(self)) - cl = self._repo.changelog - for h in bheads: - subset = cl.ancestors([h], inclusive=True) - hrevs = [r for r in bottom if r in subset] - start = self._repo.stablerange.depthrev(self._repo, h) - len(hrevs) - entry = _range(self._repo, h, start, [r for r in bottom if r in subset]) - result.append(entry) - result.append(top) - return result - - def subranges(self): - cached = self._repo.stablerange.subranges(self._repo, self) - if cached is not None: - return cached - step = _hlp2(self.depth) - standard_start = 0 - while standard_start < self.index and 0 < step: - if standard_start + step < self.depth: - standard_start += step - step //= 2 - if self.index == standard_start: - slicesize = _hlp2(len(self)) - slicepoint = self.index + slicesize - else: - assert standard_start < self.depth - slicepoint = standard_start - result = self._slicesat(slicepoint) - self._repo.stablerange.setsubranges(self, result) - return result - - @util.propertycache - def obshash(self): - cache = self._repo.obsstore.rangeobshashcache - obshash = cache.get(self) - if obshash is not None: - return obshash - pieces = [] - nullid = node.nullid - if len(self) == 1: - tmarkers = self._repo.obsstore.relevantmarkers([self.node]) - pieces = [] - for m in tmarkers: - mbin = obsolete._fm1encodeonemarker(m) - pieces.append(mbin) - pieces.sort() - else: - for subrange in self.subranges(): - obshash = subrange.obshash - if obshash != nullid: - pieces.append(obshash) - - sha = hashlib.sha1() - # note: if there is only one subrange with actual data, we'll just - # reuse the same hash. - if not pieces: - obshash = node.nullid - elif len(pieces) != 1 or obshash is None: - sha = hashlib.sha1() - for p in pieces: - sha.update(p) - obshash = cache[self] = sha.digest() - return obshash - @eh.wrapfunction(obsolete.obsstore, '_addmarkers') def _addmarkers(orig, obsstore, *args, **kwargs): obsstore.rangeobshashcache.clear()
--- a/hgext3rd/evolve/stablerange.py Sun Mar 19 03:06:53 2017 +0100 +++ b/hgext3rd/evolve/stablerange.py Sun Mar 19 03:07:01 2017 +0100 @@ -8,18 +8,23 @@ # GNU General Public License version 2 or any later version. import collections +import math +import hashlib + from mercurial import ( commands, cmdutil, localrepo, node as nodemod, scmutil, + util, ) from mercurial.i18n import _ from . import ( exthelper, + obsolete, ) eh = exthelper.exthelper() @@ -123,6 +128,10 @@ assert len(result) == len(set(result)) return result +################################# +### Stable Range computation ### +################################# + class stablerangecache(dict): def __init__(self): @@ -227,6 +236,147 @@ # this class reach. return self._subrangescache.get(rangeid) +def _hlp2(i): + """return highest power of two lower than 'i'""" + return 2 ** int(math.log(i - 1, 2)) + +class stablerange(object): + + def __init__(self, repo, head, index, revs=None): + self._repo = repo.unfiltered() + self.head = head + self.index = index + if revs is not None: + assert len(revs) == len(self) + self._revs = revs + assert index < self.depth, (head, index, self.depth, revs) + + def __repr__(self): + return '%s %d %d %s' % (nodemod.short(self.node), self.depth, self.index, nodemod.short(self.obshash)) + + def __hash__(self): + return self._id + + def __eq__(self, other): + if type(self) != type(other): + raise NotImplementedError() + return self.stablekey == other.stablekey + + @util.propertycache + def _id(self): + return hash(self.stablekey) + + @util.propertycache + def stablekey(self): + return (self.node, self.index) + + @util.propertycache + def node(self): + return self._repo.changelog.node(self.head) + + def __len__(self): + return self.depth - self.index + + @util.propertycache + def depth(self): + return self._repo.stablerange.depthrev(self._repo, self.head) + + @util.propertycache + def _revs(self): + r = stablesort(self._repo, [self.head])[self.index:] + assert len(r) == len(self), (self.head, self.index, len(r), len(self)) + return r + + def _slicesat(self, globalindex): + localindex = globalindex - self.index + + cl = self._repo.changelog + + result = [] + bottom = self._revs[:localindex] + top = stablerange(self._repo, self.head, globalindex, self._revs[localindex:]) + # + toprootdepth = self._repo.stablerange.depthrev(self._repo, top._revs[0]) + if toprootdepth + len(top) == self.depth + 1: + bheads = [bottom[-1]] + else: + bheads = set(bottom) + parentrevs = cl.parentrevs + du = bheads.difference_update + for r in bottom: + du(parentrevs(r)) + # if len(bheads) == 1: + # assert 1 == len(self._repo.revs('roots(%ld)', top._revs)) + if len(bheads) == 1: + newhead = bottom[-1] + bottomdepth = self._repo.stablerange.depthrev(self._repo, newhead) + newstart = bottomdepth - len(bottom) + result.append(stablerange(self._repo, newhead, newstart, bottom)) + else: + # assert 1 < len(bheads), (toprootdepth, len(top), len(self)) + cl = self._repo.changelog + for h in bheads: + subset = cl.ancestors([h], inclusive=True) + hrevs = [r for r in bottom if r in subset] + start = self._repo.stablerange.depthrev(self._repo, h) - len(hrevs) + entry = stablerange(self._repo, h, start, [r for r in bottom if r in subset]) + result.append(entry) + result.append(top) + return result + + def subranges(self): + cached = self._repo.stablerange.subranges(self._repo, self) + if cached is not None: + return cached + step = _hlp2(self.depth) + standard_start = 0 + while standard_start < self.index and 0 < step: + if standard_start + step < self.depth: + standard_start += step + step //= 2 + if self.index == standard_start: + slicesize = _hlp2(len(self)) + slicepoint = self.index + slicesize + else: + assert standard_start < self.depth + slicepoint = standard_start + result = self._slicesat(slicepoint) + self._repo.stablerange.setsubranges(self, result) + return result + + @util.propertycache + def obshash(self): + cache = self._repo.obsstore.rangeobshashcache + obshash = cache.get(self) + if obshash is not None: + return obshash + pieces = [] + nullid = nodemod.nullid + if len(self) == 1: + tmarkers = self._repo.obsstore.relevantmarkers([self.node]) + pieces = [] + for m in tmarkers: + mbin = obsolete._fm1encodeonemarker(m) + pieces.append(mbin) + pieces.sort() + else: + for subrange in self.subranges(): + obshash = subrange.obshash + if obshash != nullid: + pieces.append(obshash) + + sha = hashlib.sha1() + # note: if there is only one subrange with actual data, we'll just + # reuse the same hash. + if not pieces: + obshash = nodemod.nullid + elif len(pieces) != 1 or obshash is None: + sha = hashlib.sha1() + for p in pieces: + sha.update(p) + obshash = cache[self] = sha.digest() + return obshash + @eh.reposetup def setupcache(ui, repo):