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