hgext/rebase.py
changeset 23484 cf3495dfd7ed
parent 23461 ffef6d503ab2
child 23489 fe4157d839ac
equal deleted inserted replaced
23483:3805f4b0f5a9 23484:cf3495dfd7ed
   368         for rev in sortedstate:
   368         for rev in sortedstate:
   369             pos += 1
   369             pos += 1
   370             if state[rev] == -1:
   370             if state[rev] == -1:
   371                 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
   371                 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
   372                             _('changesets'), total)
   372                             _('changesets'), total)
   373                 p1, p2 = defineparents(repo, rev, target, state,
   373                 p1, p2, base = defineparents(repo, rev, target, state,
   374                                                         targetancestors)
   374                                              targetancestors)
   375                 storestatus(repo, originalwd, target, state, collapsef, keepf,
   375                 storestatus(repo, originalwd, target, state, collapsef, keepf,
   376                             keepbranchesf, external, activebookmark)
   376                             keepbranchesf, external, activebookmark)
   377                 if len(repo.parents()) == 2:
   377                 if len(repo.parents()) == 2:
   378                     repo.ui.debug('resuming interrupted rebase\n')
   378                     repo.ui.debug('resuming interrupted rebase\n')
   379                 else:
   379                 else:
   380                     try:
   380                     try:
   381                         ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
   381                         ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
   382                                      'rebase')
   382                                      'rebase')
   383                         stats = rebasenode(repo, rev, p1, state, collapsef,
   383                         stats = rebasenode(repo, rev, p1, base, state,
   384                                            target)
   384                                            collapsef, target)
   385                         if stats and stats[3] > 0:
   385                         if stats and stats[3] > 0:
   386                             raise error.InterventionRequired(
   386                             raise error.InterventionRequired(
   387                                 _('unresolved conflicts (see hg '
   387                                 _('unresolved conflicts (see hg '
   388                                   'resolve, then hg rebase --continue)'))
   388                                   'resolve, then hg rebase --continue)'))
   389                     finally:
   389                     finally:
   412 
   412 
   413         ui.progress(_('rebasing'), None)
   413         ui.progress(_('rebasing'), None)
   414         ui.note(_('rebase merging completed\n'))
   414         ui.note(_('rebase merging completed\n'))
   415 
   415 
   416         if collapsef and not keepopen:
   416         if collapsef and not keepopen:
   417             p1, p2 = defineparents(repo, min(state), target,
   417             p1, p2, _base = defineparents(repo, min(state), target,
   418                                                         state, targetancestors)
   418                                           state, targetancestors)
   419             editopt = opts.get('edit')
   419             editopt = opts.get('edit')
   420             editform = 'rebase.collapse'
   420             editform = 'rebase.collapse'
   421             if collapsemsg:
   421             if collapsemsg:
   422                 commitmsg = collapsemsg
   422                 commitmsg = collapsemsg
   423             else:
   423             else:
   507                        'than one external parent: %s') %
   507                        'than one external parent: %s') %
   508                      (max(targetancestors),
   508                      (max(targetancestors),
   509                       ', '.join(str(p) for p in sorted(parents))))
   509                       ', '.join(str(p) for p in sorted(parents))))
   510 
   510 
   511 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
   511 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
   512     '''Commit the changes and store useful information in extra.
   512     '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
       
   513     but also store useful information in extra.
   513     Return node of committed revision.'''
   514     Return node of committed revision.'''
   514     try:
   515     try:
   515         repo.dirstate.beginparentchange()
   516         repo.dirstate.beginparentchange()
   516         repo.setparents(repo[p1].node(), repo[p2].node())
   517         repo.setparents(repo[p1].node(), repo[p2].node())
   517         repo.dirstate.endparentchange()
   518         repo.dirstate.endparentchange()
   537     except util.Abort:
   538     except util.Abort:
   538         # Invalidate the previous setparents
   539         # Invalidate the previous setparents
   539         repo.dirstate.invalidate()
   540         repo.dirstate.invalidate()
   540         raise
   541         raise
   541 
   542 
   542 def rebasenode(repo, rev, p1, state, collapse, target):
   543 def rebasenode(repo, rev, p1, base, state, collapse, target):
   543     'Rebase a single revision'
   544     'Rebase a single revision rev on top of p1 using base as merge ancestor'
   544     # Merge phase
   545     # Merge phase
   545     # Update to target and merge it with local
   546     # Update to target and merge it with local
   546     if repo['.'].rev() != p1:
   547     if repo['.'].rev() != p1:
   547         repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
   548         repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
   548         merge.update(repo, p1, False, True, False)
   549         merge.update(repo, p1, False, True, False)
   549     else:
   550     else:
   550         repo.ui.debug(" already in target\n")
   551         repo.ui.debug(" already in target\n")
   551     repo.dirstate.write()
   552     repo.dirstate.write()
   552     repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
   553     repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
   553     if rev == min(state):
       
   554         # Case (1) initial changeset of a non-detaching rebase.
       
   555         # Let the merge mechanism find the base itself.
       
   556         base = None
       
   557     elif not repo[rev].p2():
       
   558         # Case (2) detaching the node with a single parent, use this parent
       
   559         base = repo[rev].p1().rev()
       
   560     else:
       
   561         # In case of merge, we need to pick the right parent as merge base.
       
   562         #
       
   563         # Imagine we have:
       
   564         # - M: currently rebase revision in this step
       
   565         # - A: one parent of M
       
   566         # - B: second parent of M
       
   567         # - D: destination of this merge step (p1 var)
       
   568         #
       
   569         # If we are rebasing on D, D is the successors of A or B. The right
       
   570         # merge base is the one D succeed to. We pretend it is B for the rest
       
   571         # of this comment
       
   572         #
       
   573         # If we pick B as the base, the merge involves:
       
   574         # - changes from B to M (actual changeset payload)
       
   575         # - changes from B to D (induced by rebase) as D is a rebased
       
   576         #   version of B)
       
   577         # Which exactly represent the rebase operation.
       
   578         #
       
   579         # If we pick the A as the base, the merge involves
       
   580         # - changes from A to M (actual changeset payload)
       
   581         # - changes from A to D (with include changes between unrelated A and B
       
   582         #   plus changes induced by rebase)
       
   583         # Which does not represent anything sensible and creates a lot of
       
   584         # conflicts.
       
   585         for p in repo[rev].parents():
       
   586             if state.get(p.rev()) == p1:
       
   587                 base = p.rev()
       
   588                 break
       
   589         else: # fallback when base not found
       
   590             base = None
       
   591 
       
   592             # Raise because this function is called wrong (see issue 4106)
       
   593             raise AssertionError('no base found to rebase on '
       
   594                                  '(rebasenode called wrong)')
       
   595     if base is not None:
   554     if base is not None:
   596         repo.ui.debug("   detach base %d:%s\n" % (base, repo[base]))
   555         repo.ui.debug("   detach base %d:%s\n" % (base, repo[base]))
   597     # When collapsing in-place, the parent is the common ancestor, we
   556     # When collapsing in-place, the parent is the common ancestor, we
   598     # have to allow merging with it.
   557     # have to allow merging with it.
   599     stats = merge.update(repo, rev, True, True, False, base, collapse,
   558     stats = merge.update(repo, rev, True, True, False, base, collapse,
   658                 raise util.Abort(_('cannot use revision %d as base, result '
   617                 raise util.Abort(_('cannot use revision %d as base, result '
   659                         'would have 3 parents') % rev)
   618                         'would have 3 parents') % rev)
   660             p2 = p2n
   619             p2 = p2n
   661     repo.ui.debug(" future parents are %d and %d\n" %
   620     repo.ui.debug(" future parents are %d and %d\n" %
   662                             (repo[p1].rev(), repo[p2].rev()))
   621                             (repo[p1].rev(), repo[p2].rev()))
   663     return p1, p2
   622 
       
   623     if rev == min(state):
       
   624         # Case (1) initial changeset of a non-detaching rebase.
       
   625         # Let the merge mechanism find the base itself.
       
   626         base = None
       
   627     elif not repo[rev].p2():
       
   628         # Case (2) detaching the node with a single parent, use this parent
       
   629         base = repo[rev].p1().rev()
       
   630     else:
       
   631         # In case of merge, we need to pick the right parent as merge base.
       
   632         #
       
   633         # Imagine we have:
       
   634         # - M: currently rebase revision in this step
       
   635         # - A: one parent of M
       
   636         # - B: second parent of M
       
   637         # - D: destination of this merge step (p1 var)
       
   638         #
       
   639         # If we are rebasing on D, D is the successors of A or B. The right
       
   640         # merge base is the one D succeed to. We pretend it is B for the rest
       
   641         # of this comment
       
   642         #
       
   643         # If we pick B as the base, the merge involves:
       
   644         # - changes from B to M (actual changeset payload)
       
   645         # - changes from B to D (induced by rebase) as D is a rebased
       
   646         #   version of B)
       
   647         # Which exactly represent the rebase operation.
       
   648         #
       
   649         # If we pick the A as the base, the merge involves
       
   650         # - changes from A to M (actual changeset payload)
       
   651         # - changes from A to D (with include changes between unrelated A and B
       
   652         #   plus changes induced by rebase)
       
   653         # Which does not represent anything sensible and creates a lot of
       
   654         # conflicts.
       
   655         for p in repo[rev].parents():
       
   656             if state.get(p.rev()) == p1:
       
   657                 base = p.rev()
       
   658                 break
       
   659         else: # fallback when base not found
       
   660             base = None
       
   661 
       
   662             # Raise because this function is called wrong (see issue 4106)
       
   663             raise AssertionError('no base found to rebase on '
       
   664                                  '(defineparents called wrong)')
       
   665     return p1, p2, base
   664 
   666 
   665 def isagitpatch(repo, patchname):
   667 def isagitpatch(repo, patchname):
   666     'Return true if the given patch is in git format'
   668     'Return true if the given patch is in git format'
   667     mqpatch = os.path.join(repo.mq.path, patchname)
   669     mqpatch = os.path.join(repo.mq.path, patchname)
   668     for line in patch.linereader(file(mqpatch, 'rb')):
   670     for line in patch.linereader(file(mqpatch, 'rb')):