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