changeset 150:58ae90a65f41

push: Improved the rebasing logic for push so that it doesn't break with keeping branch names during rebase.
author Augie Fackler <durin42@gmail.com>
date Mon, 22 Dec 2008 21:21:11 -0600
parents 04800fda7af5
children 2decec74ad0c
files push_cmd.py tests/test_push_command.py utility_commands.py
diffstat 3 files changed, 67 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -39,8 +39,16 @@ def push_revisions_to_subversion(ui, rep
             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[old_ctx.parents()[0].node()][0]
+        base_revision = svn_commit_hashes[base_n][0]
         commit_from_rev(ui, repo, old_ctx, hge, svn_url, base_revision)
         # 3. Fetch revisions from svn
         r = fetch_command.fetch_revisions(ui, svn_url, hg_repo_path,
@@ -48,16 +56,20 @@ def push_revisions_to_subversion(ui, rep
         assert not r or r == 0
         # 4. Find the new head of the target branch
         repo = hg.repository(ui, hge.path)
-        base_c = repo[base_n]
-        replacement = [c for c in base_c.children() if c not in old_children
-                       and c.branch() == old_ctx.branch()]
-        assert len(replacement) == 1
+        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()
             hg.clean(repo, needs_transplant)
-            utility_commands.rebase_commits(ui, repo, hg_repo_path, **opts)
+            utility_commands.rebase_commits(ui, repo, hg_repo_path, extrafn=extrafn, **opts)
             repo = hg.repository(ui, hge.path)
             if needs_transplant in outgoing:
                 hg.clean(repo, repo['tip'].node())
@@ -126,7 +138,7 @@ def _getdirchanges(svn, branchpath, pare
             deleted.append(d)
 
     return added, deleted
-        
+
 
 def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision):
     """Build and send a commit from Mercurial to Subversion.
@@ -140,7 +152,7 @@ def commit_from_rev(ui, repo, rev_ctx, h
     if parent_branch and parent_branch != 'default':
         branch_path = 'branches/%s' % parent_branch
 
-    addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, 
+    addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent,
                                             rev_ctx, rev_ctx.files())
     deleteddirs = set(deleteddirs)
 
@@ -221,7 +233,7 @@ def commit_from_rev(ui, repo, rev_ctx, h
     new_target_files += addeddirs + deleteddirs
     try:
         svn.commit(new_target_files, rev_ctx.description(), file_data,
-                   base_revision, set(addeddirs), set(deleteddirs), 
+                   base_revision, set(addeddirs), set(deleteddirs),
                    props, newcopies)
     except core.SubversionException, e:
         if hasattr(e, 'apr_err') and e.apr_err == 160028:
--- a/tests/test_push_command.py
+++ b/tests/test_push_command.py
@@ -114,10 +114,48 @@ class PushTests(test_util.TestBase):
         self.pushrevisions()
         tip = self.repo['tip']
         self.assertNotEqual(tip.node(), old_tip)
-        self.assertEqual(tip.parents()[0].node(), expected_parent)
+        self.assertEqual(node.hex(tip.parents()[0].node()),
+                         node.hex(expected_parent))
         self.assertEqual(tip['adding_file'].data(), 'foo')
         self.assertEqual(tip.branch(), 'default')
 
+    def test_push_two_revs_different_local_branch(self):
+        def filectxfn(repo, memctx, path):
+            return context.memfilectx(path=path,
+                                      data=path,
+                                      islink=False,
+                                      isexec=False,
+                                      copied=False)
+        oldtiphash = self.repo['default'].node()
+        ctx = context.memctx(self.repo,
+                             (self.repo[0].node(), revlog.nullid, ),
+                             'automated test',
+                             ['gamma', ],
+                             filectxfn,
+                             'testy',
+                             '2008-12-21 16:32:00 -0500',
+                             {'branch': 'localbranch', })
+        newhash = self.repo.commitctx(ctx)
+        ctx = context.memctx(self.repo,
+                             (newhash, revlog.nullid),
+                             'automated test2',
+                             ['delta', ],
+                             filectxfn,
+                             'testy',
+                             '2008-12-21 16:32:00 -0500',
+                             {'branch': 'localbranch', })
+        newhash = self.repo.commitctx(ctx)
+        repo = self.repo
+        hg.update(repo, newhash)
+        push_cmd.push_revisions_to_subversion(ui.ui(),
+                                              repo=repo,
+                                              svn_url=test_util.fileurl(self.repo_path),
+                                              hg_repo_path=self.wc_path)
+        self.assertEqual(self.repo['tip'].parents()[0].parents()[0].node(), oldtiphash)
+        self.assertEqual(self.repo['tip'].files(), ['delta', ])
+        self.assertEqual(self.repo['tip'].manifest().keys(),
+                         ['alpha', 'beta', 'gamma', 'delta'])
+
     def test_push_two_revs(self):
         # set up some work for us
         self.test_push_to_default(commit=False)
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -78,7 +78,7 @@ def print_parent_revision(ui, repo, hg_r
 
 
 @util.register_subcommand('rebase')
-def rebase_commits(ui, repo, hg_repo_path, **opts):
+def rebase_commits(ui, repo, hg_repo_path, extrafn=None, **opts):
     """Rebases current unpushed revisions onto Subversion head
 
     This moves a line of development from making its own head to the top of
@@ -86,10 +86,12 @@ def rebase_commits(ui, repo, hg_repo_pat
     rebase on top of the current top of Subversion work, you should probably run
     'hg svn pull' before running this.
     """
-    def extrafn(ctx, extra):
-        """defined here so we can add things easily.
-        """
-        extra['branch'] = ctx.branch()
+    if extrafn is None:
+        def extrafn2(ctx, extra):
+            """defined here so we can add things easily.
+            """
+            extra['branch'] = ctx.branch()
+        extrafn = extrafn2
     hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
                                            ui_=ui)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),