Details
Details
- Reviewers
durin42 indygreg - Group Reviewers
hg-reviewers - Commits
- rHG1b2fa531fd7a: narrow: move excludeddir and related classes to core
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Lint
Lint Skipped - Unit
Unit Tests Skipped
durin42 | |
indygreg |
hg-reviewers |
Lint Skipped |
Unit Tests Skipped |
Path | Packages | |||
---|---|---|---|---|
M | hgext/narrow/narrowrevlog.py (82 lines) | |||
M | mercurial/manifest.py (77 lines) |
Status | Author | Revision | |
---|---|---|---|
Closed | martinvonz | ||
Closed | martinvonz | ||
Closed | martinvonz |
# narrowrevlog.py - revlog storing irrelevant nodes as "ellipsis" nodes | # narrowrevlog.py - revlog storing irrelevant nodes as "ellipsis" nodes | ||||
# | # | ||||
# Copyright 2017 Google, Inc. | # Copyright 2017 Google, Inc. | ||||
# | # | ||||
# This software may be used and distributed according to the terms of the | # This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | # GNU General Public License version 2 or any later version. | ||||
from __future__ import absolute_import | from __future__ import absolute_import | ||||
from mercurial import ( | from mercurial import ( | ||||
error, | |||||
manifest, | manifest, | ||||
revlog, | revlog, | ||||
util, | util, | ||||
) | ) | ||||
def readtransform(self, text): | def readtransform(self, text): | ||||
return text, False | return text, False | ||||
def writetransform(self, text): | def writetransform(self, text): | ||||
return text, False | return text, False | ||||
def rawtransform(self, text): | def rawtransform(self, text): | ||||
return False | return False | ||||
revlog.addflagprocessor(revlog.REVIDX_ELLIPSIS, | revlog.addflagprocessor(revlog.REVIDX_ELLIPSIS, | ||||
(readtransform, writetransform, rawtransform)) | (readtransform, writetransform, rawtransform)) | ||||
def setup(): | def setup(): | ||||
# We just wanted to add the flag processor, which is done at module | # We just wanted to add the flag processor, which is done at module | ||||
# load time. | # load time. | ||||
pass | pass | ||||
class excludeddir(manifest.treemanifest): | |||||
"""Stand-in for a directory that is excluded from the repository. | |||||
With narrowing active on a repository that uses treemanifests, | |||||
some of the directory revlogs will be excluded from the resulting | |||||
clone. This is a huge storage win for clients, but means we need | |||||
some sort of pseudo-manifest to surface to internals so we can | |||||
detect a merge conflict outside the narrowspec. That's what this | |||||
class is: it stands in for a directory whose node is known, but | |||||
whose contents are unknown. | |||||
""" | |||||
def __init__(self, dir, node): | |||||
super(excludeddir, self).__init__(dir) | |||||
self._node = node | |||||
# Add an empty file, which will be included by iterators and such, | |||||
# appearing as the directory itself (i.e. something like "dir/") | |||||
self._files[''] = node | |||||
self._flags[''] = 't' | |||||
# Manifests outside the narrowspec should never be modified, so avoid | |||||
# copying. This makes a noticeable difference when there are very many | |||||
# directories outside the narrowspec. Also, it makes sense for the copy to | |||||
# be of the same type as the original, which would not happen with the | |||||
# super type's copy(). | |||||
def copy(self): | |||||
return self | |||||
class excludeddirmanifestctx(manifest.treemanifestctx): | |||||
"""context wrapper for excludeddir - see that docstring for rationale""" | |||||
def __init__(self, dir, node): | |||||
self._dir = dir | |||||
self._node = node | |||||
def read(self): | |||||
return excludeddir(self._dir, self._node) | |||||
def write(self, *args): | |||||
raise error.ProgrammingError( | |||||
'attempt to write manifest from excluded dir %s' % self._dir) | |||||
class excludedmanifestrevlog(manifest.manifestrevlog): | |||||
"""Stand-in for excluded treemanifest revlogs. | |||||
When narrowing is active on a treemanifest repository, we'll have | |||||
references to directories we can't see due to the revlog being | |||||
skipped. This class exists to conform to the manifestrevlog | |||||
interface for those directories and proactively prevent writes to | |||||
outside the narrowspec. | |||||
""" | |||||
def __init__(self, dir): | |||||
self._dir = dir | |||||
def __len__(self): | |||||
raise error.ProgrammingError( | |||||
'attempt to get length of excluded dir %s' % self._dir) | |||||
def rev(self, node): | |||||
raise error.ProgrammingError( | |||||
'attempt to get rev from excluded dir %s' % self._dir) | |||||
def linkrev(self, node): | |||||
raise error.ProgrammingError( | |||||
'attempt to get linkrev from excluded dir %s' % self._dir) | |||||
def node(self, rev): | |||||
raise error.ProgrammingError( | |||||
'attempt to get node from excluded dir %s' % self._dir) | |||||
def add(self, *args, **kwargs): | |||||
# We should never write entries in dirlogs outside the narrow clone. | |||||
# However, the method still gets called from writesubtree() in | |||||
# _addtree(), so we need to handle it. We should possibly make that | |||||
# avoid calling add() with a clean manifest (_dirty is always False | |||||
# in excludeddir instances). | |||||
pass | |||||
def makenarrowmanifestrevlog(mfrevlog, repo): | def makenarrowmanifestrevlog(mfrevlog, repo): | ||||
if util.safehasattr(mfrevlog, '_narrowed'): | if util.safehasattr(mfrevlog, '_narrowed'): | ||||
return | return | ||||
class narrowmanifestrevlog(mfrevlog.__class__): | class narrowmanifestrevlog(mfrevlog.__class__): | ||||
# This function is called via debug{revlog,index,data}, but also during | # This function is called via debug{revlog,index,data}, but also during | ||||
# at least some push operations. This will be used to wrap/exclude the | # at least some push operations. This will be used to wrap/exclude the | ||||
# child directories when using treemanifests. | # child directories when using treemanifests. | ||||
def dirlog(self, d): | def dirlog(self, d): | ||||
if not repo.narrowmatch().visitdir(d[:-1] or '.'): | if not repo.narrowmatch().visitdir(d[:-1] or '.'): | ||||
return excludedmanifestrevlog(d) | return manifest.excludedmanifestrevlog(d) | ||||
result = super(narrowmanifestrevlog, self).dirlog(d) | result = super(narrowmanifestrevlog, self).dirlog(d) | ||||
makenarrowmanifestrevlog(result, repo) | makenarrowmanifestrevlog(result, repo) | ||||
return result | return result | ||||
mfrevlog.__class__ = narrowmanifestrevlog | mfrevlog.__class__ = narrowmanifestrevlog | ||||
mfrevlog._narrowed = True | mfrevlog._narrowed = True | ||||
def makenarrowmanifestlog(mfl, repo): | def makenarrowmanifestlog(mfl, repo): | ||||
class narrowmanifestlog(mfl.__class__): | class narrowmanifestlog(mfl.__class__): | ||||
def get(self, dir, node, verify=True): | def get(self, dir, node, verify=True): | ||||
if not repo.narrowmatch().visitdir(dir[:-1] or '.'): | if not repo.narrowmatch().visitdir(dir[:-1] or '.'): | ||||
return excludeddirmanifestctx(dir, node) | return manifest.excludeddirmanifestctx(dir, node) | ||||
return super(narrowmanifestlog, self).get(dir, node, verify=verify) | return super(narrowmanifestlog, self).get(dir, node, verify=verify) | ||||
mfl.__class__ = narrowmanifestlog | mfl.__class__ = narrowmanifestlog | ||||
def makenarrowfilelog(fl, narrowmatch): | def makenarrowfilelog(fl, narrowmatch): | ||||
class narrowfilelog(fl.__class__): | class narrowfilelog(fl.__class__): | ||||
def renamed(self, node): | def renamed(self, node): | ||||
# Renames that come from outside the narrowspec are | # Renames that come from outside the narrowspec are | ||||
# problematic at least for git-diffs, because we lack the | # problematic at least for git-diffs, because we lack the |
if shallow: | if shallow: | ||||
return manifestdict(rl.revision(self._node)) | return manifestdict(rl.revision(self._node)) | ||||
else: | else: | ||||
return self.read() | return self.read() | ||||
def find(self, key): | def find(self, key): | ||||
return self.read().find(key) | return self.read().find(key) | ||||
class excludeddir(treemanifest): | |||||
"""Stand-in for a directory that is excluded from the repository. | |||||
With narrowing active on a repository that uses treemanifests, | |||||
some of the directory revlogs will be excluded from the resulting | |||||
clone. This is a huge storage win for clients, but means we need | |||||
some sort of pseudo-manifest to surface to internals so we can | |||||
detect a merge conflict outside the narrowspec. That's what this | |||||
class is: it stands in for a directory whose node is known, but | |||||
whose contents are unknown. | |||||
""" | |||||
def __init__(self, dir, node): | |||||
super(excludeddir, self).__init__(dir) | |||||
self._node = node | |||||
# Add an empty file, which will be included by iterators and such, | |||||
# appearing as the directory itself (i.e. something like "dir/") | |||||
self._files[''] = node | |||||
self._flags[''] = 't' | |||||
# Manifests outside the narrowspec should never be modified, so avoid | |||||
# copying. This makes a noticeable difference when there are very many | |||||
# directories outside the narrowspec. Also, it makes sense for the copy to | |||||
# be of the same type as the original, which would not happen with the | |||||
# super type's copy(). | |||||
def copy(self): | |||||
return self | |||||
class excludeddirmanifestctx(treemanifestctx): | |||||
"""context wrapper for excludeddir - see that docstring for rationale""" | |||||
def __init__(self, dir, node): | |||||
self._dir = dir | |||||
self._node = node | |||||
def read(self): | |||||
return excludeddir(self._dir, self._node) | |||||
def write(self, *args): | |||||
raise error.ProgrammingError( | |||||
'attempt to write manifest from excluded dir %s' % self._dir) | |||||
class excludedmanifestrevlog(manifestrevlog): | |||||
"""Stand-in for excluded treemanifest revlogs. | |||||
When narrowing is active on a treemanifest repository, we'll have | |||||
references to directories we can't see due to the revlog being | |||||
skipped. This class exists to conform to the manifestrevlog | |||||
interface for those directories and proactively prevent writes to | |||||
outside the narrowspec. | |||||
""" | |||||
def __init__(self, dir): | |||||
self._dir = dir | |||||
def __len__(self): | |||||
raise error.ProgrammingError( | |||||
'attempt to get length of excluded dir %s' % self._dir) | |||||
def rev(self, node): | |||||
raise error.ProgrammingError( | |||||
'attempt to get rev from excluded dir %s' % self._dir) | |||||
def linkrev(self, node): | |||||
raise error.ProgrammingError( | |||||
'attempt to get linkrev from excluded dir %s' % self._dir) | |||||
def node(self, rev): | |||||
raise error.ProgrammingError( | |||||
'attempt to get node from excluded dir %s' % self._dir) | |||||
def add(self, *args, **kwargs): | |||||
# We should never write entries in dirlogs outside the narrow clone. | |||||
# However, the method still gets called from writesubtree() in | |||||
# _addtree(), so we need to handle it. We should possibly make that | |||||
# avoid calling add() with a clean manifest (_dirty is always False | |||||
# in excludeddir instances). | |||||
pass |