##// END OF EJS Templates
bundlerepo: move the handling of bundl1 in its own method...
marmoute -
r51092:c493cb85 default
parent child Browse files
Show More
@@ -1,713 +1,714 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 308 self._handle_bundle2_cg_part(bundle, part)
309 309
310 310 if not cgpart:
311 311 raise error.Abort(_(b"No changegroups found"))
312 312
313 313 # This is required to placate a later consumer, which expects
314 314 # the payload offset to be at the beginning of the changegroup.
315 315 # We need to do this after the iterparts() generator advances
316 316 # because iterparts() will seek to end of payload after the
317 317 # generator returns control to iterparts().
318 318 cgpart.seek(0, os.SEEK_SET)
319 319
320 320 elif isinstance(bundle, changegroup.cg1unpacker):
321 if bundle.compressed():
322 f = self._writetempbundle(
323 bundle.read, b'.hg10un', header=b'HG10UN'
324 )
325 bundle = exchange.readbundle(self.ui, f, bundlepath, self.vfs)
326
327 self._bundlefile = bundle
328 self._cgunpacker = bundle
321 self._handle_bundle1(bundle, bundlepath)
329 322 else:
330 323 raise error.Abort(
331 324 _(b'bundle type %s cannot be read') % type(bundle)
332 325 )
333 326
334 327 # dict with the mapping 'filename' -> position in the changegroup.
335 328 self._cgfilespos = {}
336 329
337 330 self.firstnewrev = self.changelog.repotiprev + 1
338 331 phases.retractboundary(
339 332 self,
340 333 None,
341 334 phases.draft,
342 335 [ctx.node() for ctx in self[self.firstnewrev :]],
343 336 )
344 337
338 def _handle_bundle1(self, bundle, bundlepath):
339 if bundle.compressed():
340 f = self._writetempbundle(bundle.read, b'.hg10un', header=b'HG10UN')
341 bundle = exchange.readbundle(self.ui, f, bundlepath, self.vfs)
342
343 self._bundlefile = bundle
344 self._cgunpacker = bundle
345
345 346 def _handle_bundle2_cg_part(self, bundle, part):
346 347 assert part.type == b'changegroup'
347 348 cgstream = part
348 349 version = part.params.get(b'version', b'01')
349 350 legalcgvers = changegroup.supportedincomingversions(self)
350 351 if version not in legalcgvers:
351 352 msg = _(b'Unsupported changegroup version: %s')
352 353 raise error.Abort(msg % version)
353 354 if bundle.compressed():
354 355 cgstream = self._writetempbundle(part.read, b'.cg%sun' % version)
355 356
356 357 self._cgunpacker = changegroup.getunbundler(version, cgstream, b'UN')
357 358
358 359 def _writetempbundle(self, readfn, suffix, header=b''):
359 360 """Write a temporary file to disk"""
360 361 fdtemp, temp = self.vfs.mkstemp(prefix=b"hg-bundle-", suffix=suffix)
361 362 self.tempfile = temp
362 363
363 364 with os.fdopen(fdtemp, 'wb') as fptemp:
364 365 fptemp.write(header)
365 366 while True:
366 367 chunk = readfn(2 ** 18)
367 368 if not chunk:
368 369 break
369 370 fptemp.write(chunk)
370 371
371 372 return self.vfs.open(self.tempfile, mode=b"rb")
372 373
373 374 @localrepo.unfilteredpropertycache
374 375 def _phasecache(self):
375 376 return bundlephasecache(self, self._phasedefaults)
376 377
377 378 @localrepo.unfilteredpropertycache
378 379 def changelog(self):
379 380 # consume the header if it exists
380 381 self._cgunpacker.changelogheader()
381 382 c = bundlechangelog(self.svfs, self._cgunpacker)
382 383 self.manstart = self._cgunpacker.tell()
383 384 return c
384 385
385 386 def _refreshchangelog(self):
386 387 # changelog for bundle repo are not filecache, this method is not
387 388 # applicable.
388 389 pass
389 390
390 391 @localrepo.unfilteredpropertycache
391 392 def manifestlog(self):
392 393 self._cgunpacker.seek(self.manstart)
393 394 # consume the header if it exists
394 395 self._cgunpacker.manifestheader()
395 396 linkmapper = self.unfiltered().changelog.rev
396 397 rootstore = bundlemanifest(
397 398 self.nodeconstants, self.svfs, self._cgunpacker, linkmapper
398 399 )
399 400 self.filestart = self._cgunpacker.tell()
400 401
401 402 return manifest.manifestlog(
402 403 self.svfs, self, rootstore, self.narrowmatch()
403 404 )
404 405
405 406 def _consumemanifest(self):
406 407 """Consumes the manifest portion of the bundle, setting filestart so the
407 408 file portion can be read."""
408 409 self._cgunpacker.seek(self.manstart)
409 410 self._cgunpacker.manifestheader()
410 411 for delta in self._cgunpacker.deltaiter():
411 412 pass
412 413 self.filestart = self._cgunpacker.tell()
413 414
414 415 @localrepo.unfilteredpropertycache
415 416 def manstart(self):
416 417 self.changelog
417 418 return self.manstart
418 419
419 420 @localrepo.unfilteredpropertycache
420 421 def filestart(self):
421 422 self.manifestlog
422 423
423 424 # If filestart was not set by self.manifestlog, that means the
424 425 # manifestlog implementation did not consume the manifests from the
425 426 # changegroup (ex: it might be consuming trees from a separate bundle2
426 427 # part instead). So we need to manually consume it.
427 428 if 'filestart' not in self.__dict__:
428 429 self._consumemanifest()
429 430
430 431 return self.filestart
431 432
432 433 def url(self):
433 434 return self._url
434 435
435 436 def file(self, f):
436 437 if not self._cgfilespos:
437 438 self._cgunpacker.seek(self.filestart)
438 439 self._cgfilespos = _getfilestarts(self._cgunpacker)
439 440
440 441 if f in self._cgfilespos:
441 442 self._cgunpacker.seek(self._cgfilespos[f])
442 443 linkmapper = self.unfiltered().changelog.rev
443 444 return bundlefilelog(self.svfs, f, self._cgunpacker, linkmapper)
444 445 else:
445 446 return super(bundlerepository, self).file(f)
446 447
447 448 def close(self):
448 449 """Close assigned bundle file immediately."""
449 450 self._bundlefile.close()
450 451 if self.tempfile is not None:
451 452 self.vfs.unlink(self.tempfile)
452 453 if self._tempparent:
453 454 shutil.rmtree(self._tempparent, True)
454 455
455 456 def cancopy(self):
456 457 return False
457 458
458 459 def peer(self, path=None):
459 460 return bundlepeer(self, path=path)
460 461
461 462 def getcwd(self):
462 463 return encoding.getcwd() # always outside the repo
463 464
464 465 # Check if parents exist in localrepo before setting
465 466 def setparents(self, p1, p2=None):
466 467 if p2 is None:
467 468 p2 = self.nullid
468 469 p1rev = self.changelog.rev(p1)
469 470 p2rev = self.changelog.rev(p2)
470 471 msg = _(b"setting parent to node %s that only exists in the bundle\n")
471 472 if self.changelog.repotiprev < p1rev:
472 473 self.ui.warn(msg % hex(p1))
473 474 if self.changelog.repotiprev < p2rev:
474 475 self.ui.warn(msg % hex(p2))
475 476 return super(bundlerepository, self).setparents(p1, p2)
476 477
477 478
478 479 def instance(ui, path, create, intents=None, createopts=None):
479 480 if create:
480 481 raise error.Abort(_(b'cannot create new bundle repository'))
481 482 # internal config: bundle.mainreporoot
482 483 parentpath = ui.config(b"bundle", b"mainreporoot")
483 484 if not parentpath:
484 485 # try to find the correct path to the working directory repo
485 486 parentpath = cmdutil.findrepo(encoding.getcwd())
486 487 if parentpath is None:
487 488 parentpath = b''
488 489 if parentpath:
489 490 # Try to make the full path relative so we get a nice, short URL.
490 491 # In particular, we don't want temp dir names in test outputs.
491 492 cwd = encoding.getcwd()
492 493 if parentpath == cwd:
493 494 parentpath = b''
494 495 else:
495 496 cwd = pathutil.normasprefix(cwd)
496 497 if parentpath.startswith(cwd):
497 498 parentpath = parentpath[len(cwd) :]
498 499 u = urlutil.url(path)
499 500 path = u.localpath()
500 501 if u.scheme == b'bundle':
501 502 s = path.split(b"+", 1)
502 503 if len(s) == 1:
503 504 repopath, bundlename = parentpath, s[0]
504 505 else:
505 506 repopath, bundlename = s
506 507 else:
507 508 repopath, bundlename = parentpath, path
508 509
509 510 return makebundlerepository(ui, repopath, bundlename)
510 511
511 512
512 513 def makebundlerepository(ui, repopath, bundlepath):
513 514 """Make a bundle repository object based on repo and bundle paths."""
514 515 if repopath:
515 516 url = b'bundle:%s+%s' % (util.expandpath(repopath), bundlepath)
516 517 else:
517 518 url = b'bundle:%s' % bundlepath
518 519
519 520 # Because we can't make any guarantees about the type of the base
520 521 # repository, we can't have a static class representing the bundle
521 522 # repository. We also can't make any guarantees about how to even
522 523 # call the base repository's constructor!
523 524 #
524 525 # So, our strategy is to go through ``localrepo.instance()`` to construct
525 526 # a repo instance. Then, we dynamically create a new type derived from
526 527 # both it and our ``bundlerepository`` class which overrides some
527 528 # functionality. We then change the type of the constructed repository
528 529 # to this new type and initialize the bundle-specific bits of it.
529 530
530 531 try:
531 532 repo = localrepo.instance(ui, repopath, create=False)
532 533 tempparent = None
533 534 except error.RequirementError:
534 535 raise # no fallback if the backing repo is unsupported
535 536 except error.RepoError:
536 537 tempparent = pycompat.mkdtemp()
537 538 try:
538 539 repo = localrepo.instance(ui, tempparent, create=True)
539 540 except Exception:
540 541 shutil.rmtree(tempparent)
541 542 raise
542 543
543 544 class derivedbundlerepository(bundlerepository, repo.__class__):
544 545 pass
545 546
546 547 repo.__class__ = derivedbundlerepository
547 548 bundlerepository.__init__(repo, bundlepath, url, tempparent)
548 549
549 550 return repo
550 551
551 552
552 553 class bundletransactionmanager:
553 554 def transaction(self):
554 555 return None
555 556
556 557 def close(self):
557 558 raise NotImplementedError
558 559
559 560 def release(self):
560 561 raise NotImplementedError
561 562
562 563
563 564 def getremotechanges(
564 565 ui, repo, peer, onlyheads=None, bundlename=None, force=False
565 566 ):
566 567 """obtains a bundle of changes incoming from peer
567 568
568 569 "onlyheads" restricts the returned changes to those reachable from the
569 570 specified heads.
570 571 "bundlename", if given, stores the bundle to this file path permanently;
571 572 otherwise it's stored to a temp file and gets deleted again when you call
572 573 the returned "cleanupfn".
573 574 "force" indicates whether to proceed on unrelated repos.
574 575
575 576 Returns a tuple (local, csets, cleanupfn):
576 577
577 578 "local" is a local repo from which to obtain the actual incoming
578 579 changesets; it is a bundlerepo for the obtained bundle when the
579 580 original "peer" is remote.
580 581 "csets" lists the incoming changeset node ids.
581 582 "cleanupfn" must be called without arguments when you're done processing
582 583 the changes; it closes both the original "peer" and the one returned
583 584 here.
584 585 """
585 586 tmp = discovery.findcommonincoming(repo, peer, heads=onlyheads, force=force)
586 587 common, incoming, rheads = tmp
587 588 if not incoming:
588 589 try:
589 590 if bundlename:
590 591 os.unlink(bundlename)
591 592 except OSError:
592 593 pass
593 594 return repo, [], peer.close
594 595
595 596 commonset = set(common)
596 597 rheads = [x for x in rheads if x not in commonset]
597 598
598 599 bundle = None
599 600 bundlerepo = None
600 601 localrepo = peer.local()
601 602 if bundlename or not localrepo:
602 603 # create a bundle (uncompressed if peer repo is not local)
603 604
604 605 # developer config: devel.legacy.exchange
605 606 legexc = ui.configlist(b'devel', b'legacy.exchange')
606 607 forcebundle1 = b'bundle2' not in legexc and b'bundle1' in legexc
607 608 canbundle2 = (
608 609 not forcebundle1
609 610 and peer.capable(b'getbundle')
610 611 and peer.capable(b'bundle2')
611 612 )
612 613 if canbundle2:
613 614 with peer.commandexecutor() as e:
614 615 b2 = e.callcommand(
615 616 b'getbundle',
616 617 {
617 618 b'source': b'incoming',
618 619 b'common': common,
619 620 b'heads': rheads,
620 621 b'bundlecaps': exchange.caps20to10(
621 622 repo, role=b'client'
622 623 ),
623 624 b'cg': True,
624 625 },
625 626 ).result()
626 627
627 628 fname = bundle = changegroup.writechunks(
628 629 ui, b2._forwardchunks(), bundlename
629 630 )
630 631 else:
631 632 if peer.capable(b'getbundle'):
632 633 with peer.commandexecutor() as e:
633 634 cg = e.callcommand(
634 635 b'getbundle',
635 636 {
636 637 b'source': b'incoming',
637 638 b'common': common,
638 639 b'heads': rheads,
639 640 },
640 641 ).result()
641 642 elif onlyheads is None and not peer.capable(b'changegroupsubset'):
642 643 # compat with older servers when pulling all remote heads
643 644
644 645 with peer.commandexecutor() as e:
645 646 cg = e.callcommand(
646 647 b'changegroup',
647 648 {
648 649 b'nodes': incoming,
649 650 b'source': b'incoming',
650 651 },
651 652 ).result()
652 653
653 654 rheads = None
654 655 else:
655 656 with peer.commandexecutor() as e:
656 657 cg = e.callcommand(
657 658 b'changegroupsubset',
658 659 {
659 660 b'bases': incoming,
660 661 b'heads': rheads,
661 662 b'source': b'incoming',
662 663 },
663 664 ).result()
664 665
665 666 if localrepo:
666 667 bundletype = b"HG10BZ"
667 668 else:
668 669 bundletype = b"HG10UN"
669 670 fname = bundle = bundle2.writebundle(ui, cg, bundlename, bundletype)
670 671 # keep written bundle?
671 672 if bundlename:
672 673 bundle = None
673 674 if not localrepo:
674 675 # use the created uncompressed bundlerepo
675 676 localrepo = bundlerepo = makebundlerepository(
676 677 repo.baseui, repo.root, fname
677 678 )
678 679
679 680 # this repo contains local and peer now, so filter out local again
680 681 common = repo.heads()
681 682 if localrepo:
682 683 # Part of common may be remotely filtered
683 684 # So use an unfiltered version
684 685 # The discovery process probably need cleanup to avoid that
685 686 localrepo = localrepo.unfiltered()
686 687
687 688 csets = localrepo.changelog.findmissing(common, rheads)
688 689
689 690 if bundlerepo:
690 691 reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev :]]
691 692
692 693 with peer.commandexecutor() as e:
693 694 remotephases = e.callcommand(
694 695 b'listkeys',
695 696 {
696 697 b'namespace': b'phases',
697 698 },
698 699 ).result()
699 700
700 701 pullop = exchange.pulloperation(
701 702 bundlerepo, peer, path=None, heads=reponodes
702 703 )
703 704 pullop.trmanager = bundletransactionmanager()
704 705 exchange._pullapplyphases(pullop, remotephases)
705 706
706 707 def cleanup():
707 708 if bundlerepo:
708 709 bundlerepo.close()
709 710 if bundle:
710 711 os.unlink(bundle)
711 712 peer.close()
712 713
713 714 return (localrepo, csets, cleanup)
General Comments 0
You need to be logged in to leave comments. Login now