Commits

Pierre-Yves David  committed d3f2077 Merge

Merge with Patrick improvement

  • Participants
  • Parent commits a68b763, 691cb55
  • Tags 0.2.0

Comments (0)

Files changed (7)

File hgext/evolve.py

             rebase.rebasenode(repo, orig.node(), dest.node(),
                               {node.nullrev: node.nullrev})
         nodenew = rebase.concludenode(repo, orig.node(), dest.node(), node.nullid)
-        phases.retractboundary(repo, destphase, [nodenew])
-        repo.addobsolete(nodenew, nodesrc)
         oldbookmarks = repo.nodebookmarks(nodesrc)
-        for book in oldbookmarks:
-            repo._bookmarks[book] = nodenew
+        if nodenew is not None:
+            phases.retractboundary(repo, destphase, [nodenew])
+            repo.addobsolete(nodenew, nodesrc)
+            for book in oldbookmarks:
+                repo._bookmarks[book] = nodenew
+        else:
+            repo.addobsolete(node.nullid, nodesrc)
+            # Behave like rebase, move bookmarks to dest
+            for book in oldbookmarks:
+                repo._bookmarks[book] = dest.node()
         for book in destbookmarks: # restore bookmark that rebase move
             repo._bookmarks[book] = dest.node()
         if oldbookmarks or destbookmarks:
 cmdtable = {}
 command = cmdutil.command(cmdtable)
 
