# HG changeset patch # User Dan Villiom Podlaski Christiansen # Date 1241357323 -7200 # Node ID 1d48d9a34c19dcf241136b2059abb5391f093985 # Parent e6853c7fa3af3d7776fae6c4f4e8b9f006e54f74 Put authormap into a separate file, and make it much better too. See the doc-strings in maps.py for details. diff --git a/hg_delta_editor.py b/hg_delta_editor.py --- a/hg_delta_editor.py +++ b/hg_delta_editor.py @@ -16,6 +16,7 @@ from svn import core import svnexternals import util +from maps import * def pickle_atomic(data, file_path, dir=None): """pickle some data to a path atomically. @@ -124,14 +125,9 @@ class HgChangeReceiver(delta.Editor): self.tag_locations.reverse() self.clear_current_info() - self.author_host = author_host - self.authors = {} - if os.path.exists(self.authors_file): - self.readauthors(self.authors_file) - if authors and os.path.exists(authors): - self.readauthors(authors) - if self.authors: - self.writeauthors() + self.authors = AuthorMap(self.ui, self.authors_file, + defaulthost=author_host) + if authors: self.authors.load(authors) self.lastdate = '1970-01-01 00:00:00 -0000' self.includepaths = {} @@ -632,7 +628,7 @@ class HgChangeReceiver(delta.Editor): rev.message or ' ', files, del_all_files, - self.authorforsvnauthor(rev.author), + self.authors[rev.author], date, {'branch': 'closed-branches'}) new_hash = self.repo.commitctx(current_ctx) @@ -685,7 +681,7 @@ class HgChangeReceiver(delta.Editor): rev.message or '...', files.keys(), filectxfn, - self.authorforsvnauthor(rev.author), + self.authors[rev.author], date, extra) new_hash = self.repo.commitctx(current_ctx) @@ -712,7 +708,7 @@ class HgChangeReceiver(delta.Editor): rev.message or ' ', [], del_all_files, - self.authorforsvnauthor(rev.author), + self.authors[rev.author], date, extra) new_hash = self.repo.commitctx(current_ctx) @@ -722,50 +718,6 @@ class HgChangeReceiver(delta.Editor): self._save_metadata() self.clear_current_info() - def authorforsvnauthor(self, author): - if author in self.authors: - return self.authors[author] - return '%s%s' % (author, self.author_host) - - def svnauthorforauthor(self, author): - for svnauthor, hgauthor in self.authors.iteritems(): - if author == hgauthor: - return svnauthor - else: - # return the original svn-side author - return author.rsplit('@', 1)[0] - - def readauthors(self, authorfile): - self.ui.note(('Reading authormap from %s\n') % authorfile) - f = open(authorfile, 'r') - for line in f: - if not line.strip(): - continue - try: - srcauth, dstauth = line.split('=', 1) - srcauth = srcauth.strip() - dstauth = dstauth.strip() - if srcauth in self.authors and dstauth != self.authors[srcauth]: - self.ui.status(('Overriding author mapping for "%s" ' + - 'from "%s" to "%s"\n') - % (srcauth, self.authors[srcauth], dstauth)) - else: - self.ui.debug(('Mapping author "%s" to "%s"\n') - % (srcauth, dstauth)) - self.authors[srcauth] = dstauth - except IndexError: - self.ui.warn( - ('Ignoring bad line in author map file %s: %s\n') - % (authorfile, line.rstrip())) - f.close() - - def writeauthors(self): - self.ui.debug(('Writing author map to %s\n') % self.authors_file) - f = open(self.authors_file, 'w+') - for author in self.authors: - f.write("%s=%s\n" % (author, self.authors[author])) - f.close() - def readfilemap(self, filemapfile): self.ui.note( ('Reading file map from %s\n') diff --git a/maps.py b/maps.py new file mode 100644 --- /dev/null +++ b/maps.py @@ -0,0 +1,89 @@ +''' Module for self-contained maps. ''' + +import os +from mercurial import util as hgutil + +class AuthorMap(dict): + '''A mapping from Subversion-style authors to Mercurial-style + authors, and back. The data is stored persistently on disk. + + If the 'hgsubversion.defaultauthors' configuration option is set to false, + attempting to obtain an unknown author will fail with an Abort. + ''' + + def __init__(self, ui, path, defaulthost=None): + '''Initialise a new AuthorMap. + + The ui argument is used to print diagnostic messages. + + The path argument is the location of the backing store, + typically .hg/authormap. + ''' + self.ui = ui + self.path = path + if defaulthost: + self.defaulthost = '@%s' % defaulthost.lstrip('@') + else: + self.defaulthost = '' + self.super = super(AuthorMap, self) + self.super.__init__() + self.load(path) + + def load(self, path): + ''' Load mappings from a file at the specified path. ''' + if os.path.exists(path): + self.ui.note('Reading authormap from %s\n' % path) + f = open(path, 'r') + for number, line in enumerate(f): + if not line.strip(): + continue + try: + srcauth, dstauth = line.split('=', 1) + srcauth = srcauth.strip() + dstauth = dstauth.strip() + if srcauth in self and dstauth != self[srcauth]: + self.ui.warn(('Overriding author mapping for "%s" from ' + + '"%s" to "%s"\n') + % (srcauth, self[srcauth], dstauth)) + else: + self[srcauth] = dstauth + except IndexError: + self.ui.warn('Ignoring line %i in author map %s: %s\n' + % (number, path, line.rstrip())) + f.close() + + def __setitem__(self, key, value): + ''' Similar to dict.__setitem__, but also updates the new mapping in the + backing store. ''' + self.super.__setitem__(key, value) + + self.ui.debug(('Writing author map to %s\n') % self.path) + f = open(self.path, 'w+') + for k, v in self.iteritems(): + f.write("%s=%s\n" % (k, v)) + f.close() + + def __getitem__(self, author): + ''' Similar to dict.__getitem__, except in case of an unknown author. + In such cases, a new value is generated and added to the dictionary + as well as the backing store. ''' + if author in self: + result = self.super.__getitem__(author) + elif self.ui.configbool('hgsubversion', 'defaultauthors', True): + self[author] = result = \ + '%s <%s%s>' % (author, author, self.defaulthost) + self.ui.warn('Substituting author "%s" for default "%s"\n' + % (author, result)) + else: + raise hgutil.Abort('Author %s has no entry in the author map!' + % author) + self.ui.debug('Mapping author "%s" to "%s"\n' % (author, result)) + return result + + def reverselookup(self, author): + for svnauthor, hgauthor in self.iteritems(): + if author == hgauthor: + return svnauthor + else: + # Mercurial incorrectly splits at e.g. '.', so we roll our own. + return author.rsplit('@', 1)[0] diff --git a/stupid.py b/stupid.py --- a/stupid.py +++ b/stupid.py @@ -523,7 +523,7 @@ def svn_server_pull_rev(ui, svn, hg_edit r.message or util.default_commit_msg, files_touched, filectxfn, - hg_editor.authorforsvnauthor(r.author), + hg_editor.authors[r.author], date, extra) ha = hg_editor.repo.commitctx(current_ctx) @@ -563,7 +563,7 @@ def svn_server_pull_rev(ui, svn, hg_edit r.message or util.default_commit_msg, files_touched, filectxfn, - hg_editor.authorforsvnauthor(r.author), + hg_editor.authors[r.author], date, {'branch': 'closed-branches'}) ha = hg_editor.repo.commitctx(current_ctx) diff --git a/svncommands.py b/svncommands.py --- a/svncommands.py +++ b/svncommands.py @@ -22,7 +22,7 @@ def incoming(ui, svn_url, hg_repo_path, initializing_repo = False user, passwd = util.getuserpass(opts) svn = svnwrap.SubversionRepo(svn_url, user, passwd) - author_host = "@%s" % svn.uuid + author_host = ui.config('hgsubversion', 'defaulthost', svn.uuid) tag_locations = tag_locations.split(',') hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui, diff --git a/utility_commands.py b/utility_commands.py --- a/utility_commands.py +++ b/utility_commands.py @@ -75,7 +75,7 @@ def info(ui, repo, hg_repo_path, **opts) if url[-1] == '/': url = url[:-1] url = '%s%s' % (url, branchpath) - author = hge.svnauthorforauthor(parent.user()) + author = hge.authors.reverselookup(parent.user()) # cleverly figure out repo root w/o actually contacting the server reporoot = url[:len(url)-len(subdir)] ui.status('''URL: %(url)s diff --git a/wrappers.py b/wrappers.py --- a/wrappers.py +++ b/wrappers.py @@ -264,7 +264,7 @@ def pull(orig, ui, repo, source="default initializing_repo = False user, passwd = util.getuserpass(opts) svn = svnwrap.SubversionRepo(svn_url, user, passwd) - author_host = "@%s" % svn.uuid + author_host = ui.config('hgsubversion', 'defaulthost', svn.uuid) tag_locations = ['tags', ] authors = opts.pop('svn_authors', None) filemap = opts.pop('svn_filemap', None)