Mercurial > hgsubversion
diff push_cmd.py @ 0:f2636cfed115
Initial import of hgsubversion into a public repository.
author | Augie Fackler <durin42@gmail.com> |
---|---|
date | Tue, 30 Sep 2008 11:42:52 -0500 |
parents | |
children | 1a5bb173170b |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/push_cmd.py @@ -0,0 +1,114 @@ +from mercurial import util as merc_util +from mercurial import hg +from svn import core + +import util +import hg_delta_editor +import svnwrap +import fetch_command +import utility_commands + + +@util.register_subcommand('push') +@util.register_subcommand('dcommit') # for git expats +def push_revisions_to_subversion(ui, repo, hg_repo_path, svn_url, **opts): + """Push revisions starting at a specified head back to Subversion. + """ + #assert False # safety while the command is partially implemented. + hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, + ui_=ui) + svn_commit_hashes = dict(zip(hge.revmap.itervalues(), + hge.revmap.iterkeys())) + # Strategy: + # 1. Find all outgoing commits from this head + outgoing = utility_commands.outgoing_revisions(ui, repo, hge, + svn_commit_hashes) + if not (outgoing and len(outgoing)): + ui.status('No revisions to push.') + return 0 + if len(repo.parents()) != 1: + ui.status('Cowardly refusing to push branch merge') + return 1 + 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() + # 2. Commit oldest revision that needs to be pushed + base_revision = svn_commit_hashes[old_ctx.parents()[0].node()][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) + 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 + 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: + hg.clean(repo, needs_transplant) + utility_commands.rebase_commits(ui, repo, hg_repo_path, **opts) + repo = hg.repository(ui, hge.path) + if needs_transplant in outgoing: + hg.clean(repo, repo['tip'].node()) + hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui) + svn_commit_hashes = dict(zip(hge.revmap.itervalues(), + hge.revmap.iterkeys())) + outgoing = utility_commands.outgoing_revisions(ui, repo, hge, + svn_commit_hashes) + return 0 + + +def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision): + """Build and send a commit from Mercurial to Subversion. + """ + target_files = [] + file_data = {} + for file in rev_ctx.files(): + parent = rev_ctx.parents()[0] + new_data = base_data = '' + action = '' + if file in rev_ctx: + new_data = rev_ctx.filectx(file).data() + if file not in parent: + target_files.append(file) + action = 'add' + # TODO check for mime-type autoprops here + # TODO check for directory adds here + else: + target_files.append(file) + base_data = parent.filectx(file).data() + action = 'modify' + else: + target_files.append(file) + base_data = parent.filectx(file).data() + action = 'delete' + file_data[file] = base_data, new_data, action + + # TODO check for directory deletes here + svn = svnwrap.SubversionRepo(svn_url) + parent_branch = rev_ctx.parents()[0].branch() + branch_path = 'trunk' + if parent_branch and parent_branch != 'default': + branch_path = 'branches/%s' % parent_branch + new_target_files = ['%s/%s' % (branch_path, f) for f in target_files] + for tf, ntf in zip(target_files, new_target_files): + if tf in file_data: + file_data[ntf] = file_data[tf] + del file_data[tf] + try: + svn.commit(new_target_files, rev_ctx.description(), file_data, + base_revision, set([])) + except core.SubversionException, e: + if hasattr(e, 'apr_err') and e.apr_err == 160028: + raise merc_util.Abort('Base text was out of date, maybe rebase?') + else: + raise