-@command('^stabilize',
+@command('^stabilize|evolve',
     [
      ('n', 'dry-run', False, 'Do nothing but printing what should be done'),
      ('A', 'any', False, 'Stabilize unstable change on any topological branch'),
         return 1
 
 
-@command('^kill',
+@command('^kill|obsolete',
     [
     ('n', 'new', [], _("New changeset that justify this one to be killed"))
     ],

File hgext/obsolete.py

 New commands
 ------------
 
-A ``debugobsolete`` command was added. It adds an obsolete relation between two
-nodes.
+Note that rebased changesets are not marked obsolete rather than being stripped
+In this experimental extensions, this is done forcing the --keep option. Trying
+to use the --keep option of rebase with this extensionn this experimental
+extension will cause such a call to abort. Until better releasen please use
+graft command to rebase and copy changesets.
 
 Context object
 --------------
 from mercurial.node import hex, bin, short, nullid
 from mercurial.lock import release
 from mercurial import localrepo
+from mercurial import cmdutil
 
 try:
     from mercurial.localrepo import storecache
     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()
-    newnode = repo[newrev].node()
-    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, repo, ui, *args, **kwargs):
-    oldkeep = kwargs.pop('keep', False)
-    if oldkeep:
-        ui.warn('WARNING --keep option ignored by experimental obsolete extension')
+def cmdrebase(orig, ui, repo, *args, **kwargs):
+    if kwargs.get('keep', False):
+        raise util.Abort(_('rebase --keep option is unsupported with obsolete '
+                           'extension'), hint=_("see 'hg help obsolete'"))
+    kwargs = dict(kwargs)
     kwargs['keep'] = True
-    return orig(repo, ui, *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()):
+                if newrev == -2:  # nullmerge
+                    continue
+                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):
         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:
 ### New commands
 #############################
 
+cmdtable = {}
+command = cmdutil.command(cmdtable)
 
+@command('debugobsolete', [], _('SUBJECT OBJECT'))
 def cmddebugobsolete(ui, repo, subject, object):
-    """Add an obsolete relation between a too node
+    """add an obsolete relation between two nodes
 
-    The subject is expected to be a newer version of the object"""
+    The subject is expected to be a newer version of the object.
+    """
     lock = repo.lock()
     try:
         sub = repo[subject]
         lock.release()
     return 0
 
+@command('debugconvertobsolete', [], '')
 def cmddebugconvertobsolete(ui, repo):
+    """import markers from an .hg/obsolete-relations file"""
     cnt = 0
     l = repo.lock()
     try:
         l.release()
     ui.status('%i obsolete marker converted\n' % cnt)
 
+@command('debugsuccessors', [], '')
+def cmddebugsuccessors(ui, repo):
+    """dump obsolete changesets and their successors
 
-cmdtable = {'debugobsolete': (cmddebugobsolete, [], '<subject> <object>'),
-            'debugconvertobsolete': (cmddebugconvertobsolete, [], ''),
-           }
+    Each line matches an existing marker, the first identifier is the
+    obsolete changeset identifier, followed by it successors.
+    """
+    lock = repo.lock()
+    try:
+        allsuccessors = repo.obsoletestore.objects
+        for old in sorted(allsuccessors):
+            successors = [sorted(m['subjects']) for m in allsuccessors[old]]
+            for i, group in enumerate(sorted(successors)):
+                ui.write('%s' % short(old))
+                for new in group:
+                    ui.write(' %s' % short(new))
+                ui.write('\n')
+    finally:
+        lock.release()
 
 ### Altering existing command
 #############################

File tests/test-amend.t

   marked working directory as branch foo
   (branches are permanent and global, did you want a bookmark?)
   $ hg amend
+  $ hg debugsuccessors
+  07f494440405 a34b93d251e4
+  07f494440405 bd19cbe78fbf
+  bd19cbe78fbf a34b93d251e4
   $ hg branch
   foo
   $ hg branches
   $ hg amend --change 2
   abort: no updates found
   [255]
+  $ hg debugsuccessors
+  07f494440405 a34b93d251e4
+  07f494440405 bd19cbe78fbf
+  bd19cbe78fbf a34b93d251e4
   $ hg phase 2
   2: draft
   $ glog
   $ hg amend --change 2
   abort: no updates found
   [255]
+  $ hg debugsuccessors
+  07f494440405 a34b93d251e4
+  07f494440405 bd19cbe78fbf
+  7384bbcba36f 000000000000
+  bd19cbe78fbf a34b93d251e4
   $ glog
   @  6@foo(secret) amends a34b93d251e49c93d5685ebacad785c73a7e8605
   |

File tests/test-obsolete-rebase.t

+  $ cat >> $HGRCPATH <<EOF
+  > [defaults]
+  > amend=-d "0 0"
+  > [extensions]
+  > hgext.rebase=
+  > hgext.graphlog=
+  > EOF
+  $ echo "obsolete=$(echo $(dirname $TESTDIR))/hgext/obsolete.py" >> $HGRCPATH
+
+  $ glog() {
+  >   hg glog --template '{rev}:{node|short}@{branch}({phase}) {desc|firstline}\n'\
+  >     "$@"
+  > }
+
+  $ hg init repo
+  $ cd repo
+  $ echo a > a
+  $ hg ci -Am adda
+  adding a
+  $ echo a >> a
+  $ hg ci -m changea
+
+Test regular rebase
+
+  $ hg up 0
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo b > b
+  $ hg ci -Am addb
+  adding b
+  created new head
+  $ echo e > e
+  $ hg ci -Am adde e
+  $ hg rebase -d 1 -r . --detach --keep  
+  abort: rebase --keep option is unsupported with obsolete extension
+  (see 'hg help obsolete')
+  [255]
+  $ hg rebase -d 1 -r . --detach
+  $ glog --hidden
+  @  4:9c5494949763@default(draft) adde
+  |
+  | o  3:98e4a024635e@default(secret) adde
+  | |
+  | o  2:102a90ea7b4a@default(draft) addb
+  | |
+  o |  1:540395c44225@default(draft) changea
+  |/
+  o  0:07f494440405@default(draft) adda
+  
+  $ hg debugsuccessors
+  98e4a024635e 9c5494949763
+
+Test rebase with deleted empty revision
+
+  $ hg up 0
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg branch foo
+  marked working directory as branch foo
+  (branches are permanent and global, did you want a bookmark?)
+  $ echo a >> a
+  $ hg ci -m changea
+  $ hg rebase -d 1
+  $ glog --hidden
+  o  5:4e322f7ce8e3@foo(secret) changea
+  |
+  | o  4:9c5494949763@default(draft) adde
+  | |
+  | | o  3:98e4a024635e@default(secret) adde
+  | | |
+  +---o  2:102a90ea7b4a@default(draft) addb
+  | |
+  | @  1:540395c44225@default(draft) changea
+  |/
+  o  0:07f494440405@default(draft) adda
+  
+  $ hg debugsuccessors
+  4e322f7ce8e3 000000000000
+  98e4a024635e 9c5494949763
+
+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
+  @  8:a7773ffa7edc@default(draft) Collapsed revision
+  |
+  | o  7:03f31481307a@default(secret) changec
+  | |
+  | o  6:076e9b2ffbe1@default(secret) addc
+  | |
+  | | o  5:4e322f7ce8e3@foo(secret) changea
+  | |/
+  +---o  4:9c5494949763@default(draft) adde
+  | |
+  | | o  3:98e4a024635e@default(secret) adde
+  | | |
+  | | o  2:102a90ea7b4a@default(draft) addb
+  | |/
+  o |  1:540395c44225@default(draft) changea
+  |/
+  o  0:07f494440405@default(draft) adda
+  
+  $ hg debugsuccessors
+  03f31481307a a7773ffa7edc
+  076e9b2ffbe1 a7773ffa7edc
+  4e322f7ce8e3 000000000000
+  98e4a024635e 9c5494949763
+
+Test rebase --abort
+
+  $ hg debugsuccessors > ../successors.old
+  $ hg up 0
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo d > d
+  $ hg ci -Am addd d
+  created new head
+  $ echo b >> a
+  $ hg ci -m appendab
+  $ hg rebase -d 1
+  merging a
+  warning: conflicts during merge.
+  merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+  abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
+  [255]
+  $ hg rebase --abort
+  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/03f165c84ea8-backup.hg
+  rebase aborted
+  $ hg debugsuccessors > ../successors.new
+  $ diff -u ../successors.old ../successors.new
+
+Test rebase --continue
+
+  $ hg rebase -d 1
+  merging a
+  warning: conflicts during merge.
+  merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+  abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
+  [255]
+  $ hg resolve --tool internal:other a
+  $ hg rebase --continue
+  $ glog --hidden
+  @  12:1951ead97108@default(draft) appendab
+  |
+  o  11:03f165c84ea8@default(draft) addd
+  |
+  | o  10:4b9d80f48523@default(secret) appendab
+  | |
+  | o  9:a31943eabc43@default(secret) addd
+  | |
+  +---o  8:a7773ffa7edc@default(draft) Collapsed revision
+  | |
+  | | o  7:03f31481307a@default(secret) changec
+  | | |
+  | | o  6:076e9b2ffbe1@default(secret) addc
+  | |/
+  | | o  5:4e322f7ce8e3@foo(secret) changea
+  | |/
+  +---o  4:9c5494949763@default(draft) adde
+  | |
+  | | o  3:98e4a024635e@default(secret) adde
+  | | |
+  | | o  2:102a90ea7b4a@default(draft) addb
+  | |/
+  o |  1:540395c44225@default(draft) changea
+  |/
+  o  0:07f494440405@default(draft) adda
+  
+  $ hg debugsuccessors > ../successors.new
+  $ diff -u ../successors.old ../successors.new
+  --- ../successors.old* (glob)
+  +++ ../successors.new* (glob)
+  @@ -1,4 +1,6 @@
+   03f31481307a a7773ffa7edc
+   076e9b2ffbe1 a7773ffa7edc
+  +4b9d80f48523 1951ead97108
+   4e322f7ce8e3 000000000000
+   98e4a024635e 9c5494949763
+  +a31943eabc43 03f165c84ea8
+  [1]

File tests/test-obsolete.t

   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
+  $ cd ..

File tests/test-stabilize-order.t

 
   $ hg up 7
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg debugsuccessors > successors.old
   $ hg stabilize -v
   move:[3] addc
   atop:[8] addb
   resolving manifests
   getting c
   c
+  $ hg debugsuccessors > successors.new
+  $ diff -u successors.old successors.new
+  --- successors.old* (glob)
+  +++ successors.new* (glob)
+  @@ -1,5 +1,6 @@
+   3a4a591493f8 f5ff10856e5a
+   3ca0ded0dc50 ab8cbb6d87ff
+  +7a7552255fb5 5e819fbb0d27
+   93418d2c0979 3a4a591493f8
+   93418d2c0979 f5ff10856e5a
+   ab8cbb6d87ff 6bf44048e43f
+  [1]
   $ glog
   @  9:5e819fbb0d27@default(draft) addc
   |

File tests/test-stabilize-result.t

+  $ cat >> $HGRCPATH <<EOF
+  > [defaults]
+  > amend=-d "0 0"
+  > [extensions]
+  > hgext.rebase=
+  > hgext.graphlog=
+  > EOF
+  $ echo "obsolete=$(echo $(dirname $TESTDIR))/hgext/obsolete.py" >> $HGRCPATH
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+
+  $ glog() {
+  >   hg glog --template \
+  >     '{rev}:{node|short}@{branch}({phase}) bk:[{bookmarks}] {desc|firstline}\n' "$@"
+  > }
+
+Test stabilize removing the changeset being stabilized
+
+  $ hg init empty
+  $ cd empty
+  $ echo a > a
+  $ hg ci -Am adda a
+  $ echo b > b
+  $ hg ci -Am addb b
+  $ echo a >> a
+  $ hg ci -m changea
+  $ hg bookmark changea
+  $ hg up 1
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo a >> a
+  $ hg amend -m changea
+  1 new unstables changesets
+  $ hg stabilize -v
+  move:[2] changea
+  atop:[4] changea
+  hg rebase -Dr cce2c55b8965 -d 1447e1c4828d
+  resolving manifests
+  $ glog --hidden
+  @  4:1447e1c4828d@default(draft) bk:[changea] changea
+  |
+  | o  3:41ad4fe8c795@default(secret) bk:[] amends 102a90ea7b4a3361e4082ed620918c261189a36a
+  | |
+  | | o  2:cce2c55b8965@default(secret) bk:[] changea
+  | |/
+  | o  1:102a90ea7b4a@default(secret) bk:[] addb
+  |/
+  o  0:07f494440405@default(draft) bk:[] adda
+  
+  $ hg debugsuccessors
+  102a90ea7b4a 1447e1c4828d
+  102a90ea7b4a 41ad4fe8c795
+  41ad4fe8c795 1447e1c4828d
+  cce2c55b8965 000000000000