##// END OF EJS Templates
narrowrevlog: replace AssertionError with ProgrammingError...
Augie Fackler -
r36111:142ce66a default
parent child Browse files
Show More
@@ -1,176 +1,177 b''
1 1 # narrowrevlog.py - revlog storing irrelevant nodes as "ellipsis" nodes
2 2 #
3 3 # Copyright 2017 Google, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 from mercurial import (
11 error,
11 12 manifest,
12 13 revlog,
13 14 util,
14 15 )
15 16
16 17 def readtransform(self, text):
17 18 return text, False
18 19
19 20 def writetransform(self, text):
20 21 return text, False
21 22
22 23 def rawtransform(self, text):
23 24 return False
24 25
25 26 revlog.addflagprocessor(revlog.REVIDX_ELLIPSIS,
26 27 (readtransform, writetransform, rawtransform))
27 28
28 29 def setup():
29 30 # We just wanted to add the flag processor, which is done at module
30 31 # load time.
31 32 pass
32 33
33 34 class excludeddir(manifest.treemanifest):
34 35 """Stand-in for a directory that is excluded from the repository.
35 36
36 37 With narrowing active on a repository that uses treemanifests,
37 38 some of the directory revlogs will be excluded from the resulting
38 39 clone. This is a huge storage win for clients, but means we need
39 40 some sort of pseudo-manifest to surface to internals so we can
40 41 detect a merge conflict outside the narrowspec. That's what this
41 42 class is: it stands in for a directory whose node is known, but
42 43 whose contents are unknown.
43 44 """
44 45 def __init__(self, dir, node):
45 46 super(excludeddir, self).__init__(dir)
46 47 self._node = node
47 48 # Add an empty file, which will be included by iterators and such,
48 49 # appearing as the directory itself (i.e. something like "dir/")
49 50 self._files[''] = node
50 51 self._flags[''] = 't'
51 52
52 53 # Manifests outside the narrowspec should never be modified, so avoid
53 54 # copying. This makes a noticeable difference when there are very many
54 55 # directories outside the narrowspec. Also, it makes sense for the copy to
55 56 # be of the same type as the original, which would not happen with the
56 57 # super type's copy().
57 58 def copy(self):
58 59 return self
59 60
60 61 class excludeddirmanifestctx(manifest.treemanifestctx):
61 62 """context wrapper for excludeddir - see that docstring for rationale"""
62 63 def __init__(self, dir, node):
63 64 self._dir = dir
64 65 self._node = node
65 66
66 67 def read(self):
67 68 return excludeddir(self._dir, self._node)
68 69
69 70 def write(self, *args):
70 raise AssertionError('Attempt to write manifest from excluded dir %s' %
71 self._dir)
71 raise error.ProgrammingError(
72 'attempt to write manifest from excluded dir %s' % self._dir)
72 73
73 74 class excludedmanifestrevlog(manifest.manifestrevlog):
74 75 """Stand-in for excluded treemanifest revlogs.
75 76
76 77 When narrowing is active on a treemanifest repository, we'll have
77 78 references to directories we can't see due to the revlog being
78 79 skipped. This class exists to conform to the manifestrevlog
79 80 interface for those directories and proactively prevent writes to
80 81 outside the narrowspec.
81 82 """
82 83
83 84 def __init__(self, dir):
84 85 self._dir = dir
85 86
86 87 def __len__(self):
87 raise AssertionError('Attempt to get length of excluded dir %s' %
88 self._dir)
88 raise error.ProgrammingError(
89 'attempt to get length of excluded dir %s' % self._dir)
89 90
90 91 def rev(self, node):
91 raise AssertionError('Attempt to get rev from excluded dir %s' %
92 self._dir)
92 raise error.ProgrammingError(
93 'attempt to get rev from excluded dir %s' % self._dir)
93 94
94 95 def linkrev(self, node):
95 raise AssertionError('Attempt to get linkrev from excluded dir %s' %
96 self._dir)
96 raise error.ProgrammingError(
97 'attempt to get linkrev from excluded dir %s' % self._dir)
97 98
98 99 def node(self, rev):
99 raise AssertionError('Attempt to get node from excluded dir %s' %
100 self._dir)
100 raise error.ProgrammingError(
101 'attempt to get node from excluded dir %s' % self._dir)
101 102
102 103 def add(self, *args, **kwargs):
103 104 # We should never write entries in dirlogs outside the narrow clone.
104 105 # However, the method still gets called from writesubtree() in
105 106 # _addtree(), so we need to handle it. We should possibly make that
106 107 # avoid calling add() with a clean manifest (_dirty is always False
107 108 # in excludeddir instances).
108 109 pass
109 110
110 111 def makenarrowmanifestrevlog(mfrevlog, repo):
111 112 if util.safehasattr(mfrevlog, '_narrowed'):
112 113 return
113 114
114 115 class narrowmanifestrevlog(mfrevlog.__class__):
115 116 # This function is called via debug{revlog,index,data}, but also during
116 117 # at least some push operations. This will be used to wrap/exclude the
117 118 # child directories when using treemanifests.
118 119 def dirlog(self, dir):
119 120 if dir and not dir.endswith('/'):
120 121 dir = dir + '/'
121 122 if not repo.narrowmatch().visitdir(dir[:-1] or '.'):
122 123 return excludedmanifestrevlog(dir)
123 124 result = super(narrowmanifestrevlog, self).dirlog(dir)
124 125 makenarrowmanifestrevlog(result, repo)
125 126 return result
126 127
127 128 mfrevlog.__class__ = narrowmanifestrevlog
128 129 mfrevlog._narrowed = True
129 130
130 131 def makenarrowmanifestlog(mfl, repo):
131 132 class narrowmanifestlog(mfl.__class__):
132 133 def get(self, dir, node, verify=True):
133 134 if not repo.narrowmatch().visitdir(dir[:-1] or '.'):
134 135 return excludeddirmanifestctx(dir, node)
135 136 return super(narrowmanifestlog, self).get(dir, node, verify=verify)
136 137 mfl.__class__ = narrowmanifestlog
137 138
138 139 def makenarrowfilelog(fl, narrowmatch):
139 140 class narrowfilelog(fl.__class__):
140 141 def renamed(self, node):
141 142 m = super(narrowfilelog, self).renamed(node)
142 143 if m and not narrowmatch(m[0]):
143 144 return None
144 145 return m
145 146
146 147 def size(self, rev):
147 148 # We take advantage of the fact that remotefilelog
148 149 # lacks a node() method to just skip the
149 150 # rename-checking logic when on remotefilelog. This
150 151 # might be incorrect on other non-revlog-based storage
151 152 # engines, but for now this seems to be fine.
152 153 if util.safehasattr(self, 'node'):
153 154 node = self.node(rev)
154 155 # Because renamed() is overridden above to
155 156 # sometimes return None even if there is metadata
156 157 # in the revlog, size can be incorrect for
157 158 # copies/renames, so we need to make sure we call
158 159 # the super class's implementation of renamed()
159 160 # for the purpose of size calculation.
160 161 if super(narrowfilelog, self).renamed(node):
161 162 return len(self.read(node))
162 163 return super(narrowfilelog, self).size(rev)
163 164
164 165 def cmp(self, node, text):
165 166 different = super(narrowfilelog, self).cmp(node, text)
166 167 if different:
167 168 # Similar to size() above, if the file was copied from
168 169 # a file outside the narrowspec, the super class's
169 170 # would have returned True because we tricked it into
170 171 # thinking that the file was not renamed.
171 172 if super(narrowfilelog, self).renamed(node):
172 173 t2 = self.read(node)
173 174 return t2 != text
174 175 return different
175 176
176 177 fl.__class__ = narrowfilelog
General Comments 0
You need to be logged in to leave comments. Login now