unionrepo.py
315 lines
| 9.7 KiB
| text/x-python
|
PythonLexer
/ mercurial / unionrepo.py
|
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. | ||||
""" | ||||
|
r25988 | |||
from .i18n import _ | ||||
|
r43359 | from .pycompat import getattr | ||
|
r25988 | |||
from . import ( | ||||
changelog, | ||||
cmdutil, | ||||
|
r39843 | encoding, | ||
|
r26587 | error, | ||
|
r25988 | filelog, | ||
localrepo, | ||||
manifest, | ||||
mdiff, | ||||
pathutil, | ||||
revlog, | ||||
util, | ||||
|
r31242 | vfs as vfsmod, | ||
|
r25988 | ) | ||
|
r18944 | |||
r48023 | from .revlogutils import ( | |||
constants as revlog_constants, | ||||
) | ||||
|
r43346 | |||
|
r18944 | class unionrevlog(revlog.revlog): | ||
r47921 | def __init__(self, opener, radix, revlog2, linkmapper): | |||
|
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. | ||||
|
r31242 | opener = vfsmod.readonlyvfs(opener) | ||
r47838 | target = getattr(revlog2, 'target', None) | |||
if target is None: | ||||
# 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) | |||
|
r18944 | self.revlog2 = revlog2 | ||
n = len(self) | ||||
self.repotiprev = n - 1 | ||||
|
r43346 | self.bundlerevs = set() # used by 'bundle()' revset expression | ||
|
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 | |||
|
r27433 | flags = _start & 0xFFFF | ||
|
r18944 | |||
|
r43346 | if linkmapper is None: # link is to same revlog | ||
assert linkrev == rev2 # we never link back | ||||
|
r18944 | link = n | ||
|
r43346 | else: # rev must be mapped from repo2 cl to unified cl by linkmapper | ||
|
r18944 | link = linkmapper(linkrev) | ||
|
r43346 | if linkmapper is not None: # link is to same revlog | ||
|
r26230 | base = linkmapper(base) | ||
r43965 | this_rev = self.index.get_rev(node) | |||
if this_rev is not None: | ||||
|
r18944 | # this happens for the common revlog revisions | ||
r43965 | self.bundlerevs.add(this_rev) | |||
|
r18944 | continue | ||
p1node = self.revlog2.node(p1rev) | ||||
p2node = self.revlog2.node(p2rev) | ||||
|
r46419 | # TODO: it's probably wrong to set compressed length to -1, but | ||
|
r38194 | # I have no idea if csize is valid in the base revlog context. | ||
|
r43346 | e = ( | ||
flags, | ||||
|
r46419 | -1, | ||
|
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, | |||
|
r43346 | ) | ||
|
r38886 | self.index.append(e) | ||
|
r18944 | self.bundlerevs.add(n) | ||
n += 1 | ||||
def _chunk(self, rev): | ||||
if rev <= self.repotiprev: | ||||
return revlog.revlog._chunk(self, rev) | ||||
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)), | ||||
|
r43346 | self.revlog2.rev(self.node(rev2)), | ||
) | ||||
|
r18944 | elif rev1 <= self.repotiprev and rev2 <= self.repotiprev: | ||
r43095 | return super(unionrevlog, self).revdiff(rev1, rev2) | |||
|
r18944 | |||
r43094 | return mdiff.textdiff(self.rawdata(rev1), self.rawdata(rev2)) | |||
|
r18944 | |||
r43092 | def _revisiondata(self, nodeorrev, _df=None, raw=False): | |||
|
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 | ||||
|
r18944 | else: | ||
r43092 | func = super(unionrevlog, self)._revisiondata | |||
return func(node, _df=_df, raw=raw) | ||||
|
r18944 | |||
def addrevision(self, text, transaction, link, p1=None, p2=None, d=None): | ||||
raise NotImplementedError | ||||
|
r43346 | |||
def addgroup( | ||||
self, | ||||
deltas, | ||||
linkmapper, | ||||
transaction, | ||||
|
r47085 | alwayscache=False, | ||
|
r43346 | addrevisioncb=None, | ||
|
r46373 | duplicaterevisioncb=None, | ||
|
r43346 | maybemissingparents=False, | ||
): | ||||
|
r18944 | raise NotImplementedError | ||
|
r43346 | |||
|
r42382 | def strip(self, minlink, transaction): | ||
|
r18944 | raise NotImplementedError | ||
|
r43346 | |||
|
r18944 | def checksize(self): | ||
raise NotImplementedError | ||||
|
r43346 | |||
|
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) | |||
|
r43346 | |||
|
r18944 | |||
|
r30374 | class unionmanifest(unionrevlog, manifest.manifestrevlog): | ||
|
r47538 | def __init__(self, nodeconstants, opener, opener2, linkmapper): | ||
manifest.manifestrevlog.__init__(self, nodeconstants, opener) | ||||
manifest2 = manifest.manifestrevlog(nodeconstants, opener2) | ||||
|
r43346 | unionrevlog.__init__( | ||
r47921 | self, opener, self._revlog.radix, manifest2, linkmapper | |||
|
r43346 | ) | ||
|
r18944 | |||
|
r37515 | class unionfilelog(filelog.filelog): | ||
|
r18944 | def __init__(self, opener, path, opener2, linkmapper, repo): | ||
filelog.filelog.__init__(self, opener, path) | ||||
filelog2 = filelog.filelog(opener2, path) | ||||
|
r43346 | self._revlog = unionrevlog( | ||
r47921 | opener, self._revlog.radix, filelog2._revlog, linkmapper | |||
|
r43346 | ) | ||
|
r18944 | self._repo = repo | ||
|
r37515 | self.repotiprev = self._revlog.repotiprev | ||
self.revlog2 = self._revlog.revlog2 | ||||
|
r18944 | |||
|
r24118 | def iscensored(self, rev): | ||
"""Check if a revision is censored.""" | ||||
if rev <= self.repotiprev: | ||||
return filelog.filelog.iscensored(self, rev) | ||||
|
r27723 | node = self.node(rev) | ||
return self.revlog2.iscensored(self.revlog2.rev(node)) | ||||
|
r24118 | |||
|
r43346 | |||
|
r18944 | class unionpeer(localrepo.localpeer): | ||
def canpush(self): | ||||
return False | ||||
|
r43346 | |||
|
r49801 | class unionrepository: | ||
|
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. | ||||
""" | ||||
|
r43346 | |||
|
r39641 | def __init__(self, repo2, url): | ||
self.repo2 = repo2 | ||||
self._url = url | ||||
|
r43347 | self.ui.setconfig(b'phases', b'publish', False, b'unionrepo') | ||
|
r18944 | |||
@localrepo.unfilteredpropertycache | ||||
def changelog(self): | ||||
|
r23878 | return unionchangelog(self.svfs, self.repo2.svfs) | ||
|
r18944 | |||
|
r39799 | @localrepo.unfilteredpropertycache | ||
def manifestlog(self): | ||||
|
r43346 | rootstore = unionmanifest( | ||
|
r47538 | self.nodeconstants, | ||
self.svfs, | ||||
self.repo2.svfs, | ||||
self.unfiltered()._clrev, | ||||
|
r43346 | ) | ||
return manifest.manifestlog( | ||||
self.svfs, self, rootstore, self.narrowmatch() | ||||
) | ||||
|
r39799 | |||
|
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): | ||||
|
r43346 | return unionfilelog( | ||
self.svfs, f, self.repo2.svfs, self.unfiltered()._clrev, self | ||||
) | ||||
|
r18944 | |||
def close(self): | ||||
self.repo2.close() | ||||
def cancopy(self): | ||||
return False | ||||
def peer(self): | ||||
return unionpeer(self) | ||||
def getcwd(self): | ||||
|
r43346 | return encoding.getcwd() # always outside the repo | ||
|
r18944 | |||
|
r39585 | def instance(ui, path, create, intents=None, createopts=None): | ||
|
r18944 | if create: | ||
|
r43347 | raise error.Abort(_(b'cannot create new union repository')) | ||
parentpath = ui.config(b"bundle", b"mainreporoot") | ||||
|
r18944 | if not parentpath: | ||
# try to find the correct path to the working directory repo | ||||
|
r39843 | parentpath = cmdutil.findrepo(encoding.getcwd()) | ||
|
r18944 | if parentpath is None: | ||
|
r43347 | parentpath = b'' | ||
|
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. | ||||
|
r39843 | cwd = encoding.getcwd() | ||
|
r18944 | if parentpath == cwd: | ||
|
r43347 | parentpath = b'' | ||
|
r18944 | else: | ||
|
r24835 | cwd = pathutil.normasprefix(cwd) | ||
|
r18944 | if parentpath.startswith(cwd): | ||
|
r43346 | parentpath = parentpath[len(cwd) :] | ||
|
r43347 | if path.startswith(b'union:'): | ||
s = path.split(b":", 1)[1].split(b"+", 1) | ||||
|
r18944 | if len(s) == 1: | ||
repopath, repopath2 = parentpath, s[0] | ||||
else: | ||||
repopath, repopath2 = s | ||||
else: | ||||
repopath, repopath2 = parentpath, path | ||||
|
r39641 | |||
return makeunionrepository(ui, repopath, repopath2) | ||||
|
r43346 | |||
|
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) | ||||
|
r43347 | url = b'union:%s+%s' % ( | ||
|
r43346 | util.expandpath(repopath1), | ||
util.expandpath(repopath2), | ||||
) | ||||
|
r39641 | |||
class derivedunionrepository(unionrepository, repo1.__class__): | ||||
pass | ||||
repo = repo1 | ||||
repo.__class__ = derivedunionrepository | ||||
unionrepository.__init__(repo1, repo2, url) | ||||
return repo | ||||