Mercurial > hgsubversion
diff wrappers.py @ 257:ffccf0080e54
Move wrappers for hg commands to their own module.
author | Augie Fackler <durin42@gmail.com> |
---|---|
date | Fri, 10 Apr 2009 22:38:29 -0500 |
parents | |
children | 87dc4d0dd048 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/wrappers.py @@ -0,0 +1,320 @@ +import os + +from mercurial import cmdutil as hgcmdutil +from mercurial import commands +from mercurial import patch +from mercurial import hg +from mercurial import util as hgutil +from mercurial import node + +from svn import core +from svn import delta + +import cmdutil +import hg_delta_editor +import stupid as stupidmod +import svnwrap +import util +import utility_commands + +def parent(orig, ui, repo, *args, **opts): + """show Mercurial & Subversion parents of the working dir or revision + """ + if not opts.get('svn', False): + return orig(ui, repo, *args, **opts) + hge = hg_delta_editor.HgChangeReceiver(repo=repo) + svn_commit_hashes = dict(zip(hge.revmap.itervalues(), + hge.revmap.iterkeys())) + ha = cmdutil.parentrev(ui, repo, hge, svn_commit_hashes) + if ha.node() == node.nullid: + raise hgutil.Abort('No parent svn revision!') + displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False) + displayer.show(ha) + return 0 + + +def outgoing(orig, ui, repo, dest=None, *args, **opts): + """show changesets not found in the Subversion repository + """ + svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default') + if not (cmdutil.issvnurl(svnurl) or opts.get('svn', False)): + return orig(ui, repo, dest, *args, **opts) + + hge = hg_delta_editor.HgChangeReceiver(repo=repo) + svn_commit_hashes = dict(zip(hge.revmap.itervalues(), + hge.revmap.iterkeys())) + o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, + repo.parents()[0].node()) + if not (o_r and len(o_r)): + ui.status('no changes found\n') + return 0 + displayer = hgcmdutil.show_changeset(ui, repo, opts, buffered=False) + for node in reversed(o_r): + displayer.show(repo[node]) + + + + +def diff(orig, ui, repo, *args, **opts): + """show a diff of the most recent revision against its parent from svn + """ + if not opts.get('svn', False) or opts.get('change', None): + return orig(ui, repo, *args, **opts) + svn_commit_hashes = {} + hge = hg_delta_editor.HgChangeReceiver(repo=repo) + svn_commit_hashes = dict(zip(hge.revmap.itervalues(), + hge.revmap.iterkeys())) + if not opts.get('rev', None): + parent = repo.parents()[0] + o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, + parent.node()) + if o_r: + parent = repo[o_r[-1]].parents()[0] + opts['rev'] = ['%s:.' % node.hex(parent.node()), ] + node1, node2 = hgcmdutil.revpair(repo, opts['rev']) + baserev, _junk = svn_commit_hashes.get(node1, (-1, 'junk', )) + newrev, _junk = svn_commit_hashes.get(node2, (-1, 'junk', )) + it = patch.diff(repo, node1, node2, + opts=patch.diffopts(ui, opts={'git': True, + 'show_function': False, + 'ignore_all_space': False, + 'ignore_space_change': False, + 'ignore_blank_lines': False, + 'unified': True, + 'text': False, + })) + ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev)) + + + + +def push(orig, ui, repo, dest=None, *args, **opts): + """push revisions starting at a specified head back to Subversion. + """ + svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default') + if not cmdutil.issvnurl(svnurl): + orig(ui, repo, dest=dest, *args, **opts) + old_encoding = util.swap_out_encoding() + hge = hg_delta_editor.HgChangeReceiver(repo=repo) + svnurl = util.normalize_url(svnurl) + if svnurl != hge.url: + raise hgutil.Abort('wrong subversion url!') + 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, svnurl, + 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(None, ui, repo, True, stupid=opts.get('svn_stupid', False), + 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(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(hge.path, ui_=ui) + svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys())) + util.swap_out_encoding(old_encoding) + return 0 + + + +def clone(orig, ui, source, dest=None, *args, **opts): + '''clone Subversion repository to a local Mercurial repository. + + If no destination directory name is specified, it defaults to the + basename of the source plus "-hg". + + You can specify multiple paths for the location of tags using comma + separated values. + ''' + svnurl = ui.expandpath(source) + if not cmdutil.issvnurl(svnurl): + orig(ui, source=source, dest=dest, *args, **opts) + + if not dest: + dest = hg.defaultdest(source) + '-hg' + ui.status("Assuming destination %s\n" % dest) + + if os.path.exists(dest): + raise hgutil.Abort("destination '%s' already exists" % dest) + url = util.normalize_url(svnurl) + res = -1 + try: + try: + res = pull(None, ui, None, True, opts.pop('svn_stupid', False), + source=url, create_new_dest=dest, **opts) + except core.SubversionException, e: + if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED: + raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n' + 'Please try running svn ls on that url first.') + raise + finally: + if os.path.exists(dest): + repo = hg.repository(ui, dest) + fp = repo.opener("hgrc", "w", text=True) + fp.write("[paths]\n") + # percent needs to be escaped for ConfigParser + fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl.replace('%', '%%')}) + fp.close() + if (res is None or res == 0) and not opts.get('noupdate', False): + commands.update(ui, repo, repo['tip'].node()) + + return res + + +def pull(orig, ui, repo, svn=None, svn_stupid=False, source="default", create_new_dest=False, + *args, **opts): + """pull new revisions from Subversion + """ + url = ((repo and repo.ui) or ui).expandpath(source) + if not (cmdutil.issvnurl(url) or svn or create_new_dest): + orig(ui, repo, source=source, *args, **opts) + svn_url = url + svn_url = util.normalize_url(svn_url) + old_encoding = util.swap_out_encoding() + # TODO implement skipto support + skipto_rev = 0 + have_replay = not svn_stupid + if have_replay and not callable( + delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover + ui.status('You are using old Subversion SWIG bindings. Replay will not' + ' work until you upgrade to 1.5.0 or newer. Falling back to' + ' a slower method that may be buggier. Please upgrade, or' + ' contribute a patch to use the ctypes bindings instead' + ' of SWIG.\n') + have_replay = False + initializing_repo = False + user = opts.get('username', hgutil.getuser()) + passwd = opts.get('password', '') + svn = svnwrap.SubversionRepo(svn_url, user, passwd) + author_host = "@%s" % svn.uuid + # TODO these should be configurable again, but I'm torn on how. + # Maybe this should be configured in .hg/hgrc for each repo? Seems vaguely reasonable. + tag_locations = ['tags', ] + authors = opts.pop('svn_authors', None) + filemap = opts.pop('svn_filemap', None) + if repo: + hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo, + subdir=svn.subdir, + author_host=author_host, + tag_locations=tag_locations, + authors=authors, + filemap=filemap) + else: + hg_editor = hg_delta_editor.HgChangeReceiver(ui_=ui, + path=create_new_dest, + subdir=svn.subdir, + author_host=author_host, + tag_locations=tag_locations, + authors=authors, + filemap=filemap) + if os.path.exists(hg_editor.uuid_file): + uuid = open(hg_editor.uuid_file).read() + assert uuid == svn.uuid + start = hg_editor.last_known_revision() + else: + open(hg_editor.uuid_file, 'w').write(svn.uuid) + open(hg_editor.svn_url_file, 'w').write(svn_url) + initializing_repo = True + start = skipto_rev + + if initializing_repo and start > 0: + raise hgutil.Abort('Revision skipping at repository initialization ' + 'remains unimplemented.') + + # start converting revisions + for r in svn.revisions(start=start): + valid = True + hg_editor.update_branch_tag_map_for_rev(r) + for p in r.paths: + if hg_editor._is_path_valid(p): + valid = True + break + if valid: + # got a 502? Try more than once! + tries = 0 + converted = False + while not converted: + try: + util.describe_revision(ui, r) + if have_replay: + try: + cmdutil.replay_convert_rev(hg_editor, svn, r) + except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover + ui.status('%s\n' % e.message) + stupidmod.print_your_svn_is_old_message(ui) + have_replay = False + stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r) + else: + stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r) + converted = True + except core.SubversionException, e: #pragma: no cover + if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED + and '502' in str(e) + and tries < 3): + tries += 1 + ui.status('Got a 502, retrying (%s)\n' % tries) + else: + raise hgutil.Abort(*e.args) + util.swap_out_encoding(old_encoding)