stabilize: handle latecomer
authorPierre-Yves David <pierre-yves.david@logilab.fr>
Tue, 21 Aug 2012 10:10:44 +0200
changeset 469 abeb17a9e313
parent 468 6b1b6d338478
child 470 a2dfe82f27a0
stabilize: handle latecomer Merge are not handled yet.
hgext/evolve.py
tests/test-evolve.t
tests/test-stabilize-result.t
--- a/hgext/evolve.py	Mon Aug 20 18:59:07 2012 +0200
+++ b/hgext/evolve.py	Tue Aug 21 10:10:44 2012 +0200
@@ -176,6 +176,7 @@
             repo._bookmarks[book] = dest.node()
         if oldbookmarks or destbookmarks:
             bookmarks.write(repo)
+        return nodenew
     except util.Abort:
         # Invalidate the previous setparents
         repo.dirstate.invalidate()
@@ -263,10 +264,9 @@
             return 1
     troubles = tr.troubles()
     if 'unstable' in troubles:
-        return _stabunstable(ui, repo, tr, opts['dry_run'])
+        return _solveunstable(ui, repo, tr, opts['dry_run'])
     elif 'latecomer' in troubles:
-        ui.write_err(_('latecomer not handled yet\n'))
-        return 4
+        return _solvelatecomer(ui, repo, tr, opts['dry_run'])
     elif 'conflicting' in troubles:
         ui.write_err(_('conflicting not handled yet\n'))
         return 4
@@ -287,7 +287,7 @@
     return tr
 
 
-def _stabunstable(ui, repo, orig, dryrun=False):
+def _solveunstable(ui, repo, orig, dryrun=False):
     """Stabilize a unstable changeset"""
     obsolete = extensions.find('obsolete')
     obs = orig.parents()[0]
@@ -334,6 +334,109 @@
         finally:
             lock.release()
 
