##// END OF EJS Templates
unionrepo: avoid unnecessary node -> rev conversion
Jun Wu -
r31724:f1e0446e default
parent child Browse files
Show More
@@ -1,260 +1,259 b''
1 # unionrepo.py - repository class for viewing union of repository changesets
1 # unionrepo.py - repository class for viewing union of repository changesets
2 #
2 #
3 # Derived from bundlerepo.py
3 # Derived from bundlerepo.py
4 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
4 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
5 # Copyright 2013 Unity Technologies, Mads Kiilerich <madski@unity3d.com>
5 # Copyright 2013 Unity Technologies, Mads Kiilerich <madski@unity3d.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Repository class for "in-memory pull" of one local repository to another,
10 """Repository class for "in-memory pull" of one local repository to another,
11 allowing operations like diff and log with revsets.
11 allowing operations like diff and log with revsets.
12 """
12 """
13
13
14 from __future__ import absolute_import
14 from __future__ import absolute_import
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import nullid
17 from .node import nullid
18
18
19 from . import (
19 from . import (
20 changelog,
20 changelog,
21 cmdutil,
21 cmdutil,
22 error,
22 error,
23 filelog,
23 filelog,
24 localrepo,
24 localrepo,
25 manifest,
25 manifest,
26 mdiff,
26 mdiff,
27 pathutil,
27 pathutil,
28 pycompat,
28 pycompat,
29 revlog,
29 revlog,
30 util,
30 util,
31 vfs as vfsmod,
31 vfs as vfsmod,
32 )
32 )
33
33
34 class unionrevlog(revlog.revlog):
34 class unionrevlog(revlog.revlog):
35 def __init__(self, opener, indexfile, revlog2, linkmapper):
35 def __init__(self, opener, indexfile, revlog2, linkmapper):
36 # How it works:
36 # How it works:
37 # To retrieve a revision, we just need to know the node id so we can
37 # To retrieve a revision, we just need to know the node id so we can
38 # look it up in revlog2.
38 # look it up in revlog2.
39 #
39 #
40 # To differentiate a rev in the second revlog from a rev in the revlog,
40 # To differentiate a rev in the second revlog from a rev in the revlog,
41 # we check revision against repotiprev.
41 # we check revision against repotiprev.
42 opener = vfsmod.readonlyvfs(opener)
42 opener = vfsmod.readonlyvfs(opener)
43 revlog.revlog.__init__(self, opener, indexfile)
43 revlog.revlog.__init__(self, opener, indexfile)
44 self.revlog2 = revlog2
44 self.revlog2 = revlog2
45
45
46 n = len(self)
46 n = len(self)
47 self.repotiprev = n - 1
47 self.repotiprev = n - 1
48 self.bundlerevs = set() # used by 'bundle()' revset expression
48 self.bundlerevs = set() # used by 'bundle()' revset expression
49 for rev2 in self.revlog2:
49 for rev2 in self.revlog2:
50 rev = self.revlog2.index[rev2]
50 rev = self.revlog2.index[rev2]
51 # rev numbers - in revlog2, very different from self.rev
51 # rev numbers - in revlog2, very different from self.rev
52 _start, _csize, _rsize, base, linkrev, p1rev, p2rev, node = rev
52 _start, _csize, _rsize, base, linkrev, p1rev, p2rev, node = rev
53 flags = _start & 0xFFFF
53 flags = _start & 0xFFFF
54
54
55 if linkmapper is None: # link is to same revlog
55 if linkmapper is None: # link is to same revlog
56 assert linkrev == rev2 # we never link back
56 assert linkrev == rev2 # we never link back
57 link = n
57 link = n
58 else: # rev must be mapped from repo2 cl to unified cl by linkmapper
58 else: # rev must be mapped from repo2 cl to unified cl by linkmapper
59 link = linkmapper(linkrev)
59 link = linkmapper(linkrev)
60
60
61 if linkmapper is not None: # link is to same revlog
61 if linkmapper is not None: # link is to same revlog
62 base = linkmapper(base)
62 base = linkmapper(base)
63
63
64 if node in self.nodemap:
64 if node in self.nodemap:
65 # this happens for the common revlog revisions
65 # this happens for the common revlog revisions
66 self.bundlerevs.add(self.nodemap[node])
66 self.bundlerevs.add(self.nodemap[node])
67 continue
67 continue
68
68
69 p1node = self.revlog2.node(p1rev)
69 p1node = self.revlog2.node(p1rev)
70 p2node = self.revlog2.node(p2rev)
70 p2node = self.revlog2.node(p2rev)
71
71
72 e = (flags, None, None, base,
72 e = (flags, None, None, base,
73 link, self.rev(p1node), self.rev(p2node), node)
73 link, self.rev(p1node), self.rev(p2node), node)
74 self.index.insert(-1, e)
74 self.index.insert(-1, e)
75 self.nodemap[node] = n
75 self.nodemap[node] = n
76 self.bundlerevs.add(n)
76 self.bundlerevs.add(n)
77 n += 1
77 n += 1
78
78
79 def _chunk(self, rev):
79 def _chunk(self, rev):
80 if rev <= self.repotiprev:
80 if rev <= self.repotiprev:
81 return revlog.revlog._chunk(self, rev)
81 return revlog.revlog._chunk(self, rev)
82 return self.revlog2._chunk(self.node(rev))
82 return self.revlog2._chunk(self.node(rev))
83
83
84 def revdiff(self, rev1, rev2):
84 def revdiff(self, rev1, rev2):
85 """return or calculate a delta between two revisions"""
85 """return or calculate a delta between two revisions"""
86 if rev1 > self.repotiprev and rev2 > self.repotiprev:
86 if rev1 > self.repotiprev and rev2 > self.repotiprev:
87 return self.revlog2.revdiff(
87 return self.revlog2.revdiff(
88 self.revlog2.rev(self.node(rev1)),
88 self.revlog2.rev(self.node(rev1)),
89 self.revlog2.rev(self.node(rev2)))
89 self.revlog2.rev(self.node(rev2)))
90 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
90 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
91 return self.baserevdiff(rev1, rev2)
91 return self.baserevdiff(rev1, rev2)
92
92
93 return mdiff.textdiff(self.revision(self.node(rev1)),
93 return mdiff.textdiff(self.revision(rev1), self.revision(rev2))
94 self.revision(self.node(rev2)))
95
94
96 def revision(self, nodeorrev, raw=False):
95 def revision(self, nodeorrev, raw=False):
97 """return an uncompressed revision of a given node or revision
96 """return an uncompressed revision of a given node or revision
98 number.
97 number.
99 """
98 """
100 if isinstance(nodeorrev, int):
99 if isinstance(nodeorrev, int):
101 rev = nodeorrev
100 rev = nodeorrev
102 node = self.node(rev)
101 node = self.node(rev)
103 else:
102 else:
104 node = nodeorrev
103 node = nodeorrev
105 rev = self.rev(node)
104 rev = self.rev(node)
106
105
107 if node == nullid:
106 if node == nullid:
108 return ""
107 return ""
109
108
110 if rev > self.repotiprev:
109 if rev > self.repotiprev:
111 text = self.revlog2.revision(node)
110 text = self.revlog2.revision(node)
112 self._cache = (node, rev, text)
111 self._cache = (node, rev, text)
113 else:
112 else:
114 text = self.baserevision(rev)
113 text = self.baserevision(rev)
115 # already cached
114 # already cached
116 return text
115 return text
117
116
118 def baserevision(self, nodeorrev):
117 def baserevision(self, nodeorrev):
119 # Revlog subclasses may override 'revision' method to modify format of
118 # Revlog subclasses may override 'revision' method to modify format of
120 # content retrieved from revlog. To use unionrevlog with such class one
119 # content retrieved from revlog. To use unionrevlog with such class one
121 # needs to override 'baserevision' and make more specific call here.
120 # needs to override 'baserevision' and make more specific call here.
122 return revlog.revlog.revision(self, nodeorrev)
121 return revlog.revlog.revision(self, nodeorrev)
123
122
124 def baserevdiff(self, rev1, rev2):
123 def baserevdiff(self, rev1, rev2):
125 # Exists for the same purpose as baserevision.
124 # Exists for the same purpose as baserevision.
126 return revlog.revlog.revdiff(self, rev1, rev2)
125 return revlog.revlog.revdiff(self, rev1, rev2)
127
126
128 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
127 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
129 raise NotImplementedError
128 raise NotImplementedError
130 def addgroup(self, revs, linkmapper, transaction):
129 def addgroup(self, revs, linkmapper, transaction):
131 raise NotImplementedError
130 raise NotImplementedError
132 def strip(self, rev, minlink):
131 def strip(self, rev, minlink):
133 raise NotImplementedError
132 raise NotImplementedError
134 def checksize(self):
133 def checksize(self):
135 raise NotImplementedError
134 raise NotImplementedError
136
135
137 class unionchangelog(unionrevlog, changelog.changelog):
136 class unionchangelog(unionrevlog, changelog.changelog):
138 def __init__(self, opener, opener2):
137 def __init__(self, opener, opener2):
139 changelog.changelog.__init__(self, opener)
138 changelog.changelog.__init__(self, opener)
140 linkmapper = None
139 linkmapper = None
141 changelog2 = changelog.changelog(opener2)
140 changelog2 = changelog.changelog(opener2)
142 unionrevlog.__init__(self, opener, self.indexfile, changelog2,
141 unionrevlog.__init__(self, opener, self.indexfile, changelog2,
143 linkmapper)
142 linkmapper)
144
143
145 def baserevision(self, nodeorrev):
144 def baserevision(self, nodeorrev):
146 # Although changelog doesn't override 'revision' method, some extensions
145 # Although changelog doesn't override 'revision' method, some extensions
147 # may replace this class with another that does. Same story with
146 # may replace this class with another that does. Same story with
148 # manifest and filelog classes.
147 # manifest and filelog classes.
149 return changelog.changelog.revision(self, nodeorrev)
148 return changelog.changelog.revision(self, nodeorrev)
150
149
151 def baserevdiff(self, rev1, rev2):
150 def baserevdiff(self, rev1, rev2):
152 return changelog.changelog.revdiff(self, rev1, rev2)
151 return changelog.changelog.revdiff(self, rev1, rev2)
153
152
154 class unionmanifest(unionrevlog, manifest.manifestrevlog):
153 class unionmanifest(unionrevlog, manifest.manifestrevlog):
155 def __init__(self, opener, opener2, linkmapper):
154 def __init__(self, opener, opener2, linkmapper):
156 manifest.manifestrevlog.__init__(self, opener)
155 manifest.manifestrevlog.__init__(self, opener)
157 manifest2 = manifest.manifestrevlog(opener2)
156 manifest2 = manifest.manifestrevlog(opener2)
158 unionrevlog.__init__(self, opener, self.indexfile, manifest2,
157 unionrevlog.__init__(self, opener, self.indexfile, manifest2,
159 linkmapper)
158 linkmapper)
160
159
161 def baserevision(self, nodeorrev):
160 def baserevision(self, nodeorrev):
162 return manifest.manifestrevlog.revision(self, nodeorrev)
161 return manifest.manifestrevlog.revision(self, nodeorrev)
163
162
164 def baserevdiff(self, rev1, rev2):
163 def baserevdiff(self, rev1, rev2):
165 return manifest.manifestrevlog.revdiff(self, rev1, rev2)
164 return manifest.manifestrevlog.revdiff(self, rev1, rev2)
166
165
167 class unionfilelog(unionrevlog, filelog.filelog):
166 class unionfilelog(unionrevlog, filelog.filelog):
168 def __init__(self, opener, path, opener2, linkmapper, repo):
167 def __init__(self, opener, path, opener2, linkmapper, repo):
169 filelog.filelog.__init__(self, opener, path)
168 filelog.filelog.__init__(self, opener, path)
170 filelog2 = filelog.filelog(opener2, path)
169 filelog2 = filelog.filelog(opener2, path)
171 unionrevlog.__init__(self, opener, self.indexfile, filelog2,
170 unionrevlog.__init__(self, opener, self.indexfile, filelog2,
172 linkmapper)
171 linkmapper)
173 self._repo = repo
172 self._repo = repo
174
173
175 def baserevision(self, nodeorrev):
174 def baserevision(self, nodeorrev):
176 return filelog.filelog.revision(self, nodeorrev)
175 return filelog.filelog.revision(self, nodeorrev)
177
176
178 def baserevdiff(self, rev1, rev2):
177 def baserevdiff(self, rev1, rev2):
179 return filelog.filelog.revdiff(self, rev1, rev2)
178 return filelog.filelog.revdiff(self, rev1, rev2)
180
179
181 def iscensored(self, rev):
180 def iscensored(self, rev):
182 """Check if a revision is censored."""
181 """Check if a revision is censored."""
183 if rev <= self.repotiprev:
182 if rev <= self.repotiprev:
184 return filelog.filelog.iscensored(self, rev)
183 return filelog.filelog.iscensored(self, rev)
185 node = self.node(rev)
184 node = self.node(rev)
186 return self.revlog2.iscensored(self.revlog2.rev(node))
185 return self.revlog2.iscensored(self.revlog2.rev(node))
187
186
188 class unionpeer(localrepo.localpeer):
187 class unionpeer(localrepo.localpeer):
189 def canpush(self):
188 def canpush(self):
190 return False
189 return False
191
190
192 class unionrepository(localrepo.localrepository):
191 class unionrepository(localrepo.localrepository):
193 def __init__(self, ui, path, path2):
192 def __init__(self, ui, path, path2):
194 localrepo.localrepository.__init__(self, ui, path)
193 localrepo.localrepository.__init__(self, ui, path)
195 self.ui.setconfig('phases', 'publish', False, 'unionrepo')
194 self.ui.setconfig('phases', 'publish', False, 'unionrepo')
196
195
197 self._url = 'union:%s+%s' % (util.expandpath(path),
196 self._url = 'union:%s+%s' % (util.expandpath(path),
198 util.expandpath(path2))
197 util.expandpath(path2))
199 self.repo2 = localrepo.localrepository(ui, path2)
198 self.repo2 = localrepo.localrepository(ui, path2)
200
199
201 @localrepo.unfilteredpropertycache
200 @localrepo.unfilteredpropertycache
202 def changelog(self):
201 def changelog(self):
203 return unionchangelog(self.svfs, self.repo2.svfs)
202 return unionchangelog(self.svfs, self.repo2.svfs)
204
203
205 def _clrev(self, rev2):
204 def _clrev(self, rev2):
206 """map from repo2 changelog rev to temporary rev in self.changelog"""
205 """map from repo2 changelog rev to temporary rev in self.changelog"""
207 node = self.repo2.changelog.node(rev2)
206 node = self.repo2.changelog.node(rev2)
208 return self.changelog.rev(node)
207 return self.changelog.rev(node)
209
208
210 def _constructmanifest(self):
209 def _constructmanifest(self):
211 return unionmanifest(self.svfs, self.repo2.svfs,
210 return unionmanifest(self.svfs, self.repo2.svfs,
212 self.unfiltered()._clrev)
211 self.unfiltered()._clrev)
213
212
214 def url(self):
213 def url(self):
215 return self._url
214 return self._url
216
215
217 def file(self, f):
216 def file(self, f):
218 return unionfilelog(self.svfs, f, self.repo2.svfs,
217 return unionfilelog(self.svfs, f, self.repo2.svfs,
219 self.unfiltered()._clrev, self)
218 self.unfiltered()._clrev, self)
220
219
221 def close(self):
220 def close(self):
222 self.repo2.close()
221 self.repo2.close()
223
222
224 def cancopy(self):
223 def cancopy(self):
225 return False
224 return False
226
225
227 def peer(self):
226 def peer(self):
228 return unionpeer(self)
227 return unionpeer(self)
229
228
230 def getcwd(self):
229 def getcwd(self):
231 return pycompat.getcwd() # always outside the repo
230 return pycompat.getcwd() # always outside the repo
232
231
233 def instance(ui, path, create):
232 def instance(ui, path, create):
234 if create:
233 if create:
235 raise error.Abort(_('cannot create new union repository'))
234 raise error.Abort(_('cannot create new union repository'))
236 parentpath = ui.config("bundle", "mainreporoot", "")
235 parentpath = ui.config("bundle", "mainreporoot", "")
237 if not parentpath:
236 if not parentpath:
238 # try to find the correct path to the working directory repo
237 # try to find the correct path to the working directory repo
239 parentpath = cmdutil.findrepo(pycompat.getcwd())
238 parentpath = cmdutil.findrepo(pycompat.getcwd())
240 if parentpath is None:
239 if parentpath is None:
241 parentpath = ''
240 parentpath = ''
242 if parentpath:
241 if parentpath:
243 # Try to make the full path relative so we get a nice, short URL.
242 # Try to make the full path relative so we get a nice, short URL.
244 # In particular, we don't want temp dir names in test outputs.
243 # In particular, we don't want temp dir names in test outputs.
245 cwd = pycompat.getcwd()
244 cwd = pycompat.getcwd()
246 if parentpath == cwd:
245 if parentpath == cwd:
247 parentpath = ''
246 parentpath = ''
248 else:
247 else:
249 cwd = pathutil.normasprefix(cwd)
248 cwd = pathutil.normasprefix(cwd)
250 if parentpath.startswith(cwd):
249 if parentpath.startswith(cwd):
251 parentpath = parentpath[len(cwd):]
250 parentpath = parentpath[len(cwd):]
252 if path.startswith('union:'):
251 if path.startswith('union:'):
253 s = path.split(":", 1)[1].split("+", 1)
252 s = path.split(":", 1)[1].split("+", 1)
254 if len(s) == 1:
253 if len(s) == 1:
255 repopath, repopath2 = parentpath, s[0]
254 repopath, repopath2 = parentpath, s[0]
256 else:
255 else:
257 repopath, repopath2 = s
256 repopath, repopath2 = s
258 else:
257 else:
259 repopath, repopath2 = parentpath, path
258 repopath, repopath2 = parentpath, path
260 return unionrepository(ui, repopath, repopath2)
259 return unionrepository(ui, repopath, repopath2)
@@ -1,65 +1,59 b''
1 #require test-repo
1 #require test-repo
2
2
3 $ . "$TESTDIR/helpers-testrepo.sh"
3 $ . "$TESTDIR/helpers-testrepo.sh"
4 $ check_code="$TESTDIR"/../contrib/check-code.py
4 $ check_code="$TESTDIR"/../contrib/check-code.py
5 $ cd "$TESTDIR"/..
5 $ cd "$TESTDIR"/..
6
6
7 New errors are not allowed. Warnings are strongly discouraged.
7 New errors are not allowed. Warnings are strongly discouraged.
8 (The writing "no-che?k-code" is for not skipping this file when checking.)
8 (The writing "no-che?k-code" is for not skipping this file when checking.)
9
9
10 $ hg locate -X contrib/python-zstandard -X hgext/fsmonitor/pywatchman |
10 $ hg locate -X contrib/python-zstandard -X hgext/fsmonitor/pywatchman |
11 > sed 's-\\-/-g' | xargs "$check_code" --warnings --per-file=0 || false
11 > sed 's-\\-/-g' | xargs "$check_code" --warnings --per-file=0 || false
12 contrib/perf.py:859:
12 contrib/perf.py:859:
13 > r.revision(r.node(x))
13 > r.revision(r.node(x))
14 don't covert rev to node before passing to revision(nodeorrev)
14 don't covert rev to node before passing to revision(nodeorrev)
15 Skipping i18n/polib.py it has no-che?k-code (glob)
15 Skipping i18n/polib.py it has no-che?k-code (glob)
16 mercurial/demandimport.py:312:
16 mercurial/demandimport.py:312:
17 > if os.environ.get('HGDEMANDIMPORT') != 'disable':
17 > if os.environ.get('HGDEMANDIMPORT') != 'disable':
18 use encoding.environ instead (py3)
18 use encoding.environ instead (py3)
19 mercurial/encoding.py:54:
19 mercurial/encoding.py:54:
20 > environ = os.environ
20 > environ = os.environ
21 use encoding.environ instead (py3)
21 use encoding.environ instead (py3)
22 mercurial/encoding.py:56:
22 mercurial/encoding.py:56:
23 > environ = os.environb
23 > environ = os.environb
24 use encoding.environ instead (py3)
24 use encoding.environ instead (py3)
25 mercurial/encoding.py:61:
25 mercurial/encoding.py:61:
26 > for k, v in os.environ.items())
26 > for k, v in os.environ.items())
27 use encoding.environ instead (py3)
27 use encoding.environ instead (py3)
28 mercurial/encoding.py:221:
28 mercurial/encoding.py:221:
29 > for k, v in os.environ.items())
29 > for k, v in os.environ.items())
30 use encoding.environ instead (py3)
30 use encoding.environ instead (py3)
31 Skipping mercurial/httpclient/__init__.py it has no-che?k-code (glob)
31 Skipping mercurial/httpclient/__init__.py it has no-che?k-code (glob)
32 Skipping mercurial/httpclient/_readers.py it has no-che?k-code (glob)
32 Skipping mercurial/httpclient/_readers.py it has no-che?k-code (glob)
33 mercurial/policy.py:46:
33 mercurial/policy.py:46:
34 > if 'HGMODULEPOLICY' in os.environ:
34 > if 'HGMODULEPOLICY' in os.environ:
35 use encoding.environ instead (py3)
35 use encoding.environ instead (py3)
36 mercurial/policy.py:47:
36 mercurial/policy.py:47:
37 > policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
37 > policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
38 use encoding.environ instead (py3)
38 use encoding.environ instead (py3)
39 mercurial/policy.py:49:
39 mercurial/policy.py:49:
40 > policy = os.environ.get('HGMODULEPOLICY', policy)
40 > policy = os.environ.get('HGMODULEPOLICY', policy)
41 use encoding.environ instead (py3)
41 use encoding.environ instead (py3)
42 Skipping mercurial/statprof.py it has no-che?k-code (glob)
42 Skipping mercurial/statprof.py it has no-che?k-code (glob)
43 mercurial/unionrepo.py:93:
44 > return mdiff.textdiff(self.revision(self.node(rev1)),
45 don't covert rev to node before passing to revision(nodeorrev)
46 mercurial/unionrepo.py:94:
47 > self.revision(self.node(rev2)))
48 don't covert rev to node before passing to revision(nodeorrev)
49 [1]
43 [1]
50
44
51 @commands in debugcommands.py should be in alphabetical order.
45 @commands in debugcommands.py should be in alphabetical order.
52
46
53 >>> import re
47 >>> import re
54 >>> commands = []
48 >>> commands = []
55 >>> with open('mercurial/debugcommands.py', 'rb') as fh:
49 >>> with open('mercurial/debugcommands.py', 'rb') as fh:
56 ... for line in fh:
50 ... for line in fh:
57 ... m = re.match("^@command\('([a-z]+)", line)
51 ... m = re.match("^@command\('([a-z]+)", line)
58 ... if m:
52 ... if m:
59 ... commands.append(m.group(1))
53 ... commands.append(m.group(1))
60 >>> scommands = list(sorted(commands))
54 >>> scommands = list(sorted(commands))
61 >>> for i, command in enumerate(scommands):
55 >>> for i, command in enumerate(scommands):
62 ... if command != commands[i]:
56 ... if command != commands[i]:
63 ... print('commands in debugcommands.py not sorted; first differing '
57 ... print('commands in debugcommands.py not sorted; first differing '
64 ... 'command is %s; expected %s' % (commands[i], command))
58 ... 'command is %s; expected %s' % (commands[i], command))
65 ... break
59 ... break
General Comments 0
You need to be logged in to leave comments. Login now