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 | |||||