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