hgext/evolve.py
changeset 469 abeb17a9e313
parent 468 6b1b6d338478
child 470 a2dfe82f27a0
--- 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',