[PATCH 2 of 3] Add support for username mapping

Edouard Gomez ed.gomez at free.fr
Wed Jun 13 17:13:53 CDT 2007


# HG changeset patch
# User Edouard Gomez <ed.gomez at free.fr>
# Date 1181772083 -7200
# Node ID b055fe1ba41383e3db9541a6beb7497459a8c4d8
# Parent  b325d6e14326839c5c687f7a550e9c172c927e62
Add support for username mapping

Allows mapping usernames to new ones during conversion process.
 - Use -A option for first import
 - Then at the end of the conversion process and if the destination
   repo supports authorfile attribute, author map content is copied
   to the file pointed by the authorfile call.
 - On incremental conversions w/o any -A option specified, the
   destination authorfile, if any, gets read automatically.

EG: This allows mapping unix system usernames used in CVS accounts
    to a more typical "Firstname Lastname <address at server.org>" pair.

diff -r b325d6e14326 -r b055fe1ba413 hgext/convert/__init__.py
--- a/hgext/convert/__init__.py	Thu Jun 14 00:01:22 2007 +0200
+++ b/hgext/convert/__init__.py	Thu Jun 14 00:01:23 2007 +0200
@@ -37,6 +37,8 @@ class convert(object):
         self.commitcache = {}
         self.mapfile = mapfile
         self.mapfilefd = None
+        self.authors = {}
+        self.writeauthors = False
 
         self.map = {}
         try:
@@ -47,6 +49,14 @@ class convert(object):
             origmapfile.close()
         except IOError:
             pass
+
+        # Read first the dst author map if any
+        if  hasattr(self.dest, 'authorfile'):
+            self.readauthormap(self.dest.authorfile())
+        # Extend/Override with new author map if necessary
+        if 'authors' in opts:
+            self.readauthormap(opts.get('authors'))
+            self.writeauthors = True
 
     def walktree(self, heads):
         visit = heads
@@ -131,6 +141,39 @@ class convert(object):
         self.mapfilefd.write("%s %s\n" % (src, dst))
         self.mapfilefd.flush()
 
+    def writeauthormap(self):
+        if self.writeauthors == True and len(self.authors) > 0 and hasattr(self.dest, 'authorfile'):
+           authorfile = self.dest.authorfile()
+           self.ui.status('Writing author map file %s\n' % authorfile)
+           ofile = open(authorfile, 'w+')
+           for author in self.authors:
+               ofile.write("%s=%s\n" % (author, self.authors[author]))
+           ofile.close()
+
+    def readauthormap(self, authorfile):
+        try:
+            afile = open(authorfile, 'r')
+            for line in afile:
+                try:
+                    srcauthor = line.split('=')[0].strip()
+                    dstauthor = line.split('=')[1].strip()
+                    if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
+                        self.ui.status(
+                            'Overriding mapping for author %s, was %s, will be %s\n'
+                            % (srcauthor, self.authors[srcauthor], dstauthor))
+                    else:
+                        self.ui.debug('Mapping author %s to %s\n'
+			    % (srcauthor, dstauthor))
+                    self.authors[srcauthor] = dstauthor
+		    
+                except IndexError:
+                    self.ui.warn(
+                        'Ignoring bad line in author file map %s: %s\n'
+                        % (authorfile, line))
+            afile.close()
+        except IOError:
+            self.ui.warn('Error reading author file map %s.\n' % authorfile)
+
     def copy(self, rev):
         c = self.commitcache[rev]
         files = self.source.getchanges(rev)
@@ -165,6 +208,9 @@ class convert(object):
                 desc = self.commitcache[c].desc
                 if "\n" in desc:
                     desc = desc.splitlines()[0]
+                author = self.commitcache[c].author
+                author = self.authors.get(author, author)
+                self.commitcache[c].author = author
                 self.ui.status("%d %s\n" % (num, desc))
                 self.copy(c)
 
@@ -181,6 +227,8 @@ class convert(object):
                 # one so we don't end up with extra tag heads
                 if nrev:
                     self.mapentry(c, nrev)
+
+            self.writeauthormap()
         finally:
             self.cleanup()
 
@@ -204,12 +252,17 @@ def _convert(ui, src, dest=None, mapfile
 
     The <mapfile> is a simple text file that maps each source commit ID to
     the destination ID for that revision, like so:
-
     <source ID> <destination ID>
 
     If the file doesn't exist, it's automatically created.  It's updated
     on each commit copied, so convert-repo can be interrupted and can
     be run repeatedly to copy new commits.
+
+    The [username mapping] file is a simple text file that maps each source
+    commit author to a destination commit author. It is handy for source SCMs
+    that use unix logins to identify authors (eg: CVS). One line per author
+    mapping and the line format is:
+    srcauthor=whatever string you want
     '''
 
     srcc = converter(ui, src)
@@ -257,6 +310,7 @@ cmdtable = {
 cmdtable = {
     "convert":
         (_convert,
-         [('', 'datesort', None, 'try to sort changesets by date')],
+         [('A', 'authors', '', 'username mapping filename'),
+          ('', 'datesort', None, 'try to sort changesets by date')],
          'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
 }
diff -r b325d6e14326 -r b055fe1ba413 hgext/convert/common.py
--- a/hgext/convert/common.py	Thu Jun 14 00:01:22 2007 +0200
+++ b/hgext/convert/common.py	Thu Jun 14 00:01:23 2007 +0200
@@ -62,6 +62,12 @@ class converter_sink(object):
         mapping equivalent revision identifiers for each system."""
         raise NotImplementedError()
 
+    def authorfile(self):
+        """Path to a file that will contain lines
+        srcauthor=dstauthor
+        mapping equivalent authors identifiers for each system."""
+        raise NotImplementedError()
+
     def putfile(self, f, e, data):
         """Put file for next putcommit().
         f: path to file
diff -r b325d6e14326 -r b055fe1ba413 hgext/convert/hg.py
--- a/hgext/convert/hg.py	Thu Jun 14 00:01:22 2007 +0200
+++ b/hgext/convert/hg.py	Thu Jun 14 00:01:23 2007 +0200
@@ -16,6 +16,9 @@ class convert_mercurial(converter_sink):
 
     def mapfile(self):
         return os.path.join(self.path, ".hg", "shamap")
+
+    def authorfile(self):
+        return os.path.join(self.path, ".hg", "authormap")
 
     def getheads(self):
         h = self.repo.changelog.heads()


More information about the Mercurial-devel mailing list