##// END OF EJS Templates
revlog: update signature of dummy addgroup() in bundlerepo and unionrepo...
Yuya Nishihara -
r34216:21fc747e default
parent child Browse files
Show More
@@ -1,563 +1,563 b''
1 1 # bundlerepo.py - repository class for viewing uncompressed bundles
2 2 #
3 3 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
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 """Repository class for viewing uncompressed bundles.
9 9
10 10 This provides a read-only repository interface to bundles as if they
11 11 were part of the actual repository.
12 12 """
13 13
14 14 from __future__ import absolute_import
15 15
16 16 import os
17 17 import shutil
18 18 import tempfile
19 19
20 20 from .i18n import _
21 21 from .node import nullid
22 22
23 23 from . import (
24 24 bundle2,
25 25 changegroup,
26 26 changelog,
27 27 cmdutil,
28 28 discovery,
29 29 error,
30 30 exchange,
31 31 filelog,
32 32 localrepo,
33 33 manifest,
34 34 mdiff,
35 35 node as nodemod,
36 36 pathutil,
37 37 phases,
38 38 pycompat,
39 39 revlog,
40 40 util,
41 41 vfs as vfsmod,
42 42 )
43 43
44 44 class bundlerevlog(revlog.revlog):
45 45 def __init__(self, opener, indexfile, bundle, linkmapper):
46 46 # How it works:
47 47 # To retrieve a revision, we need to know the offset of the revision in
48 48 # the bundle (an unbundle object). We store this offset in the index
49 49 # (start). The base of the delta is stored in the base field.
50 50 #
51 51 # To differentiate a rev in the bundle from a rev in the revlog, we
52 52 # check revision against repotiprev.
53 53 opener = vfsmod.readonlyvfs(opener)
54 54 revlog.revlog.__init__(self, opener, indexfile)
55 55 self.bundle = bundle
56 56 n = len(self)
57 57 self.repotiprev = n - 1
58 58 chain = None
59 59 self.bundlerevs = set() # used by 'bundle()' revset expression
60 60 getchunk = lambda: bundle.deltachunk(chain)
61 61 for chunkdata in iter(getchunk, {}):
62 62 node = chunkdata['node']
63 63 p1 = chunkdata['p1']
64 64 p2 = chunkdata['p2']
65 65 cs = chunkdata['cs']
66 66 deltabase = chunkdata['deltabase']
67 67 delta = chunkdata['delta']
68 68 flags = chunkdata['flags']
69 69
70 70 size = len(delta)
71 71 start = bundle.tell() - size
72 72
73 73 link = linkmapper(cs)
74 74 if node in self.nodemap:
75 75 # this can happen if two branches make the same change
76 76 chain = node
77 77 self.bundlerevs.add(self.nodemap[node])
78 78 continue
79 79
80 80 for p in (p1, p2):
81 81 if p not in self.nodemap:
82 82 raise error.LookupError(p, self.indexfile,
83 83 _("unknown parent"))
84 84
85 85 if deltabase not in self.nodemap:
86 86 raise LookupError(deltabase, self.indexfile,
87 87 _('unknown delta base'))
88 88
89 89 baserev = self.rev(deltabase)
90 90 # start, size, full unc. size, base (unused), link, p1, p2, node
91 91 e = (revlog.offset_type(start, flags), size, -1, baserev, link,
92 92 self.rev(p1), self.rev(p2), node)
93 93 self.index.insert(-1, e)
94 94 self.nodemap[node] = n
95 95 self.bundlerevs.add(n)
96 96 chain = node
97 97 n += 1
98 98
99 99 def _chunk(self, rev):
100 100 # Warning: in case of bundle, the diff is against what we stored as
101 101 # delta base, not against rev - 1
102 102 # XXX: could use some caching
103 103 if rev <= self.repotiprev:
104 104 return revlog.revlog._chunk(self, rev)
105 105 self.bundle.seek(self.start(rev))
106 106 return self.bundle.read(self.length(rev))
107 107
108 108 def revdiff(self, rev1, rev2):
109 109 """return or calculate a delta between two revisions"""
110 110 if rev1 > self.repotiprev and rev2 > self.repotiprev:
111 111 # hot path for bundle
112 112 revb = self.index[rev2][3]
113 113 if revb == rev1:
114 114 return self._chunk(rev2)
115 115 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
116 116 return revlog.revlog.revdiff(self, rev1, rev2)
117 117
118 118 return mdiff.textdiff(self.revision(rev1, raw=True),
119 119 self.revision(rev2, raw=True))
120 120
121 121 def revision(self, nodeorrev, raw=False):
122 122 """return an uncompressed revision of a given node or revision
123 123 number.
124 124 """
125 125 if isinstance(nodeorrev, int):
126 126 rev = nodeorrev
127 127 node = self.node(rev)
128 128 else:
129 129 node = nodeorrev
130 130 rev = self.rev(node)
131 131
132 132 if node == nullid:
133 133 return ""
134 134
135 135 rawtext = None
136 136 chain = []
137 137 iterrev = rev
138 138 # reconstruct the revision if it is from a changegroup
139 139 while iterrev > self.repotiprev:
140 140 if self._cache and self._cache[1] == iterrev:
141 141 rawtext = self._cache[2]
142 142 break
143 143 chain.append(iterrev)
144 144 iterrev = self.index[iterrev][3]
145 145 if rawtext is None:
146 146 rawtext = self.baserevision(iterrev)
147 147
148 148 while chain:
149 149 delta = self._chunk(chain.pop())
150 150 rawtext = mdiff.patches(rawtext, [delta])
151 151
152 152 text, validatehash = self._processflags(rawtext, self.flags(rev),
153 153 'read', raw=raw)
154 154 if validatehash:
155 155 self.checkhash(text, node, rev=rev)
156 156 self._cache = (node, rev, rawtext)
157 157 return text
158 158
159 159 def baserevision(self, nodeorrev):
160 160 # Revlog subclasses may override 'revision' method to modify format of
161 161 # content retrieved from revlog. To use bundlerevlog with such class one
162 162 # needs to override 'baserevision' and make more specific call here.
163 163 return revlog.revlog.revision(self, nodeorrev, raw=True)
164 164
165 165 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
166 166 raise NotImplementedError
167 def addgroup(self, revs, linkmapper, transaction):
167 def addgroup(self, deltas, transaction, addrevisioncb=None):
168 168 raise NotImplementedError
169 169 def strip(self, rev, minlink):
170 170 raise NotImplementedError
171 171 def checksize(self):
172 172 raise NotImplementedError
173 173
174 174 class bundlechangelog(bundlerevlog, changelog.changelog):
175 175 def __init__(self, opener, bundle):
176 176 changelog.changelog.__init__(self, opener)
177 177 linkmapper = lambda x: x
178 178 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
179 179 linkmapper)
180 180
181 181 def baserevision(self, nodeorrev):
182 182 # Although changelog doesn't override 'revision' method, some extensions
183 183 # may replace this class with another that does. Same story with
184 184 # manifest and filelog classes.
185 185
186 186 # This bypasses filtering on changelog.node() and rev() because we need
187 187 # revision text of the bundle base even if it is hidden.
188 188 oldfilter = self.filteredrevs
189 189 try:
190 190 self.filteredrevs = ()
191 191 return changelog.changelog.revision(self, nodeorrev, raw=True)
192 192 finally:
193 193 self.filteredrevs = oldfilter
194 194
195 195 class bundlemanifest(bundlerevlog, manifest.manifestrevlog):
196 196 def __init__(self, opener, bundle, linkmapper, dirlogstarts=None, dir=''):
197 197 manifest.manifestrevlog.__init__(self, opener, dir=dir)
198 198 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
199 199 linkmapper)
200 200 if dirlogstarts is None:
201 201 dirlogstarts = {}
202 202 if self.bundle.version == "03":
203 203 dirlogstarts = _getfilestarts(self.bundle)
204 204 self._dirlogstarts = dirlogstarts
205 205 self._linkmapper = linkmapper
206 206
207 207 def baserevision(self, nodeorrev):
208 208 node = nodeorrev
209 209 if isinstance(node, int):
210 210 node = self.node(node)
211 211
212 212 if node in self.fulltextcache:
213 213 result = '%s' % self.fulltextcache[node]
214 214 else:
215 215 result = manifest.manifestrevlog.revision(self, nodeorrev, raw=True)
216 216 return result
217 217
218 218 def dirlog(self, d):
219 219 if d in self._dirlogstarts:
220 220 self.bundle.seek(self._dirlogstarts[d])
221 221 return bundlemanifest(
222 222 self.opener, self.bundle, self._linkmapper,
223 223 self._dirlogstarts, dir=d)
224 224 return super(bundlemanifest, self).dirlog(d)
225 225
226 226 class bundlefilelog(bundlerevlog, filelog.filelog):
227 227 def __init__(self, opener, path, bundle, linkmapper):
228 228 filelog.filelog.__init__(self, opener, path)
229 229 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
230 230 linkmapper)
231 231
232 232 def baserevision(self, nodeorrev):
233 233 return filelog.filelog.revision(self, nodeorrev, raw=True)
234 234
235 235 class bundlepeer(localrepo.localpeer):
236 236 def canpush(self):
237 237 return False
238 238
239 239 class bundlephasecache(phases.phasecache):
240 240 def __init__(self, *args, **kwargs):
241 241 super(bundlephasecache, self).__init__(*args, **kwargs)
242 242 if util.safehasattr(self, 'opener'):
243 243 self.opener = vfsmod.readonlyvfs(self.opener)
244 244
245 245 def write(self):
246 246 raise NotImplementedError
247 247
248 248 def _write(self, fp):
249 249 raise NotImplementedError
250 250
251 251 def _updateroots(self, phase, newroots, tr):
252 252 self.phaseroots[phase] = newroots
253 253 self.invalidate()
254 254 self.dirty = True
255 255
256 256 def _getfilestarts(bundle):
257 257 bundlefilespos = {}
258 258 for chunkdata in iter(bundle.filelogheader, {}):
259 259 fname = chunkdata['filename']
260 260 bundlefilespos[fname] = bundle.tell()
261 261 for chunk in iter(lambda: bundle.deltachunk(None), {}):
262 262 pass
263 263 return bundlefilespos
264 264
265 265 class bundlerepository(localrepo.localrepository):
266 266 def __init__(self, ui, path, bundlename):
267 267 self._tempparent = None
268 268 try:
269 269 localrepo.localrepository.__init__(self, ui, path)
270 270 except error.RepoError:
271 271 self._tempparent = tempfile.mkdtemp()
272 272 localrepo.instance(ui, self._tempparent, 1)
273 273 localrepo.localrepository.__init__(self, ui, self._tempparent)
274 274 self.ui.setconfig('phases', 'publish', False, 'bundlerepo')
275 275
276 276 if path:
277 277 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
278 278 else:
279 279 self._url = 'bundle:' + bundlename
280 280
281 281 self.tempfile = None
282 282 f = util.posixfile(bundlename, "rb")
283 283 self.bundlefile = self.bundle = exchange.readbundle(ui, f, bundlename)
284 284
285 285 if isinstance(self.bundle, bundle2.unbundle20):
286 286 hadchangegroup = False
287 287 for part in self.bundle.iterparts():
288 288 if part.type == 'changegroup':
289 289 if hadchangegroup:
290 290 raise NotImplementedError("can't process "
291 291 "multiple changegroups")
292 292 hadchangegroup = True
293 293
294 294 self._handlebundle2part(part)
295 295
296 296 if not hadchangegroup:
297 297 raise error.Abort(_("No changegroups found"))
298 298
299 299 elif self.bundle.compressed():
300 300 f = self._writetempbundle(self.bundle.read, '.hg10un',
301 301 header='HG10UN')
302 302 self.bundlefile = self.bundle = exchange.readbundle(ui, f,
303 303 bundlename,
304 304 self.vfs)
305 305
306 306 # dict with the mapping 'filename' -> position in the bundle
307 307 self.bundlefilespos = {}
308 308
309 309 self.firstnewrev = self.changelog.repotiprev + 1
310 310 phases.retractboundary(self, None, phases.draft,
311 311 [ctx.node() for ctx in self[self.firstnewrev:]])
312 312
313 313 def _handlebundle2part(self, part):
314 314 if part.type == 'changegroup':
315 315 cgstream = part
316 316 version = part.params.get('version', '01')
317 317 legalcgvers = changegroup.supportedincomingversions(self)
318 318 if version not in legalcgvers:
319 319 msg = _('Unsupported changegroup version: %s')
320 320 raise error.Abort(msg % version)
321 321 if self.bundle.compressed():
322 322 cgstream = self._writetempbundle(part.read,
323 323 ".cg%sun" % version)
324 324
325 325 self.bundle = changegroup.getunbundler(version, cgstream, 'UN')
326 326
327 327 def _writetempbundle(self, readfn, suffix, header=''):
328 328 """Write a temporary file to disk
329 329 """
330 330 fdtemp, temp = self.vfs.mkstemp(prefix="hg-bundle-",
331 331 suffix=".hg10un")
332 332 self.tempfile = temp
333 333
334 334 with os.fdopen(fdtemp, pycompat.sysstr('wb')) as fptemp:
335 335 fptemp.write(header)
336 336 while True:
337 337 chunk = readfn(2**18)
338 338 if not chunk:
339 339 break
340 340 fptemp.write(chunk)
341 341
342 342 return self.vfs.open(self.tempfile, mode="rb")
343 343
344 344 @localrepo.unfilteredpropertycache
345 345 def _phasecache(self):
346 346 return bundlephasecache(self, self._phasedefaults)
347 347
348 348 @localrepo.unfilteredpropertycache
349 349 def changelog(self):
350 350 # consume the header if it exists
351 351 self.bundle.changelogheader()
352 352 c = bundlechangelog(self.svfs, self.bundle)
353 353 self.manstart = self.bundle.tell()
354 354 return c
355 355
356 356 def _constructmanifest(self):
357 357 self.bundle.seek(self.manstart)
358 358 # consume the header if it exists
359 359 self.bundle.manifestheader()
360 360 linkmapper = self.unfiltered().changelog.rev
361 361 m = bundlemanifest(self.svfs, self.bundle, linkmapper)
362 362 self.filestart = self.bundle.tell()
363 363 return m
364 364
365 365 @localrepo.unfilteredpropertycache
366 366 def manstart(self):
367 367 self.changelog
368 368 return self.manstart
369 369
370 370 @localrepo.unfilteredpropertycache
371 371 def filestart(self):
372 372 self.manifestlog
373 373 return self.filestart
374 374
375 375 def url(self):
376 376 return self._url
377 377
378 378 def file(self, f):
379 379 if not self.bundlefilespos:
380 380 self.bundle.seek(self.filestart)
381 381 self.bundlefilespos = _getfilestarts(self.bundle)
382 382
383 383 if f in self.bundlefilespos:
384 384 self.bundle.seek(self.bundlefilespos[f])
385 385 linkmapper = self.unfiltered().changelog.rev
386 386 return bundlefilelog(self.svfs, f, self.bundle, linkmapper)
387 387 else:
388 388 return filelog.filelog(self.svfs, f)
389 389
390 390 def close(self):
391 391 """Close assigned bundle file immediately."""
392 392 self.bundlefile.close()
393 393 if self.tempfile is not None:
394 394 self.vfs.unlink(self.tempfile)
395 395 if self._tempparent:
396 396 shutil.rmtree(self._tempparent, True)
397 397
398 398 def cancopy(self):
399 399 return False
400 400
401 401 def peer(self):
402 402 return bundlepeer(self)
403 403
404 404 def getcwd(self):
405 405 return pycompat.getcwd() # always outside the repo
406 406
407 407 # Check if parents exist in localrepo before setting
408 408 def setparents(self, p1, p2=nullid):
409 409 p1rev = self.changelog.rev(p1)
410 410 p2rev = self.changelog.rev(p2)
411 411 msg = _("setting parent to node %s that only exists in the bundle\n")
412 412 if self.changelog.repotiprev < p1rev:
413 413 self.ui.warn(msg % nodemod.hex(p1))
414 414 if self.changelog.repotiprev < p2rev:
415 415 self.ui.warn(msg % nodemod.hex(p2))
416 416 return super(bundlerepository, self).setparents(p1, p2)
417 417
418 418 def instance(ui, path, create):
419 419 if create:
420 420 raise error.Abort(_('cannot create new bundle repository'))
421 421 # internal config: bundle.mainreporoot
422 422 parentpath = ui.config("bundle", "mainreporoot")
423 423 if not parentpath:
424 424 # try to find the correct path to the working directory repo
425 425 parentpath = cmdutil.findrepo(pycompat.getcwd())
426 426 if parentpath is None:
427 427 parentpath = ''
428 428 if parentpath:
429 429 # Try to make the full path relative so we get a nice, short URL.
430 430 # In particular, we don't want temp dir names in test outputs.
431 431 cwd = pycompat.getcwd()
432 432 if parentpath == cwd:
433 433 parentpath = ''
434 434 else:
435 435 cwd = pathutil.normasprefix(cwd)
436 436 if parentpath.startswith(cwd):
437 437 parentpath = parentpath[len(cwd):]
438 438 u = util.url(path)
439 439 path = u.localpath()
440 440 if u.scheme == 'bundle':
441 441 s = path.split("+", 1)
442 442 if len(s) == 1:
443 443 repopath, bundlename = parentpath, s[0]
444 444 else:
445 445 repopath, bundlename = s
446 446 else:
447 447 repopath, bundlename = parentpath, path
448 448 return bundlerepository(ui, repopath, bundlename)
449 449
450 450 class bundletransactionmanager(object):
451 451 def transaction(self):
452 452 return None
453 453
454 454 def close(self):
455 455 raise NotImplementedError
456 456
457 457 def release(self):
458 458 raise NotImplementedError
459 459
460 460 def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None,
461 461 force=False):
462 462 '''obtains a bundle of changes incoming from other
463 463
464 464 "onlyheads" restricts the returned changes to those reachable from the
465 465 specified heads.
466 466 "bundlename", if given, stores the bundle to this file path permanently;
467 467 otherwise it's stored to a temp file and gets deleted again when you call
468 468 the returned "cleanupfn".
469 469 "force" indicates whether to proceed on unrelated repos.
470 470
471 471 Returns a tuple (local, csets, cleanupfn):
472 472
473 473 "local" is a local repo from which to obtain the actual incoming
474 474 changesets; it is a bundlerepo for the obtained bundle when the
475 475 original "other" is remote.
476 476 "csets" lists the incoming changeset node ids.
477 477 "cleanupfn" must be called without arguments when you're done processing
478 478 the changes; it closes both the original "other" and the one returned
479 479 here.
480 480 '''
481 481 tmp = discovery.findcommonincoming(repo, other, heads=onlyheads,
482 482 force=force)
483 483 common, incoming, rheads = tmp
484 484 if not incoming:
485 485 try:
486 486 if bundlename:
487 487 os.unlink(bundlename)
488 488 except OSError:
489 489 pass
490 490 return repo, [], other.close
491 491
492 492 commonset = set(common)
493 493 rheads = [x for x in rheads if x not in commonset]
494 494
495 495 bundle = None
496 496 bundlerepo = None
497 497 localrepo = other.local()
498 498 if bundlename or not localrepo:
499 499 # create a bundle (uncompressed if other repo is not local)
500 500
501 501 # developer config: devel.legacy.exchange
502 502 legexc = ui.configlist('devel', 'legacy.exchange')
503 503 forcebundle1 = 'bundle2' not in legexc and 'bundle1' in legexc
504 504 canbundle2 = (not forcebundle1
505 505 and other.capable('getbundle')
506 506 and other.capable('bundle2'))
507 507 if canbundle2:
508 508 kwargs = {}
509 509 kwargs['common'] = common
510 510 kwargs['heads'] = rheads
511 511 kwargs['bundlecaps'] = exchange.caps20to10(repo)
512 512 kwargs['cg'] = True
513 513 b2 = other.getbundle('incoming', **kwargs)
514 514 fname = bundle = changegroup.writechunks(ui, b2._forwardchunks(),
515 515 bundlename)
516 516 else:
517 517 if other.capable('getbundle'):
518 518 cg = other.getbundle('incoming', common=common, heads=rheads)
519 519 elif onlyheads is None and not other.capable('changegroupsubset'):
520 520 # compat with older servers when pulling all remote heads
521 521 cg = other.changegroup(incoming, "incoming")
522 522 rheads = None
523 523 else:
524 524 cg = other.changegroupsubset(incoming, rheads, 'incoming')
525 525 if localrepo:
526 526 bundletype = "HG10BZ"
527 527 else:
528 528 bundletype = "HG10UN"
529 529 fname = bundle = bundle2.writebundle(ui, cg, bundlename,
530 530 bundletype)
531 531 # keep written bundle?
532 532 if bundlename:
533 533 bundle = None
534 534 if not localrepo:
535 535 # use the created uncompressed bundlerepo
536 536 localrepo = bundlerepo = bundlerepository(repo.baseui, repo.root,
537 537 fname)
538 538 # this repo contains local and other now, so filter out local again
539 539 common = repo.heads()
540 540 if localrepo:
541 541 # Part of common may be remotely filtered
542 542 # So use an unfiltered version
543 543 # The discovery process probably need cleanup to avoid that
544 544 localrepo = localrepo.unfiltered()
545 545
546 546 csets = localrepo.changelog.findmissing(common, rheads)
547 547
548 548 if bundlerepo:
549 549 reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]]
550 550 remotephases = other.listkeys('phases')
551 551
552 552 pullop = exchange.pulloperation(bundlerepo, other, heads=reponodes)
553 553 pullop.trmanager = bundletransactionmanager()
554 554 exchange._pullapplyphases(pullop, remotephases)
555 555
556 556 def cleanup():
557 557 if bundlerepo:
558 558 bundlerepo.close()
559 559 if bundle:
560 560 os.unlink(bundle)
561 561 other.close()
562 562
563 563 return (localrepo, csets, cleanup)
@@ -1,259 +1,259 b''
1 1 # unionrepo.py - repository class for viewing union of repository changesets
2 2 #
3 3 # Derived from bundlerepo.py
4 4 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
5 5 # Copyright 2013 Unity Technologies, Mads Kiilerich <madski@unity3d.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 """Repository class for "in-memory pull" of one local repository to another,
11 11 allowing operations like diff and log with revsets.
12 12 """
13 13
14 14 from __future__ import absolute_import
15 15
16 16 from .i18n import _
17 17 from .node import nullid
18 18
19 19 from . import (
20 20 changelog,
21 21 cmdutil,
22 22 error,
23 23 filelog,
24 24 localrepo,
25 25 manifest,
26 26 mdiff,
27 27 pathutil,
28 28 pycompat,
29 29 revlog,
30 30 util,
31 31 vfs as vfsmod,
32 32 )
33 33
34 34 class unionrevlog(revlog.revlog):
35 35 def __init__(self, opener, indexfile, revlog2, linkmapper):
36 36 # How it works:
37 37 # To retrieve a revision, we just need to know the node id so we can
38 38 # look it up in revlog2.
39 39 #
40 40 # To differentiate a rev in the second revlog from a rev in the revlog,
41 41 # we check revision against repotiprev.
42 42 opener = vfsmod.readonlyvfs(opener)
43 43 revlog.revlog.__init__(self, opener, indexfile)
44 44 self.revlog2 = revlog2
45 45
46 46 n = len(self)
47 47 self.repotiprev = n - 1
48 48 self.bundlerevs = set() # used by 'bundle()' revset expression
49 49 for rev2 in self.revlog2:
50 50 rev = self.revlog2.index[rev2]
51 51 # rev numbers - in revlog2, very different from self.rev
52 52 _start, _csize, _rsize, base, linkrev, p1rev, p2rev, node = rev
53 53 flags = _start & 0xFFFF
54 54
55 55 if linkmapper is None: # link is to same revlog
56 56 assert linkrev == rev2 # we never link back
57 57 link = n
58 58 else: # rev must be mapped from repo2 cl to unified cl by linkmapper
59 59 link = linkmapper(linkrev)
60 60
61 61 if linkmapper is not None: # link is to same revlog
62 62 base = linkmapper(base)
63 63
64 64 if node in self.nodemap:
65 65 # this happens for the common revlog revisions
66 66 self.bundlerevs.add(self.nodemap[node])
67 67 continue
68 68
69 69 p1node = self.revlog2.node(p1rev)
70 70 p2node = self.revlog2.node(p2rev)
71 71
72 72 e = (flags, None, None, base,
73 73 link, self.rev(p1node), self.rev(p2node), node)
74 74 self.index.insert(-1, e)
75 75 self.nodemap[node] = n
76 76 self.bundlerevs.add(n)
77 77 n += 1
78 78
79 79 def _chunk(self, rev):
80 80 if rev <= self.repotiprev:
81 81 return revlog.revlog._chunk(self, rev)
82 82 return self.revlog2._chunk(self.node(rev))
83 83
84 84 def revdiff(self, rev1, rev2):
85 85 """return or calculate a delta between two revisions"""
86 86 if rev1 > self.repotiprev and rev2 > self.repotiprev:
87 87 return self.revlog2.revdiff(
88 88 self.revlog2.rev(self.node(rev1)),
89 89 self.revlog2.rev(self.node(rev2)))
90 90 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
91 91 return self.baserevdiff(rev1, rev2)
92 92
93 93 return mdiff.textdiff(self.revision(rev1), self.revision(rev2))
94 94
95 95 def revision(self, nodeorrev, raw=False):
96 96 """return an uncompressed revision of a given node or revision
97 97 number.
98 98 """
99 99 if isinstance(nodeorrev, int):
100 100 rev = nodeorrev
101 101 node = self.node(rev)
102 102 else:
103 103 node = nodeorrev
104 104 rev = self.rev(node)
105 105
106 106 if node == nullid:
107 107 return ""
108 108
109 109 if rev > self.repotiprev:
110 110 text = self.revlog2.revision(node)
111 111 self._cache = (node, rev, text)
112 112 else:
113 113 text = self.baserevision(rev)
114 114 # already cached
115 115 return text
116 116
117 117 def baserevision(self, nodeorrev):
118 118 # Revlog subclasses may override 'revision' method to modify format of
119 119 # content retrieved from revlog. To use unionrevlog with such class one
120 120 # needs to override 'baserevision' and make more specific call here.
121 121 return revlog.revlog.revision(self, nodeorrev)
122 122
123 123 def baserevdiff(self, rev1, rev2):
124 124 # Exists for the same purpose as baserevision.
125 125 return revlog.revlog.revdiff(self, rev1, rev2)
126 126
127 127 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
128 128 raise NotImplementedError
129 def addgroup(self, revs, linkmapper, transaction):
129 def addgroup(self, deltas, transaction, addrevisioncb=None):
130 130 raise NotImplementedError
131 131 def strip(self, rev, minlink):
132 132 raise NotImplementedError
133 133 def checksize(self):
134 134 raise NotImplementedError
135 135
136 136 class unionchangelog(unionrevlog, changelog.changelog):
137 137 def __init__(self, opener, opener2):
138 138 changelog.changelog.__init__(self, opener)
139 139 linkmapper = None
140 140 changelog2 = changelog.changelog(opener2)
141 141 unionrevlog.__init__(self, opener, self.indexfile, changelog2,
142 142 linkmapper)
143 143
144 144 def baserevision(self, nodeorrev):
145 145 # Although changelog doesn't override 'revision' method, some extensions
146 146 # may replace this class with another that does. Same story with
147 147 # manifest and filelog classes.
148 148 return changelog.changelog.revision(self, nodeorrev)
149 149
150 150 def baserevdiff(self, rev1, rev2):
151 151 return changelog.changelog.revdiff(self, rev1, rev2)
152 152
153 153 class unionmanifest(unionrevlog, manifest.manifestrevlog):
154 154 def __init__(self, opener, opener2, linkmapper):
155 155 manifest.manifestrevlog.__init__(self, opener)
156 156 manifest2 = manifest.manifestrevlog(opener2)
157 157 unionrevlog.__init__(self, opener, self.indexfile, manifest2,
158 158 linkmapper)
159 159
160 160 def baserevision(self, nodeorrev):
161 161 return manifest.manifestrevlog.revision(self, nodeorrev)
162 162
163 163 def baserevdiff(self, rev1, rev2):
164 164 return manifest.manifestrevlog.revdiff(self, rev1, rev2)
165 165
166 166 class unionfilelog(unionrevlog, filelog.filelog):
167 167 def __init__(self, opener, path, opener2, linkmapper, repo):
168 168 filelog.filelog.__init__(self, opener, path)
169 169 filelog2 = filelog.filelog(opener2, path)
170 170 unionrevlog.__init__(self, opener, self.indexfile, filelog2,
171 171 linkmapper)
172 172 self._repo = repo
173 173
174 174 def baserevision(self, nodeorrev):
175 175 return filelog.filelog.revision(self, nodeorrev)
176 176
177 177 def baserevdiff(self, rev1, rev2):
178 178 return filelog.filelog.revdiff(self, rev1, rev2)
179 179
180 180 def iscensored(self, rev):
181 181 """Check if a revision is censored."""
182 182 if rev <= self.repotiprev:
183 183 return filelog.filelog.iscensored(self, rev)
184 184 node = self.node(rev)
185 185 return self.revlog2.iscensored(self.revlog2.rev(node))
186 186
187 187 class unionpeer(localrepo.localpeer):
188 188 def canpush(self):
189 189 return False
190 190
191 191 class unionrepository(localrepo.localrepository):
192 192 def __init__(self, ui, path, path2):
193 193 localrepo.localrepository.__init__(self, ui, path)
194 194 self.ui.setconfig('phases', 'publish', False, 'unionrepo')
195 195
196 196 self._url = 'union:%s+%s' % (util.expandpath(path),
197 197 util.expandpath(path2))
198 198 self.repo2 = localrepo.localrepository(ui, path2)
199 199
200 200 @localrepo.unfilteredpropertycache
201 201 def changelog(self):
202 202 return unionchangelog(self.svfs, self.repo2.svfs)
203 203
204 204 def _clrev(self, rev2):
205 205 """map from repo2 changelog rev to temporary rev in self.changelog"""
206 206 node = self.repo2.changelog.node(rev2)
207 207 return self.changelog.rev(node)
208 208
209 209 def _constructmanifest(self):
210 210 return unionmanifest(self.svfs, self.repo2.svfs,
211 211 self.unfiltered()._clrev)
212 212
213 213 def url(self):
214 214 return self._url
215 215
216 216 def file(self, f):
217 217 return unionfilelog(self.svfs, f, self.repo2.svfs,
218 218 self.unfiltered()._clrev, self)
219 219
220 220 def close(self):
221 221 self.repo2.close()
222 222
223 223 def cancopy(self):
224 224 return False
225 225
226 226 def peer(self):
227 227 return unionpeer(self)
228 228
229 229 def getcwd(self):
230 230 return pycompat.getcwd() # always outside the repo
231 231
232 232 def instance(ui, path, create):
233 233 if create:
234 234 raise error.Abort(_('cannot create new union repository'))
235 235 parentpath = ui.config("bundle", "mainreporoot")
236 236 if not parentpath:
237 237 # try to find the correct path to the working directory repo
238 238 parentpath = cmdutil.findrepo(pycompat.getcwd())
239 239 if parentpath is None:
240 240 parentpath = ''
241 241 if parentpath:
242 242 # Try to make the full path relative so we get a nice, short URL.
243 243 # In particular, we don't want temp dir names in test outputs.
244 244 cwd = pycompat.getcwd()
245 245 if parentpath == cwd:
246 246 parentpath = ''
247 247 else:
248 248 cwd = pathutil.normasprefix(cwd)
249 249 if parentpath.startswith(cwd):
250 250 parentpath = parentpath[len(cwd):]
251 251 if path.startswith('union:'):
252 252 s = path.split(":", 1)[1].split("+", 1)
253 253 if len(s) == 1:
254 254 repopath, repopath2 = parentpath, s[0]
255 255 else:
256 256 repopath, repopath2 = s
257 257 else:
258 258 repopath, repopath2 = parentpath, path
259 259 return unionrepository(ui, repopath, repopath2)
General Comments 0
You need to be logged in to leave comments. Login now