##// END OF EJS Templates
unionrepo: fix mismatches with revlog classes...
Matt Harbison -
r52768:8315175f default
parent child Browse files
Show More
@@ -1,360 +1,366
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 annotations
14 from __future__ import annotations
15
15
16 import contextlib
16 import contextlib
17 import typing
17 import typing
18
18
19 from .i18n import _
19 from .i18n import _
20
20
21 from . import (
21 from . import (
22 changelog,
22 changelog,
23 cmdutil,
23 cmdutil,
24 encoding,
24 encoding,
25 error,
25 error,
26 filelog,
26 filelog,
27 localrepo,
27 localrepo,
28 manifest,
28 manifest,
29 mdiff,
29 mdiff,
30 pathutil,
30 pathutil,
31 revlog,
31 revlog,
32 util,
32 util,
33 vfs as vfsmod,
33 vfs as vfsmod,
34 )
34 )
35
35
36 from .revlogutils import (
36 from .revlogutils import (
37 constants as revlog_constants,
37 constants as revlog_constants,
38 )
38 )
39
39
40
40
41 class unionrevlog(revlog.revlog):
41 class unionrevlog(revlog.revlog):
42 def __init__(self, opener, radix, revlog2, linkmapper):
42 def __init__(self, opener: typing.Any, radix, revlog2, linkmapper):
43 # TODO: figure out real type of opener
44 #
43 # How it works:
45 # How it works:
44 # To retrieve a revision, we just need to know the node id so we can
46 # To retrieve a revision, we just need to know the node id so we can
45 # look it up in revlog2.
47 # look it up in revlog2.
46 #
48 #
47 # To differentiate a rev in the second revlog from a rev in the revlog,
49 # To differentiate a rev in the second revlog from a rev in the revlog,
48 # we check revision against repotiprev.
50 # we check revision against repotiprev.
49 opener = vfsmod.readonlyvfs(opener)
51 opener = vfsmod.readonlyvfs(opener)
50 target = getattr(revlog2, 'target', None)
52 target = getattr(revlog2, 'target', None)
51 if target is None:
53 if target is None:
54 # Help pytype- changelog and revlog are not possible here because
55 # they both have a 'target' attr.
56 assert not isinstance(revlog2, (changelog.changelog, revlog.revlog))
57
52 # a revlog wrapper, eg: the manifestlog that is not an actual revlog
58 # a revlog wrapper, eg: the manifestlog that is not an actual revlog
53 target = revlog2._revlog.target
59 target = revlog2._revlog.target
54 revlog.revlog.__init__(self, opener, target=target, radix=radix)
60 revlog.revlog.__init__(self, opener, target=target, radix=radix)
55 self.revlog2 = revlog2
61 self.revlog2 = revlog2
56
62
57 n = len(self)
63 n = len(self)
58 self.repotiprev = n - 1
64 self.repotiprev = n - 1
59 self.bundlerevs = set() # used by 'bundle()' revset expression
65 self.bundlerevs = set() # used by 'bundle()' revset expression
60 for rev2 in self.revlog2:
66 for rev2 in self.revlog2:
61 rev = self.revlog2.index[rev2]
67 rev = self.revlog2.index[rev2]
62 # rev numbers - in revlog2, very different from self.rev
68 # rev numbers - in revlog2, very different from self.rev
63 (
69 (
64 _start,
70 _start,
65 _csize,
71 _csize,
66 rsize,
72 rsize,
67 base,
73 base,
68 linkrev,
74 linkrev,
69 p1rev,
75 p1rev,
70 p2rev,
76 p2rev,
71 node,
77 node,
72 _sdo,
78 _sdo,
73 _sds,
79 _sds,
74 _dcm,
80 _dcm,
75 _sdcm,
81 _sdcm,
76 rank,
82 rank,
77 ) = rev
83 ) = rev
78 flags = _start & 0xFFFF
84 flags = _start & 0xFFFF
79
85
80 if linkmapper is None: # link is to same revlog
86 if linkmapper is None: # link is to same revlog
81 assert linkrev == rev2 # we never link back
87 assert linkrev == rev2 # we never link back
82 link = n
88 link = n
83 else: # rev must be mapped from repo2 cl to unified cl by linkmapper
89 else: # rev must be mapped from repo2 cl to unified cl by linkmapper
84 link = linkmapper(linkrev)
90 link = linkmapper(linkrev)
85
91
86 if linkmapper is not None: # link is to same revlog
92 if linkmapper is not None: # link is to same revlog
87 base = linkmapper(base)
93 base = linkmapper(base)
88
94
89 this_rev = self.index.get_rev(node)
95 this_rev = self.index.get_rev(node)
90 if this_rev is not None:
96 if this_rev is not None:
91 # this happens for the common revlog revisions
97 # this happens for the common revlog revisions
92 self.bundlerevs.add(this_rev)
98 self.bundlerevs.add(this_rev)
93 continue
99 continue
94
100
95 p1node = self.revlog2.node(p1rev)
101 p1node = self.revlog2.node(p1rev)
96 p2node = self.revlog2.node(p2rev)
102 p2node = self.revlog2.node(p2rev)
97
103
98 # TODO: it's probably wrong to set compressed length to -1, but
104 # TODO: it's probably wrong to set compressed length to -1, but
99 # I have no idea if csize is valid in the base revlog context.
105 # I have no idea if csize is valid in the base revlog context.
100 e = (
106 e = (
101 flags,
107 flags,
102 -1,
108 -1,
103 rsize,
109 rsize,
104 base,
110 base,
105 link,
111 link,
106 self.rev(p1node),
112 self.rev(p1node),
107 self.rev(p2node),
113 self.rev(p2node),
108 node,
114 node,
109 0, # sidedata offset
115 0, # sidedata offset
110 0, # sidedata size
116 0, # sidedata size
111 revlog_constants.COMP_MODE_INLINE,
117 revlog_constants.COMP_MODE_INLINE,
112 revlog_constants.COMP_MODE_INLINE,
118 revlog_constants.COMP_MODE_INLINE,
113 rank,
119 rank,
114 )
120 )
115 self.index.append(e)
121 self.index.append(e)
116 self.bundlerevs.add(n)
122 self.bundlerevs.add(n)
117 n += 1
123 n += 1
118
124
119 @contextlib.contextmanager
125 @contextlib.contextmanager
120 def reading(self):
126 def reading(self):
121 if 0 <= len(self.bundlerevs) < len(self.index):
127 if 0 <= len(self.bundlerevs) < len(self.index):
122 read_1 = super().reading
128 read_1 = super().reading
123 else:
129 else:
124 read_1 = util.nullcontextmanager
130 read_1 = util.nullcontextmanager
125 if 0 < len(self.bundlerevs):
131 if 0 < len(self.bundlerevs):
126 read_2 = self.revlog2.reading
132 read_2 = self.revlog2.reading
127 else:
133 else:
128 read_2 = util.nullcontextmanager
134 read_2 = util.nullcontextmanager
129 with read_1(), read_2():
135 with read_1(), read_2():
130 yield
136 yield
131
137
132 def _chunk(self, rev):
138 def _chunk(self, rev):
133 if rev <= self.repotiprev:
139 if rev <= self.repotiprev:
134 return revlog.revlog._chunk(self, rev)
140 return super(unionrevlog, self)._inner._chunk(rev)
135 return self.revlog2._chunk(self.node(rev))
141 return self.revlog2._chunk(self.node(rev))
136
142
137 def revdiff(self, rev1, rev2):
143 def revdiff(self, rev1, rev2):
138 """return or calculate a delta between two revisions"""
144 """return or calculate a delta between two revisions"""
139 if rev1 > self.repotiprev and rev2 > self.repotiprev:
145 if rev1 > self.repotiprev and rev2 > self.repotiprev:
140 return self.revlog2.revdiff(
146 return self.revlog2.revdiff(
141 self.revlog2.rev(self.node(rev1)),
147 self.revlog2.rev(self.node(rev1)),
142 self.revlog2.rev(self.node(rev2)),
148 self.revlog2.rev(self.node(rev2)),
143 )
149 )
144 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
150 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
145 return super(unionrevlog, self).revdiff(rev1, rev2)
151 return super(unionrevlog, self).revdiff(rev1, rev2)
146
152
147 return mdiff.textdiff(self.rawdata(rev1), self.rawdata(rev2))
153 return mdiff.textdiff(self.rawdata(rev1), self.rawdata(rev2))
148
154
149 def _revisiondata(self, nodeorrev, raw=False):
155 def _revisiondata(self, nodeorrev, raw=False):
150 if isinstance(nodeorrev, int):
156 if isinstance(nodeorrev, int):
151 rev = nodeorrev
157 rev = nodeorrev
152 node = self.node(rev)
158 node = self.node(rev)
153 else:
159 else:
154 node = nodeorrev
160 node = nodeorrev
155 rev = self.rev(node)
161 rev = self.rev(node)
156
162
157 if rev > self.repotiprev:
163 if rev > self.repotiprev:
158 # work around manifestrevlog NOT being a revlog
164 # work around manifestrevlog NOT being a revlog
159 revlog2 = getattr(self.revlog2, '_revlog', self.revlog2)
165 revlog2 = getattr(self.revlog2, '_revlog', self.revlog2)
160 func = revlog2._revisiondata
166 func = revlog2._revisiondata
161 else:
167 else:
162 func = super(unionrevlog, self)._revisiondata
168 func = super(unionrevlog, self)._revisiondata
163 return func(node, raw=raw)
169 return func(node, raw=raw)
164
170
165 def addrevision(
171 def addrevision(
166 self,
172 self,
167 text,
173 text,
168 transaction,
174 transaction,
169 link,
175 link,
170 p1,
176 p1,
171 p2,
177 p2,
172 cachedelta=None,
178 cachedelta=None,
173 node=None,
179 node=None,
174 flags=revlog.REVIDX_DEFAULT_FLAGS,
180 flags=revlog.REVIDX_DEFAULT_FLAGS,
175 deltacomputer=None,
181 deltacomputer=None,
176 sidedata=None,
182 sidedata=None,
177 ):
183 ):
178 raise NotImplementedError
184 raise NotImplementedError
179
185
180 def addgroup(
186 def addgroup(
181 self,
187 self,
182 deltas,
188 deltas,
183 linkmapper,
189 linkmapper,
184 transaction,
190 transaction,
185 alwayscache=False,
191 alwayscache=False,
186 addrevisioncb=None,
192 addrevisioncb=None,
187 duplicaterevisioncb=None,
193 duplicaterevisioncb=None,
188 debug_info=None,
194 debug_info=None,
189 delta_base_reuse_policy=None,
195 delta_base_reuse_policy=None,
190 ):
196 ):
191 raise NotImplementedError
197 raise NotImplementedError
192
198
193 def strip(self, minlink, transaction):
199 def strip(self, minlink, transaction):
194 raise NotImplementedError
200 raise NotImplementedError
195
201
196 def checksize(self):
202 def checksize(self):
197 raise NotImplementedError
203 raise NotImplementedError
198
204
199
205
200 class unionchangelog(unionrevlog, changelog.changelog):
206 class unionchangelog(unionrevlog, changelog.changelog):
201 def __init__(self, opener, opener2):
207 def __init__(self, opener, opener2):
202 changelog.changelog.__init__(self, opener)
208 changelog.changelog.__init__(self, opener)
203 linkmapper = None
209 linkmapper = None
204 changelog2 = changelog.changelog(opener2)
210 changelog2 = changelog.changelog(opener2)
205 unionrevlog.__init__(self, opener, self.radix, changelog2, linkmapper)
211 unionrevlog.__init__(self, opener, self.radix, changelog2, linkmapper)
206
212
207
213
208 class unionmanifest(unionrevlog, manifest.manifestrevlog):
214 class unionmanifest(unionrevlog, manifest.manifestrevlog):
209 repotiprev: int
215 repotiprev: int
210 revlog2: manifest.ManifestRevlog
216 revlog2: manifest.ManifestRevlog
211
217
212 def __init__(self, nodeconstants, opener, opener2, linkmapper):
218 def __init__(self, nodeconstants, opener, opener2, linkmapper):
213 # XXX manifestrevlog is not actually a revlog , so mixing it with
219 # XXX manifestrevlog is not actually a revlog , so mixing it with
214 # bundlerevlog is not a good idea.
220 # bundlerevlog is not a good idea.
215 manifest.manifestrevlog.__init__(self, nodeconstants, opener)
221 manifest.manifestrevlog.__init__(self, nodeconstants, opener)
216 manifest2 = manifest.manifestrevlog(nodeconstants, opener2)
222 manifest2 = manifest.manifestrevlog(nodeconstants, opener2)
217 unionrevlog.__init__(
223 unionrevlog.__init__(
218 self, opener, self._revlog.radix, manifest2, linkmapper
224 self, opener, self._revlog.radix, manifest2, linkmapper
219 )
225 )
220
226
221
227
222 class unionfilelog(filelog.filelog):
228 class unionfilelog(filelog.filelog):
223 _revlog: unionrevlog
229 _revlog: unionrevlog
224 repotiprev: int
230 repotiprev: int
225 revlog2: revlog.revlog
231 revlog2: revlog.revlog
226
232
227 def __init__(self, opener, path, opener2, linkmapper, repo):
233 def __init__(self, opener, path, opener2, linkmapper, repo):
228 filelog.filelog.__init__(self, opener, path)
234 filelog.filelog.__init__(self, opener, path)
229 filelog2 = filelog.filelog(opener2, path)
235 filelog2 = filelog.filelog(opener2, path)
230 self._revlog = unionrevlog(
236 self._revlog = unionrevlog(
231 opener, self._revlog.radix, filelog2._revlog, linkmapper
237 opener, self._revlog.radix, filelog2._revlog, linkmapper
232 )
238 )
233 self._repo = repo
239 self._repo = repo
234 self.repotiprev = self._revlog.repotiprev
240 self.repotiprev = self._revlog.repotiprev
235 self.revlog2 = self._revlog.revlog2
241 self.revlog2 = self._revlog.revlog2
236
242
237 def iscensored(self, rev):
243 def iscensored(self, rev):
238 """Check if a revision is censored."""
244 """Check if a revision is censored."""
239 if rev <= self.repotiprev:
245 if rev <= self.repotiprev:
240 return filelog.filelog.iscensored(self, rev)
246 return filelog.filelog.iscensored(self, rev)
241 node = self.node(rev)
247 node = self.node(rev)
242 return self.revlog2.iscensored(self.revlog2.rev(node))
248 return self.revlog2.iscensored(self.revlog2.rev(node))
243
249
244
250
245 class unionpeer(localrepo.localpeer):
251 class unionpeer(localrepo.localpeer):
246 def canpush(self):
252 def canpush(self):
247 return False
253 return False
248
254
249
255
250 _union_repo_baseclass = object
256 _union_repo_baseclass = object
251
257
252 if typing.TYPE_CHECKING:
258 if typing.TYPE_CHECKING:
253 _union_repo_baseclass = localrepo.localrepository
259 _union_repo_baseclass = localrepo.localrepository
254
260
255
261
256 class unionrepository(_union_repo_baseclass):
262 class unionrepository(_union_repo_baseclass):
257 """Represents the union of data in 2 repositories.
263 """Represents the union of data in 2 repositories.
258
264
259 Instances are not usable if constructed directly. Use ``instance()``
265 Instances are not usable if constructed directly. Use ``instance()``
260 or ``makeunionrepository()`` to create a usable instance.
266 or ``makeunionrepository()`` to create a usable instance.
261 """
267 """
262
268
263 # noinspection PyMissingConstructor
269 # noinspection PyMissingConstructor
264 def __init__(self, repo2, url):
270 def __init__(self, repo2, url):
265 self.repo2 = repo2
271 self.repo2 = repo2
266 self._url = url
272 self._url = url
267
273
268 self.ui.setconfig(b'phases', b'publish', False, b'unionrepo')
274 self.ui.setconfig(b'phases', b'publish', False, b'unionrepo')
269
275
270 @localrepo.unfilteredpropertycache
276 @localrepo.unfilteredpropertycache
271 def changelog(self):
277 def changelog(self):
272 return unionchangelog(self.svfs, self.repo2.svfs)
278 return unionchangelog(self.svfs, self.repo2.svfs)
273
279
274 @localrepo.unfilteredpropertycache
280 @localrepo.unfilteredpropertycache
275 def manifestlog(self):
281 def manifestlog(self):
276 rootstore = unionmanifest(
282 rootstore = unionmanifest(
277 self.nodeconstants,
283 self.nodeconstants,
278 self.svfs,
284 self.svfs,
279 self.repo2.svfs,
285 self.repo2.svfs,
280 self.unfiltered()._clrev,
286 self.unfiltered()._clrev,
281 )
287 )
282 return manifest.manifestlog(
288 return manifest.manifestlog(
283 self.svfs, self, rootstore, self.narrowmatch()
289 self.svfs, self, rootstore, self.narrowmatch()
284 )
290 )
285
291
286 def _clrev(self, rev2):
292 def _clrev(self, rev2):
287 """map from repo2 changelog rev to temporary rev in self.changelog"""
293 """map from repo2 changelog rev to temporary rev in self.changelog"""
288 node = self.repo2.changelog.node(rev2)
294 node = self.repo2.changelog.node(rev2)
289 return self.changelog.rev(node)
295 return self.changelog.rev(node)
290
296
291 def url(self):
297 def url(self):
292 return self._url
298 return self._url
293
299
294 def file(self, f):
300 def file(self, f):
295 return unionfilelog(
301 return unionfilelog(
296 self.svfs, f, self.repo2.svfs, self.unfiltered()._clrev, self
302 self.svfs, f, self.repo2.svfs, self.unfiltered()._clrev, self
297 )
303 )
298
304
299 def close(self):
305 def close(self):
300 self.repo2.close()
306 self.repo2.close()
301
307
302 def cancopy(self):
308 def cancopy(self):
303 return False
309 return False
304
310
305 def peer(self, path=None, remotehidden=False):
311 def peer(self, path=None, remotehidden=False):
306 return unionpeer(self, path=None, remotehidden=remotehidden)
312 return unionpeer(self, path=None, remotehidden=remotehidden)
307
313
308 def getcwd(self):
314 def getcwd(self):
309 return encoding.getcwd() # always outside the repo
315 return encoding.getcwd() # always outside the repo
310
316
311
317
312 def instance(ui, path, create, intents=None, createopts=None):
318 def instance(ui, path, create, intents=None, createopts=None):
313 if create:
319 if create:
314 raise error.Abort(_(b'cannot create new union repository'))
320 raise error.Abort(_(b'cannot create new union repository'))
315 parentpath = ui.config(b"bundle", b"mainreporoot")
321 parentpath = ui.config(b"bundle", b"mainreporoot")
316 if not parentpath:
322 if not parentpath:
317 # try to find the correct path to the working directory repo
323 # try to find the correct path to the working directory repo
318 parentpath = cmdutil.findrepo(encoding.getcwd())
324 parentpath = cmdutil.findrepo(encoding.getcwd())
319 if parentpath is None:
325 if parentpath is None:
320 parentpath = b''
326 parentpath = b''
321 if parentpath:
327 if parentpath:
322 # Try to make the full path relative so we get a nice, short URL.
328 # Try to make the full path relative so we get a nice, short URL.
323 # In particular, we don't want temp dir names in test outputs.
329 # In particular, we don't want temp dir names in test outputs.
324 cwd = encoding.getcwd()
330 cwd = encoding.getcwd()
325 if parentpath == cwd:
331 if parentpath == cwd:
326 parentpath = b''
332 parentpath = b''
327 else:
333 else:
328 cwd = pathutil.normasprefix(cwd)
334 cwd = pathutil.normasprefix(cwd)
329 if parentpath.startswith(cwd):
335 if parentpath.startswith(cwd):
330 parentpath = parentpath[len(cwd) :]
336 parentpath = parentpath[len(cwd) :]
331 if path.startswith(b'union:'):
337 if path.startswith(b'union:'):
332 s = path.split(b":", 1)[1].split(b"+", 1)
338 s = path.split(b":", 1)[1].split(b"+", 1)
333 if len(s) == 1:
339 if len(s) == 1:
334 repopath, repopath2 = parentpath, s[0]
340 repopath, repopath2 = parentpath, s[0]
335 else:
341 else:
336 repopath, repopath2 = s
342 repopath, repopath2 = s
337 else:
343 else:
338 repopath, repopath2 = parentpath, path
344 repopath, repopath2 = parentpath, path
339
345
340 return makeunionrepository(ui, repopath, repopath2)
346 return makeunionrepository(ui, repopath, repopath2)
341
347
342
348
343 def makeunionrepository(ui, repopath1, repopath2):
349 def makeunionrepository(ui, repopath1, repopath2):
344 """Make a union repository object from 2 local repo paths."""
350 """Make a union repository object from 2 local repo paths."""
345 repo1 = localrepo.instance(ui, repopath1, create=False)
351 repo1 = localrepo.instance(ui, repopath1, create=False)
346 repo2 = localrepo.instance(ui, repopath2, create=False)
352 repo2 = localrepo.instance(ui, repopath2, create=False)
347
353
348 url = b'union:%s+%s' % (
354 url = b'union:%s+%s' % (
349 util.expandpath(repopath1),
355 util.expandpath(repopath1),
350 util.expandpath(repopath2),
356 util.expandpath(repopath2),
351 )
357 )
352
358
353 class derivedunionrepository(unionrepository, repo1.__class__):
359 class derivedunionrepository(unionrepository, repo1.__class__):
354 pass
360 pass
355
361
356 repo = repo1
362 repo = repo1
357 repo.__class__ = derivedunionrepository
363 repo.__class__ = derivedunionrepository
358 unionrepository.__init__(repo1, repo2, url)
364 unionrepository.__init__(repo1, repo2, url)
359
365
360 return repo
366 return repo
General Comments 0
You need to be logged in to leave comments. Login now