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