+def _solvelatecomer(ui, repo, latecomer, dryrun=False):
+    """Stabilize a latecomer changeset"""
+    # For now we deny latecomer merge
+    if len(latecomer.parents()) > 1:
+        raise util.Abort('late comer stabilization is confused by latecomer'
+                         ' %s being a merge' % latecomer)
+    prec = repo.set('last(allprecursors(%d) and public())', latecomer).next()
+    # For now we deny target merge
+    if len(prec.parents()) > 1:
+        raise util.Abort('late comer stabilization is confused by precursors'
+                         ' %s being a merge' % prec)
+
+    displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
+    repo.ui.status(_('recreate:'))
+    if not ui.quiet:
+        displayer.show(latecomer)
+    repo.ui.status(_('atop:'))
+    if not ui.quiet:
+        displayer.show(prec)
+    obsolete = extensions.find('obsolete')
+    if dryrun:
+        todo = 'hg rebase --rev %s --detach %s;\n' % (latecomer, prec.p1())
+        repo.ui.write(todo)
+        repo.ui.write('hg update %s;\n' % prec)
+        repo.ui.write('hg revert --all --rev %s;\n' % latecomer)
+        repo.ui.write('hg commit --msg "latecomer update to %s"')
+        return 0
+    wlock = repo.wlock()
+    try:
+        newid = tmpctx = None
+        tmpctx = latecomer
+        lock = repo.lock()
+        try:
+            bmupdate = _bookmarksupdater(repo, latecomer.node())
+            # Basic check for common parent. Far too complicated and fragile
+            tr = repo.transaction('latecomer-stabilize')
+            try:
+                if not list(repo.set('parents(%d) and parents(%d)', latecomer, prec)):
+                    # Need to rebase the changeset at the right place
+                    repo.ui.status(_('rebasing to destination parent: %s\n') % prec.p1())
+                    try:
+                        tmpid = relocate(repo, latecomer, prec.p1())
+                        if tmpid is not None:
+                            tmpctx = repo[tmpid]
+                            obsolete.createmarkers(repo, [(latecomer, (tmpctx,))])
+                    except MergeFailure:
+                        repo.opener.write('graftstate', latecomer.hex() + '\n')
+                        repo.ui.write_err(_('stabilize failed!\n'))
+                        repo.ui.write_err(_('fix conflict and run "hg stabilize --continue"\n'))
+                        raise
+                # Create the new commit context
+                repo.ui.status(_('computing new diff\n'))
+                files = set()
+                copied = copies.pathcopies(prec, latecomer)
+                precmanifest = prec.manifest()
+                for key, val in latecomer.manifest().iteritems():
+                    if precmanifest.pop(key, None) != val:
+                        files.add(key)
+                files.update(precmanifest)  # add missing files
+                # commit it
+                if files: # something to commit!
+                    def filectxfn(repo, ctx, path):
+                        if path in latecomer:
+                            fctx = latecomer[path]
+                            flags = fctx.flags()
+                            mctx = context.memfilectx(fctx.path(), fctx.data(),
+                                                      islink='l' in flags,
+                                                      isexec='x' in flags,
+                                                      copied=copied.get(path))
+                            return mctx
+                        raise IOError()
+                    text = 'latecomer update to %s:\n\n' % prec
+                    text += latecomer.description()
+
+                    new = context.memctx(repo,
+                                         parents=[prec.node(), node.nullid],
+                                         text=text,
+                                         files=files,
+                                         filectxfn=filectxfn,
+                                         user=latecomer.user(),
+                                         date=latecomer.date(),
+                                         extra=latecomer.extra())
+
+                    newid = repo.commitctx(new)
+                if newid is None:
+                    obsolete.createmarkers(repo, [(tmpctx, ())])
+                    newid = prec.node()
+                else:
+                    phases.retractboundary(repo, latecomer.phase(), [newid])
+                    obsolete.createmarkers(repo, [(tmpctx, (repo[newid],))])
+                bmupdate(newid)
+                tr.close()
+                repo.ui.status(_('commited as %s\n') % node.short(newid))
+            finally:
+                tr.release()
+        finally:
+            lock.release()
+        # reroute the working copy parent to the new changeset
+        repo.dirstate.setparents(newid, node.nullid)
+    finally:
+        wlock.release()
+
+
 shorttemplate = '[{rev}] {desc|firstline}\n'
 
 @command('^gdown',
--- a/tests/test-evolve.t	Mon Aug 20 18:59:07 2012 +0200
+++ b/tests/test-evolve.t	Tue Aug 21 10:10:44 2012 +0200
@@ -298,18 +298,28 @@
   |
   o  0	: base - test
   
-  $ hg stabilize --any
-  latecomer not handled yet
-  [4]
+  $ hg stabilize --any --traceback
+  recreate:[8] another feature that rox
+  atop:[7] another feature
+  computing new diff
+  commited as 8d77fa12ab0c
   $ hg glog
-  @  8	feature-B: another feature that rox - test
+  @  9	feature-B: latecomer update to 5f4744038ed5: - test
   |
-  | o  7	: another feature - test
-  |/
+  o  7	: another feature - test
+  |
   o  6	feature-A: a nifty feature - test
   |
   o  0	: base - test
   
+  $ hg diff -r 9 -r 8
+  $ hg diff -r 9^ -r 9
+  diff --git a/main-file-1 b/main-file-1
+  --- a/main-file-1
+  +++ b/main-file-1
+  @@ -3,1 +3,1 @@
+  -Zwei
+  +deux
 
   $ cd ..
 
--- a/tests/test-stabilize-result.t	Mon Aug 20 18:59:07 2012 +0200
+++ b/tests/test-stabilize-result.t	Tue Aug 21 10:10:44 2012 +0200
@@ -96,3 +96,77 @@
   $ hg resolve -m a
   $ hg stabilize --continue
   grafting revision 5
+
+Stabilize of late comer with different parent
+==================================================
+(the with same parent is handled in test-evolve.t)
+
+  $ glog
+  @  8:e3183e9c0961@default(draft) bk:[] newer a
+  |
+  o  7:e8cc1b534401@default(draft) bk:[changea] changea
+  |
+  o  0:07f494440405@default(draft) bk:[] adda
+  
+Add another commit
+
+  $ hg gdown
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  [7] changea
+  $ echo 'c' > c
+  $ hg add c
+  $ hg commit -m 'add c'
+  created new head
+
+Get a successors of 8 on it
+
+  $ hg graft -O 8
+  grafting revision 8
+
+Add real change to the successors
+
+  $ echo 'babar' >> a
+  $ hg amend
+
+Make precursors public
+
+  $ hg phase --public 8
+  1 new latecomers changesets
+  $ glog
+  @  12:15c83af6f3a3@default(draft) bk:[] newer a
+  |
+  o  9:355c5cda4de1@default(draft) bk:[] add c
+  |
+  | o  8:e3183e9c0961@default(public) bk:[] newer a
+  |/
+  o  7:e8cc1b534401@default(public) bk:[changea] changea
+  |
+  o  0:07f494440405@default(public) bk:[] adda
+  
+
+Stabilize !
+
+  $ hg stabilize --any --dry-run
+  recreate:[12] newer a
+  atop:[8] newer a
+  hg rebase --rev 15c83af6f3a3 --detach e8cc1b534401;
+  hg update e3183e9c0961;
+  hg revert --all --rev 15c83af6f3a3;
+  hg commit --msg "latecomer update to %s" (no-eol)
+  $ hg stabilize --any
+  recreate:[12] newer a
+  atop:[8] newer a
+  rebasing to destination parent: e8cc1b534401
+  computing new diff
+  commited as 1d94fef80e85
+  $ glog
+  @  14:1d94fef80e85@default(draft) bk:[] latecomer update to e3183e9c0961:
+  |
+  | o  9:355c5cda4de1@default(draft) bk:[] add c
+  | |
+  o |  8:e3183e9c0961@default(public) bk:[] newer a
+  |/
+  o  7:e8cc1b534401@default(public) bk:[changea] changea
+  |
+  o  0:07f494440405@default(public) bk:[] adda
+