Mercurial > hgsubversion
diff svncommands.py @ 304:ce676eff002b
First merge, totally untested.
author | Dan Villiom Podlaski Christiansen <danchr@gmail.com> |
---|---|
date | Fri, 01 May 2009 10:28:59 +0200 |
parents | ba8e91a7c077 |
children | 1d48d9a34c19 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/svncommands.py @@ -0,0 +1,245 @@ +import os +import cPickle as pickle + +from mercurial import hg +from mercurial import node +from mercurial import util as hgutil + + +import hg_delta_editor +import svnwrap +import util +import utility_commands +import svnexternals + + +def incoming(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None, + tag_locations='tags', authors=None, filemap=None, **opts): + """show incoming revisions from Subversion + """ + svn_url = util.normalize_url(svn_url) + + initializing_repo = False + user, passwd = util.getuserpass(opts) + svn = svnwrap.SubversionRepo(svn_url, user, passwd) + author_host = "@%s" % svn.uuid + tag_locations = tag_locations.split(',') + hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path, + ui_=ui, + 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.') + + rev_stuff = (('revision', 'revnum'), + ('user', 'author'), + ('date', 'date'), + ('message', 'message') + ) + + ui.status('incoming changes from %s\n' % svn_url) + + for r in svn.revisions(start=start): + ui.status('\n') + for label, attr in rev_stuff: + l1 = label+':' + ui.status('%s%s\n' % (l1.ljust(13), + str(r.__getattribute__(attr)).strip(), )) + + +def rebuildmeta(ui, repo, hg_repo_path, args, **opts): + """rebuild hgsubversion metadata using values stored in revisions + """ + if len(args) != 1: + raise hgutil.Abort('You must pass the svn URI used to create this repo.') + uuid = None + url = args[0].rstrip('/') + user, passwd = util.getuserpass(opts) + svn = svnwrap.SubversionRepo(url, user, passwd) + subdir = svn.subdir + svnmetadir = os.path.join(repo.path, 'svn') + if not os.path.exists(svnmetadir): + os.makedirs(svnmetadir) + + revmap = open(os.path.join(svnmetadir, 'rev_map'), 'w') + revmap.write('1\n') + last_rev = -1 + branchinfo = {} + noderevnums = {} + for rev in repo: + ctx = repo[rev] + convinfo = ctx.extra().get('convert_revision', None) + if convinfo: + assert convinfo.startswith('svn:') + revpath, revision = convinfo[40:].split('@') + if subdir and subdir[0] != '/': + subdir = '/' + subdir + if subdir and subdir[-1] == '/': + subdir = subdir[:-1] + assert revpath.startswith(subdir), ('That does not look like the ' + 'right location in the repo.') + if uuid is None: + uuid = convinfo[4:40] + assert uuid == svn.uuid, 'UUIDs did not match!' + urlfile = open(os.path.join(svnmetadir, 'url'), 'w') + urlfile.write(url) + urlfile.close() + uuidfile = open(os.path.join(svnmetadir, 'uuid'), 'w') + uuidfile.write(uuid) + uuidfile.close() + commitpath = revpath[len(subdir)+1:] + if commitpath.startswith('branches'): + commitpath = commitpath[len('branches/'):] + elif commitpath == 'trunk': + commitpath = '' + else: + assert False, 'Unhandled case in rebuildmeta' + revmap.write('%s %s %s\n' % (revision, + node.hex(ctx.node()), + commitpath)) + revision = int(revision) + noderevnums[ctx.node()] = revision + if revision > last_rev: + last_rev = revision + branch = ctx.branch() + if branch == 'default': + branch = None + if branch not in branchinfo: + parent = ctx.parents()[0] + if (parent.node() in noderevnums + and parent.branch() != ctx.branch()): + parentbranch = parent.branch() + if parentbranch == 'default': + parentbranch = None + else: + parentbranch = None + branchinfo[branch] = (parentbranch, + noderevnums.get(parent.node(), 0), + revision) + for c in ctx.children(): + if c.branch() == 'closed-branches': + if branch in branchinfo: + del branchinfo[branch] + branchinfofile = open(os.path.join(svnmetadir, 'branch_info'), 'w') + pickle.dump(branchinfo, branchinfofile) + branchinfofile.close() + + # now handle tags + tagsinfo = {} + realtags = svn.tags + tagsleft = realtags.items() + while tagsleft: + tag, tagparent = tagsleft.pop(0) + source, rev = tagparent + if source.startswith('tags/'): + src = source[len('tags/'):] + if src in tagsinfo: + tagsinfo[tag] = tagsinfo[src] + elif src in realtags: + if (realtags[src][1] <= last_rev + or realtags[src][0].startswith('tags/')): + tagsleft.append(src) + else: + older_tags = svn.tags_at_rev(rev) + newsrc, newrev = older_tags[src] + tagsleft.append((tag, (newsrc, newrev))) + continue + else: + # determine the branch + assert not source.startswith('tags/'), "Tags can't be tags of other tags." + if source.startswith('branches/'): + source = source[len('branches/'):] + elif source == 'trunk': + source = None + else: + source = '../' + source + if rev <= last_rev and (source or 'default') in repo.branchtags(): + tagsinfo[tag] = source, rev + + tagsinfofile = open(os.path.join(svnmetadir, 'tag_info'), 'w') + pickle.dump(tagsinfo, tagsinfofile) + tagsinfofile.close() + + +def help(ui, args=None, **opts): + """show help for a given subcommands or a help overview + """ + if args: + subcommand = args[0] + if subcommand not in table: + candidates = [] + for c in table: + if c.startswith(subcommand): + candidates.append(c) + if len(candidates) == 1: + subcommand = candidates[0] + elif len(candidates) > 1: + ui.status('Ambiguous command. Could have been:\n%s\n' % + ' '.join(candidates)) + return + doc = table[subcommand].__doc__ + if doc is None: + doc = "No documentation available for %s." % subcommand + ui.status(doc.strip(), '\n') + return + ui.status(_helpgen()) + + +def update(ui, args, repo, clean=False, **opts): + """update to a specified Subversion revision number + """ + assert len(args) == 1 + rev = int(args[0]) + path = os.path.join(repo.path, 'svn', 'rev_map') + answers = [] + for k,v in util.parse_revmap(path).iteritems(): + if k[0] == rev: + answers.append((v, k[1])) + if len(answers) == 1: + if clean: + return hg.clean(repo, answers[0][0]) + return hg.update(repo, answers[0][0]) + elif len(answers) == 0: + ui.status('Revision %s did not produce an hg revision.\n' % rev) + return 1 + else: + ui.status('Ambiguous revision!\n') + ui.status('\n'.join(['%s on %s' % (node.hex(a[0]), a[1]) for a in + answers]+[''])) + return 1 + + +nourl = ['rebuildmeta', 'help'] + utility_commands.nourl +table = { + 'update': update, + 'help': help, + 'rebuildmeta': rebuildmeta, + 'incoming': incoming, + 'updateexternals': svnexternals.updateexternals, +} + +table.update(utility_commands.table) + + +def _helpgen(): + ret = ['hg svn ...', '', + 'subcommands for Subversion integration', '', + 'list of subcommands:', ''] + for name, func in sorted(table.items()): + short_description = (func.__doc__ or '').splitlines()[0] + ret.append(" %-10s %s" % (name, short_description)) + return '\n'.join(ret) + '\n'