diff svncommands.py @ 242:06130689a2c8

Move push into svncommands.
author Dirkjan Ochtman <dirkjan@ochtman.nl>
date Wed, 08 Apr 2009 17:53:48 +0200
parents 4950b18cf949
children 28d0ee605308
line wrap: on
line diff
--- a/svncommands.py
+++ b/svncommands.py
@@ -1,6 +1,9 @@
 import os
 
+from mercurial import hg
+from mercurial import node
 from mercurial import util as hgutil
+
 from svn import core
 from svn import delta
 
@@ -9,6 +12,7 @@ import svnwrap
 import stupid as stupidmod
 import cmdutil
 import util
+import utility_commands
 
 
 def pull(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
@@ -91,3 +95,93 @@ def pull(ui, svn_url, hg_repo_path, skip
     util.swap_out_encoding(old_encoding)
 
 pull = util.register_subcommand('pull')(pull)
+
+
+def push(ui, repo, hg_repo_path, svn_url, stupid=False, **opts):
+    """push revisions starting at a specified head back to Subversion.
+    """
+    old_encoding = util.swap_out_encoding()
+    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
+                                           ui_=ui)
+    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+                                 hge.revmap.iterkeys()))
+    user = opts.get('username', hgutil.getuser())
+    passwd = opts.get('password', '')
+
+    # Strategy:
+    # 1. Find all outgoing commits from this head
+    if len(repo.parents()) != 1:
+        ui.status('Cowardly refusing to push branch merge')
+        return 1
+    workingrev = repo.parents()[0]
+    outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
+    if not (outgoing and len(outgoing)):
+        ui.status('No revisions to push.')
+        return 0
+    while outgoing:
+        oldest = outgoing.pop(-1)
+        old_ctx = repo[oldest]
+        if len(old_ctx.parents()) != 1:
+            ui.status('Found a branch merge, this needs discussion and '
+                      'implementation.')
+            return 1
+        base_n = old_ctx.parents()[0].node()
+        old_children = repo[base_n].children()
+        svnbranch = repo[base_n].branch()
+        oldtip = base_n
+        samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
+                              and c.node() in svn_commit_hashes]
+        while samebranchchildren:
+            oldtip = samebranchchildren[0].node()
+            samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
+                                  and c.node() in svn_commit_hashes]
+        # 2. Commit oldest revision that needs to be pushed
+        base_revision = svn_commit_hashes[base_n][0]
+        try:
+            cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svn_url,
+                                    base_revision, user, passwd)
+        except cmdutil.NoFilesException:
+            ui.warn("Could not push revision %s because it had no changes in svn.\n" %
+                     old_ctx)
+            return 1
+        # 3. Fetch revisions from svn
+        r = pull(ui, svn_url, hg_repo_path, stupid=stupid,
+                 username=user, password=passwd)
+        assert not r or r == 0
+        # 4. Find the new head of the target branch
+        repo = hg.repository(ui, hge.path)
+        oldtipctx = repo[oldtip]
+        replacement = [c for c in oldtipctx.children() if c not in old_children
+                       and c.branch() == oldtipctx.branch()]
+        assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement
+        replacement = replacement[0]
+        # 5. Rebase all children of the currently-pushing rev to the new branch
+        heads = repo.heads(old_ctx.node())
+        for needs_transplant in heads:
+            def extrafn(ctx, extra):
+                if ctx.node() == oldest:
+                    return
+                extra['branch'] = ctx.branch()
+            utility_commands.rebase_commits(ui, repo,
+                                            extrafn=extrafn,
+                                            sourcerev=needs_transplant,
+                                            **opts)
+            repo = hg.repository(ui, hge.path)
+            for child in repo[replacement.node()].children():
+                rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+                if rebasesrc in outgoing:
+                    while rebasesrc in outgoing:
+                        rebsrcindex = outgoing.index(rebasesrc)
+                        outgoing = (outgoing[0:rebsrcindex] +
+                                    [child.node(), ] + outgoing[rebsrcindex+1:])
+                        children = [c for c in child.children() if c.branch() == child.branch()]
+                        if children:
+                            child = children[0]
+                        rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
+        hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui)
+        svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
+    util.swap_out_encoding(old_encoding)
+    return 0
+push = util.register_subcommand('push')(push)
+# for git expats
+dcommit = util.register_subcommand('dcommit')(push)