Mercurial > hg
comparison hgext/rebase.py @ 36931:28f988093911
rebase: extract function for rebasing a single node
We currently have _performrebase() and _performrebasesubset(), but we
don't have a method for rebasing a single node (that's inside a loop
in _performrebasesubset()). I think it makes sense to have such a
method, so that's what this patch does. I think it may simplify future
patches I'm working on that have to do with transactions, but I think
this patch makes sense on its own whether or not that future work
happens.
Differential Revision: https://phab.mercurial-scm.org/D2809
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Mon, 12 Mar 2018 10:39:42 -0700 |
parents | 36de8a3c429c |
children | 437f80436186 |
comparison
equal
deleted
inserted
replaced
36930:36de8a3c429c | 36931:28f988093911 |
---|---|
428 pos = self._performrebasesubset(tr, subset, pos, total) | 428 pos = self._performrebasesubset(tr, subset, pos, total) |
429 ui.progress(_('rebasing'), None) | 429 ui.progress(_('rebasing'), None) |
430 ui.note(_('rebase merging completed\n')) | 430 ui.note(_('rebase merging completed\n')) |
431 | 431 |
432 def _performrebasesubset(self, tr, subset, pos, total): | 432 def _performrebasesubset(self, tr, subset, pos, total): |
433 repo, ui, opts = self.repo, self.ui, self.opts | 433 sortedrevs = self.repo.revs('sort(%ld, -topo)', subset) |
434 sortedrevs = repo.revs('sort(%ld, -topo)', subset) | |
435 allowdivergence = self.ui.configbool( | 434 allowdivergence = self.ui.configbool( |
436 'experimental', 'evolution.allowdivergence') | 435 'experimental', 'evolution.allowdivergence') |
437 if not allowdivergence: | 436 if not allowdivergence: |
438 sortedrevs -= repo.revs( | 437 sortedrevs -= self.repo.revs( |
439 'descendants(%ld) and not %ld', | 438 'descendants(%ld) and not %ld', |
440 self.obsoletewithoutsuccessorindestination, | 439 self.obsoletewithoutsuccessorindestination, |
441 self.obsoletewithoutsuccessorindestination, | 440 self.obsoletewithoutsuccessorindestination, |
442 ) | 441 ) |
442 posholder = [pos] | |
443 def progress(ctx): | |
444 posholder[0] += 1 | |
445 self.repo.ui.progress(_("rebasing"), posholder[0], | |
446 ("%d:%s" % (ctx.rev(), ctx)), _('changesets'), | |
447 total) | |
443 for rev in sortedrevs: | 448 for rev in sortedrevs: |
444 dest = self.destmap[rev] | 449 self._rebasenode(tr, rev, allowdivergence, progress) |
445 ctx = repo[rev] | 450 return posholder[0] |
446 desc = _ctxdesc(ctx) | 451 |
447 if self.state[rev] == rev: | 452 def _rebasenode(self, tr, rev, allowdivergence, progressfn): |
448 ui.status(_('already rebased %s\n') % desc) | 453 repo, ui, opts = self.repo, self.ui, self.opts |
449 elif (not allowdivergence | 454 dest = self.destmap[rev] |
450 and rev in self.obsoletewithoutsuccessorindestination): | 455 ctx = repo[rev] |
451 msg = _('note: not rebasing %s and its descendants as ' | 456 desc = _ctxdesc(ctx) |
452 'this would cause divergence\n') % desc | 457 if self.state[rev] == rev: |
453 repo.ui.status(msg) | 458 ui.status(_('already rebased %s\n') % desc) |
454 self.skipped.add(rev) | 459 elif (not allowdivergence |
455 elif rev in self.obsoletenotrebased: | 460 and rev in self.obsoletewithoutsuccessorindestination): |
456 succ = self.obsoletenotrebased[rev] | 461 msg = _('note: not rebasing %s and its descendants as ' |
457 if succ is None: | 462 'this would cause divergence\n') % desc |
458 msg = _('note: not rebasing %s, it has no ' | 463 repo.ui.status(msg) |
459 'successor\n') % desc | 464 self.skipped.add(rev) |
465 elif rev in self.obsoletenotrebased: | |
466 succ = self.obsoletenotrebased[rev] | |
467 if succ is None: | |
468 msg = _('note: not rebasing %s, it has no ' | |
469 'successor\n') % desc | |
470 else: | |
471 succdesc = _ctxdesc(repo[succ]) | |
472 msg = (_('note: not rebasing %s, already in ' | |
473 'destination as %s\n') % (desc, succdesc)) | |
474 repo.ui.status(msg) | |
475 # Make clearrebased aware state[rev] is not a true successor | |
476 self.skipped.add(rev) | |
477 # Record rev as moved to its desired destination in self.state. | |
478 # This helps bookmark and working parent movement. | |
479 dest = max(adjustdest(repo, rev, self.destmap, self.state, | |
480 self.skipped)) | |
481 self.state[rev] = dest | |
482 elif self.state[rev] == revtodo: | |
483 ui.status(_('rebasing %s\n') % desc) | |
484 progressfn(ctx) | |
485 p1, p2, base = defineparents(repo, rev, self.destmap, | |
486 self.state, self.skipped, | |
487 self.obsoletenotrebased) | |
488 self.storestatus(tr=tr) | |
489 if len(repo[None].parents()) == 2: | |
490 repo.ui.debug('resuming interrupted rebase\n') | |
491 else: | |
492 overrides = {('ui', 'forcemerge'): opts.get('tool', '')} | |
493 with ui.configoverride(overrides, 'rebase'): | |
494 stats = rebasenode(repo, rev, p1, base, self.collapsef, | |
495 dest, wctx=self.wctx) | |
496 if stats and stats[3] > 0: | |
497 if self.wctx.isinmemory(): | |
498 raise error.InMemoryMergeConflictsError() | |
499 else: | |
500 raise error.InterventionRequired( | |
501 _('unresolved conflicts (see hg ' | |
502 'resolve, then hg rebase --continue)')) | |
503 if not self.collapsef: | |
504 merging = p2 != nullrev | |
505 editform = cmdutil.mergeeditform(merging, 'rebase') | |
506 editor = cmdutil.getcommiteditor(editform=editform, | |
507 **pycompat.strkwargs(opts)) | |
508 if self.wctx.isinmemory(): | |
509 newnode = concludememorynode(repo, rev, p1, p2, | |
510 wctx=self.wctx, | |
511 extrafn=_makeextrafn(self.extrafns), | |
512 editor=editor, | |
513 keepbranches=self.keepbranchesf, | |
514 date=self.date) | |
515 mergemod.mergestate.clean(repo) | |
460 else: | 516 else: |
461 succdesc = _ctxdesc(repo[succ]) | 517 newnode = concludenode(repo, rev, p1, p2, |
462 msg = (_('note: not rebasing %s, already in ' | 518 extrafn=_makeextrafn(self.extrafns), |
463 'destination as %s\n') % (desc, succdesc)) | 519 editor=editor, |
464 repo.ui.status(msg) | 520 keepbranches=self.keepbranchesf, |
465 # Make clearrebased aware state[rev] is not a true successor | 521 date=self.date) |
466 self.skipped.add(rev) | 522 |
467 # Record rev as moved to its desired destination in self.state. | 523 if newnode is None: |
468 # This helps bookmark and working parent movement. | 524 # If it ended up being a no-op commit, then the normal |
469 dest = max(adjustdest(repo, rev, self.destmap, self.state, | 525 # merge state clean-up path doesn't happen, so do it |
470 self.skipped)) | 526 # here. Fix issue5494 |
471 self.state[rev] = dest | 527 mergemod.mergestate.clean(repo) |
472 elif self.state[rev] == revtodo: | 528 else: |
473 pos += 1 | 529 # Skip commit if we are collapsing |
474 ui.status(_('rebasing %s\n') % desc) | 530 if self.wctx.isinmemory(): |
475 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)), | 531 self.wctx.setbase(repo[p1]) |
476 _('changesets'), total) | |
477 p1, p2, base = defineparents(repo, rev, self.destmap, | |
478 self.state, self.skipped, | |
479 self.obsoletenotrebased) | |
480 self.storestatus(tr=tr) | |
481 if len(repo[None].parents()) == 2: | |
482 repo.ui.debug('resuming interrupted rebase\n') | |
483 else: | 532 else: |
484 overrides = {('ui', 'forcemerge'): opts.get('tool', '')} | 533 repo.setparents(repo[p1].node()) |
485 with ui.configoverride(overrides, 'rebase'): | 534 newnode = None |
486 stats = rebasenode(repo, rev, p1, base, self.collapsef, | 535 # Update the state |
487 dest, wctx=self.wctx) | 536 if newnode is not None: |
488 if stats and stats[3] > 0: | 537 self.state[rev] = repo[newnode].rev() |
489 if self.wctx.isinmemory(): | 538 ui.debug('rebased as %s\n' % short(newnode)) |
490 raise error.InMemoryMergeConflictsError() | 539 else: |
491 else: | |
492 raise error.InterventionRequired( | |
493 _('unresolved conflicts (see hg ' | |
494 'resolve, then hg rebase --continue)')) | |
495 if not self.collapsef: | 540 if not self.collapsef: |
496 merging = p2 != nullrev | 541 ui.warn(_('note: rebase of %d:%s created no changes ' |
497 editform = cmdutil.mergeeditform(merging, 'rebase') | 542 'to commit\n') % (rev, ctx)) |
498 editor = cmdutil.getcommiteditor(editform=editform, | 543 self.skipped.add(rev) |
499 **pycompat.strkwargs(opts)) | 544 self.state[rev] = p1 |
500 if self.wctx.isinmemory(): | 545 ui.debug('next revision set to %d\n' % p1) |
501 newnode = concludememorynode(repo, rev, p1, p2, | 546 else: |
502 wctx=self.wctx, | 547 ui.status(_('already rebased %s as %s\n') % |
503 extrafn=_makeextrafn(self.extrafns), | 548 (desc, repo[self.state[rev]])) |
504 editor=editor, | |
505 keepbranches=self.keepbranchesf, | |
506 date=self.date) | |
507 mergemod.mergestate.clean(repo) | |
508 else: | |
509 newnode = concludenode(repo, rev, p1, p2, | |
510 extrafn=_makeextrafn(self.extrafns), | |
511 editor=editor, | |
512 keepbranches=self.keepbranchesf, | |
513 date=self.date) | |
514 | |
515 if newnode is None: | |
516 # If it ended up being a no-op commit, then the normal | |
517 # merge state clean-up path doesn't happen, so do it | |
518 # here. Fix issue5494 | |
519 mergemod.mergestate.clean(repo) | |
520 else: | |
521 # Skip commit if we are collapsing | |
522 if self.wctx.isinmemory(): | |
523 self.wctx.setbase(repo[p1]) | |
524 else: | |
525 repo.setparents(repo[p1].node()) | |
526 newnode = None | |
527 # Update the state | |
528 if newnode is not None: | |
529 self.state[rev] = repo[newnode].rev() | |
530 ui.debug('rebased as %s\n' % short(newnode)) | |
531 else: | |
532 if not self.collapsef: | |
533 ui.warn(_('note: rebase of %d:%s created no changes ' | |
534 'to commit\n') % (rev, ctx)) | |
535 self.skipped.add(rev) | |
536 self.state[rev] = p1 | |
537 ui.debug('next revision set to %d\n' % p1) | |
538 else: | |
539 ui.status(_('already rebased %s as %s\n') % | |
540 (desc, repo[self.state[rev]])) | |
541 return pos | |
542 | 549 |
543 def _finishrebase(self): | 550 def _finishrebase(self): |
544 repo, ui, opts = self.repo, self.ui, self.opts | 551 repo, ui, opts = self.repo, self.ui, self.opts |
545 fm = ui.formatter('rebase', opts) | 552 fm = ui.formatter('rebase', opts) |
546 fm.startitem() | 553 fm.startitem() |