##// END OF EJS Templates
revlog: adapt the `reading` check for `bundlerepo`...
marmoute -
r51905:3470a39f default
parent child Browse files
Show More
@@ -1,742 +1,751 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
15 import contextlib
15 16 import os
16 17 import shutil
17 18
18 19 from .i18n import _
19 20 from .node import (
20 21 hex,
21 22 nullrev,
22 23 )
23 24
24 25 from . import (
25 26 bundle2,
26 27 changegroup,
27 28 changelog,
28 29 cmdutil,
29 30 discovery,
30 31 encoding,
31 32 error,
32 33 exchange,
33 34 filelog,
34 35 localrepo,
35 36 manifest,
36 37 mdiff,
37 38 pathutil,
38 39 phases,
39 40 pycompat,
40 41 revlog,
41 42 revlogutils,
42 43 util,
43 44 vfs as vfsmod,
44 45 )
45 46 from .utils import (
46 47 urlutil,
47 48 )
48 49
49 50 from .revlogutils import (
50 51 constants as revlog_constants,
51 52 )
52 53
53 54
54 55 class bundlerevlog(revlog.revlog):
55 56 def __init__(self, opener, target, radix, cgunpacker, linkmapper):
56 57 # How it works:
57 58 # To retrieve a revision, we need to know the offset of the revision in
58 59 # the bundle (an unbundle object). We store this offset in the index
59 60 # (start). The base of the delta is stored in the base field.
60 61 #
61 62 # To differentiate a rev in the bundle from a rev in the revlog, we
62 63 # check revision against repotiprev.
63 64 opener = vfsmod.readonlyvfs(opener)
64 65 revlog.revlog.__init__(self, opener, target=target, radix=radix)
65 66 self.bundle = cgunpacker
66 67 n = len(self)
67 68 self.repotiprev = n - 1
68 69 self.bundlerevs = set() # used by 'bundle()' revset expression
69 70 for deltadata in cgunpacker.deltaiter():
70 71 node, p1, p2, cs, deltabase, delta, flags, sidedata = deltadata
71 72
72 73 size = len(delta)
73 74 start = cgunpacker.tell() - size
74 75
75 76 if self.index.has_node(node):
76 77 # this can happen if two branches make the same change
77 78 self.bundlerevs.add(self.index.rev(node))
78 79 continue
79 80 if cs == node:
80 81 linkrev = nullrev
81 82 else:
82 83 linkrev = linkmapper(cs)
83 84
84 85 for p in (p1, p2):
85 86 if not self.index.has_node(p):
86 87 raise error.LookupError(
87 88 p, self.display_id, _(b"unknown parent")
88 89 )
89 90
90 91 if not self.index.has_node(deltabase):
91 92 raise error.LookupError(
92 93 deltabase, self.display_id, _(b'unknown delta base')
93 94 )
94 95
95 96 baserev = self.rev(deltabase)
96 97 # start, size, full unc. size, base (unused), link, p1, p2, node, sidedata_offset (unused), sidedata_size (unused)
97 98 e = revlogutils.entry(
98 99 flags=flags,
99 100 data_offset=start,
100 101 data_compressed_length=size,
101 102 data_delta_base=baserev,
102 103 link_rev=linkrev,
103 104 parent_rev_1=self.rev(p1),
104 105 parent_rev_2=self.rev(p2),
105 106 node_id=node,
106 107 )
107 108 self.index.append(e)
108 109 self.bundlerevs.add(n)
109 110 n += 1
110 111
112 @contextlib.contextmanager
113 def reading(self):
114 if self.repotiprev < 0:
115 yield
116 else:
117 with super().reading() as x:
118 yield x
119
111 120 def _chunk(self, rev, df=None):
112 121 # Warning: in case of bundle, the diff is against what we stored as
113 122 # delta base, not against rev - 1
114 123 # XXX: could use some caching
115 124 if rev <= self.repotiprev:
116 125 return revlog.revlog._chunk(self, rev)
117 126 self.bundle.seek(self.start(rev))
118 127 return self.bundle.read(self.length(rev))
119 128
120 129 def revdiff(self, rev1, rev2):
121 130 """return or calculate a delta between two revisions"""
122 131 if rev1 > self.repotiprev and rev2 > self.repotiprev:
123 132 # hot path for bundle
124 133 revb = self.index[rev2][3]
125 134 if revb == rev1:
126 135 return self._chunk(rev2)
127 136 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
128 137 return revlog.revlog.revdiff(self, rev1, rev2)
129 138
130 139 return mdiff.textdiff(self.rawdata(rev1), self.rawdata(rev2))
131 140
132 141 def _rawtext(self, node, rev, _df=None):
133 142 if rev is None:
134 143 rev = self.rev(node)
135 144 validated = False
136 145 rawtext = None
137 146 chain = []
138 147 iterrev = rev
139 148 # reconstruct the revision if it is from a changegroup
140 149 while iterrev > self.repotiprev:
141 150 if self._revisioncache and self._revisioncache[1] == iterrev:
142 151 rawtext = self._revisioncache[2]
143 152 break
144 153 chain.append(iterrev)
145 154 iterrev = self.index[iterrev][3]
146 155 if iterrev == nullrev:
147 156 rawtext = b''
148 157 elif rawtext is None:
149 158 r = super(bundlerevlog, self)._rawtext(
150 159 self.node(iterrev), iterrev, _df=_df
151 160 )
152 161 __, rawtext, validated = r
153 162 if chain:
154 163 validated = False
155 164 while chain:
156 165 delta = self._chunk(chain.pop())
157 166 rawtext = mdiff.patches(rawtext, [delta])
158 167 return rev, rawtext, validated
159 168
160 169 def addrevision(self, *args, **kwargs):
161 170 raise NotImplementedError
162 171
163 172 def addgroup(self, *args, **kwargs):
164 173 raise NotImplementedError
165 174
166 175 def strip(self, *args, **kwargs):
167 176 raise NotImplementedError
168 177
169 178 def checksize(self):
170 179 raise NotImplementedError
171 180
172 181
173 182 class bundlechangelog(bundlerevlog, changelog.changelog):
174 183 def __init__(self, opener, cgunpacker):
175 184 changelog.changelog.__init__(self, opener)
176 185 linkmapper = lambda x: x
177 186 bundlerevlog.__init__(
178 187 self,
179 188 opener,
180 189 (revlog_constants.KIND_CHANGELOG, None),
181 190 self.radix,
182 191 cgunpacker,
183 192 linkmapper,
184 193 )
185 194
186 195
187 196 class bundlemanifest(bundlerevlog, manifest.manifestrevlog):
188 197 def __init__(
189 198 self,
190 199 nodeconstants,
191 200 opener,
192 201 cgunpacker,
193 202 linkmapper,
194 203 dirlogstarts=None,
195 204 dir=b'',
196 205 ):
197 206 manifest.manifestrevlog.__init__(self, nodeconstants, opener, tree=dir)
198 207 bundlerevlog.__init__(
199 208 self,
200 209 opener,
201 210 (revlog_constants.KIND_MANIFESTLOG, dir),
202 211 self._revlog.radix,
203 212 cgunpacker,
204 213 linkmapper,
205 214 )
206 215 if dirlogstarts is None:
207 216 dirlogstarts = {}
208 217 if self.bundle.version == b"03":
209 218 dirlogstarts = _getfilestarts(self.bundle)
210 219 self._dirlogstarts = dirlogstarts
211 220 self._linkmapper = linkmapper
212 221
213 222 def dirlog(self, d):
214 223 if d in self._dirlogstarts:
215 224 self.bundle.seek(self._dirlogstarts[d])
216 225 return bundlemanifest(
217 226 self.nodeconstants,
218 227 self.opener,
219 228 self.bundle,
220 229 self._linkmapper,
221 230 self._dirlogstarts,
222 231 dir=d,
223 232 )
224 233 return super(bundlemanifest, self).dirlog(d)
225 234
226 235
227 236 class bundlefilelog(filelog.filelog):
228 237 def __init__(self, opener, path, cgunpacker, linkmapper):
229 238 filelog.filelog.__init__(self, opener, path)
230 239 self._revlog = bundlerevlog(
231 240 opener,
232 241 # XXX should use the unencoded path
233 242 target=(revlog_constants.KIND_FILELOG, path),
234 243 radix=self._revlog.radix,
235 244 cgunpacker=cgunpacker,
236 245 linkmapper=linkmapper,
237 246 )
238 247
239 248
240 249 class bundlepeer(localrepo.localpeer):
241 250 def canpush(self):
242 251 return False
243 252
244 253
245 254 class bundlephasecache(phases.phasecache):
246 255 def __init__(self, *args, **kwargs):
247 256 super(bundlephasecache, self).__init__(*args, **kwargs)
248 257 if hasattr(self, 'opener'):
249 258 self.opener = vfsmod.readonlyvfs(self.opener)
250 259
251 260 def write(self):
252 261 raise NotImplementedError
253 262
254 263 def _write(self, fp):
255 264 raise NotImplementedError
256 265
257 266 def _updateroots(self, phase, newroots, tr):
258 267 self.phaseroots[phase] = newroots
259 268 self.invalidate()
260 269 self.dirty = True
261 270
262 271
263 272 def _getfilestarts(cgunpacker):
264 273 filespos = {}
265 274 for chunkdata in iter(cgunpacker.filelogheader, {}):
266 275 fname = chunkdata[b'filename']
267 276 filespos[fname] = cgunpacker.tell()
268 277 for chunk in iter(lambda: cgunpacker.deltachunk(None), {}):
269 278 pass
270 279 return filespos
271 280
272 281
273 282 class bundlerepository:
274 283 """A repository instance that is a union of a local repo and a bundle.
275 284
276 285 Instances represent a read-only repository composed of a local repository
277 286 with the contents of a bundle file applied. The repository instance is
278 287 conceptually similar to the state of a repository after an
279 288 ``hg unbundle`` operation. However, the contents of the bundle are never
280 289 applied to the actual base repository.
281 290
282 291 Instances constructed directly are not usable as repository objects.
283 292 Use instance() or makebundlerepository() to create instances.
284 293 """
285 294
286 295 def __init__(self, bundlepath, url, tempparent):
287 296 self._tempparent = tempparent
288 297 self._url = url
289 298
290 299 self.ui.setconfig(b'phases', b'publish', False, b'bundlerepo')
291 300
292 301 # dict with the mapping 'filename' -> position in the changegroup.
293 302 self._cgfilespos = {}
294 303 self._bundlefile = None
295 304 self._cgunpacker = None
296 305 self.tempfile = None
297 306 f = util.posixfile(bundlepath, b"rb")
298 307 bundle = exchange.readbundle(self.ui, f, bundlepath)
299 308
300 309 if isinstance(bundle, bundle2.unbundle20):
301 310 self._bundlefile = bundle
302 311
303 312 cgpart = None
304 313 for part in bundle.iterparts(seekable=True):
305 314 if part.type == b'phase-heads':
306 315 self._handle_bundle2_phase_part(bundle, part)
307 316 elif part.type == b'changegroup':
308 317 if cgpart:
309 318 raise NotImplementedError(
310 319 b"can't process multiple changegroups"
311 320 )
312 321 cgpart = part
313 322 self._handle_bundle2_cg_part(bundle, part)
314 323
315 324 if not cgpart:
316 325 raise error.Abort(_(b"No changegroups found"))
317 326
318 327 # This is required to placate a later consumer, which expects
319 328 # the payload offset to be at the beginning of the changegroup.
320 329 # We need to do this after the iterparts() generator advances
321 330 # because iterparts() will seek to end of payload after the
322 331 # generator returns control to iterparts().
323 332 cgpart.seek(0, os.SEEK_SET)
324 333
325 334 elif isinstance(bundle, changegroup.cg1unpacker):
326 335 self._handle_bundle1(bundle, bundlepath)
327 336 else:
328 337 raise error.Abort(
329 338 _(b'bundle type %r cannot be read') % type(bundle)
330 339 )
331 340
332 341 def _handle_bundle1(self, bundle, bundlepath):
333 342 if bundle.compressed():
334 343 f = self._writetempbundle(bundle.read, b'.hg10un', header=b'HG10UN')
335 344 bundle = exchange.readbundle(self.ui, f, bundlepath, self.vfs)
336 345
337 346 self._bundlefile = bundle
338 347 self._cgunpacker = bundle
339 348
340 349 self.firstnewrev = self.changelog.repotiprev + 1
341 350 phases.retractboundary(
342 351 self,
343 352 None,
344 353 phases.draft,
345 354 [ctx.node() for ctx in self[self.firstnewrev :]],
346 355 )
347 356
348 357 def _handle_bundle2_cg_part(self, bundle, part):
349 358 assert part.type == b'changegroup'
350 359 cgstream = part
351 360 targetphase = part.params.get(b'targetphase')
352 361 try:
353 362 targetphase = int(targetphase)
354 363 except TypeError:
355 364 pass
356 365 if targetphase is None:
357 366 targetphase = phases.draft
358 367 if targetphase not in phases.allphases:
359 368 m = _(b'unsupported targetphase: %d')
360 369 m %= targetphase
361 370 raise error.Abort(m)
362 371 version = part.params.get(b'version', b'01')
363 372 legalcgvers = changegroup.supportedincomingversions(self)
364 373 if version not in legalcgvers:
365 374 msg = _(b'Unsupported changegroup version: %s')
366 375 raise error.Abort(msg % version)
367 376 if bundle.compressed():
368 377 cgstream = self._writetempbundle(part.read, b'.cg%sun' % version)
369 378
370 379 self._cgunpacker = changegroup.getunbundler(version, cgstream, b'UN')
371 380
372 381 self.firstnewrev = self.changelog.repotiprev + 1
373 382 phases.retractboundary(
374 383 self,
375 384 None,
376 385 targetphase,
377 386 [ctx.node() for ctx in self[self.firstnewrev :]],
378 387 )
379 388
380 389 def _handle_bundle2_phase_part(self, bundle, part):
381 390 assert part.type == b'phase-heads'
382 391
383 392 unfi = self.unfiltered()
384 393 headsbyphase = phases.binarydecode(part)
385 394 phases.updatephases(unfi, lambda: None, headsbyphase)
386 395
387 396 def _writetempbundle(self, readfn, suffix, header=b''):
388 397 """Write a temporary file to disk"""
389 398 fdtemp, temp = self.vfs.mkstemp(prefix=b"hg-bundle-", suffix=suffix)
390 399 self.tempfile = temp
391 400
392 401 with os.fdopen(fdtemp, 'wb') as fptemp:
393 402 fptemp.write(header)
394 403 while True:
395 404 chunk = readfn(2 ** 18)
396 405 if not chunk:
397 406 break
398 407 fptemp.write(chunk)
399 408
400 409 return self.vfs.open(self.tempfile, mode=b"rb")
401 410
402 411 @localrepo.unfilteredpropertycache
403 412 def _phasecache(self):
404 413 return bundlephasecache(self, self._phasedefaults)
405 414
406 415 @localrepo.unfilteredpropertycache
407 416 def changelog(self):
408 417 # consume the header if it exists
409 418 self._cgunpacker.changelogheader()
410 419 c = bundlechangelog(self.svfs, self._cgunpacker)
411 420 self.manstart = self._cgunpacker.tell()
412 421 return c
413 422
414 423 def _refreshchangelog(self):
415 424 # changelog for bundle repo are not filecache, this method is not
416 425 # applicable.
417 426 pass
418 427
419 428 @localrepo.unfilteredpropertycache
420 429 def manifestlog(self):
421 430 self._cgunpacker.seek(self.manstart)
422 431 # consume the header if it exists
423 432 self._cgunpacker.manifestheader()
424 433 linkmapper = self.unfiltered().changelog.rev
425 434 rootstore = bundlemanifest(
426 435 self.nodeconstants, self.svfs, self._cgunpacker, linkmapper
427 436 )
428 437 self.filestart = self._cgunpacker.tell()
429 438
430 439 return manifest.manifestlog(
431 440 self.svfs, self, rootstore, self.narrowmatch()
432 441 )
433 442
434 443 def _consumemanifest(self):
435 444 """Consumes the manifest portion of the bundle, setting filestart so the
436 445 file portion can be read."""
437 446 self._cgunpacker.seek(self.manstart)
438 447 self._cgunpacker.manifestheader()
439 448 for delta in self._cgunpacker.deltaiter():
440 449 pass
441 450 self.filestart = self._cgunpacker.tell()
442 451
443 452 @localrepo.unfilteredpropertycache
444 453 def manstart(self):
445 454 self.changelog
446 455 return self.manstart
447 456
448 457 @localrepo.unfilteredpropertycache
449 458 def filestart(self):
450 459 self.manifestlog
451 460
452 461 # If filestart was not set by self.manifestlog, that means the
453 462 # manifestlog implementation did not consume the manifests from the
454 463 # changegroup (ex: it might be consuming trees from a separate bundle2
455 464 # part instead). So we need to manually consume it.
456 465 if 'filestart' not in self.__dict__:
457 466 self._consumemanifest()
458 467
459 468 return self.filestart
460 469
461 470 def url(self):
462 471 return self._url
463 472
464 473 def file(self, f):
465 474 if not self._cgfilespos:
466 475 self._cgunpacker.seek(self.filestart)
467 476 self._cgfilespos = _getfilestarts(self._cgunpacker)
468 477
469 478 if f in self._cgfilespos:
470 479 self._cgunpacker.seek(self._cgfilespos[f])
471 480 linkmapper = self.unfiltered().changelog.rev
472 481 return bundlefilelog(self.svfs, f, self._cgunpacker, linkmapper)
473 482 else:
474 483 return super(bundlerepository, self).file(f)
475 484
476 485 def close(self):
477 486 """Close assigned bundle file immediately."""
478 487 self._bundlefile.close()
479 488 if self.tempfile is not None:
480 489 self.vfs.unlink(self.tempfile)
481 490 if self._tempparent:
482 491 shutil.rmtree(self._tempparent, True)
483 492
484 493 def cancopy(self):
485 494 return False
486 495
487 496 def peer(self, path=None, remotehidden=False):
488 497 return bundlepeer(self, path=path, remotehidden=remotehidden)
489 498
490 499 def getcwd(self):
491 500 return encoding.getcwd() # always outside the repo
492 501
493 502 # Check if parents exist in localrepo before setting
494 503 def setparents(self, p1, p2=None):
495 504 if p2 is None:
496 505 p2 = self.nullid
497 506 p1rev = self.changelog.rev(p1)
498 507 p2rev = self.changelog.rev(p2)
499 508 msg = _(b"setting parent to node %s that only exists in the bundle\n")
500 509 if self.changelog.repotiprev < p1rev:
501 510 self.ui.warn(msg % hex(p1))
502 511 if self.changelog.repotiprev < p2rev:
503 512 self.ui.warn(msg % hex(p2))
504 513 return super(bundlerepository, self).setparents(p1, p2)
505 514
506 515
507 516 def instance(ui, path, create, intents=None, createopts=None):
508 517 if create:
509 518 raise error.Abort(_(b'cannot create new bundle repository'))
510 519 # internal config: bundle.mainreporoot
511 520 parentpath = ui.config(b"bundle", b"mainreporoot")
512 521 if not parentpath:
513 522 # try to find the correct path to the working directory repo
514 523 parentpath = cmdutil.findrepo(encoding.getcwd())
515 524 if parentpath is None:
516 525 parentpath = b''
517 526 if parentpath:
518 527 # Try to make the full path relative so we get a nice, short URL.
519 528 # In particular, we don't want temp dir names in test outputs.
520 529 cwd = encoding.getcwd()
521 530 if parentpath == cwd:
522 531 parentpath = b''
523 532 else:
524 533 cwd = pathutil.normasprefix(cwd)
525 534 if parentpath.startswith(cwd):
526 535 parentpath = parentpath[len(cwd) :]
527 536 u = urlutil.url(path)
528 537 path = u.localpath()
529 538 if u.scheme == b'bundle':
530 539 s = path.split(b"+", 1)
531 540 if len(s) == 1:
532 541 repopath, bundlename = parentpath, s[0]
533 542 else:
534 543 repopath, bundlename = s
535 544 else:
536 545 repopath, bundlename = parentpath, path
537 546
538 547 return makebundlerepository(ui, repopath, bundlename)
539 548
540 549
541 550 def makebundlerepository(ui, repopath, bundlepath):
542 551 """Make a bundle repository object based on repo and bundle paths."""
543 552 if repopath:
544 553 url = b'bundle:%s+%s' % (util.expandpath(repopath), bundlepath)
545 554 else:
546 555 url = b'bundle:%s' % bundlepath
547 556
548 557 # Because we can't make any guarantees about the type of the base
549 558 # repository, we can't have a static class representing the bundle
550 559 # repository. We also can't make any guarantees about how to even
551 560 # call the base repository's constructor!
552 561 #
553 562 # So, our strategy is to go through ``localrepo.instance()`` to construct
554 563 # a repo instance. Then, we dynamically create a new type derived from
555 564 # both it and our ``bundlerepository`` class which overrides some
556 565 # functionality. We then change the type of the constructed repository
557 566 # to this new type and initialize the bundle-specific bits of it.
558 567
559 568 try:
560 569 repo = localrepo.instance(ui, repopath, create=False)
561 570 tempparent = None
562 571 except error.RequirementError:
563 572 raise # no fallback if the backing repo is unsupported
564 573 except error.RepoError:
565 574 tempparent = pycompat.mkdtemp()
566 575 try:
567 576 repo = localrepo.instance(ui, tempparent, create=True)
568 577 except Exception:
569 578 shutil.rmtree(tempparent)
570 579 raise
571 580
572 581 class derivedbundlerepository(bundlerepository, repo.__class__):
573 582 pass
574 583
575 584 repo.__class__ = derivedbundlerepository
576 585 bundlerepository.__init__(repo, bundlepath, url, tempparent)
577 586
578 587 return repo
579 588
580 589
581 590 class bundletransactionmanager:
582 591 def transaction(self):
583 592 return None
584 593
585 594 def close(self):
586 595 raise NotImplementedError
587 596
588 597 def release(self):
589 598 raise NotImplementedError
590 599
591 600
592 601 def getremotechanges(
593 602 ui, repo, peer, onlyheads=None, bundlename=None, force=False
594 603 ):
595 604 """obtains a bundle of changes incoming from peer
596 605
597 606 "onlyheads" restricts the returned changes to those reachable from the
598 607 specified heads.
599 608 "bundlename", if given, stores the bundle to this file path permanently;
600 609 otherwise it's stored to a temp file and gets deleted again when you call
601 610 the returned "cleanupfn".
602 611 "force" indicates whether to proceed on unrelated repos.
603 612
604 613 Returns a tuple (local, csets, cleanupfn):
605 614
606 615 "local" is a local repo from which to obtain the actual incoming
607 616 changesets; it is a bundlerepo for the obtained bundle when the
608 617 original "peer" is remote.
609 618 "csets" lists the incoming changeset node ids.
610 619 "cleanupfn" must be called without arguments when you're done processing
611 620 the changes; it closes both the original "peer" and the one returned
612 621 here.
613 622 """
614 623 tmp = discovery.findcommonincoming(repo, peer, heads=onlyheads, force=force)
615 624 common, incoming, rheads = tmp
616 625 if not incoming:
617 626 try:
618 627 if bundlename:
619 628 os.unlink(bundlename)
620 629 except OSError:
621 630 pass
622 631 return repo, [], peer.close
623 632
624 633 commonset = set(common)
625 634 rheads = [x for x in rheads if x not in commonset]
626 635
627 636 bundle = None
628 637 bundlerepo = None
629 638 localrepo = peer.local()
630 639 if bundlename or not localrepo:
631 640 # create a bundle (uncompressed if peer repo is not local)
632 641
633 642 # developer config: devel.legacy.exchange
634 643 legexc = ui.configlist(b'devel', b'legacy.exchange')
635 644 forcebundle1 = b'bundle2' not in legexc and b'bundle1' in legexc
636 645 canbundle2 = (
637 646 not forcebundle1
638 647 and peer.capable(b'getbundle')
639 648 and peer.capable(b'bundle2')
640 649 )
641 650 if canbundle2:
642 651 with peer.commandexecutor() as e:
643 652 b2 = e.callcommand(
644 653 b'getbundle',
645 654 {
646 655 b'source': b'incoming',
647 656 b'common': common,
648 657 b'heads': rheads,
649 658 b'bundlecaps': exchange.caps20to10(
650 659 repo, role=b'client'
651 660 ),
652 661 b'cg': True,
653 662 },
654 663 ).result()
655 664
656 665 fname = bundle = changegroup.writechunks(
657 666 ui, b2._forwardchunks(), bundlename
658 667 )
659 668 else:
660 669 if peer.capable(b'getbundle'):
661 670 with peer.commandexecutor() as e:
662 671 cg = e.callcommand(
663 672 b'getbundle',
664 673 {
665 674 b'source': b'incoming',
666 675 b'common': common,
667 676 b'heads': rheads,
668 677 },
669 678 ).result()
670 679 elif onlyheads is None and not peer.capable(b'changegroupsubset'):
671 680 # compat with older servers when pulling all remote heads
672 681
673 682 with peer.commandexecutor() as e:
674 683 cg = e.callcommand(
675 684 b'changegroup',
676 685 {
677 686 b'nodes': incoming,
678 687 b'source': b'incoming',
679 688 },
680 689 ).result()
681 690
682 691 rheads = None
683 692 else:
684 693 with peer.commandexecutor() as e:
685 694 cg = e.callcommand(
686 695 b'changegroupsubset',
687 696 {
688 697 b'bases': incoming,
689 698 b'heads': rheads,
690 699 b'source': b'incoming',
691 700 },
692 701 ).result()
693 702
694 703 if localrepo:
695 704 bundletype = b"HG10BZ"
696 705 else:
697 706 bundletype = b"HG10UN"
698 707 fname = bundle = bundle2.writebundle(ui, cg, bundlename, bundletype)
699 708 # keep written bundle?
700 709 if bundlename:
701 710 bundle = None
702 711 if not localrepo:
703 712 # use the created uncompressed bundlerepo
704 713 localrepo = bundlerepo = makebundlerepository(
705 714 repo.baseui, repo.root, fname
706 715 )
707 716
708 717 # this repo contains local and peer now, so filter out local again
709 718 common = repo.heads()
710 719 if localrepo:
711 720 # Part of common may be remotely filtered
712 721 # So use an unfiltered version
713 722 # The discovery process probably need cleanup to avoid that
714 723 localrepo = localrepo.unfiltered()
715 724
716 725 csets = localrepo.changelog.findmissing(common, rheads)
717 726
718 727 if bundlerepo:
719 728 reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev :]]
720 729
721 730 with peer.commandexecutor() as e:
722 731 remotephases = e.callcommand(
723 732 b'listkeys',
724 733 {
725 734 b'namespace': b'phases',
726 735 },
727 736 ).result()
728 737
729 738 pullop = exchange.pulloperation(
730 739 bundlerepo, peer, path=None, heads=reponodes
731 740 )
732 741 pullop.trmanager = bundletransactionmanager()
733 742 exchange._pullapplyphases(pullop, remotephases)
734 743
735 744 def cleanup():
736 745 if bundlerepo:
737 746 bundlerepo.close()
738 747 if bundle:
739 748 os.unlink(bundle)
740 749 peer.close()
741 750
742 751 return (localrepo, csets, cleanup)
General Comments 0
You need to be logged in to leave comments. Login now