push: Improved the rebasing logic for push so that it doesn't break with

keeping branch names during rebase.
This commit is contained in:
Augie Fackler 2008-12-22 21:21:11 -06:00
parent a8a83f58cc
commit 59a1a426b6
3 changed files with 67 additions and 15 deletions

View File

@ -39,8 +39,16 @@ def push_revisions_to_subversion(ui, repo, hg_repo_path, svn_url,
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, repo, hg_repo_path, svn_url,
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, parentctx, ctx, changedfiles):
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, hg_editor, svn_url, base_revision):
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, hg_editor, svn_url, base_revision):
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:

View File

@ -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)

View File

@ -78,7 +78,7 @@ def print_parent_revision(ui, repo, hg_repo_path, **opts):
@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_path, **opts):
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(),