hgext/rebase.py
changeset 26349 92409f8dff5d
parent 26301 3f8c5c284c86
child 26360 b2415e94b2f5
equal deleted inserted replaced
26348:b80b2ee71a08 26349:92409f8dff5d
    24 import os, errno
    24 import os, errno
    25 
    25 
    26 revtodo = -1
    26 revtodo = -1
    27 nullmerge = -2
    27 nullmerge = -2
    28 revignored = -3
    28 revignored = -3
       
    29 revprecursor = -4
    29 
    30 
    30 cmdtable = {}
    31 cmdtable = {}
    31 command = cmdutil.command(cmdtable)
    32 command = cmdutil.command(cmdtable)
    32 # Note for extension authors: ONLY specify testedwith = 'internal' for
    33 # Note for extension authors: ONLY specify testedwith = 'internal' for
    33 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    34 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
   331                 raise util.Abort(
   332                 raise util.Abort(
   332                     _("can't remove original changesets with"
   333                     _("can't remove original changesets with"
   333                       " unrebased descendants"),
   334                       " unrebased descendants"),
   334                     hint=_('use --keep to keep original changesets'))
   335                     hint=_('use --keep to keep original changesets'))
   335 
   336 
   336             result = buildstate(repo, dest, rebaseset, collapsef)
   337             obsoletenotrebased = {}
       
   338             if ui.configbool('experimental', 'rebaseskipobsolete'):
       
   339                 rebasesetrevs = set(rebaseset)
       
   340                 obsoletenotrebased = _computeobsoletenotrebased(repo,
       
   341                                                                 rebasesetrevs,
       
   342                                                                 dest)
       
   343 
       
   344                 # - plain prune (no successor) changesets are rebased
       
   345                 # - split changesets are not rebased if at least one of the
       
   346                 # changeset resulting from the split is an ancestor of dest
       
   347                 rebaseset = rebasesetrevs - set(obsoletenotrebased)
       
   348             result = buildstate(repo, dest, rebaseset, collapsef,
       
   349                                 obsoletenotrebased)
       
   350 
   337             if not result:
   351             if not result:
   338                 # Empty state built, nothing to rebase
   352                 # Empty state built, nothing to rebase
   339                 ui.status(_('nothing to rebase\n'))
   353                 ui.status(_('nothing to rebase\n'))
   340                 return 1
   354                 return 1
   341 
   355 
   437                     ui.debug('next revision set to %s\n' % p1)
   451                     ui.debug('next revision set to %s\n' % p1)
   438             elif state[rev] == nullmerge:
   452             elif state[rev] == nullmerge:
   439                 ui.debug('ignoring null merge rebase of %s\n' % rev)
   453                 ui.debug('ignoring null merge rebase of %s\n' % rev)
   440             elif state[rev] == revignored:
   454             elif state[rev] == revignored:
   441                 ui.status(_('not rebasing ignored %s\n') % desc)
   455                 ui.status(_('not rebasing ignored %s\n') % desc)
       
   456             elif state[rev] == revprecursor:
       
   457                 targetctx = repo[obsoletenotrebased[rev]]
       
   458                 desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx,
       
   459                              targetctx.description().split('\n', 1)[0])
       
   460                 msg = _('note: not rebasing %s, already in destination as %s\n')
       
   461                 ui.status(msg % (desc, desctarget))
   442             else:
   462             else:
   443                 ui.status(_('already rebased %s as %s\n') %
   463                 ui.status(_('already rebased %s as %s\n') %
   444                           (desc, repo[state[rev]]))
   464                           (desc, repo[state[rev]]))
   445 
   465 
   446         ui.progress(_('rebasing'), None)
   466         ui.progress(_('rebasing'), None)
   618     if p1n in targetancestors:
   638     if p1n in targetancestors:
   619         p1 = target
   639         p1 = target
   620     elif p1n in state:
   640     elif p1n in state:
   621         if state[p1n] == nullmerge:
   641         if state[p1n] == nullmerge:
   622             p1 = target
   642             p1 = target
   623         elif state[p1n] == revignored:
   643         elif state[p1n] in (revignored, revprecursor):
   624             p1 = nearestrebased(repo, p1n, state)
   644             p1 = nearestrebased(repo, p1n, state)
   625             if p1 is None:
   645             if p1 is None:
   626                 p1 = target
   646                 p1 = target
   627         else:
   647         else:
   628             p1 = state[p1n]
   648             p1 = state[p1n]
   634         p2n = parents[1].rev()
   654         p2n = parents[1].rev()
   635         # interesting second parent
   655         # interesting second parent
   636         if p2n in state:
   656         if p2n in state:
   637             if p1 == target: # p1n in targetancestors or external
   657             if p1 == target: # p1n in targetancestors or external
   638                 p1 = state[p2n]
   658                 p1 = state[p2n]
   639             elif state[p2n] == revignored:
   659             elif state[p2n] in (revignored, revprecursor):
   640                 p2 = nearestrebased(repo, p2n, state)
   660                 p2 = nearestrebased(repo, p2n, state)
   641                 if p2 is None:
   661                 if p2 is None:
   642                     # no ancestors rebased yet, detach
   662                     # no ancestors rebased yet, detach
   643                     p2 = target
   663                     p2 = target
   644             else:
   664             else:
   822                 # line 6 is a recent addition, so for backwards compatibility
   842                 # line 6 is a recent addition, so for backwards compatibility
   823                 # check that the line doesn't look like the oldrev:newrev lines
   843                 # check that the line doesn't look like the oldrev:newrev lines
   824                 activebookmark = l
   844                 activebookmark = l
   825             else:
   845             else:
   826                 oldrev, newrev = l.split(':')
   846                 oldrev, newrev = l.split(':')
   827                 if newrev in (str(nullmerge), str(revignored)):
   847                 if newrev in (str(nullmerge), str(revignored),
       
   848                               str(revprecursor)):
   828                     state[repo[oldrev].rev()] = int(newrev)
   849                     state[repo[oldrev].rev()] = int(newrev)
   829                 elif newrev == nullid:
   850                 elif newrev == nullid:
   830                     state[repo[oldrev].rev()] = revtodo
   851                     state[repo[oldrev].rev()] = revtodo
   831                     # Legacy compat special case
   852                     # Legacy compat special case
   832                 else:
   853                 else:
   910 
   931 
   911     clearstatus(repo)
   932     clearstatus(repo)
   912     repo.ui.warn(_('rebase aborted\n'))
   933     repo.ui.warn(_('rebase aborted\n'))
   913     return 0
   934     return 0
   914 
   935 
   915 def buildstate(repo, dest, rebaseset, collapse):
   936 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
   916     '''Define which revisions are going to be rebased and where
   937     '''Define which revisions are going to be rebased and where
   917 
   938 
   918     repo: repo
   939     repo: repo
   919     dest: context
   940     dest: context
   920     rebaseset: set of rev
   941     rebaseset: set of rev
   997         # the revision should be ignored but that `defineparents` should search
  1018         # the revision should be ignored but that `defineparents` should search
   998         # a rebase destination that make sense regarding rebased topology.
  1019         # a rebase destination that make sense regarding rebased topology.
   999         rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
  1020         rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
  1000         for ignored in set(rebasedomain) - set(rebaseset):
  1021         for ignored in set(rebasedomain) - set(rebaseset):
  1001             state[ignored] = revignored
  1022             state[ignored] = revignored
       
  1023     for r in obsoletenotrebased:
       
  1024         state[r] = revprecursor
  1002     return repo['.'].rev(), dest.rev(), state
  1025     return repo['.'].rev(), dest.rev(), state
  1003 
  1026 
  1004 def clearrebased(ui, repo, state, skipped, collapsedas=None):
  1027 def clearrebased(ui, repo, state, skipped, collapsedas=None):
  1005     """dispose of rebased revision at the end of the rebase
  1028     """dispose of rebased revision at the end of the rebase
  1006 
  1029 
  1105     """ensure rebased revs stay visible (see issue4505)"""
  1128     """ensure rebased revs stay visible (see issue4505)"""
  1106     blockers = orig(repo)
  1129     blockers = orig(repo)
  1107     blockers.update(getattr(repo, '_rebaseset', ()))
  1130     blockers.update(getattr(repo, '_rebaseset', ()))
  1108     return blockers
  1131     return blockers
  1109 
  1132 
       
  1133 def _computeobsoletenotrebased(repo, rebasesetrevs, dest):
       
  1134     """return a mapping obsolete => successor for all obsolete nodes to be
       
  1135     rebased that have a successors in the destination"""
       
  1136     obsoletenotrebased = {}
       
  1137 
       
  1138     # Build a mapping succesor => obsolete nodes for the obsolete
       
  1139     # nodes to be rebased
       
  1140     allsuccessors = {}
       
  1141     for r in rebasesetrevs:
       
  1142         n = repo[r]
       
  1143         if n.obsolete():
       
  1144             node = repo.changelog.node(r)
       
  1145             for s in obsolete.allsuccessors(repo.obsstore, [node]):
       
  1146                 allsuccessors[repo.changelog.rev(s)] = repo.changelog.rev(node)
       
  1147 
       
  1148     if allsuccessors:
       
  1149         # Look for successors of obsolete nodes to be rebased among
       
  1150         # the ancestors of dest
       
  1151         ancs = repo.changelog.ancestors([repo[dest].rev()],
       
  1152                                         stoprev=min(allsuccessors),
       
  1153                                         inclusive=True)
       
  1154         for s in allsuccessors:
       
  1155             if s in ancs:
       
  1156                 obsoletenotrebased[allsuccessors[s]] = s
       
  1157     return obsoletenotrebased
       
  1158 
  1110 def summaryhook(ui, repo):
  1159 def summaryhook(ui, repo):
  1111     if not os.path.exists(repo.join('rebasestate')):
  1160     if not os.path.exists(repo.join('rebasestate')):
  1112         return
  1161         return
  1113     try:
  1162     try:
  1114         state = restorestatus(repo)[2]
  1163         state = restorestatus(repo)[2]