unionrepo.py
366 lines
| 11.0 KiB
| text/x-python
|
PythonLexer
/ mercurial / unionrepo.py
Mads Kiilerich
|
r18944 | # unionrepo.py - repository class for viewing union of repository changesets | ||
# | ||||
# Derived from bundlerepo.py | ||||
# Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com> | ||||
# Copyright 2013 Unity Technologies, Mads Kiilerich <madski@unity3d.com> | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
"""Repository class for "in-memory pull" of one local repository to another, | ||||
allowing operations like diff and log with revsets. | ||||
""" | ||||
Matt Harbison
|
r52756 | from __future__ import annotations | ||
r51906 | import contextlib | |||
Matt Harbison
|
r52767 | import typing | ||
Gregory Szorc
|
r25988 | |||
from .i18n import _ | ||||
from . import ( | ||||
changelog, | ||||
cmdutil, | ||||
Matt Harbison
|
r39843 | encoding, | ||
Pierre-Yves David
|
r26587 | error, | ||
Gregory Szorc
|
r25988 | filelog, | ||
localrepo, | ||||
manifest, | ||||
mdiff, | ||||
pathutil, | ||||
revlog, | ||||
util, | ||||
Pierre-Yves David
|
r31242 | vfs as vfsmod, | ||
Gregory Szorc
|
r25988 | ) | ||
Mads Kiilerich
|
r18944 | |||
r48023 | from .revlogutils import ( | |||
constants as revlog_constants, | ||||
) | ||||
Augie Fackler
|
r43346 | |||
Mads Kiilerich
|
r18944 | class unionrevlog(revlog.revlog): | ||
Matt Harbison
|
r52768 | def __init__(self, opener: typing.Any, radix, revlog2, linkmapper): | ||
# TODO: figure out real type of opener | ||||
# | ||||
Mads Kiilerich
|
r18944 | # How it works: | ||
# To retrieve a revision, we just need to know the node id so we can | ||||
# look it up in revlog2. | ||||
# | ||||
# To differentiate a rev in the second revlog from a rev in the revlog, | ||||
# we check revision against repotiprev. | ||||
Pierre-Yves David
|
r31242 | opener = vfsmod.readonlyvfs(opener) | ||
r47838 | target = getattr(revlog2, 'target', None) | |||
if target is None: | ||||
Matt Harbison
|
r52768 | # Help pytype- changelog and revlog are not possible here because | ||
# they both have a 'target' attr. | ||||
assert not isinstance(revlog2, (changelog.changelog, revlog.revlog)) | ||||
r47838 | # a revlog wrapper, eg: the manifestlog that is not an actual revlog | |||
target = revlog2._revlog.target | ||||
r47921 | revlog.revlog.__init__(self, opener, target=target, radix=radix) | |||
Mads Kiilerich
|
r18944 | self.revlog2 = revlog2 | ||
n = len(self) | ||||
self.repotiprev = n - 1 | ||||
Augie Fackler
|
r43346 | self.bundlerevs = set() # used by 'bundle()' revset expression | ||
Mads Kiilerich
|
r18944 | for rev2 in self.revlog2: | ||
rev = self.revlog2.index[rev2] | ||||
# rev numbers - in revlog2, very different from self.rev | ||||
r47913 | ( | |||
_start, | ||||
_csize, | ||||
rsize, | ||||
base, | ||||
linkrev, | ||||
p1rev, | ||||
p2rev, | ||||
node, | ||||
_sdo, | ||||
_sds, | ||||
r48023 | _dcm, | |||
r48030 | _sdcm, | |||
r49330 | rank, | |||
r47913 | ) = rev | |||
Mike Edgar
|
r27433 | flags = _start & 0xFFFF | ||
Mads Kiilerich
|
r18944 | |||
Augie Fackler
|
r43346 | if linkmapper is None: # link is to same revlog | ||
assert linkrev == rev2 # we never link back | ||||
Mads Kiilerich
|
r18944 | link = n | ||
Augie Fackler
|
r43346 | else: # rev must be mapped from repo2 cl to unified cl by linkmapper | ||
Mads Kiilerich
|
r18944 | link = linkmapper(linkrev) | ||
Augie Fackler
|
r43346 | if linkmapper is not None: # link is to same revlog | ||
Pierre-Yves David
|
r26230 | base = linkmapper(base) | ||
r43965 | this_rev = self.index.get_rev(node) | |||
if this_rev is not None: | ||||
Mads Kiilerich
|
r18944 | # this happens for the common revlog revisions | ||
r43965 | self.bundlerevs.add(this_rev) | |||
Mads Kiilerich
|
r18944 | continue | ||
p1node = self.revlog2.node(p1rev) | ||||
p2node = self.revlog2.node(p2rev) | ||||
Joerg Sonnenberger
|
r46419 | # TODO: it's probably wrong to set compressed length to -1, but | ||
Yuya Nishihara
|
r38194 | # I have no idea if csize is valid in the base revlog context. | ||
Augie Fackler
|
r43346 | e = ( | ||
flags, | ||||
Joerg Sonnenberger
|
r46419 | -1, | ||
Augie Fackler
|
r43346 | rsize, | ||
base, | ||||
link, | ||||
self.rev(p1node), | ||||
self.rev(p2node), | ||||
node, | ||||
r47914 | 0, # sidedata offset | |||
0, # sidedata size | ||||
r48023 | revlog_constants.COMP_MODE_INLINE, | |||
r48030 | revlog_constants.COMP_MODE_INLINE, | |||
r49330 | rank, | |||
Augie Fackler
|
r43346 | ) | ||
Martin von Zweigbergk
|
r38886 | self.index.append(e) | ||
Mads Kiilerich
|
r18944 | self.bundlerevs.add(n) | ||
n += 1 | ||||
r51906 | @contextlib.contextmanager | |||
def reading(self): | ||||
if 0 <= len(self.bundlerevs) < len(self.index): | ||||
read_1 = super().reading | ||||
else: | ||||
read_1 = util.nullcontextmanager | ||||
if 0 < len(self.bundlerevs): | ||||
read_2 = self.revlog2.reading | ||||
else: | ||||
read_2 = util.nullcontextmanager | ||||
with read_1(), read_2(): | ||||
yield | ||||
r51919 | def _chunk(self, rev): | |||
Mads Kiilerich
|
r18944 | if rev <= self.repotiprev: | ||
Matt Harbison
|
r52768 | return super(unionrevlog, self)._inner._chunk(rev) | ||
Mads Kiilerich
|
r18944 | return self.revlog2._chunk(self.node(rev)) | ||
def revdiff(self, rev1, rev2): | ||||
"""return or calculate a delta between two revisions""" | ||||
if rev1 > self.repotiprev and rev2 > self.repotiprev: | ||||
return self.revlog2.revdiff( | ||||
self.revlog2.rev(self.node(rev1)), | ||||
Augie Fackler
|
r43346 | self.revlog2.rev(self.node(rev2)), | ||
) | ||||
Mads Kiilerich
|
r18944 | elif rev1 <= self.repotiprev and rev2 <= self.repotiprev: | ||
r43095 | return super(unionrevlog, self).revdiff(rev1, rev2) | |||
Mads Kiilerich
|
r18944 | |||
r43094 | return mdiff.textdiff(self.rawdata(rev1), self.rawdata(rev2)) | |||
Mads Kiilerich
|
r18944 | |||
r51919 | def _revisiondata(self, nodeorrev, raw=False): | |||
Mads Kiilerich
|
r18944 | if isinstance(nodeorrev, int): | ||
rev = nodeorrev | ||||
node = self.node(rev) | ||||
else: | ||||
node = nodeorrev | ||||
rev = self.rev(node) | ||||
if rev > self.repotiprev: | ||||
r43092 | # work around manifestrevlog NOT being a revlog | |||
revlog2 = getattr(self.revlog2, '_revlog', self.revlog2) | ||||
func = revlog2._revisiondata | ||||
Mads Kiilerich
|
r18944 | else: | ||
r43092 | func = super(unionrevlog, self)._revisiondata | |||
r51919 | return func(node, raw=raw) | |||
Mads Kiilerich
|
r18944 | |||
Matt Harbison
|
r50869 | def addrevision( | ||
self, | ||||
text, | ||||
transaction, | ||||
link, | ||||
p1, | ||||
p2, | ||||
cachedelta=None, | ||||
node=None, | ||||
flags=revlog.REVIDX_DEFAULT_FLAGS, | ||||
deltacomputer=None, | ||||
sidedata=None, | ||||
): | ||||
Mads Kiilerich
|
r18944 | raise NotImplementedError | ||
Augie Fackler
|
r43346 | |||
def addgroup( | ||||
self, | ||||
deltas, | ||||
linkmapper, | ||||
transaction, | ||||
Joerg Sonnenberger
|
r47085 | alwayscache=False, | ||
Augie Fackler
|
r43346 | addrevisioncb=None, | ||
Joerg Sonnenberger
|
r46373 | duplicaterevisioncb=None, | ||
Matt Harbison
|
r50869 | debug_info=None, | ||
delta_base_reuse_policy=None, | ||||
Augie Fackler
|
r43346 | ): | ||
Mads Kiilerich
|
r18944 | raise NotImplementedError | ||
Augie Fackler
|
r43346 | |||
Joerg Sonnenberger
|
r42382 | def strip(self, minlink, transaction): | ||
Mads Kiilerich
|
r18944 | raise NotImplementedError | ||
Augie Fackler
|
r43346 | |||
Mads Kiilerich
|
r18944 | def checksize(self): | ||
raise NotImplementedError | ||||
Augie Fackler
|
r43346 | |||
Mads Kiilerich
|
r18944 | class unionchangelog(unionrevlog, changelog.changelog): | ||
def __init__(self, opener, opener2): | ||||
changelog.changelog.__init__(self, opener) | ||||
linkmapper = None | ||||
changelog2 = changelog.changelog(opener2) | ||||
r47921 | unionrevlog.__init__(self, opener, self.radix, changelog2, linkmapper) | |||
Augie Fackler
|
r43346 | |||
Mads Kiilerich
|
r18944 | |||
Durham Goode
|
r30374 | class unionmanifest(unionrevlog, manifest.manifestrevlog): | ||
Matt Harbison
|
r52719 | repotiprev: int | ||
Matt Harbison
|
r52962 | revlog2: manifest.manifestrevlog | ||
Matt Harbison
|
r52719 | |||
Joerg Sonnenberger
|
r47538 | def __init__(self, nodeconstants, opener, opener2, linkmapper): | ||
r51921 | # XXX manifestrevlog is not actually a revlog , so mixing it with | |||
# bundlerevlog is not a good idea. | ||||
Joerg Sonnenberger
|
r47538 | manifest.manifestrevlog.__init__(self, nodeconstants, opener) | ||
manifest2 = manifest.manifestrevlog(nodeconstants, opener2) | ||||
Augie Fackler
|
r43346 | unionrevlog.__init__( | ||
r47921 | self, opener, self._revlog.radix, manifest2, linkmapper | |||
Augie Fackler
|
r43346 | ) | ||
Mads Kiilerich
|
r18944 | |||
Gregory Szorc
|
r37515 | class unionfilelog(filelog.filelog): | ||
Matt Harbison
|
r52719 | _revlog: unionrevlog | ||
repotiprev: int | ||||
revlog2: revlog.revlog | ||||
Mads Kiilerich
|
r18944 | def __init__(self, opener, path, opener2, linkmapper, repo): | ||
filelog.filelog.__init__(self, opener, path) | ||||
filelog2 = filelog.filelog(opener2, path) | ||||
Augie Fackler
|
r43346 | self._revlog = unionrevlog( | ||
r47921 | opener, self._revlog.radix, filelog2._revlog, linkmapper | |||
Augie Fackler
|
r43346 | ) | ||
Mads Kiilerich
|
r18944 | self._repo = repo | ||
Gregory Szorc
|
r37515 | self.repotiprev = self._revlog.repotiprev | ||
self.revlog2 = self._revlog.revlog2 | ||||
Mads Kiilerich
|
r18944 | |||
Mike Edgar
|
r24118 | def iscensored(self, rev): | ||
"""Check if a revision is censored.""" | ||||
if rev <= self.repotiprev: | ||||
return filelog.filelog.iscensored(self, rev) | ||||
Sean Farley
|
r27723 | node = self.node(rev) | ||
return self.revlog2.iscensored(self.revlog2.rev(node)) | ||||
Mike Edgar
|
r24118 | |||
Augie Fackler
|
r43346 | |||
Mads Kiilerich
|
r18944 | class unionpeer(localrepo.localpeer): | ||
def canpush(self): | ||||
return False | ||||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r52767 | _union_repo_baseclass = object | ||
if typing.TYPE_CHECKING: | ||||
_union_repo_baseclass = localrepo.localrepository | ||||
class unionrepository(_union_repo_baseclass): | ||||
Gregory Szorc
|
r39641 | """Represents the union of data in 2 repositories. | ||
Instances are not usable if constructed directly. Use ``instance()`` | ||||
or ``makeunionrepository()`` to create a usable instance. | ||||
""" | ||||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r52767 | # noinspection PyMissingConstructor | ||
Gregory Szorc
|
r39641 | def __init__(self, repo2, url): | ||
self.repo2 = repo2 | ||||
self._url = url | ||||
Augie Fackler
|
r43347 | self.ui.setconfig(b'phases', b'publish', False, b'unionrepo') | ||
Mads Kiilerich
|
r18944 | |||
@localrepo.unfilteredpropertycache | ||||
def changelog(self): | ||||
Angel Ezquerra
|
r23878 | return unionchangelog(self.svfs, self.repo2.svfs) | ||
Mads Kiilerich
|
r18944 | |||
Gregory Szorc
|
r39799 | @localrepo.unfilteredpropertycache | ||
def manifestlog(self): | ||||
Augie Fackler
|
r43346 | rootstore = unionmanifest( | ||
Joerg Sonnenberger
|
r47538 | self.nodeconstants, | ||
self.svfs, | ||||
self.repo2.svfs, | ||||
self.unfiltered()._clrev, | ||||
Augie Fackler
|
r43346 | ) | ||
return manifest.manifestlog( | ||||
self.svfs, self, rootstore, self.narrowmatch() | ||||
) | ||||
Gregory Szorc
|
r39799 | |||
Mads Kiilerich
|
r18944 | def _clrev(self, rev2): | ||
"""map from repo2 changelog rev to temporary rev in self.changelog""" | ||||
node = self.repo2.changelog.node(rev2) | ||||
return self.changelog.rev(node) | ||||
def url(self): | ||||
return self._url | ||||
def file(self, f): | ||||
Augie Fackler
|
r43346 | return unionfilelog( | ||
self.svfs, f, self.repo2.svfs, self.unfiltered()._clrev, self | ||||
) | ||||
Mads Kiilerich
|
r18944 | |||
def close(self): | ||||
self.repo2.close() | ||||
def cancopy(self): | ||||
return False | ||||
Manuel Jacob
|
r51309 | def peer(self, path=None, remotehidden=False): | ||
return unionpeer(self, path=None, remotehidden=remotehidden) | ||||
Mads Kiilerich
|
r18944 | |||
def getcwd(self): | ||||
Augie Fackler
|
r43346 | return encoding.getcwd() # always outside the repo | ||
Mads Kiilerich
|
r18944 | |||
Gregory Szorc
|
r39585 | def instance(ui, path, create, intents=None, createopts=None): | ||
Mads Kiilerich
|
r18944 | if create: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'cannot create new union repository')) | ||
parentpath = ui.config(b"bundle", b"mainreporoot") | ||||
Mads Kiilerich
|
r18944 | if not parentpath: | ||
# try to find the correct path to the working directory repo | ||||
Matt Harbison
|
r39843 | parentpath = cmdutil.findrepo(encoding.getcwd()) | ||
Mads Kiilerich
|
r18944 | if parentpath is None: | ||
Augie Fackler
|
r43347 | parentpath = b'' | ||
Mads Kiilerich
|
r18944 | if parentpath: | ||
# Try to make the full path relative so we get a nice, short URL. | ||||
# In particular, we don't want temp dir names in test outputs. | ||||
Matt Harbison
|
r39843 | cwd = encoding.getcwd() | ||
Mads Kiilerich
|
r18944 | if parentpath == cwd: | ||
Augie Fackler
|
r43347 | parentpath = b'' | ||
Mads Kiilerich
|
r18944 | else: | ||
FUJIWARA Katsunori
|
r24835 | cwd = pathutil.normasprefix(cwd) | ||
Mads Kiilerich
|
r18944 | if parentpath.startswith(cwd): | ||
Augie Fackler
|
r43346 | parentpath = parentpath[len(cwd) :] | ||
Augie Fackler
|
r43347 | if path.startswith(b'union:'): | ||
s = path.split(b":", 1)[1].split(b"+", 1) | ||||
Mads Kiilerich
|
r18944 | if len(s) == 1: | ||
repopath, repopath2 = parentpath, s[0] | ||||
else: | ||||
repopath, repopath2 = s | ||||
else: | ||||
repopath, repopath2 = parentpath, path | ||||
Gregory Szorc
|
r39641 | |||
return makeunionrepository(ui, repopath, repopath2) | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r39641 | def makeunionrepository(ui, repopath1, repopath2): | ||
"""Make a union repository object from 2 local repo paths.""" | ||||
repo1 = localrepo.instance(ui, repopath1, create=False) | ||||
repo2 = localrepo.instance(ui, repopath2, create=False) | ||||
Augie Fackler
|
r43347 | url = b'union:%s+%s' % ( | ||
Augie Fackler
|
r43346 | util.expandpath(repopath1), | ||
util.expandpath(repopath2), | ||||
) | ||||
Gregory Szorc
|
r39641 | |||
class derivedunionrepository(unionrepository, repo1.__class__): | ||||
pass | ||||
repo = repo1 | ||||
repo.__class__ = derivedunionrepository | ||||
unionrepository.__init__(repo1, repo2, url) | ||||
return repo | ||||