diff hgext/evolve.py @ 485:41bf6c27a122

evolve: stabilize now handle conflicting changeset Some a lot of rough edge but it just damn work.
author Pierre-Yves David <pierre-yves.david@logilab.fr>
date Thu, 23 Aug 2012 17:40:28 +0200
parents 20e2f2dd71f1
children 8096833e9226
line wrap: on
line diff
--- a/hgext/evolve.py	Thu Aug 23 14:15:36 2012 +0200
+++ b/hgext/evolve.py	Thu Aug 23 17:40:28 2012 +0200
@@ -22,6 +22,7 @@
 from mercurial import context
 from mercurial import copies
 from mercurial import util
+from mercurial import merge
 from mercurial.i18n import _
 from mercurial.commands import walkopts, commitopts, commitopts2
 from mercurial import hg
@@ -264,14 +265,14 @@
         else:
             ui.write_err(_('no troubled changeset\n'))
             return 1
+    cmdutil.bailifchanged(repo)
     troubles = tr.troubles()
     if 'unstable' in troubles:
         return _solveunstable(ui, repo, tr, opts['dry_run'])
     elif 'latecomer' in troubles:
         return _solvelatecomer(ui, repo, tr, opts['dry_run'])
     elif 'conflicting' in troubles:
-        ui.write_err(_('conflicting not handled yet\n'))
-        return 4
+        return _solveconflicting(ui, repo, tr, opts['dry_run'])
     else:
         assert False  # WHAT? unknown troubles
 
@@ -444,6 +445,99 @@
     finally:
         wlock.release()
 
+def _solveconflicting(ui, repo, conflicting, dryrun=False):
+    obsolete = extensions.find('obsolete')
+    base, others = conflictingdata(conflicting)
+    if len(others) > 1:
+        raise util.Abort("We do not handle split yet")
+    other = others[0]
+    if conflicting.phase() <= phases.public:
+        raise util.Abort("We can't resolve this conflict from the public side")
+    if len(other.parents()) > 1:
+        raise util.Abort("conflicting changeset can't be a merge (yet)")
+    if other.p1() not in conflicting.parents():
+        raise util.Abort("parent are not common (not handled yet)")
+
+    displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
+    ui.status(_('merge:'))
+    if not ui.quiet:
+        displayer.show(conflicting)
+    ui.status(_('with: '))
+    if not ui.quiet:
+        displayer.show(other)
+    ui.status(_('base: '))
+    if not ui.quiet:
+        displayer.show(base)
+    if dryrun:
+        ui.write('hg update -c %s &&\n' % conflicting)
+        ui.write('hg merge %s && \n' % other)
+        ui.write('hg commit -m "auto merge resolving conflict between %s and %s"&&\n'
+                  % (conflicting, other))
+        ui.write('hg up -C %s &&\n' % base)
+        ui.write('hg revert --all --rev tip &&\n')
+        ui.write('hg commit -m "`hg log -r %s --template={desc}`";\n' % conflicting)
+        return
+    #oldphase = max(conflicting.phase(), other.phase())
+    wlock = repo.wlock()
+    try:
+        lock = repo.lock()
+        try:
+            if conflicting not in repo[None].parents():
+                repo.ui.status(_('updating to "local" conflict\n'))
+                hg.update(repo, conflicting.rev())
+            repo.ui.note(_('merging conflicting changeset\n'))
+            stats = merge.update(repo,
+                                 other.node(),
+                                 branchmerge=True,
+                                 force=False,
+                                 partial=None,
+                                 ancestor=base.node(),
+                                 mergeancestor=True)
+            hg._showstats(repo, stats)
+            if stats[3]:
+                repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
+                                 "or 'hg update -C .' to abandon\n"))
+            #repo.dirstate.write()
+            if stats[3] > 0:
+                raise util.Abort('GASP! Merge Conflict! You are on you own chap!')
+            tr = repo.transaction('stabilize-conflicting')
+            try:
+                repo.dirstate.setparents(conflicting.node(), node.nullid)
+                oldlen = len(repo)
+                amend(ui, repo)
+                if oldlen == len(repo):
+                    new = conflicting
+                    # no changes
+                else:
+                    new = repo['.']
+                obsolete.createmarkers(repo, [(other, (new,))])
+                phases.retractboundary(repo, other.phase(), [new.node()])
+                tr.close()
+            finally:
+                tr.release()
+        finally:
+            lock.release()
+    finally:
+        wlock.release()
+
+
+def conflictingdata(ctx):
+    """return base, other part of a conflict
+
+    This only return the first one.
+
+    XXX this woobly function won't survive XXX
+    """
+    obsolete = extensions.find('obsolete')
+    for base in ctx._repo.set('reverse(precursors(%d))', ctx):
+        newer = obsolete.newerversion(ctx._repo, base.node())
+        # drop filter and solution including the original ctx
+        newer = [n for n in newer if n and ctx.node() not in n]
+        if newer:
+            return base, tuple(ctx._repo[o] for o in newer[0])
+    raise KeyError('Base seem unknown. This case is not handled yet.')
+
+
 
 shorttemplate = '[{rev}] {desc|firstline}\n'