Mercurial > hg
comparison hgext/rebase.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | 43c84b816445 |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
68 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | 68 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should |
69 # be specifying the version(s) of Mercurial they are tested with, or | 69 # be specifying the version(s) of Mercurial they are tested with, or |
70 # leave the attribute unspecified. | 70 # leave the attribute unspecified. |
71 testedwith = 'ships-with-hg-core' | 71 testedwith = 'ships-with-hg-core' |
72 | 72 |
73 | |
73 def _nothingtorebase(): | 74 def _nothingtorebase(): |
74 return 1 | 75 return 1 |
76 | |
75 | 77 |
76 def _savegraft(ctx, extra): | 78 def _savegraft(ctx, extra): |
77 s = ctx.extra().get('source', None) | 79 s = ctx.extra().get('source', None) |
78 if s is not None: | 80 if s is not None: |
79 extra['source'] = s | 81 extra['source'] = s |
80 s = ctx.extra().get('intermediate-source', None) | 82 s = ctx.extra().get('intermediate-source', None) |
81 if s is not None: | 83 if s is not None: |
82 extra['intermediate-source'] = s | 84 extra['intermediate-source'] = s |
83 | 85 |
86 | |
84 def _savebranch(ctx, extra): | 87 def _savebranch(ctx, extra): |
85 extra['branch'] = ctx.branch() | 88 extra['branch'] = ctx.branch() |
86 | 89 |
90 | |
87 def _destrebase(repo, sourceset, destspace=None): | 91 def _destrebase(repo, sourceset, destspace=None): |
88 """small wrapper around destmerge to pass the right extra args | 92 """small wrapper around destmerge to pass the right extra args |
89 | 93 |
90 Please wrap destutil.destmerge instead.""" | 94 Please wrap destutil.destmerge instead.""" |
91 return destutil.destmerge(repo, action='rebase', sourceset=sourceset, | 95 return destutil.destmerge( |
92 onheadcheck=False, destspace=destspace) | 96 repo, |
97 action='rebase', | |
98 sourceset=sourceset, | |
99 onheadcheck=False, | |
100 destspace=destspace, | |
101 ) | |
102 | |
93 | 103 |
94 revsetpredicate = registrar.revsetpredicate() | 104 revsetpredicate = registrar.revsetpredicate() |
105 | |
95 | 106 |
96 @revsetpredicate('_destrebase') | 107 @revsetpredicate('_destrebase') |
97 def _revsetdestrebase(repo, subset, x): | 108 def _revsetdestrebase(repo, subset, x): |
98 # ``_rebasedefaultdest()`` | 109 # ``_rebasedefaultdest()`` |
99 | 110 |
104 sourceset = None | 115 sourceset = None |
105 if x is not None: | 116 if x is not None: |
106 sourceset = revset.getset(repo, smartset.fullreposet(repo), x) | 117 sourceset = revset.getset(repo, smartset.fullreposet(repo), x) |
107 return subset & smartset.baseset([_destrebase(repo, sourceset)]) | 118 return subset & smartset.baseset([_destrebase(repo, sourceset)]) |
108 | 119 |
120 | |
109 @revsetpredicate('_destautoorphanrebase') | 121 @revsetpredicate('_destautoorphanrebase') |
110 def _revsetdestautoorphanrebase(repo, subset, x): | 122 def _revsetdestautoorphanrebase(repo, subset, x): |
111 # ``_destautoorphanrebase()`` | 123 # ``_destautoorphanrebase()`` |
112 | 124 |
113 # automatic rebase destination for a single orphan revision. | 125 # automatic rebase destination for a single orphan revision. |
120 if not src or src in obsoleted: | 132 if not src or src in obsoleted: |
121 return smartset.baseset() | 133 return smartset.baseset() |
122 dests = destutil.orphanpossibledestination(repo, src) | 134 dests = destutil.orphanpossibledestination(repo, src) |
123 if len(dests) > 1: | 135 if len(dests) > 1: |
124 raise error.Abort( | 136 raise error.Abort( |
125 _("ambiguous automatic rebase: %r could end up on any of %r") % ( | 137 _("ambiguous automatic rebase: %r could end up on any of %r") |
126 src, dests)) | 138 % (src, dests) |
139 ) | |
127 # We have zero or one destination, so we can just return here. | 140 # We have zero or one destination, so we can just return here. |
128 return smartset.baseset(dests) | 141 return smartset.baseset(dests) |
129 | 142 |
143 | |
130 def _ctxdesc(ctx): | 144 def _ctxdesc(ctx): |
131 """short description for a context""" | 145 """short description for a context""" |
132 desc = '%d:%s "%s"' % (ctx.rev(), ctx, | 146 desc = '%d:%s "%s"' % (ctx.rev(), ctx, ctx.description().split('\n', 1)[0]) |
133 ctx.description().split('\n', 1)[0]) | |
134 repo = ctx.repo() | 147 repo = ctx.repo() |
135 names = [] | 148 names = [] |
136 for nsname, ns in repo.names.iteritems(): | 149 for nsname, ns in repo.names.iteritems(): |
137 if nsname == 'branches': | 150 if nsname == 'branches': |
138 continue | 151 continue |
139 names.extend(ns.names(repo, ctx.node())) | 152 names.extend(ns.names(repo, ctx.node())) |
140 if names: | 153 if names: |
141 desc += ' (%s)' % ' '.join(names) | 154 desc += ' (%s)' % ' '.join(names) |
142 return desc | 155 return desc |
143 | 156 |
157 | |
144 class rebaseruntime(object): | 158 class rebaseruntime(object): |
145 """This class is a container for rebase runtime state""" | 159 """This class is a container for rebase runtime state""" |
160 | |
146 def __init__(self, repo, ui, inmemory=False, opts=None): | 161 def __init__(self, repo, ui, inmemory=False, opts=None): |
147 if opts is None: | 162 if opts is None: |
148 opts = {} | 163 opts = {} |
149 | 164 |
150 # prepared: whether we have rebasestate prepared or not. Currently it | 165 # prepared: whether we have rebasestate prepared or not. Currently it |
172 | 187 |
173 self.collapsef = opts.get('collapse', False) | 188 self.collapsef = opts.get('collapse', False) |
174 self.collapsemsg = cmdutil.logmessage(ui, opts) | 189 self.collapsemsg = cmdutil.logmessage(ui, opts) |
175 self.date = opts.get('date', None) | 190 self.date = opts.get('date', None) |
176 | 191 |
177 e = opts.get('extrafn') # internal, used by e.g. hgsubversion | 192 e = opts.get('extrafn') # internal, used by e.g. hgsubversion |
178 self.extrafns = [_savegraft] | 193 self.extrafns = [_savegraft] |
179 if e: | 194 if e: |
180 self.extrafns = [e] | 195 self.extrafns = [e] |
181 | 196 |
182 self.backupf = ui.configbool('rewrite', 'backup-bundle') | 197 self.backupf = ui.configbool('rewrite', 'backup-bundle') |
195 return self._repo | 210 return self._repo |
196 | 211 |
197 def storestatus(self, tr=None): | 212 def storestatus(self, tr=None): |
198 """Store the current status to allow recovery""" | 213 """Store the current status to allow recovery""" |
199 if tr: | 214 if tr: |
200 tr.addfilegenerator('rebasestate', ('rebasestate',), | 215 tr.addfilegenerator( |
201 self._writestatus, location='plain') | 216 'rebasestate', |
217 ('rebasestate',), | |
218 self._writestatus, | |
219 location='plain', | |
220 ) | |
202 else: | 221 else: |
203 with self.repo.vfs("rebasestate", "w") as f: | 222 with self.repo.vfs("rebasestate", "w") as f: |
204 self._writestatus(f) | 223 self._writestatus(f) |
205 | 224 |
206 def _writestatus(self, f): | 225 def _writestatus(self, f): |
245 | 264 |
246 def _read(self): | 265 def _read(self): |
247 self.prepared = True | 266 self.prepared = True |
248 repo = self.repo | 267 repo = self.repo |
249 assert repo.filtername is None | 268 assert repo.filtername is None |
250 data = {'keepbranches': None, 'collapse': None, 'activebookmark': None, | 269 data = { |
251 'external': nullrev, 'keep': None, 'originalwd': None} | 270 'keepbranches': None, |
271 'collapse': None, | |
272 'activebookmark': None, | |
273 'external': nullrev, | |
274 'keep': None, | |
275 'originalwd': None, | |
276 } | |
252 legacydest = None | 277 legacydest = None |
253 state = {} | 278 state = {} |
254 destmap = {} | 279 destmap = {} |
255 | 280 |
256 if True: | 281 if True: |
305 for old, new in sorted(state.items()): | 330 for old, new in sorted(state.items()): |
306 if new != revtodo and new in seen: | 331 if new != revtodo and new in seen: |
307 skipped.add(old) | 332 skipped.add(old) |
308 seen.add(new) | 333 seen.add(new) |
309 data['skipped'] = skipped | 334 data['skipped'] = skipped |
310 repo.ui.debug('computed skipped revs: %s\n' % | 335 repo.ui.debug( |
311 (' '.join('%d' % r for r in sorted(skipped)) or '')) | 336 'computed skipped revs: %s\n' |
337 % (' '.join('%d' % r for r in sorted(skipped)) or '') | |
338 ) | |
312 | 339 |
313 return data | 340 return data |
314 | 341 |
315 def _handleskippingobsolete(self, obsoleterevs, destmap): | 342 def _handleskippingobsolete(self, obsoleterevs, destmap): |
316 """Compute structures necessary for skipping obsolete revisions | 343 """Compute structures necessary for skipping obsolete revisions |
320 """ | 347 """ |
321 self.obsoletenotrebased = {} | 348 self.obsoletenotrebased = {} |
322 if not self.ui.configbool('experimental', 'rebaseskipobsolete'): | 349 if not self.ui.configbool('experimental', 'rebaseskipobsolete'): |
323 return | 350 return |
324 obsoleteset = set(obsoleterevs) | 351 obsoleteset = set(obsoleterevs) |
325 (self.obsoletenotrebased, | 352 ( |
326 self.obsoletewithoutsuccessorindestination, | 353 self.obsoletenotrebased, |
327 obsoleteextinctsuccessors) = _computeobsoletenotrebased( | 354 self.obsoletewithoutsuccessorindestination, |
328 self.repo, obsoleteset, destmap) | 355 obsoleteextinctsuccessors, |
356 ) = _computeobsoletenotrebased(self.repo, obsoleteset, destmap) | |
329 skippedset = set(self.obsoletenotrebased) | 357 skippedset = set(self.obsoletenotrebased) |
330 skippedset.update(self.obsoletewithoutsuccessorindestination) | 358 skippedset.update(self.obsoletewithoutsuccessorindestination) |
331 skippedset.update(obsoleteextinctsuccessors) | 359 skippedset.update(obsoleteextinctsuccessors) |
332 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset) | 360 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset) |
333 | 361 |
337 self.collapsemsg = restorecollapsemsg(self.repo, isabort) | 365 self.collapsemsg = restorecollapsemsg(self.repo, isabort) |
338 except error.RepoLookupError: | 366 except error.RepoLookupError: |
339 if isabort: | 367 if isabort: |
340 clearstatus(self.repo) | 368 clearstatus(self.repo) |
341 clearcollapsemsg(self.repo) | 369 clearcollapsemsg(self.repo) |
342 self.repo.ui.warn(_('rebase aborted (no revision is removed,' | 370 self.repo.ui.warn( |
343 ' only broken state is cleared)\n')) | 371 _( |
372 'rebase aborted (no revision is removed,' | |
373 ' only broken state is cleared)\n' | |
374 ) | |
375 ) | |
344 return 0 | 376 return 0 |
345 else: | 377 else: |
346 msg = _('cannot continue inconsistent rebase') | 378 msg = _('cannot continue inconsistent rebase') |
347 hint = _('use "hg rebase --abort" to clear broken state') | 379 hint = _('use "hg rebase --abort" to clear broken state') |
348 raise error.Abort(msg, hint=hint) | 380 raise error.Abort(msg, hint=hint) |
355 if not destmap: | 387 if not destmap: |
356 return _nothingtorebase() | 388 return _nothingtorebase() |
357 | 389 |
358 rebaseset = destmap.keys() | 390 rebaseset = destmap.keys() |
359 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt) | 391 allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt) |
360 if (not (self.keepf or allowunstable) | 392 if not (self.keepf or allowunstable) and self.repo.revs( |
361 and self.repo.revs('first(children(%ld) - %ld)', | 393 'first(children(%ld) - %ld)', rebaseset, rebaseset |
362 rebaseset, rebaseset)): | 394 ): |
363 raise error.Abort( | 395 raise error.Abort( |
364 _("can't remove original changesets with" | 396 _( |
365 " unrebased descendants"), | 397 "can't remove original changesets with" |
366 hint=_('use --keep to keep original changesets')) | 398 " unrebased descendants" |
399 ), | |
400 hint=_('use --keep to keep original changesets'), | |
401 ) | |
367 | 402 |
368 result = buildstate(self.repo, destmap, self.collapsef) | 403 result = buildstate(self.repo, destmap, self.collapsef) |
369 | 404 |
370 if not result: | 405 if not result: |
371 # Empty state built, nothing to rebase | 406 # Empty state built, nothing to rebase |
372 self.ui.status(_('nothing to rebase\n')) | 407 self.ui.status(_('nothing to rebase\n')) |
373 return _nothingtorebase() | 408 return _nothingtorebase() |
374 | 409 |
375 for root in self.repo.set('roots(%ld)', rebaseset): | 410 for root in self.repo.set('roots(%ld)', rebaseset): |
376 if not self.keepf and not root.mutable(): | 411 if not self.keepf and not root.mutable(): |
377 raise error.Abort(_("can't rebase public changeset %s") | 412 raise error.Abort( |
378 % root, | 413 _("can't rebase public changeset %s") % root, |
379 hint=_("see 'hg help phases' for details")) | 414 hint=_("see 'hg help phases' for details"), |
415 ) | |
380 | 416 |
381 (self.originalwd, self.destmap, self.state) = result | 417 (self.originalwd, self.destmap, self.state) = result |
382 if self.collapsef: | 418 if self.collapsef: |
383 dests = set(self.destmap.values()) | 419 dests = set(self.destmap.values()) |
384 if len(dests) != 1: | 420 if len(dests) != 1: |
385 raise error.Abort( | 421 raise error.Abort( |
386 _('--collapse does not work with multiple destinations')) | 422 _('--collapse does not work with multiple destinations') |
423 ) | |
387 destrev = next(iter(dests)) | 424 destrev = next(iter(dests)) |
388 destancestors = self.repo.changelog.ancestors([destrev], | 425 destancestors = self.repo.changelog.ancestors( |
389 inclusive=True) | 426 [destrev], inclusive=True |
427 ) | |
390 self.external = externalparent(self.repo, self.state, destancestors) | 428 self.external = externalparent(self.repo, self.state, destancestors) |
391 | 429 |
392 for destrev in sorted(set(destmap.values())): | 430 for destrev in sorted(set(destmap.values())): |
393 dest = self.repo[destrev] | 431 dest = self.repo[destrev] |
394 if dest.closesbranch() and not self.keepbranchesf: | 432 if dest.closesbranch() and not self.keepbranchesf: |
397 self.prepared = True | 435 self.prepared = True |
398 | 436 |
399 def _assignworkingcopy(self): | 437 def _assignworkingcopy(self): |
400 if self.inmemory: | 438 if self.inmemory: |
401 from mercurial.context import overlayworkingctx | 439 from mercurial.context import overlayworkingctx |
440 | |
402 self.wctx = overlayworkingctx(self.repo) | 441 self.wctx = overlayworkingctx(self.repo) |
403 self.repo.ui.debug("rebasing in-memory\n") | 442 self.repo.ui.debug("rebasing in-memory\n") |
404 else: | 443 else: |
405 self.wctx = self.repo[None] | 444 self.wctx = self.repo[None] |
406 self.repo.ui.debug("rebasing on disk\n") | 445 self.repo.ui.debug("rebasing on disk\n") |
407 self.repo.ui.log("rebase", | 446 self.repo.ui.log( |
408 "using in-memory rebase: %r\n", self.inmemory, | 447 "rebase", |
409 rebase_imm_used=self.inmemory) | 448 "using in-memory rebase: %r\n", |
449 self.inmemory, | |
450 rebase_imm_used=self.inmemory, | |
451 ) | |
410 | 452 |
411 def _performrebase(self, tr): | 453 def _performrebase(self, tr): |
412 self._assignworkingcopy() | 454 self._assignworkingcopy() |
413 repo, ui = self.repo, self.ui | 455 repo, ui = self.repo, self.ui |
414 if self.keepbranchesf: | 456 if self.keepbranchesf: |
419 if self.collapsef: | 461 if self.collapsef: |
420 branches = set() | 462 branches = set() |
421 for rev in self.state: | 463 for rev in self.state: |
422 branches.add(repo[rev].branch()) | 464 branches.add(repo[rev].branch()) |
423 if len(branches) > 1: | 465 if len(branches) > 1: |
424 raise error.Abort(_('cannot collapse multiple named ' | 466 raise error.Abort( |
425 'branches')) | 467 _('cannot collapse multiple named ' 'branches') |
468 ) | |
426 | 469 |
427 # Calculate self.obsoletenotrebased | 470 # Calculate self.obsoletenotrebased |
428 obsrevs = _filterobsoleterevs(self.repo, self.state) | 471 obsrevs = _filterobsoleterevs(self.repo, self.state) |
429 self._handleskippingobsolete(obsrevs, self.destmap) | 472 self._handleskippingobsolete(obsrevs, self.destmap) |
430 | 473 |
440 # When using single transaction, store state when transaction | 483 # When using single transaction, store state when transaction |
441 # commits. | 484 # commits. |
442 self.storestatus(tr) | 485 self.storestatus(tr) |
443 | 486 |
444 cands = [k for k, v in self.state.iteritems() if v == revtodo] | 487 cands = [k for k, v in self.state.iteritems() if v == revtodo] |
445 p = repo.ui.makeprogress(_("rebasing"), unit=_('changesets'), | 488 p = repo.ui.makeprogress( |
446 total=len(cands)) | 489 _("rebasing"), unit=_('changesets'), total=len(cands) |
490 ) | |
491 | |
447 def progress(ctx): | 492 def progress(ctx): |
448 p.increment(item=("%d:%s" % (ctx.rev(), ctx))) | 493 p.increment(item=("%d:%s" % (ctx.rev(), ctx))) |
494 | |
449 allowdivergence = self.ui.configbool( | 495 allowdivergence = self.ui.configbool( |
450 'experimental', 'evolution.allowdivergence') | 496 'experimental', 'evolution.allowdivergence' |
497 ) | |
451 for subset in sortsource(self.destmap): | 498 for subset in sortsource(self.destmap): |
452 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset) | 499 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset) |
453 if not allowdivergence: | 500 if not allowdivergence: |
454 sortedrevs -= self.repo.revs( | 501 sortedrevs -= self.repo.revs( |
455 'descendants(%ld) and not %ld', | 502 'descendants(%ld) and not %ld', |
481 overrides = {('phases', 'new-commit'): destphase} | 528 overrides = {('phases', 'new-commit'): destphase} |
482 if keepbranch: | 529 if keepbranch: |
483 overrides[('ui', 'allowemptycommit')] = True | 530 overrides[('ui', 'allowemptycommit')] = True |
484 with repo.ui.configoverride(overrides, 'rebase'): | 531 with repo.ui.configoverride(overrides, 'rebase'): |
485 if self.inmemory: | 532 if self.inmemory: |
486 newnode = commitmemorynode(repo, p1, p2, | 533 newnode = commitmemorynode( |
534 repo, | |
535 p1, | |
536 p2, | |
487 wctx=self.wctx, | 537 wctx=self.wctx, |
488 extra=extra, | 538 extra=extra, |
489 commitmsg=commitmsg, | 539 commitmsg=commitmsg, |
490 editor=editor, | 540 editor=editor, |
491 user=ctx.user(), | 541 user=ctx.user(), |
492 date=date) | 542 date=date, |
543 ) | |
493 mergemod.mergestate.clean(repo) | 544 mergemod.mergestate.clean(repo) |
494 else: | 545 else: |
495 newnode = commitnode(repo, p1, p2, | 546 newnode = commitnode( |
547 repo, | |
548 p1, | |
549 p2, | |
496 extra=extra, | 550 extra=extra, |
497 commitmsg=commitmsg, | 551 commitmsg=commitmsg, |
498 editor=editor, | 552 editor=editor, |
499 user=ctx.user(), | 553 user=ctx.user(), |
500 date=date) | 554 date=date, |
555 ) | |
501 | 556 |
502 if newnode is None: | 557 if newnode is None: |
503 # If it ended up being a no-op commit, then the normal | 558 # If it ended up being a no-op commit, then the normal |
504 # merge state clean-up path doesn't happen, so do it | 559 # merge state clean-up path doesn't happen, so do it |
505 # here. Fix issue5494 | 560 # here. Fix issue5494 |
511 dest = self.destmap[rev] | 566 dest = self.destmap[rev] |
512 ctx = repo[rev] | 567 ctx = repo[rev] |
513 desc = _ctxdesc(ctx) | 568 desc = _ctxdesc(ctx) |
514 if self.state[rev] == rev: | 569 if self.state[rev] == rev: |
515 ui.status(_('already rebased %s\n') % desc) | 570 ui.status(_('already rebased %s\n') % desc) |
516 elif (not allowdivergence | 571 elif ( |
517 and rev in self.obsoletewithoutsuccessorindestination): | 572 not allowdivergence |
518 msg = _('note: not rebasing %s and its descendants as ' | 573 and rev in self.obsoletewithoutsuccessorindestination |
519 'this would cause divergence\n') % desc | 574 ): |
575 msg = ( | |
576 _( | |
577 'note: not rebasing %s and its descendants as ' | |
578 'this would cause divergence\n' | |
579 ) | |
580 % desc | |
581 ) | |
520 repo.ui.status(msg) | 582 repo.ui.status(msg) |
521 self.skipped.add(rev) | 583 self.skipped.add(rev) |
522 elif rev in self.obsoletenotrebased: | 584 elif rev in self.obsoletenotrebased: |
523 succ = self.obsoletenotrebased[rev] | 585 succ = self.obsoletenotrebased[rev] |
524 if succ is None: | 586 if succ is None: |
525 msg = _('note: not rebasing %s, it has no ' | 587 msg = ( |
526 'successor\n') % desc | 588 _('note: not rebasing %s, it has no ' 'successor\n') % desc |
589 ) | |
527 else: | 590 else: |
528 succdesc = _ctxdesc(repo[succ]) | 591 succdesc = _ctxdesc(repo[succ]) |
529 msg = (_('note: not rebasing %s, already in ' | 592 msg = _( |
530 'destination as %s\n') % (desc, succdesc)) | 593 'note: not rebasing %s, already in ' 'destination as %s\n' |
594 ) % (desc, succdesc) | |
531 repo.ui.status(msg) | 595 repo.ui.status(msg) |
532 # Make clearrebased aware state[rev] is not a true successor | 596 # Make clearrebased aware state[rev] is not a true successor |
533 self.skipped.add(rev) | 597 self.skipped.add(rev) |
534 # Record rev as moved to its desired destination in self.state. | 598 # Record rev as moved to its desired destination in self.state. |
535 # This helps bookmark and working parent movement. | 599 # This helps bookmark and working parent movement. |
536 dest = max(adjustdest(repo, rev, self.destmap, self.state, | 600 dest = max( |
537 self.skipped)) | 601 adjustdest(repo, rev, self.destmap, self.state, self.skipped) |
602 ) | |
538 self.state[rev] = dest | 603 self.state[rev] = dest |
539 elif self.state[rev] == revtodo: | 604 elif self.state[rev] == revtodo: |
540 ui.status(_('rebasing %s\n') % desc) | 605 ui.status(_('rebasing %s\n') % desc) |
541 progressfn(ctx) | 606 progressfn(ctx) |
542 p1, p2, base = defineparents(repo, rev, self.destmap, | 607 p1, p2, base = defineparents( |
543 self.state, self.skipped, | 608 repo, |
544 self.obsoletenotrebased) | 609 rev, |
610 self.destmap, | |
611 self.state, | |
612 self.skipped, | |
613 self.obsoletenotrebased, | |
614 ) | |
545 if not self.inmemory and len(repo[None].parents()) == 2: | 615 if not self.inmemory and len(repo[None].parents()) == 2: |
546 repo.ui.debug('resuming interrupted rebase\n') | 616 repo.ui.debug('resuming interrupted rebase\n') |
547 else: | 617 else: |
548 overrides = {('ui', 'forcemerge'): opts.get('tool', '')} | 618 overrides = {('ui', 'forcemerge'): opts.get('tool', '')} |
549 with ui.configoverride(overrides, 'rebase'): | 619 with ui.configoverride(overrides, 'rebase'): |
550 stats = rebasenode(repo, rev, p1, base, self.collapsef, | 620 stats = rebasenode( |
551 dest, wctx=self.wctx) | 621 repo, |
622 rev, | |
623 p1, | |
624 base, | |
625 self.collapsef, | |
626 dest, | |
627 wctx=self.wctx, | |
628 ) | |
552 if stats.unresolvedcount > 0: | 629 if stats.unresolvedcount > 0: |
553 if self.inmemory: | 630 if self.inmemory: |
554 raise error.InMemoryMergeConflictsError() | 631 raise error.InMemoryMergeConflictsError() |
555 else: | 632 else: |
556 raise error.InterventionRequired( | 633 raise error.InterventionRequired( |
557 _('unresolved conflicts (see hg ' | 634 _( |
558 'resolve, then hg rebase --continue)')) | 635 'unresolved conflicts (see hg ' |
636 'resolve, then hg rebase --continue)' | |
637 ) | |
638 ) | |
559 if not self.collapsef: | 639 if not self.collapsef: |
560 merging = p2 != nullrev | 640 merging = p2 != nullrev |
561 editform = cmdutil.mergeeditform(merging, 'rebase') | 641 editform = cmdutil.mergeeditform(merging, 'rebase') |
562 editor = cmdutil.getcommiteditor(editform=editform, | 642 editor = cmdutil.getcommiteditor( |
563 **pycompat.strkwargs(opts)) | 643 editform=editform, **pycompat.strkwargs(opts) |
644 ) | |
564 newnode = self._concludenode(rev, p1, p2, editor) | 645 newnode = self._concludenode(rev, p1, p2, editor) |
565 else: | 646 else: |
566 # Skip commit if we are collapsing | 647 # Skip commit if we are collapsing |
567 if self.inmemory: | 648 if self.inmemory: |
568 self.wctx.setbase(repo[p1]) | 649 self.wctx.setbase(repo[p1]) |
573 if newnode is not None: | 654 if newnode is not None: |
574 self.state[rev] = repo[newnode].rev() | 655 self.state[rev] = repo[newnode].rev() |
575 ui.debug('rebased as %s\n' % short(newnode)) | 656 ui.debug('rebased as %s\n' % short(newnode)) |
576 else: | 657 else: |
577 if not self.collapsef: | 658 if not self.collapsef: |
578 ui.warn(_('note: not rebasing %s, its destination already ' | 659 ui.warn( |
579 'has all its changes\n') % desc) | 660 _( |
661 'note: not rebasing %s, its destination already ' | |
662 'has all its changes\n' | |
663 ) | |
664 % desc | |
665 ) | |
580 self.skipped.add(rev) | 666 self.skipped.add(rev) |
581 self.state[rev] = p1 | 667 self.state[rev] = p1 |
582 ui.debug('next revision set to %d\n' % p1) | 668 ui.debug('next revision set to %d\n' % p1) |
583 else: | 669 else: |
584 ui.status(_('already rebased %s as %s\n') % | 670 ui.status( |
585 (desc, repo[self.state[rev]])) | 671 _('already rebased %s as %s\n') % (desc, repo[self.state[rev]]) |
672 ) | |
586 if not tr: | 673 if not tr: |
587 # When not using single transaction, store state after each | 674 # When not using single transaction, store state after each |
588 # commit is completely done. On InterventionRequired, we thus | 675 # commit is completely done. On InterventionRequired, we thus |
589 # won't store the status. Instead, we'll hit the "len(parents) == 2" | 676 # won't store the status. Instead, we'll hit the "len(parents) == 2" |
590 # case and realize that the commit was in progress. | 677 # case and realize that the commit was in progress. |
593 def _finishrebase(self): | 680 def _finishrebase(self): |
594 repo, ui, opts = self.repo, self.ui, self.opts | 681 repo, ui, opts = self.repo, self.ui, self.opts |
595 fm = ui.formatter('rebase', opts) | 682 fm = ui.formatter('rebase', opts) |
596 fm.startitem() | 683 fm.startitem() |
597 if self.collapsef: | 684 if self.collapsef: |
598 p1, p2, _base = defineparents(repo, min(self.state), self.destmap, | 685 p1, p2, _base = defineparents( |
599 self.state, self.skipped, | 686 repo, |
600 self.obsoletenotrebased) | 687 min(self.state), |
688 self.destmap, | |
689 self.state, | |
690 self.skipped, | |
691 self.obsoletenotrebased, | |
692 ) | |
601 editopt = opts.get('edit') | 693 editopt = opts.get('edit') |
602 editform = 'rebase.collapse' | 694 editform = 'rebase.collapse' |
603 if self.collapsemsg: | 695 if self.collapsemsg: |
604 commitmsg = self.collapsemsg | 696 commitmsg = self.collapsemsg |
605 else: | 697 else: |
609 commitmsg += '\n* %s' % repo[rebased].description() | 701 commitmsg += '\n* %s' % repo[rebased].description() |
610 editopt = True | 702 editopt = True |
611 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform) | 703 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform) |
612 revtoreuse = max(self.state) | 704 revtoreuse = max(self.state) |
613 | 705 |
614 newnode = self._concludenode(revtoreuse, p1, self.external, | 706 newnode = self._concludenode( |
615 editor, commitmsg=commitmsg) | 707 revtoreuse, p1, self.external, editor, commitmsg=commitmsg |
708 ) | |
616 | 709 |
617 if newnode is not None: | 710 if newnode is not None: |
618 newrev = repo[newnode].rev() | 711 newrev = repo[newnode].rev() |
619 for oldrev in self.state: | 712 for oldrev in self.state: |
620 self.state[oldrev] = newrev | 713 self.state[oldrev] = newrev |
621 | 714 |
622 if 'qtip' in repo.tags(): | 715 if 'qtip' in repo.tags(): |
623 updatemq(repo, self.state, self.skipped, | 716 updatemq(repo, self.state, self.skipped, **pycompat.strkwargs(opts)) |
624 **pycompat.strkwargs(opts)) | |
625 | 717 |
626 # restore original working directory | 718 # restore original working directory |
627 # (we do this before stripping) | 719 # (we do this before stripping) |
628 newwd = self.state.get(self.originalwd, self.originalwd) | 720 newwd = self.state.get(self.originalwd, self.originalwd) |
629 if newwd < 0: | 721 if newwd < 0: |
634 hg.updaterepo(repo, newwd, overwrite=False) | 726 hg.updaterepo(repo, newwd, overwrite=False) |
635 | 727 |
636 collapsedas = None | 728 collapsedas = None |
637 if self.collapsef and not self.keepf: | 729 if self.collapsef and not self.keepf: |
638 collapsedas = newnode | 730 collapsedas = newnode |
639 clearrebased(ui, repo, self.destmap, self.state, self.skipped, | 731 clearrebased( |
640 collapsedas, self.keepf, fm=fm, backup=self.backupf) | 732 ui, |
733 repo, | |
734 self.destmap, | |
735 self.state, | |
736 self.skipped, | |
737 collapsedas, | |
738 self.keepf, | |
739 fm=fm, | |
740 backup=self.backupf, | |
741 ) | |
641 | 742 |
642 clearstatus(repo) | 743 clearstatus(repo) |
643 clearcollapsemsg(repo) | 744 clearcollapsemsg(repo) |
644 | 745 |
645 ui.note(_("rebase completed\n")) | 746 ui.note(_("rebase completed\n")) |
647 if self.skipped: | 748 if self.skipped: |
648 skippedlen = len(self.skipped) | 749 skippedlen = len(self.skipped) |
649 ui.note(_("%d revisions have been skipped\n") % skippedlen) | 750 ui.note(_("%d revisions have been skipped\n") % skippedlen) |
650 fm.end() | 751 fm.end() |
651 | 752 |
652 if (self.activebookmark and self.activebookmark in repo._bookmarks and | 753 if ( |
653 repo['.'].node() == repo._bookmarks[self.activebookmark]): | 754 self.activebookmark |
654 bookmarks.activate(repo, self.activebookmark) | 755 and self.activebookmark in repo._bookmarks |
756 and repo['.'].node() == repo._bookmarks[self.activebookmark] | |
757 ): | |
758 bookmarks.activate(repo, self.activebookmark) | |
655 | 759 |
656 def _abort(self, backup=True, suppwarns=False): | 760 def _abort(self, backup=True, suppwarns=False): |
657 '''Restore the repository to its original state.''' | 761 '''Restore the repository to its original state.''' |
658 | 762 |
659 repo = self.repo | 763 repo = self.repo |
660 try: | 764 try: |
661 # If the first commits in the rebased set get skipped during the | 765 # If the first commits in the rebased set get skipped during the |
662 # rebase, their values within the state mapping will be the dest | 766 # rebase, their values within the state mapping will be the dest |
663 # rev id. The rebased list must must not contain the dest rev | 767 # rev id. The rebased list must must not contain the dest rev |
664 # (issue4896) | 768 # (issue4896) |
665 rebased = [s for r, s in self.state.items() | 769 rebased = [ |
666 if s >= 0 and s != r and s != self.destmap[r]] | 770 s |
771 for r, s in self.state.items() | |
772 if s >= 0 and s != r and s != self.destmap[r] | |
773 ] | |
667 immutable = [d for d in rebased if not repo[d].mutable()] | 774 immutable = [d for d in rebased if not repo[d].mutable()] |
668 cleanup = True | 775 cleanup = True |
669 if immutable: | 776 if immutable: |
670 repo.ui.warn(_("warning: can't clean up public changesets %s\n") | 777 repo.ui.warn( |
671 % ', '.join(bytes(repo[r]) for r in immutable), | 778 _("warning: can't clean up public changesets %s\n") |
672 hint=_("see 'hg help phases' for details")) | 779 % ', '.join(bytes(repo[r]) for r in immutable), |
780 hint=_("see 'hg help phases' for details"), | |
781 ) | |
673 cleanup = False | 782 cleanup = False |
674 | 783 |
675 descendants = set() | 784 descendants = set() |
676 if rebased: | 785 if rebased: |
677 descendants = set(repo.changelog.descendants(rebased)) | 786 descendants = set(repo.changelog.descendants(rebased)) |
678 if descendants - set(rebased): | 787 if descendants - set(rebased): |
679 repo.ui.warn(_("warning: new changesets detected on " | 788 repo.ui.warn( |
680 "destination branch, can't strip\n")) | 789 _( |
790 "warning: new changesets detected on " | |
791 "destination branch, can't strip\n" | |
792 ) | |
793 ) | |
681 cleanup = False | 794 cleanup = False |
682 | 795 |
683 if cleanup: | 796 if cleanup: |
684 shouldupdate = False | 797 shouldupdate = False |
685 if rebased: | 798 if rebased: |
686 strippoints = [ | 799 strippoints = [ |
687 c.node() for c in repo.set('roots(%ld)', rebased)] | 800 c.node() for c in repo.set('roots(%ld)', rebased) |
801 ] | |
688 | 802 |
689 updateifonnodes = set(rebased) | 803 updateifonnodes = set(rebased) |
690 updateifonnodes.update(self.destmap.values()) | 804 updateifonnodes.update(self.destmap.values()) |
691 updateifonnodes.add(self.originalwd) | 805 updateifonnodes.add(self.originalwd) |
692 shouldupdate = repo['.'].rev() in updateifonnodes | 806 shouldupdate = repo['.'].rev() in updateifonnodes |
693 | 807 |
694 # Update away from the rebase if necessary | 808 # Update away from the rebase if necessary |
695 if shouldupdate or needupdate(repo, self.state): | 809 if shouldupdate or needupdate(repo, self.state): |
696 mergemod.update(repo, self.originalwd, branchmerge=False, | 810 mergemod.update( |
697 force=True) | 811 repo, self.originalwd, branchmerge=False, force=True |
812 ) | |
698 | 813 |
699 # Strip from the first rebased revision | 814 # Strip from the first rebased revision |
700 if rebased: | 815 if rebased: |
701 repair.strip(repo.ui, repo, strippoints, backup=backup) | 816 repair.strip(repo.ui, repo, strippoints, backup=backup) |
702 | 817 |
708 clearcollapsemsg(repo) | 823 clearcollapsemsg(repo) |
709 if not suppwarns: | 824 if not suppwarns: |
710 repo.ui.warn(_('rebase aborted\n')) | 825 repo.ui.warn(_('rebase aborted\n')) |
711 return 0 | 826 return 0 |
712 | 827 |
713 @command('rebase', | 828 |
714 [('s', 'source', '', | 829 @command( |
715 _('rebase the specified changeset and descendants'), _('REV')), | 830 'rebase', |
716 ('b', 'base', '', | 831 [ |
717 _('rebase everything from branching point of specified changeset'), | 832 ( |
718 _('REV')), | 833 's', |
719 ('r', 'rev', [], | 834 'source', |
720 _('rebase these revisions'), | 835 '', |
721 _('REV')), | 836 _('rebase the specified changeset and descendants'), |
722 ('d', 'dest', '', | 837 _('REV'), |
723 _('rebase onto the specified changeset'), _('REV')), | 838 ), |
724 ('', 'collapse', False, _('collapse the rebased changesets')), | 839 ( |
725 ('m', 'message', '', | 840 'b', |
726 _('use text as collapse commit message'), _('TEXT')), | 841 'base', |
727 ('e', 'edit', False, _('invoke editor on commit messages')), | 842 '', |
728 ('l', 'logfile', '', | 843 _('rebase everything from branching point of specified changeset'), |
729 _('read collapse commit message from file'), _('FILE')), | 844 _('REV'), |
730 ('k', 'keep', False, _('keep original changesets')), | 845 ), |
731 ('', 'keepbranches', False, _('keep original branch names')), | 846 ('r', 'rev', [], _('rebase these revisions'), _('REV')), |
732 ('D', 'detach', False, _('(DEPRECATED)')), | 847 ('d', 'dest', '', _('rebase onto the specified changeset'), _('REV')), |
733 ('i', 'interactive', False, _('(DEPRECATED)')), | 848 ('', 'collapse', False, _('collapse the rebased changesets')), |
734 ('t', 'tool', '', _('specify merge tool')), | 849 ( |
735 ('', 'stop', False, _('stop interrupted rebase')), | 850 'm', |
736 ('c', 'continue', False, _('continue an interrupted rebase')), | 851 'message', |
737 ('a', 'abort', False, _('abort an interrupted rebase')), | 852 '', |
738 ('', 'auto-orphans', '', _('automatically rebase orphan revisions ' | 853 _('use text as collapse commit message'), |
739 'in the specified revset (EXPERIMENTAL)')), | 854 _('TEXT'), |
740 ] + cmdutil.dryrunopts + cmdutil.formatteropts + cmdutil.confirmopts, | 855 ), |
856 ('e', 'edit', False, _('invoke editor on commit messages')), | |
857 ( | |
858 'l', | |
859 'logfile', | |
860 '', | |
861 _('read collapse commit message from file'), | |
862 _('FILE'), | |
863 ), | |
864 ('k', 'keep', False, _('keep original changesets')), | |
865 ('', 'keepbranches', False, _('keep original branch names')), | |
866 ('D', 'detach', False, _('(DEPRECATED)')), | |
867 ('i', 'interactive', False, _('(DEPRECATED)')), | |
868 ('t', 'tool', '', _('specify merge tool')), | |
869 ('', 'stop', False, _('stop interrupted rebase')), | |
870 ('c', 'continue', False, _('continue an interrupted rebase')), | |
871 ('a', 'abort', False, _('abort an interrupted rebase')), | |
872 ( | |
873 '', | |
874 'auto-orphans', | |
875 '', | |
876 _( | |
877 'automatically rebase orphan revisions ' | |
878 'in the specified revset (EXPERIMENTAL)' | |
879 ), | |
880 ), | |
881 ] | |
882 + cmdutil.dryrunopts | |
883 + cmdutil.formatteropts | |
884 + cmdutil.confirmopts, | |
741 _('[-s REV | -b REV] [-d REV] [OPTION]'), | 885 _('[-s REV | -b REV] [-d REV] [OPTION]'), |
742 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT) | 886 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, |
887 ) | |
743 def rebase(ui, repo, **opts): | 888 def rebase(ui, repo, **opts): |
744 """move changeset (and descendants) to a different branch | 889 """move changeset (and descendants) to a different branch |
745 | 890 |
746 Rebase uses repeated merging to graft changesets from one part of | 891 Rebase uses repeated merging to graft changesets from one part of |
747 history (the source) onto another (the destination). This can be | 892 history (the source) onto another (the destination). This can be |
867 inmemory = ui.configbool('rebase', 'experimental.inmemory') | 1012 inmemory = ui.configbool('rebase', 'experimental.inmemory') |
868 dryrun = opts.get('dry_run') | 1013 dryrun = opts.get('dry_run') |
869 confirm = opts.get('confirm') | 1014 confirm = opts.get('confirm') |
870 selactions = [k for k in ['abort', 'stop', 'continue'] if opts.get(k)] | 1015 selactions = [k for k in ['abort', 'stop', 'continue'] if opts.get(k)] |
871 if len(selactions) > 1: | 1016 if len(selactions) > 1: |
872 raise error.Abort(_('cannot use --%s with --%s') | 1017 raise error.Abort( |
873 % tuple(selactions[:2])) | 1018 _('cannot use --%s with --%s') % tuple(selactions[:2]) |
1019 ) | |
874 action = selactions[0] if selactions else None | 1020 action = selactions[0] if selactions else None |
875 if dryrun and action: | 1021 if dryrun and action: |
876 raise error.Abort(_('cannot specify both --dry-run and --%s') % action) | 1022 raise error.Abort(_('cannot specify both --dry-run and --%s') % action) |
877 if confirm and action: | 1023 if confirm and action: |
878 raise error.Abort(_('cannot specify both --confirm and --%s') % action) | 1024 raise error.Abort(_('cannot specify both --confirm and --%s') % action) |
886 inmemory = False | 1032 inmemory = False |
887 | 1033 |
888 if opts.get('auto_orphans'): | 1034 if opts.get('auto_orphans'): |
889 for key in opts: | 1035 for key in opts: |
890 if key != 'auto_orphans' and opts.get(key): | 1036 if key != 'auto_orphans' and opts.get(key): |
891 raise error.Abort(_('--auto-orphans is incompatible with %s') % | 1037 raise error.Abort( |
892 ('--' + key)) | 1038 _('--auto-orphans is incompatible with %s') % ('--' + key) |
1039 ) | |
893 userrevs = list(repo.revs(opts.get('auto_orphans'))) | 1040 userrevs = list(repo.revs(opts.get('auto_orphans'))) |
894 opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)] | 1041 opts['rev'] = [revsetlang.formatspec('%ld and orphan()', userrevs)] |
895 opts['dest'] = '_destautoorphanrebase(SRC)' | 1042 opts['dest'] = '_destautoorphanrebase(SRC)' |
896 | 1043 |
897 if dryrun or confirm: | 1044 if dryrun or confirm: |
902 rbsrt.restorestatus() | 1049 rbsrt.restorestatus() |
903 if rbsrt.collapsef: | 1050 if rbsrt.collapsef: |
904 raise error.Abort(_("cannot stop in --collapse session")) | 1051 raise error.Abort(_("cannot stop in --collapse session")) |
905 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt) | 1052 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt) |
906 if not (rbsrt.keepf or allowunstable): | 1053 if not (rbsrt.keepf or allowunstable): |
907 raise error.Abort(_("cannot remove original changesets with" | 1054 raise error.Abort( |
908 " unrebased descendants"), | 1055 _( |
909 hint=_('either enable obsmarkers to allow unstable ' | 1056 "cannot remove original changesets with" |
910 'revisions or use --keep to keep original ' | 1057 " unrebased descendants" |
911 'changesets')) | 1058 ), |
1059 hint=_( | |
1060 'either enable obsmarkers to allow unstable ' | |
1061 'revisions or use --keep to keep original ' | |
1062 'changesets' | |
1063 ), | |
1064 ) | |
912 if needupdate(repo, rbsrt.state): | 1065 if needupdate(repo, rbsrt.state): |
913 # update to the current working revision | 1066 # update to the current working revision |
914 # to clear interrupted merge | 1067 # to clear interrupted merge |
915 hg.updaterepo(repo, rbsrt.originalwd, overwrite=True) | 1068 hg.updaterepo(repo, rbsrt.originalwd, overwrite=True) |
916 rbsrt._finishrebase() | 1069 rbsrt._finishrebase() |
921 # and re-run as an on-disk merge. | 1074 # and re-run as an on-disk merge. |
922 overrides = {('rebase', 'singletransaction'): True} | 1075 overrides = {('rebase', 'singletransaction'): True} |
923 with ui.configoverride(overrides, 'rebase'): | 1076 with ui.configoverride(overrides, 'rebase'): |
924 return _dorebase(ui, repo, action, opts, inmemory=inmemory) | 1077 return _dorebase(ui, repo, action, opts, inmemory=inmemory) |
925 except error.InMemoryMergeConflictsError: | 1078 except error.InMemoryMergeConflictsError: |
926 ui.warn(_('hit merge conflicts; re-running rebase without in-memory' | 1079 ui.warn( |
927 ' merge\n')) | 1080 _( |
1081 'hit merge conflicts; re-running rebase without in-memory' | |
1082 ' merge\n' | |
1083 ) | |
1084 ) | |
928 # TODO: Make in-memory merge not use the on-disk merge state, so | 1085 # TODO: Make in-memory merge not use the on-disk merge state, so |
929 # we don't have to clean it here | 1086 # we don't have to clean it here |
930 mergemod.mergestate.clean(repo) | 1087 mergemod.mergestate.clean(repo) |
931 clearstatus(repo) | 1088 clearstatus(repo) |
932 clearcollapsemsg(repo) | 1089 clearcollapsemsg(repo) |
933 return _dorebase(ui, repo, action, opts, inmemory=False) | 1090 return _dorebase(ui, repo, action, opts, inmemory=False) |
934 else: | 1091 else: |
935 return _dorebase(ui, repo, action, opts) | 1092 return _dorebase(ui, repo, action, opts) |
936 | 1093 |
1094 | |
937 def _dryrunrebase(ui, repo, action, opts): | 1095 def _dryrunrebase(ui, repo, action, opts): |
938 rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts) | 1096 rbsrt = rebaseruntime(repo, ui, inmemory=True, opts=opts) |
939 confirm = opts.get('confirm') | 1097 confirm = opts.get('confirm') |
940 if confirm: | 1098 if confirm: |
941 ui.status(_('starting in-memory rebase\n')) | 1099 ui.status(_('starting in-memory rebase\n')) |
942 else: | 1100 else: |
943 ui.status(_('starting dry-run rebase; repository will not be ' | 1101 ui.status( |
944 'changed\n')) | 1102 _('starting dry-run rebase; repository will not be ' 'changed\n') |
1103 ) | |
945 with repo.wlock(), repo.lock(): | 1104 with repo.wlock(), repo.lock(): |
946 needsabort = True | 1105 needsabort = True |
947 try: | 1106 try: |
948 overrides = {('rebase', 'singletransaction'): True} | 1107 overrides = {('rebase', 'singletransaction'): True} |
949 with ui.configoverride(overrides, 'rebase'): | 1108 with ui.configoverride(overrides, 'rebase'): |
950 _origrebase(ui, repo, action, opts, rbsrt, inmemory=True, | 1109 _origrebase( |
951 leaveunfinished=True) | 1110 ui, |
1111 repo, | |
1112 action, | |
1113 opts, | |
1114 rbsrt, | |
1115 inmemory=True, | |
1116 leaveunfinished=True, | |
1117 ) | |
952 except error.InMemoryMergeConflictsError: | 1118 except error.InMemoryMergeConflictsError: |
953 ui.status(_('hit a merge conflict\n')) | 1119 ui.status(_('hit a merge conflict\n')) |
954 return 1 | 1120 return 1 |
955 except error.Abort: | 1121 except error.Abort: |
956 needsabort = False | 1122 needsabort = False |
957 raise | 1123 raise |
958 else: | 1124 else: |
959 if confirm: | 1125 if confirm: |
960 ui.status(_('rebase completed successfully\n')) | 1126 ui.status(_('rebase completed successfully\n')) |
961 if not ui.promptchoice(_(b'apply changes (yn)?' | 1127 if not ui.promptchoice( |
962 b'$$ &Yes $$ &No')): | 1128 _(b'apply changes (yn)?' b'$$ &Yes $$ &No') |
1129 ): | |
963 # finish unfinished rebase | 1130 # finish unfinished rebase |
964 rbsrt._finishrebase() | 1131 rbsrt._finishrebase() |
965 else: | 1132 else: |
966 rbsrt._prepareabortorcontinue(isabort=True, backup=False, | 1133 rbsrt._prepareabortorcontinue( |
967 suppwarns=True) | 1134 isabort=True, backup=False, suppwarns=True |
1135 ) | |
968 needsabort = False | 1136 needsabort = False |
969 else: | 1137 else: |
970 ui.status(_('dry-run rebase completed successfully; run without' | 1138 ui.status( |
971 ' -n/--dry-run to perform this rebase\n')) | 1139 _( |
1140 'dry-run rebase completed successfully; run without' | |
1141 ' -n/--dry-run to perform this rebase\n' | |
1142 ) | |
1143 ) | |
972 return 0 | 1144 return 0 |
973 finally: | 1145 finally: |
974 if needsabort: | 1146 if needsabort: |
975 # no need to store backup in case of dryrun | 1147 # no need to store backup in case of dryrun |
976 rbsrt._prepareabortorcontinue(isabort=True, backup=False, | 1148 rbsrt._prepareabortorcontinue( |
977 suppwarns=True) | 1149 isabort=True, backup=False, suppwarns=True |
1150 ) | |
1151 | |
978 | 1152 |
979 def _dorebase(ui, repo, action, opts, inmemory=False): | 1153 def _dorebase(ui, repo, action, opts, inmemory=False): |
980 rbsrt = rebaseruntime(repo, ui, inmemory, opts) | 1154 rbsrt = rebaseruntime(repo, ui, inmemory, opts) |
981 return _origrebase(ui, repo, action, opts, rbsrt, inmemory=inmemory) | 1155 return _origrebase(ui, repo, action, opts, rbsrt, inmemory=inmemory) |
982 | 1156 |
983 def _origrebase(ui, repo, action, opts, rbsrt, inmemory=False, | 1157 |
984 leaveunfinished=False): | 1158 def _origrebase( |
1159 ui, repo, action, opts, rbsrt, inmemory=False, leaveunfinished=False | |
1160 ): | |
985 assert action != 'stop' | 1161 assert action != 'stop' |
986 with repo.wlock(), repo.lock(): | 1162 with repo.wlock(), repo.lock(): |
987 # Validate input and define rebasing points | 1163 # Validate input and define rebasing points |
988 destf = opts.get('dest', None) | 1164 destf = opts.get('dest', None) |
989 srcf = opts.get('source', None) | 1165 srcf = opts.get('source', None) |
997 if extensions.find('histedit'): | 1173 if extensions.find('histedit'): |
998 enablehistedit = '' | 1174 enablehistedit = '' |
999 except KeyError: | 1175 except KeyError: |
1000 enablehistedit = " --config extensions.histedit=" | 1176 enablehistedit = " --config extensions.histedit=" |
1001 help = "hg%s help -e histedit" % enablehistedit | 1177 help = "hg%s help -e histedit" % enablehistedit |
1002 msg = _("interactive history editing is supported by the " | 1178 msg = ( |
1003 "'histedit' extension (see \"%s\")") % help | 1179 _( |
1180 "interactive history editing is supported by the " | |
1181 "'histedit' extension (see \"%s\")" | |
1182 ) | |
1183 % help | |
1184 ) | |
1004 raise error.Abort(msg) | 1185 raise error.Abort(msg) |
1005 | 1186 |
1006 if rbsrt.collapsemsg and not rbsrt.collapsef: | 1187 if rbsrt.collapsemsg and not rbsrt.collapsef: |
1007 raise error.Abort( | 1188 raise error.Abort(_('message can only be specified with collapse')) |
1008 _('message can only be specified with collapse')) | |
1009 | 1189 |
1010 if action: | 1190 if action: |
1011 if rbsrt.collapsef: | 1191 if rbsrt.collapsef: |
1012 raise error.Abort( | 1192 raise error.Abort( |
1013 _('cannot use collapse with continue or abort')) | 1193 _('cannot use collapse with continue or abort') |
1194 ) | |
1014 if srcf or basef or destf: | 1195 if srcf or basef or destf: |
1015 raise error.Abort( | 1196 raise error.Abort( |
1016 _('abort and continue do not allow specifying revisions')) | 1197 _('abort and continue do not allow specifying revisions') |
1198 ) | |
1017 if action == 'abort' and opts.get('tool', False): | 1199 if action == 'abort' and opts.get('tool', False): |
1018 ui.warn(_('tool option will be ignored\n')) | 1200 ui.warn(_('tool option will be ignored\n')) |
1019 if action == 'continue': | 1201 if action == 'continue': |
1020 ms = mergemod.mergestate.read(repo) | 1202 ms = mergemod.mergestate.read(repo) |
1021 mergeutil.checkunresolved(ms) | 1203 mergeutil.checkunresolved(ms) |
1022 | 1204 |
1023 retcode = rbsrt._prepareabortorcontinue(isabort=(action == 'abort')) | 1205 retcode = rbsrt._prepareabortorcontinue(isabort=(action == 'abort')) |
1024 if retcode is not None: | 1206 if retcode is not None: |
1025 return retcode | 1207 return retcode |
1026 else: | 1208 else: |
1027 destmap = _definedestmap(ui, repo, inmemory, destf, srcf, basef, | 1209 destmap = _definedestmap( |
1028 revf, destspace=destspace) | 1210 ui, |
1211 repo, | |
1212 inmemory, | |
1213 destf, | |
1214 srcf, | |
1215 basef, | |
1216 revf, | |
1217 destspace=destspace, | |
1218 ) | |
1029 retcode = rbsrt._preparenewrebase(destmap) | 1219 retcode = rbsrt._preparenewrebase(destmap) |
1030 if retcode is not None: | 1220 if retcode is not None: |
1031 return retcode | 1221 return retcode |
1032 storecollapsemsg(repo, rbsrt.collapsemsg) | 1222 storecollapsemsg(repo, rbsrt.collapsemsg) |
1033 | 1223 |
1049 with util.acceptintervention(dsguard): | 1239 with util.acceptintervention(dsguard): |
1050 rbsrt._performrebase(tr) | 1240 rbsrt._performrebase(tr) |
1051 if not leaveunfinished: | 1241 if not leaveunfinished: |
1052 rbsrt._finishrebase() | 1242 rbsrt._finishrebase() |
1053 | 1243 |
1054 def _definedestmap(ui, repo, inmemory, destf=None, srcf=None, basef=None, | 1244 |
1055 revf=None, destspace=None): | 1245 def _definedestmap( |
1246 ui, | |
1247 repo, | |
1248 inmemory, | |
1249 destf=None, | |
1250 srcf=None, | |
1251 basef=None, | |
1252 revf=None, | |
1253 destspace=None, | |
1254 ): | |
1056 """use revisions argument to define destmap {srcrev: destrev}""" | 1255 """use revisions argument to define destmap {srcrev: destrev}""" |
1057 if revf is None: | 1256 if revf is None: |
1058 revf = [] | 1257 revf = [] |
1059 | 1258 |
1060 # destspace is here to work around issues with `hg pull --rebase` see | 1259 # destspace is here to work around issues with `hg pull --rebase` see |
1069 if not inmemory: | 1268 if not inmemory: |
1070 cmdutil.checkunfinished(repo) | 1269 cmdutil.checkunfinished(repo) |
1071 cmdutil.bailifchanged(repo) | 1270 cmdutil.bailifchanged(repo) |
1072 | 1271 |
1073 if ui.configbool('commands', 'rebase.requiredest') and not destf: | 1272 if ui.configbool('commands', 'rebase.requiredest') and not destf: |
1074 raise error.Abort(_('you must specify a destination'), | 1273 raise error.Abort( |
1075 hint=_('use: hg rebase -d REV')) | 1274 _('you must specify a destination'), hint=_('use: hg rebase -d REV') |
1275 ) | |
1076 | 1276 |
1077 dest = None | 1277 dest = None |
1078 | 1278 |
1079 if revf: | 1279 if revf: |
1080 rebaseset = scmutil.revrange(repo, revf) | 1280 rebaseset = scmutil.revrange(repo, revf) |
1089 rebaseset = repo.revs('(%ld)::', src) | 1289 rebaseset = repo.revs('(%ld)::', src) |
1090 assert rebaseset | 1290 assert rebaseset |
1091 else: | 1291 else: |
1092 base = scmutil.revrange(repo, [basef or '.']) | 1292 base = scmutil.revrange(repo, [basef or '.']) |
1093 if not base: | 1293 if not base: |
1094 ui.status(_('empty "base" revision set - ' | 1294 ui.status( |
1095 "can't compute rebase set\n")) | 1295 _('empty "base" revision set - ' "can't compute rebase set\n") |
1296 ) | |
1096 return None | 1297 return None |
1097 if destf: | 1298 if destf: |
1098 # --base does not support multiple destinations | 1299 # --base does not support multiple destinations |
1099 dest = scmutil.revsingle(repo, destf) | 1300 dest = scmutil.revsingle(repo, destf) |
1100 else: | 1301 else: |
1101 dest = repo[_destrebase(repo, base, destspace=destspace)] | 1302 dest = repo[_destrebase(repo, base, destspace=destspace)] |
1102 destf = bytes(dest) | 1303 destf = bytes(dest) |
1103 | 1304 |
1104 roots = [] # selected children of branching points | 1305 roots = [] # selected children of branching points |
1105 bpbase = {} # {branchingpoint: [origbase]} | 1306 bpbase = {} # {branchingpoint: [origbase]} |
1106 for b in base: # group bases by branching points | 1307 for b in base: # group bases by branching points |
1107 bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first() | 1308 bp = repo.revs('ancestor(%d, %d)', b, dest.rev()).first() |
1108 bpbase[bp] = bpbase.get(bp, []) + [b] | 1309 bpbase[bp] = bpbase.get(bp, []) + [b] |
1109 if None in bpbase: | 1310 if None in bpbase: |
1110 # emulate the old behavior, showing "nothing to rebase" (a better | 1311 # emulate the old behavior, showing "nothing to rebase" (a better |
1111 # behavior may be abort with "cannot find branching point" error) | 1312 # behavior may be abort with "cannot find branching point" error) |
1112 bpbase.clear() | 1313 bpbase.clear() |
1113 for bp, bs in bpbase.iteritems(): # calculate roots | 1314 for bp, bs in bpbase.iteritems(): # calculate roots |
1114 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs)) | 1315 roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs)) |
1115 | 1316 |
1116 rebaseset = repo.revs('%ld::', roots) | 1317 rebaseset = repo.revs('%ld::', roots) |
1117 | 1318 |
1118 if not rebaseset: | 1319 if not rebaseset: |
1119 # transform to list because smartsets are not comparable to | 1320 # transform to list because smartsets are not comparable to |
1120 # lists. This should be improved to honor laziness of | 1321 # lists. This should be improved to honor laziness of |
1121 # smartset. | 1322 # smartset. |
1122 if list(base) == [dest.rev()]: | 1323 if list(base) == [dest.rev()]: |
1123 if basef: | 1324 if basef: |
1124 ui.status(_('nothing to rebase - %s is both "base"' | 1325 ui.status( |
1125 ' and destination\n') % dest) | 1326 _( |
1327 'nothing to rebase - %s is both "base"' | |
1328 ' and destination\n' | |
1329 ) | |
1330 % dest | |
1331 ) | |
1126 else: | 1332 else: |
1127 ui.status(_('nothing to rebase - working directory ' | 1333 ui.status( |
1128 'parent is also destination\n')) | 1334 _( |
1335 'nothing to rebase - working directory ' | |
1336 'parent is also destination\n' | |
1337 ) | |
1338 ) | |
1129 elif not repo.revs('%ld - ::%d', base, dest.rev()): | 1339 elif not repo.revs('%ld - ::%d', base, dest.rev()): |
1130 if basef: | 1340 if basef: |
1131 ui.status(_('nothing to rebase - "base" %s is ' | 1341 ui.status( |
1132 'already an ancestor of destination ' | 1342 _( |
1133 '%s\n') % | 1343 'nothing to rebase - "base" %s is ' |
1134 ('+'.join(bytes(repo[r]) for r in base), | 1344 'already an ancestor of destination ' |
1135 dest)) | 1345 '%s\n' |
1346 ) | |
1347 % ('+'.join(bytes(repo[r]) for r in base), dest) | |
1348 ) | |
1136 else: | 1349 else: |
1137 ui.status(_('nothing to rebase - working ' | 1350 ui.status( |
1138 'directory parent is already an ' | 1351 _( |
1139 'ancestor of destination %s\n') % dest) | 1352 'nothing to rebase - working ' |
1140 else: # can it happen? | 1353 'directory parent is already an ' |
1141 ui.status(_('nothing to rebase from %s to %s\n') % | 1354 'ancestor of destination %s\n' |
1142 ('+'.join(bytes(repo[r]) for r in base), dest)) | 1355 ) |
1356 % dest | |
1357 ) | |
1358 else: # can it happen? | |
1359 ui.status( | |
1360 _('nothing to rebase from %s to %s\n') | |
1361 % ('+'.join(bytes(repo[r]) for r in base), dest) | |
1362 ) | |
1143 return None | 1363 return None |
1144 | 1364 |
1145 rebasingwcp = repo['.'].rev() in rebaseset | 1365 rebasingwcp = repo['.'].rev() in rebaseset |
1146 ui.log("rebase", "rebasing working copy parent: %r\n", rebasingwcp, | 1366 ui.log( |
1147 rebase_rebasing_wcp=rebasingwcp) | 1367 "rebase", |
1368 "rebasing working copy parent: %r\n", | |
1369 rebasingwcp, | |
1370 rebase_rebasing_wcp=rebasingwcp, | |
1371 ) | |
1148 if inmemory and rebasingwcp: | 1372 if inmemory and rebasingwcp: |
1149 # Check these since we did not before. | 1373 # Check these since we did not before. |
1150 cmdutil.checkunfinished(repo) | 1374 cmdutil.checkunfinished(repo) |
1151 cmdutil.bailifchanged(repo) | 1375 cmdutil.bailifchanged(repo) |
1152 | 1376 |
1173 if size == 1: | 1397 if size == 1: |
1174 destmap[r] = destset.first() | 1398 destmap[r] = destset.first() |
1175 elif size == 0: | 1399 elif size == 0: |
1176 ui.note(_('skipping %s - empty destination\n') % repo[r]) | 1400 ui.note(_('skipping %s - empty destination\n') % repo[r]) |
1177 else: | 1401 else: |
1178 raise error.Abort(_('rebase destination for %s is not ' | 1402 raise error.Abort( |
1179 'unique') % repo[r]) | 1403 _('rebase destination for %s is not ' 'unique') |
1404 % repo[r] | |
1405 ) | |
1180 | 1406 |
1181 if dest is not None: | 1407 if dest is not None: |
1182 # single-dest case: assign dest to each rev in rebaseset | 1408 # single-dest case: assign dest to each rev in rebaseset |
1183 destrev = dest.rev() | 1409 destrev = dest.rev() |
1184 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev} | 1410 destmap = {r: destrev for r in rebaseset} # {srcrev: destrev} |
1185 | 1411 |
1186 if not destmap: | 1412 if not destmap: |
1187 ui.status(_('nothing to rebase - empty destination\n')) | 1413 ui.status(_('nothing to rebase - empty destination\n')) |
1188 return None | 1414 return None |
1189 | 1415 |
1190 return destmap | 1416 return destmap |
1417 | |
1191 | 1418 |
1192 def externalparent(repo, state, destancestors): | 1419 def externalparent(repo, state, destancestors): |
1193 """Return the revision that should be used as the second parent | 1420 """Return the revision that should be used as the second parent |
1194 when the revisions in state is collapsed on top of destancestors. | 1421 when the revisions in state is collapsed on top of destancestors. |
1195 Abort if there is more than one parent. | 1422 Abort if there is more than one parent. |
1198 source = min(state) | 1425 source = min(state) |
1199 for rev in state: | 1426 for rev in state: |
1200 if rev == source: | 1427 if rev == source: |
1201 continue | 1428 continue |
1202 for p in repo[rev].parents(): | 1429 for p in repo[rev].parents(): |
1203 if (p.rev() not in state | 1430 if p.rev() not in state and p.rev() not in destancestors: |
1204 and p.rev() not in destancestors): | |
1205 parents.add(p.rev()) | 1431 parents.add(p.rev()) |
1206 if not parents: | 1432 if not parents: |
1207 return nullrev | 1433 return nullrev |
1208 if len(parents) == 1: | 1434 if len(parents) == 1: |
1209 return parents.pop() | 1435 return parents.pop() |
1210 raise error.Abort(_('unable to collapse on top of %d, there is more ' | 1436 raise error.Abort( |
1211 'than one external parent: %s') % | 1437 _( |
1212 (max(destancestors), | 1438 'unable to collapse on top of %d, there is more ' |
1213 ', '.join("%d" % p for p in sorted(parents)))) | 1439 'than one external parent: %s' |
1440 ) | |
1441 % (max(destancestors), ', '.join("%d" % p for p in sorted(parents))) | |
1442 ) | |
1443 | |
1214 | 1444 |
1215 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg): | 1445 def commitmemorynode(repo, p1, p2, wctx, editor, extra, user, date, commitmsg): |
1216 '''Commit the memory changes with parents p1 and p2. | 1446 '''Commit the memory changes with parents p1 and p2. |
1217 Return node of committed revision.''' | 1447 Return node of committed revision.''' |
1218 # Replicates the empty check in ``repo.commit``. | 1448 # Replicates the empty check in ``repo.commit``. |
1223 # ``branch`` (used when passing ``--keepbranches``). | 1453 # ``branch`` (used when passing ``--keepbranches``). |
1224 branch = repo[p1].branch() | 1454 branch = repo[p1].branch() |
1225 if 'branch' in extra: | 1455 if 'branch' in extra: |
1226 branch = extra['branch'] | 1456 branch = extra['branch'] |
1227 | 1457 |
1228 memctx = wctx.tomemctx(commitmsg, parents=(p1, p2), date=date, | 1458 memctx = wctx.tomemctx( |
1229 extra=extra, user=user, branch=branch, editor=editor) | 1459 commitmsg, |
1460 parents=(p1, p2), | |
1461 date=date, | |
1462 extra=extra, | |
1463 user=user, | |
1464 branch=branch, | |
1465 editor=editor, | |
1466 ) | |
1230 commitres = repo.commitctx(memctx) | 1467 commitres = repo.commitctx(memctx) |
1231 wctx.clean() # Might be reused | 1468 wctx.clean() # Might be reused |
1232 return commitres | 1469 return commitres |
1470 | |
1233 | 1471 |
1234 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg): | 1472 def commitnode(repo, p1, p2, editor, extra, user, date, commitmsg): |
1235 '''Commit the wd changes with parents p1 and p2. | 1473 '''Commit the wd changes with parents p1 and p2. |
1236 Return node of committed revision.''' | 1474 Return node of committed revision.''' |
1237 dsguard = util.nullcontextmanager() | 1475 dsguard = util.nullcontextmanager() |
1239 dsguard = dirstateguard.dirstateguard(repo, 'rebase') | 1477 dsguard = dirstateguard.dirstateguard(repo, 'rebase') |
1240 with dsguard: | 1478 with dsguard: |
1241 repo.setparents(repo[p1].node(), repo[p2].node()) | 1479 repo.setparents(repo[p1].node(), repo[p2].node()) |
1242 | 1480 |
1243 # Commit might fail if unresolved files exist | 1481 # Commit might fail if unresolved files exist |
1244 newnode = repo.commit(text=commitmsg, user=user, date=date, | 1482 newnode = repo.commit( |
1245 extra=extra, editor=editor) | 1483 text=commitmsg, user=user, date=date, extra=extra, editor=editor |
1484 ) | |
1246 | 1485 |
1247 repo.dirstate.setbranch(repo[newnode].branch()) | 1486 repo.dirstate.setbranch(repo[newnode].branch()) |
1248 return newnode | 1487 return newnode |
1488 | |
1249 | 1489 |
1250 def rebasenode(repo, rev, p1, base, collapse, dest, wctx): | 1490 def rebasenode(repo, rev, p1, base, collapse, dest, wctx): |
1251 'Rebase a single revision rev on top of p1 using base as merge ancestor' | 1491 'Rebase a single revision rev on top of p1 using base as merge ancestor' |
1252 # Merge phase | 1492 # Merge phase |
1253 # Update to destination and merge it with local | 1493 # Update to destination and merge it with local |
1266 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev])) | 1506 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev])) |
1267 if base is not None: | 1507 if base is not None: |
1268 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base])) | 1508 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base])) |
1269 # When collapsing in-place, the parent is the common ancestor, we | 1509 # When collapsing in-place, the parent is the common ancestor, we |
1270 # have to allow merging with it. | 1510 # have to allow merging with it. |
1271 stats = mergemod.update(repo, rev, branchmerge=True, force=True, | 1511 stats = mergemod.update( |
1272 ancestor=base, mergeancestor=collapse, | 1512 repo, |
1273 labels=['dest', 'source'], wc=wctx) | 1513 rev, |
1514 branchmerge=True, | |
1515 force=True, | |
1516 ancestor=base, | |
1517 mergeancestor=collapse, | |
1518 labels=['dest', 'source'], | |
1519 wc=wctx, | |
1520 ) | |
1274 if collapse: | 1521 if collapse: |
1275 copies.duplicatecopies(repo, wctx, rev, dest) | 1522 copies.duplicatecopies(repo, wctx, rev, dest) |
1276 else: | 1523 else: |
1277 # If we're not using --collapse, we need to | 1524 # If we're not using --collapse, we need to |
1278 # duplicate copies between the revision we're | 1525 # duplicate copies between the revision we're |
1280 # duplicate any copies that have already been | 1527 # duplicate any copies that have already been |
1281 # performed in the destination. | 1528 # performed in the destination. |
1282 p1rev = repo[rev].p1().rev() | 1529 p1rev = repo[rev].p1().rev() |
1283 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest) | 1530 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest) |
1284 return stats | 1531 return stats |
1532 | |
1285 | 1533 |
1286 def adjustdest(repo, rev, destmap, state, skipped): | 1534 def adjustdest(repo, rev, destmap, state, skipped): |
1287 r"""adjust rebase destination given the current rebase state | 1535 r"""adjust rebase destination given the current rebase state |
1288 | 1536 |
1289 rev is what is being rebased. Return a list of two revs, which are the | 1537 rev is what is being rebased. Return a list of two revs, which are the |
1335 \ / | 1583 \ / |
1336 A | 1584 A |
1337 """ | 1585 """ |
1338 # pick already rebased revs with same dest from state as interesting source | 1586 # pick already rebased revs with same dest from state as interesting source |
1339 dest = destmap[rev] | 1587 dest = destmap[rev] |
1340 source = [s for s, d in state.items() | 1588 source = [ |
1341 if d > 0 and destmap[s] == dest and s not in skipped] | 1589 s |
1590 for s, d in state.items() | |
1591 if d > 0 and destmap[s] == dest and s not in skipped | |
1592 ] | |
1342 | 1593 |
1343 result = [] | 1594 result = [] |
1344 for prev in repo.changelog.parentrevs(rev): | 1595 for prev in repo.changelog.parentrevs(rev): |
1345 adjusted = dest | 1596 adjusted = dest |
1346 if prev != nullrev: | 1597 if prev != nullrev: |
1350 if adjusted == dest and dest in state: | 1601 if adjusted == dest and dest in state: |
1351 adjusted = state[dest] | 1602 adjusted = state[dest] |
1352 if adjusted == revtodo: | 1603 if adjusted == revtodo: |
1353 # sortsource should produce an order that makes this impossible | 1604 # sortsource should produce an order that makes this impossible |
1354 raise error.ProgrammingError( | 1605 raise error.ProgrammingError( |
1355 'rev %d should be rebased already at this time' % dest) | 1606 'rev %d should be rebased already at this time' % dest |
1607 ) | |
1356 result.append(adjusted) | 1608 result.append(adjusted) |
1357 return result | 1609 return result |
1610 | |
1358 | 1611 |
1359 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped): | 1612 def _checkobsrebase(repo, ui, rebaseobsrevs, rebaseobsskipped): |
1360 """ | 1613 """ |
1361 Abort if rebase will create divergence or rebase is noop because of markers | 1614 Abort if rebase will create divergence or rebase is noop because of markers |
1362 | 1615 |
1363 `rebaseobsrevs`: set of obsolete revision in source | 1616 `rebaseobsrevs`: set of obsolete revision in source |
1364 `rebaseobsskipped`: set of revisions from source skipped because they have | 1617 `rebaseobsskipped`: set of revisions from source skipped because they have |
1365 successors in destination or no non-obsolete successor. | 1618 successors in destination or no non-obsolete successor. |
1366 """ | 1619 """ |
1367 # Obsolete node with successors not in dest leads to divergence | 1620 # Obsolete node with successors not in dest leads to divergence |
1368 divergenceok = ui.configbool('experimental', | 1621 divergenceok = ui.configbool('experimental', 'evolution.allowdivergence') |
1369 'evolution.allowdivergence') | |
1370 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped | 1622 divergencebasecandidates = rebaseobsrevs - rebaseobsskipped |
1371 | 1623 |
1372 if divergencebasecandidates and not divergenceok: | 1624 if divergencebasecandidates and not divergenceok: |
1373 divhashes = (bytes(repo[r]) | 1625 divhashes = (bytes(repo[r]) for r in divergencebasecandidates) |
1374 for r in divergencebasecandidates) | 1626 msg = _("this rebase will cause " "divergences from: %s") |
1375 msg = _("this rebase will cause " | 1627 h = _( |
1376 "divergences from: %s") | 1628 "to force the rebase please set " |
1377 h = _("to force the rebase please set " | 1629 "experimental.evolution.allowdivergence=True" |
1378 "experimental.evolution.allowdivergence=True") | 1630 ) |
1379 raise error.Abort(msg % (",".join(divhashes),), hint=h) | 1631 raise error.Abort(msg % (",".join(divhashes),), hint=h) |
1632 | |
1380 | 1633 |
1381 def successorrevs(unfi, rev): | 1634 def successorrevs(unfi, rev): |
1382 """yield revision numbers for successors of rev""" | 1635 """yield revision numbers for successors of rev""" |
1383 assert unfi.filtername is None | 1636 assert unfi.filtername is None |
1384 nodemap = unfi.changelog.nodemap | 1637 nodemap = unfi.changelog.nodemap |
1385 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]): | 1638 for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]): |
1386 if s in nodemap: | 1639 if s in nodemap: |
1387 yield nodemap[s] | 1640 yield nodemap[s] |
1388 | 1641 |
1642 | |
1389 def defineparents(repo, rev, destmap, state, skipped, obsskipped): | 1643 def defineparents(repo, rev, destmap, state, skipped, obsskipped): |
1390 """Return new parents and optionally a merge base for rev being rebased | 1644 """Return new parents and optionally a merge base for rev being rebased |
1391 | 1645 |
1392 The destination specified by "dest" cannot always be used directly because | 1646 The destination specified by "dest" cannot always be used directly because |
1393 previously rebase result could affect destination. For example, | 1647 previously rebase result could affect destination. For example, |
1405 assert repo.filtername is None | 1659 assert repo.filtername is None |
1406 cl = repo.changelog | 1660 cl = repo.changelog |
1407 isancestor = cl.isancestorrev | 1661 isancestor = cl.isancestorrev |
1408 | 1662 |
1409 dest = destmap[rev] | 1663 dest = destmap[rev] |
1410 oldps = repo.changelog.parentrevs(rev) # old parents | 1664 oldps = repo.changelog.parentrevs(rev) # old parents |
1411 newps = [nullrev, nullrev] # new parents | 1665 newps = [nullrev, nullrev] # new parents |
1412 dests = adjustdest(repo, rev, destmap, state, skipped) | 1666 dests = adjustdest(repo, rev, destmap, state, skipped) |
1413 bases = list(oldps) # merge base candidates, initially just old parents | 1667 bases = list(oldps) # merge base candidates, initially just old parents |
1414 | 1668 |
1415 if all(r == nullrev for r in oldps[1:]): | 1669 if all(r == nullrev for r in oldps[1:]): |
1416 # For non-merge changeset, just move p to adjusted dest as requested. | 1670 # For non-merge changeset, just move p to adjusted dest as requested. |
1417 newps[0] = dests[0] | 1671 newps[0] = dests[0] |
1418 else: | 1672 else: |
1437 # A B D # B (using rule "2."), since B will be rebased. | 1691 # A B D # B (using rule "2."), since B will be rebased. |
1438 # | 1692 # |
1439 # The loop tries to be not rely on the fact that a Mercurial node has | 1693 # The loop tries to be not rely on the fact that a Mercurial node has |
1440 # at most 2 parents. | 1694 # at most 2 parents. |
1441 for i, p in enumerate(oldps): | 1695 for i, p in enumerate(oldps): |
1442 np = p # new parent | 1696 np = p # new parent |
1443 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)): | 1697 if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)): |
1444 np = dests[i] | 1698 np = dests[i] |
1445 elif p in state and state[p] > 0: | 1699 elif p in state and state[p] > 0: |
1446 np = state[p] | 1700 np = state[p] |
1447 | 1701 |
1463 | 1717 |
1464 # If one parent becomes an ancestor of the other, drop the ancestor | 1718 # If one parent becomes an ancestor of the other, drop the ancestor |
1465 for j, x in enumerate(newps[:i]): | 1719 for j, x in enumerate(newps[:i]): |
1466 if x == nullrev: | 1720 if x == nullrev: |
1467 continue | 1721 continue |
1468 if isancestor(np, x): # CASE-1 | 1722 if isancestor(np, x): # CASE-1 |
1469 np = nullrev | 1723 np = nullrev |
1470 elif isancestor(x, np): # CASE-2 | 1724 elif isancestor(x, np): # CASE-2 |
1471 newps[j] = np | 1725 newps[j] = np |
1472 np = nullrev | 1726 np = nullrev |
1473 # New parents forming an ancestor relationship does not | 1727 # New parents forming an ancestor relationship does not |
1474 # mean the old parents have a similar relationship. Do not | 1728 # mean the old parents have a similar relationship. Do not |
1475 # set bases[x] to nullrev. | 1729 # set bases[x] to nullrev. |
1490 # | 1744 # |
1491 # C # rebase -r C -d D | 1745 # C # rebase -r C -d D |
1492 # /| # None of A and B will be changed to D and rebase fails. | 1746 # /| # None of A and B will be changed to D and rebase fails. |
1493 # A B D | 1747 # A B D |
1494 if set(newps) == set(oldps) and dest not in newps: | 1748 if set(newps) == set(oldps) and dest not in newps: |
1495 raise error.Abort(_('cannot rebase %d:%s without ' | 1749 raise error.Abort( |
1496 'moving at least one of its parents') | 1750 _( |
1497 % (rev, repo[rev])) | 1751 'cannot rebase %d:%s without ' |
1752 'moving at least one of its parents' | |
1753 ) | |
1754 % (rev, repo[rev]) | |
1755 ) | |
1498 | 1756 |
1499 # Source should not be ancestor of dest. The check here guarantees it's | 1757 # Source should not be ancestor of dest. The check here guarantees it's |
1500 # impossible. With multi-dest, the initial check does not cover complex | 1758 # impossible. With multi-dest, the initial check does not cover complex |
1501 # cases since we don't have abstractions to dry-run rebase cheaply. | 1759 # cases since we don't have abstractions to dry-run rebase cheaply. |
1502 if any(p != nullrev and isancestor(rev, p) for p in newps): | 1760 if any(p != nullrev and isancestor(rev, p) for p in newps): |
1522 # | 1780 # |
1523 # But our merge base candidates (D and E in above case) could still be | 1781 # But our merge base candidates (D and E in above case) could still be |
1524 # better than the default (ancestor(F, Z) == null). Therefore still | 1782 # better than the default (ancestor(F, Z) == null). Therefore still |
1525 # pick one (so choose p1 above). | 1783 # pick one (so choose p1 above). |
1526 if sum(1 for b in bases if b != nullrev) > 1: | 1784 if sum(1 for b in bases if b != nullrev) > 1: |
1527 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i] | 1785 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i] |
1528 for i, base in enumerate(bases): | 1786 for i, base in enumerate(bases): |
1529 if base == nullrev: | 1787 if base == nullrev: |
1530 continue | 1788 continue |
1531 # Revisions in the side (not chosen as merge base) branch that | 1789 # Revisions in the side (not chosen as merge base) branch that |
1532 # might contain "surprising" contents | 1790 # might contain "surprising" contents |
1533 siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))', | 1791 siderevs = list( |
1534 bases, base, base, dest)) | 1792 repo.revs('((%ld-%d) %% (%d+%d))', bases, base, base, dest) |
1793 ) | |
1535 | 1794 |
1536 # If those revisions are covered by rebaseset, the result is good. | 1795 # If those revisions are covered by rebaseset, the result is good. |
1537 # A merge in rebaseset would be considered to cover its ancestors. | 1796 # A merge in rebaseset would be considered to cover its ancestors. |
1538 if siderevs: | 1797 if siderevs: |
1539 rebaseset = [r for r, d in state.items() | 1798 rebaseset = [ |
1540 if d > 0 and r not in obsskipped] | 1799 r for r, d in state.items() if d > 0 and r not in obsskipped |
1541 merges = [r for r in rebaseset | 1800 ] |
1542 if cl.parentrevs(r)[1] != nullrev] | 1801 merges = [ |
1543 unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld', | 1802 r for r in rebaseset if cl.parentrevs(r)[1] != nullrev |
1544 siderevs, merges, rebaseset)) | 1803 ] |
1804 unwanted[i] = list( | |
1805 repo.revs( | |
1806 '%ld - (::%ld) - %ld', siderevs, merges, rebaseset | |
1807 ) | |
1808 ) | |
1545 | 1809 |
1546 # Choose a merge base that has a minimal number of unwanted revs. | 1810 # Choose a merge base that has a minimal number of unwanted revs. |
1547 l, i = min((len(revs), i) | 1811 l, i = min( |
1548 for i, revs in enumerate(unwanted) if revs is not None) | 1812 (len(revs), i) |
1813 for i, revs in enumerate(unwanted) | |
1814 if revs is not None | |
1815 ) | |
1549 base = bases[i] | 1816 base = bases[i] |
1550 | 1817 |
1551 # newps[0] should match merge base if possible. Currently, if newps[i] | 1818 # newps[0] should match merge base if possible. Currently, if newps[i] |
1552 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is | 1819 # is nullrev, the only case is newps[i] and newps[j] (j < i), one is |
1553 # the other's ancestor. In that case, it's fine to not swap newps here. | 1820 # the other's ancestor. In that case, it's fine to not swap newps here. |
1557 | 1824 |
1558 # The merge will include unwanted revisions. Abort now. Revisit this if | 1825 # The merge will include unwanted revisions. Abort now. Revisit this if |
1559 # we have a more advanced merge algorithm that handles multiple bases. | 1826 # we have a more advanced merge algorithm that handles multiple bases. |
1560 if l > 0: | 1827 if l > 0: |
1561 unwanteddesc = _(' or ').join( | 1828 unwanteddesc = _(' or ').join( |
1562 (', '.join('%d:%s' % (r, repo[r]) for r in revs) | 1829 ( |
1563 for revs in unwanted if revs is not None)) | 1830 ', '.join('%d:%s' % (r, repo[r]) for r in revs) |
1831 for revs in unwanted | |
1832 if revs is not None | |
1833 ) | |
1834 ) | |
1564 raise error.Abort( | 1835 raise error.Abort( |
1565 _('rebasing %d:%s will include unwanted changes from %s') | 1836 _('rebasing %d:%s will include unwanted changes from %s') |
1566 % (rev, repo[rev], unwanteddesc)) | 1837 % (rev, repo[rev], unwanteddesc) |
1838 ) | |
1567 | 1839 |
1568 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps)) | 1840 repo.ui.debug(" future parents are %d and %d\n" % tuple(newps)) |
1569 | 1841 |
1570 return newps[0], newps[1], base | 1842 return newps[0], newps[1], base |
1843 | |
1571 | 1844 |
1572 def isagitpatch(repo, patchname): | 1845 def isagitpatch(repo, patchname): |
1573 'Return true if the given patch is in git format' | 1846 'Return true if the given patch is in git format' |
1574 mqpatch = os.path.join(repo.mq.path, patchname) | 1847 mqpatch = os.path.join(repo.mq.path, patchname) |
1575 for line in patch.linereader(open(mqpatch, 'rb')): | 1848 for line in patch.linereader(open(mqpatch, 'rb')): |
1576 if line.startswith('diff --git'): | 1849 if line.startswith('diff --git'): |
1577 return True | 1850 return True |
1578 return False | 1851 return False |
1579 | 1852 |
1853 | |
1580 def updatemq(repo, state, skipped, **opts): | 1854 def updatemq(repo, state, skipped, **opts): |
1581 'Update rebased mq patches - finalize and then import them' | 1855 'Update rebased mq patches - finalize and then import them' |
1582 mqrebase = {} | 1856 mqrebase = {} |
1583 mq = repo.mq | 1857 mq = repo.mq |
1584 original_series = mq.fullseries[:] | 1858 original_series = mq.fullseries[:] |
1585 skippedpatches = set() | 1859 skippedpatches = set() |
1586 | 1860 |
1587 for p in mq.applied: | 1861 for p in mq.applied: |
1588 rev = repo[p.node].rev() | 1862 rev = repo[p.node].rev() |
1589 if rev in state: | 1863 if rev in state: |
1590 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' % | 1864 repo.ui.debug( |
1591 (rev, p.name)) | 1865 'revision %d is an mq patch (%s), finalize it.\n' |
1866 % (rev, p.name) | |
1867 ) | |
1592 mqrebase[rev] = (p.name, isagitpatch(repo, p.name)) | 1868 mqrebase[rev] = (p.name, isagitpatch(repo, p.name)) |
1593 else: | 1869 else: |
1594 # Applied but not rebased, not sure this should happen | 1870 # Applied but not rebased, not sure this should happen |
1595 skippedpatches.add(p.name) | 1871 skippedpatches.add(p.name) |
1596 | 1872 |
1599 | 1875 |
1600 # We must start import from the newest revision | 1876 # We must start import from the newest revision |
1601 for rev in sorted(mqrebase, reverse=True): | 1877 for rev in sorted(mqrebase, reverse=True): |
1602 if rev not in skipped: | 1878 if rev not in skipped: |
1603 name, isgit = mqrebase[rev] | 1879 name, isgit = mqrebase[rev] |
1604 repo.ui.note(_('updating mq patch %s to %d:%s\n') % | 1880 repo.ui.note( |
1605 (name, state[rev], repo[state[rev]])) | 1881 _('updating mq patch %s to %d:%s\n') |
1606 mq.qimport(repo, (), patchname=name, git=isgit, | 1882 % (name, state[rev], repo[state[rev]]) |
1607 rev=["%d" % state[rev]]) | 1883 ) |
1884 mq.qimport( | |
1885 repo, (), patchname=name, git=isgit, rev=["%d" % state[rev]] | |
1886 ) | |
1608 else: | 1887 else: |
1609 # Rebased and skipped | 1888 # Rebased and skipped |
1610 skippedpatches.add(mqrebase[rev][0]) | 1889 skippedpatches.add(mqrebase[rev][0]) |
1611 | 1890 |
1612 # Patches were either applied and rebased and imported in | 1891 # Patches were either applied and rebased and imported in |
1613 # order, applied and removed or unapplied. Discard the removed | 1892 # order, applied and removed or unapplied. Discard the removed |
1614 # ones while preserving the original series order and guards. | 1893 # ones while preserving the original series order and guards. |
1615 newseries = [s for s in original_series | 1894 newseries = [ |
1616 if mq.guard_re.split(s, 1)[0] not in skippedpatches] | 1895 s |
1896 for s in original_series | |
1897 if mq.guard_re.split(s, 1)[0] not in skippedpatches | |
1898 ] | |
1617 mq.fullseries[:] = newseries | 1899 mq.fullseries[:] = newseries |
1618 mq.seriesdirty = True | 1900 mq.seriesdirty = True |
1619 mq.savedirty() | 1901 mq.savedirty() |
1902 | |
1620 | 1903 |
1621 def storecollapsemsg(repo, collapsemsg): | 1904 def storecollapsemsg(repo, collapsemsg): |
1622 'Store the collapse message to allow recovery' | 1905 'Store the collapse message to allow recovery' |
1623 collapsemsg = collapsemsg or '' | 1906 collapsemsg = collapsemsg or '' |
1624 f = repo.vfs("last-message.txt", "w") | 1907 f = repo.vfs("last-message.txt", "w") |
1625 f.write("%s\n" % collapsemsg) | 1908 f.write("%s\n" % collapsemsg) |
1626 f.close() | 1909 f.close() |
1627 | 1910 |
1911 | |
1628 def clearcollapsemsg(repo): | 1912 def clearcollapsemsg(repo): |
1629 'Remove collapse message file' | 1913 'Remove collapse message file' |
1630 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True) | 1914 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True) |
1915 | |
1631 | 1916 |
1632 def restorecollapsemsg(repo, isabort): | 1917 def restorecollapsemsg(repo, isabort): |
1633 'Restore previously stored collapse message' | 1918 'Restore previously stored collapse message' |
1634 try: | 1919 try: |
1635 f = repo.vfs("last-message.txt") | 1920 f = repo.vfs("last-message.txt") |
1643 collapsemsg = '' | 1928 collapsemsg = '' |
1644 else: | 1929 else: |
1645 raise error.Abort(_('missing .hg/last-message.txt for rebase')) | 1930 raise error.Abort(_('missing .hg/last-message.txt for rebase')) |
1646 return collapsemsg | 1931 return collapsemsg |
1647 | 1932 |
1933 | |
1648 def clearstatus(repo): | 1934 def clearstatus(repo): |
1649 'Remove the status files' | 1935 'Remove the status files' |
1650 # Make sure the active transaction won't write the state file | 1936 # Make sure the active transaction won't write the state file |
1651 tr = repo.currenttransaction() | 1937 tr = repo.currenttransaction() |
1652 if tr: | 1938 if tr: |
1653 tr.removefilegenerator('rebasestate') | 1939 tr.removefilegenerator('rebasestate') |
1654 repo.vfs.unlinkpath("rebasestate", ignoremissing=True) | 1940 repo.vfs.unlinkpath("rebasestate", ignoremissing=True) |
1655 | 1941 |
1942 | |
1656 def needupdate(repo, state): | 1943 def needupdate(repo, state): |
1657 '''check whether we should `update --clean` away from a merge, or if | 1944 '''check whether we should `update --clean` away from a merge, or if |
1658 somehow the working dir got forcibly updated, e.g. by older hg''' | 1945 somehow the working dir got forcibly updated, e.g. by older hg''' |
1659 parents = [p.rev() for p in repo[None].parents()] | 1946 parents = [p.rev() for p in repo[None].parents()] |
1660 | 1947 |
1661 # Are we in a merge state at all? | 1948 # Are we in a merge state at all? |
1662 if len(parents) < 2: | 1949 if len(parents) < 2: |
1663 return False | 1950 return False |
1664 | 1951 |
1665 # We should be standing on the first as-of-yet unrebased commit. | 1952 # We should be standing on the first as-of-yet unrebased commit. |
1666 firstunrebased = min([old for old, new in state.iteritems() | 1953 firstunrebased = min( |
1667 if new == nullrev]) | 1954 [old for old, new in state.iteritems() if new == nullrev] |
1955 ) | |
1668 if firstunrebased in parents: | 1956 if firstunrebased in parents: |
1669 return True | 1957 return True |
1670 | 1958 |
1671 return False | 1959 return False |
1960 | |
1672 | 1961 |
1673 def sortsource(destmap): | 1962 def sortsource(destmap): |
1674 """yield source revisions in an order that we only rebase things once | 1963 """yield source revisions in an order that we only rebase things once |
1675 | 1964 |
1676 If source and destination overlaps, we should filter out revisions | 1965 If source and destination overlaps, we should filter out revisions |
1693 if not result: | 1982 if not result: |
1694 raise error.Abort(_('source and destination form a cycle')) | 1983 raise error.Abort(_('source and destination form a cycle')) |
1695 srcset -= set(result) | 1984 srcset -= set(result) |
1696 yield result | 1985 yield result |
1697 | 1986 |
1987 | |
1698 def buildstate(repo, destmap, collapse): | 1988 def buildstate(repo, destmap, collapse): |
1699 '''Define which revisions are going to be rebased and where | 1989 '''Define which revisions are going to be rebased and where |
1700 | 1990 |
1701 repo: repo | 1991 repo: repo |
1702 destmap: {srcrev: destrev} | 1992 destmap: {srcrev: destrev} |
1711 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied) | 2001 mqapplied = set(repo[s.node].rev() for s in repo.mq.applied) |
1712 if set(destmap.values()) & mqapplied: | 2002 if set(destmap.values()) & mqapplied: |
1713 raise error.Abort(_('cannot rebase onto an applied mq patch')) | 2003 raise error.Abort(_('cannot rebase onto an applied mq patch')) |
1714 | 2004 |
1715 # Get "cycle" error early by exhausting the generator. | 2005 # Get "cycle" error early by exhausting the generator. |
1716 sortedsrc = list(sortsource(destmap)) # a list of sorted revs | 2006 sortedsrc = list(sortsource(destmap)) # a list of sorted revs |
1717 if not sortedsrc: | 2007 if not sortedsrc: |
1718 raise error.Abort(_('no matching revisions')) | 2008 raise error.Abort(_('no matching revisions')) |
1719 | 2009 |
1720 # Only check the first batch of revisions to rebase not depending on other | 2010 # Only check the first batch of revisions to rebase not depending on other |
1721 # rebaseset. This means "source is ancestor of destination" for the second | 2011 # rebaseset. This means "source is ancestor of destination" for the second |
1722 # (and following) batches of revisions are not checked here. We rely on | 2012 # (and following) batches of revisions are not checked here. We rely on |
1723 # "defineparents" to do that check. | 2013 # "defineparents" to do that check. |
1724 roots = list(repo.set('roots(%ld)', sortedsrc[0])) | 2014 roots = list(repo.set('roots(%ld)', sortedsrc[0])) |
1725 if not roots: | 2015 if not roots: |
1726 raise error.Abort(_('no matching revisions')) | 2016 raise error.Abort(_('no matching revisions')) |
2017 | |
1727 def revof(r): | 2018 def revof(r): |
1728 return r.rev() | 2019 return r.rev() |
2020 | |
1729 roots = sorted(roots, key=revof) | 2021 roots = sorted(roots, key=revof) |
1730 state = dict.fromkeys(rebaseset, revtodo) | 2022 state = dict.fromkeys(rebaseset, revtodo) |
1731 emptyrebase = (len(sortedsrc) == 1) | 2023 emptyrebase = len(sortedsrc) == 1 |
1732 for root in roots: | 2024 for root in roots: |
1733 dest = repo[destmap[root.rev()]] | 2025 dest = repo[destmap[root.rev()]] |
1734 commonbase = root.ancestor(dest) | 2026 commonbase = root.ancestor(dest) |
1735 if commonbase == root: | 2027 if commonbase == root: |
1736 raise error.Abort(_('source is ancestor of destination')) | 2028 raise error.Abort(_('source is ancestor of destination')) |
1757 # if all parents of this revision are done, then so is this revision | 2049 # if all parents of this revision are done, then so is this revision |
1758 if parents and all((state.get(p) == p for p in parents)): | 2050 if parents and all((state.get(p) == p for p in parents)): |
1759 state[rev] = rev | 2051 state[rev] = rev |
1760 return originalwd, destmap, state | 2052 return originalwd, destmap, state |
1761 | 2053 |
1762 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None, | 2054 |
1763 keepf=False, fm=None, backup=True): | 2055 def clearrebased( |
2056 ui, | |
2057 repo, | |
2058 destmap, | |
2059 state, | |
2060 skipped, | |
2061 collapsedas=None, | |
2062 keepf=False, | |
2063 fm=None, | |
2064 backup=True, | |
2065 ): | |
1764 """dispose of rebased revision at the end of the rebase | 2066 """dispose of rebased revision at the end of the rebase |
1765 | 2067 |
1766 If `collapsedas` is not None, the rebase was a collapse whose result if the | 2068 If `collapsedas` is not None, the rebase was a collapse whose result if the |
1767 `collapsedas` node. | 2069 `collapsedas` node. |
1768 | 2070 |
1807 fm.data(nodechanges=nodechanges) | 2109 fm.data(nodechanges=nodechanges) |
1808 if keepf: | 2110 if keepf: |
1809 replacements = {} | 2111 replacements = {} |
1810 scmutil.cleanupnodes(repo, replacements, 'rebase', moves, backup=backup) | 2112 scmutil.cleanupnodes(repo, replacements, 'rebase', moves, backup=backup) |
1811 | 2113 |
2114 | |
1812 def pullrebase(orig, ui, repo, *args, **opts): | 2115 def pullrebase(orig, ui, repo, *args, **opts): |
1813 'Call rebase after pull if the latter has been invoked with --rebase' | 2116 'Call rebase after pull if the latter has been invoked with --rebase' |
1814 if opts.get(r'rebase'): | 2117 if opts.get(r'rebase'): |
1815 if ui.configbool('commands', 'rebase.requiredest'): | 2118 if ui.configbool('commands', 'rebase.requiredest'): |
1816 msg = _('rebase destination required by configuration') | 2119 msg = _('rebase destination required by configuration') |
1818 raise error.Abort(msg, hint=hint) | 2121 raise error.Abort(msg, hint=hint) |
1819 | 2122 |
1820 with repo.wlock(), repo.lock(): | 2123 with repo.wlock(), repo.lock(): |
1821 if opts.get(r'update'): | 2124 if opts.get(r'update'): |
1822 del opts[r'update'] | 2125 del opts[r'update'] |
1823 ui.debug('--update and --rebase are not compatible, ignoring ' | 2126 ui.debug( |
1824 'the update flag\n') | 2127 '--update and --rebase are not compatible, ignoring ' |
2128 'the update flag\n' | |
2129 ) | |
1825 | 2130 |
1826 cmdutil.checkunfinished(repo, skipmerge=True) | 2131 cmdutil.checkunfinished(repo, skipmerge=True) |
1827 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: ' | 2132 cmdutil.bailifchanged( |
1828 'please commit or shelve your changes first')) | 2133 repo, |
2134 hint=_( | |
2135 'cannot pull with rebase: ' | |
2136 'please commit or shelve your changes first' | |
2137 ), | |
2138 ) | |
1829 | 2139 |
1830 revsprepull = len(repo) | 2140 revsprepull = len(repo) |
1831 origpostincoming = commands.postincoming | 2141 origpostincoming = commands.postincoming |
2142 | |
1832 def _dummy(*args, **kwargs): | 2143 def _dummy(*args, **kwargs): |
1833 pass | 2144 pass |
2145 | |
1834 commands.postincoming = _dummy | 2146 commands.postincoming = _dummy |
1835 try: | 2147 try: |
1836 ret = orig(ui, repo, *args, **opts) | 2148 ret = orig(ui, repo, *args, **opts) |
1837 finally: | 2149 finally: |
1838 commands.postincoming = origpostincoming | 2150 commands.postincoming = origpostincoming |
1866 raise error.Abort(_('--tool can only be used with --rebase')) | 2178 raise error.Abort(_('--tool can only be used with --rebase')) |
1867 ret = orig(ui, repo, *args, **opts) | 2179 ret = orig(ui, repo, *args, **opts) |
1868 | 2180 |
1869 return ret | 2181 return ret |
1870 | 2182 |
2183 | |
1871 def _filterobsoleterevs(repo, revs): | 2184 def _filterobsoleterevs(repo, revs): |
1872 """returns a set of the obsolete revisions in revs""" | 2185 """returns a set of the obsolete revisions in revs""" |
1873 return set(r for r in revs if repo[r].obsolete()) | 2186 return set(r for r in revs if repo[r].obsolete()) |
2187 | |
1874 | 2188 |
1875 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap): | 2189 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap): |
1876 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination). | 2190 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination). |
1877 | 2191 |
1878 `obsoletenotrebased` is a mapping mapping obsolete => successor for all | 2192 `obsoletenotrebased` is a mapping mapping obsolete => successor for all |
1922 obsoletenotrebased, | 2236 obsoletenotrebased, |
1923 obsoletewithoutsuccessorindestination, | 2237 obsoletewithoutsuccessorindestination, |
1924 obsoleteextinctsuccessors, | 2238 obsoleteextinctsuccessors, |
1925 ) | 2239 ) |
1926 | 2240 |
2241 | |
1927 def abortrebase(ui, repo): | 2242 def abortrebase(ui, repo): |
1928 with repo.wlock(), repo.lock(): | 2243 with repo.wlock(), repo.lock(): |
1929 rbsrt = rebaseruntime(repo, ui) | 2244 rbsrt = rebaseruntime(repo, ui) |
1930 rbsrt._prepareabortorcontinue(isabort=True) | 2245 rbsrt._prepareabortorcontinue(isabort=True) |
2246 | |
1931 | 2247 |
1932 def continuerebase(ui, repo): | 2248 def continuerebase(ui, repo): |
1933 with repo.wlock(), repo.lock(): | 2249 with repo.wlock(), repo.lock(): |
1934 rbsrt = rebaseruntime(repo, ui) | 2250 rbsrt = rebaseruntime(repo, ui) |
1935 ms = mergemod.mergestate.read(repo) | 2251 ms = mergemod.mergestate.read(repo) |
1937 retcode = rbsrt._prepareabortorcontinue(isabort=False) | 2253 retcode = rbsrt._prepareabortorcontinue(isabort=False) |
1938 if retcode is not None: | 2254 if retcode is not None: |
1939 return retcode | 2255 return retcode |
1940 rbsrt._performrebase(None) | 2256 rbsrt._performrebase(None) |
1941 rbsrt._finishrebase() | 2257 rbsrt._finishrebase() |
2258 | |
1942 | 2259 |
1943 def summaryhook(ui, repo): | 2260 def summaryhook(ui, repo): |
1944 if not repo.vfs.exists('rebasestate'): | 2261 if not repo.vfs.exists('rebasestate'): |
1945 return | 2262 return |
1946 try: | 2263 try: |
1952 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n') | 2269 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n') |
1953 ui.write(msg) | 2270 ui.write(msg) |
1954 return | 2271 return |
1955 numrebased = len([i for i in state.itervalues() if i >= 0]) | 2272 numrebased = len([i for i in state.itervalues() if i >= 0]) |
1956 # i18n: column positioning for "hg summary" | 2273 # i18n: column positioning for "hg summary" |
1957 ui.write(_('rebase: %s, %s (rebase --continue)\n') % | 2274 ui.write( |
1958 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased, | 2275 _('rebase: %s, %s (rebase --continue)\n') |
1959 ui.label(_('%d remaining'), 'rebase.remaining') % | 2276 % ( |
1960 (len(state) - numrebased))) | 2277 ui.label(_('%d rebased'), 'rebase.rebased') % numrebased, |
2278 ui.label(_('%d remaining'), 'rebase.remaining') | |
2279 % (len(state) - numrebased), | |
2280 ) | |
2281 ) | |
2282 | |
1961 | 2283 |
1962 def uisetup(ui): | 2284 def uisetup(ui): |
1963 #Replace pull with a decorator to provide --rebase option | 2285 # Replace pull with a decorator to provide --rebase option |
1964 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase) | 2286 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase) |
1965 entry[1].append(('', 'rebase', None, | 2287 entry[1].append( |
1966 _("rebase working directory to branch head"))) | 2288 ('', 'rebase', None, _("rebase working directory to branch head")) |
1967 entry[1].append(('t', 'tool', '', | 2289 ) |
1968 _("specify merge tool for rebase"))) | 2290 entry[1].append(('t', 'tool', '', _("specify merge tool for rebase"))) |
1969 cmdutil.summaryhooks.add('rebase', summaryhook) | 2291 cmdutil.summaryhooks.add('rebase', summaryhook) |
1970 statemod.addunfinished('rebase', fname='rebasestate', stopflag=True, | 2292 statemod.addunfinished( |
1971 continueflag=True, abortfunc=abortrebase, | 2293 'rebase', |
1972 continuefunc=continuerebase) | 2294 fname='rebasestate', |
2295 stopflag=True, | |
2296 continueflag=True, | |
2297 abortfunc=abortrebase, | |
2298 continuefunc=continuerebase, | |
2299 ) |