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