changeset 282:05ab164c6593

obsolete: handle rebase --collapse Recording obsolete in concludenode() did not work for --collapse because only the first revision was passed to the call. The new strategy is to track the rebase state in defineparents() and concludenode() and to create markers only after a successful non-abort call. In theory, this should also fix --continue/--abort cases. The change in test-stabilize-order.t comes from concludenode() no longer creating obsolete marker. stabilize command was actually duplicating markers, once in concludenode(), once explicitely.
author Patrick Mezard <patrick@mezard.eu>
date Tue, 19 Jun 2012 18:05:23 +0200
parents 258169d3428b
children 8d1a8eeb5a84
files hgext/obsolete.py tests/test-obsolete-rebase.t tests/test-stabilize-order.t
diffstat 3 files changed, 90 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/obsolete.py	Tue Jun 19 17:05:39 2012 +0200
+++ b/hgext/obsolete.py	Tue Jun 19 18:05:23 2012 +0200
@@ -225,17 +225,23 @@
     rebaseset = repo.revs('%ld - extinct()', rebaseset)
     return orig(repo, dest, rebaseset, *ags, **kws)
 
+def defineparents(orig, repo, rev, target, state, *args, **kwargs):
+    rebasestate = getattr(repo, '_rebasestate', None)
+    if rebasestate is not None:
+        repo._rebasestate = dict(state)
+        repo._rebasetarget = target
+    return orig(repo, rev, target, state, *args, **kwargs)
 
-def concludenode(orig, repo, rev, *args, **kwargs):
+def concludenode(orig, repo, rev, p1, *args, **kwargs):
     """wrapper for rebase 's concludenode that set obsolete relation"""
-    newrev = orig(repo, rev, *args, **kwargs)
-    oldnode = repo[rev].node()
-    if newrev is not None:
-        newnode = repo[newrev].node()
-    else:
-        # Revision was emptied and removed, there is no successor.
-        newnode = nullid
-    repo.addobsolete(newnode, oldnode)
+    newrev = orig(repo, rev, p1, *args, **kwargs)
+    rebasestate = getattr(repo, '_rebasestate', None)
+    if rebasestate is not None:
+        if newrev is not None:
+            nrev = repo[newrev].rev()
+        else:
+            nrev = p1
+        repo._rebasestate[rev] = nrev
     return newrev
 
 def cmdrebase(orig, ui, repo, *args, **kwargs):
@@ -244,8 +250,45 @@
                            'extension'), hint=_("see 'hg help obsolete'"))
     kwargs = dict(kwargs)
     kwargs['keep'] = True
-    return orig(ui, repo, *args, **kwargs)
 
+    # We want to mark rebased revision as obsolete and set their
+    # replacements if any. Doing it in concludenode() prevents
+    # aborting the rebase, and is not called with all relevant
+    # revisions in --collapse case. Instead, we try to track the
+    # rebase state structure by sampling/updating it in
+    # defineparents() and concludenode(). The obsolete markers are
+    # added from this state after a successful call.
+    repo._rebasestate = {}
+    repo._rebasetarget = None
+    maxrev = len(repo) - 1
+    try:
+        res = orig(ui, repo, *args, **kwargs)
+        if not res and not kwargs.get('abort') and repo._rebasetarget:
+            # We have to tell rewritten revisions from removed
+            # ones. When collapsing, removed revisions are considered
+            # to be collapsed onto the final one, while in the normal
+            # case their are marked obsolete without successor.
+            emptynode = nullid
+            if kwargs.get('collapse'):
+                emptynode = repo[max(repo._rebasestate.values())].node()
+            # Rebased revisions are assumed to be descendants of
+            # targetrev. If a source revision is mapped to targetrev
+            # or to another rebased revision, it must have been
+            # removed.
+            targetrev = repo[repo._rebasetarget].rev()
+            newrevs = set([targetrev])
+            for rev, newrev in sorted(repo._rebasestate.items()):
+                oldnode = repo[rev].node()
+                if newrev not in newrevs and newrev >= 0:
+                    newnode = repo[newrev].node()
+                    newrevs.add(newrev)
+                else:
+                    newnode = emptynode
+                repo.addobsolete(newnode, oldnode)
+        return res
+    finally:
+        delattr(repo, '_rebasestate')
+        delattr(repo, '_rebasetarget')
 
 
 def extsetup(ui):
@@ -262,6 +305,7 @@
         rebase = extensions.find('rebase')
         if rebase:
             extensions.wrapfunction(rebase, 'buildstate', buildstate)
+            extensions.wrapfunction(rebase, 'defineparents', defineparents)
             extensions.wrapfunction(rebase, 'concludenode', concludenode)
             extensions.wrapcommand(rebase.cmdtable, "rebase", cmdrebase)
     except KeyError:
--- a/tests/test-obsolete-rebase.t	Tue Jun 19 17:05:39 2012 +0200
+++ b/tests/test-obsolete-rebase.t	Tue Jun 19 18:05:23 2012 +0200
@@ -69,3 +69,38 @@
   $ hg debugsuccessors
   102a90ea7b4a 03f017c74faa
   4e322f7ce8e3 000000000000
+
+Test rebase --collapse
+
+  $ hg up 0
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo c > c
+  $ hg ci -Am addc
+  adding c
+  created new head
+  $ echo c >> c
+  $ hg ci -m changec
+  $ hg rebase --collapse -d 1
+  merging c
+  $ glog --hidden
+  @  7:a7773ffa7edc@default(draft) Collapsed revision
+  |
+  | o  6:03f31481307a@default(secret) changec
+  | |
+  | o  5:076e9b2ffbe1@default(secret) addc
+  | |
+  | | o  4:4e322f7ce8e3@foo(secret) changea
+  | |/
+  +---o  3:03f017c74faa@default(draft) addb
+  | |
+  | | o  2:102a90ea7b4a@default(secret) addb
+  | |/
+  o |  1:540395c44225@default(draft) changea
+  |/
+  o  0:07f494440405@default(draft) adda
+  
+  $ hg debugsuccessors
+  03f31481307a a7773ffa7edc
+  076e9b2ffbe1 a7773ffa7edc
+  102a90ea7b4a 03f017c74faa
+  4e322f7ce8e3 000000000000
--- a/tests/test-stabilize-order.t	Tue Jun 19 17:05:39 2012 +0200
+++ b/tests/test-stabilize-order.t	Tue Jun 19 18:05:23 2012 +0200
@@ -102,11 +102,10 @@
   $ diff -u successors.old successors.new
   --- successors.old* (glob)
   +++ successors.new* (glob)
-  @@ -1,5 +1,7 @@
+  @@ -1,5 +1,6 @@
    3a4a591493f8 f5ff10856e5a
    3ca0ded0dc50 ab8cbb6d87ff
   +7a7552255fb5 5e819fbb0d27
-  +7a7552255fb5 5e819fbb0d27
    93418d2c0979 3a4a591493f8
    93418d2c0979 f5ff10856e5a
    ab8cbb6d87ff 6bf44048e43f