changeset 307:1d48d9a34c19

Put authormap into a separate file, and make it much better too. See the doc-strings in maps.py for details.
author Dan Villiom Podlaski Christiansen <danchr@gmail.com>
date Sun, 03 May 2009 15:28:43 +0200
parents e6853c7fa3af
children 41aa4c3f789e
files hg_delta_editor.py maps.py stupid.py svncommands.py utility_commands.py wrappers.py
diffstat 6 files changed, 101 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- 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')
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]
--- 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)
--- 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,
--- 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
--- 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)