hgext/rebase.py
changeset 26349 92409f8dff5d
parent 26301 3f8c5c284c86
child 26360 b2415e94b2f5
--- a/hgext/rebase.py	Thu Sep 24 00:34:15 2015 -0700
+++ b/hgext/rebase.py	Mon Sep 14 17:31:48 2015 -0700
@@ -26,6 +26,7 @@
 revtodo = -1
 nullmerge = -2
 revignored = -3
+revprecursor = -4
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
@@ -333,7 +334,20 @@
                       " unrebased descendants"),
                     hint=_('use --keep to keep original changesets'))
 
-            result = buildstate(repo, dest, rebaseset, collapsef)
+            obsoletenotrebased = {}
+            if ui.configbool('experimental', 'rebaseskipobsolete'):
+                rebasesetrevs = set(rebaseset)
+                obsoletenotrebased = _computeobsoletenotrebased(repo,
+                                                                rebasesetrevs,
+                                                                dest)
+
+                # - plain prune (no successor) changesets are rebased
+                # - split changesets are not rebased if at least one of the
+                # changeset resulting from the split is an ancestor of dest
+                rebaseset = rebasesetrevs - set(obsoletenotrebased)
+            result = buildstate(repo, dest, rebaseset, collapsef,
+                                obsoletenotrebased)
+
             if not result:
                 # Empty state built, nothing to rebase
                 ui.status(_('nothing to rebase\n'))
@@ -439,6 +453,12 @@
                 ui.debug('ignoring null merge rebase of %s\n' % rev)
             elif state[rev] == revignored:
                 ui.status(_('not rebasing ignored %s\n') % desc)
+            elif state[rev] == revprecursor:
+                targetctx = repo[obsoletenotrebased[rev]]
+                desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx,
+                             targetctx.description().split('\n', 1)[0])
+                msg = _('note: not rebasing %s, already in destination as %s\n')
+                ui.status(msg % (desc, desctarget))
             else:
                 ui.status(_('already rebased %s as %s\n') %
                           (desc, repo[state[rev]]))
@@ -620,7 +640,7 @@
     elif p1n in state:
         if state[p1n] == nullmerge:
             p1 = target
-        elif state[p1n] == revignored:
+        elif state[p1n] in (revignored, revprecursor):
             p1 = nearestrebased(repo, p1n, state)
             if p1 is None:
                 p1 = target
@@ -636,7 +656,7 @@
         if p2n in state:
             if p1 == target: # p1n in targetancestors or external
                 p1 = state[p2n]
-            elif state[p2n] == revignored:
+            elif state[p2n] in (revignored, revprecursor):
                 p2 = nearestrebased(repo, p2n, state)
                 if p2 is None:
                     # no ancestors rebased yet, detach
@@ -824,7 +844,8 @@
                 activebookmark = l
             else:
                 oldrev, newrev = l.split(':')
-                if newrev in (str(nullmerge), str(revignored)):
+                if newrev in (str(nullmerge), str(revignored),
+                              str(revprecursor)):
                     state[repo[oldrev].rev()] = int(newrev)
                 elif newrev == nullid:
                     state[repo[oldrev].rev()] = revtodo
@@ -912,7 +933,7 @@
     repo.ui.warn(_('rebase aborted\n'))
     return 0
 
-def buildstate(repo, dest, rebaseset, collapse):
+def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
     '''Define which revisions are going to be rebased and where
 
     repo: repo
@@ -999,6 +1020,8 @@
         rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
         for ignored in set(rebasedomain) - set(rebaseset):
             state[ignored] = revignored
+    for r in obsoletenotrebased:
+        state[r] = revprecursor
     return repo['.'].rev(), dest.rev(), state
 
 def clearrebased(ui, repo, state, skipped, collapsedas=None):
@@ -1107,6 +1130,32 @@
     blockers.update(getattr(repo, '_rebaseset', ()))
     return blockers
 
+def _computeobsoletenotrebased(repo, rebasesetrevs, dest):
+    """return a mapping obsolete => successor for all obsolete nodes to be
+    rebased that have a successors in the destination"""
+    obsoletenotrebased = {}
+
+    # Build a mapping succesor => obsolete nodes for the obsolete
+    # nodes to be rebased
+    allsuccessors = {}
+    for r in rebasesetrevs:
+        n = repo[r]
+        if n.obsolete():
+            node = repo.changelog.node(r)
+            for s in obsolete.allsuccessors(repo.obsstore, [node]):
+                allsuccessors[repo.changelog.rev(s)] = repo.changelog.rev(node)
+
+    if allsuccessors:
+        # Look for successors of obsolete nodes to be rebased among
+        # the ancestors of dest
+        ancs = repo.changelog.ancestors([repo[dest].rev()],
+                                        stoprev=min(allsuccessors),
+                                        inclusive=True)
+        for s in allsuccessors:
+            if s in ancs:
+                obsoletenotrebased[allsuccessors[s]] = s
+    return obsoletenotrebased
+
 def summaryhook(ui, repo):
     if not os.path.exists(repo.join('rebasestate')):
         return