comparison hgext/convert/__init__.py @ 4589:451e91ed535e

convert extension: 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@server.org>" pair.
author Edouard Gomez <ed.gomez@free.fr>
date Thu, 14 Jun 2007 23:25:55 +0200
parents 9855939d0c82
children 80fb4ec512b5
comparison
equal deleted inserted replaced
4588:9855939d0c82 4589:451e91ed535e
35 self.ui = ui 35 self.ui = ui
36 self.opts = opts 36 self.opts = opts
37 self.commitcache = {} 37 self.commitcache = {}
38 self.mapfile = mapfile 38 self.mapfile = mapfile
39 self.mapfilefd = None 39 self.mapfilefd = None
40 self.authors = {}
41 self.writeauthors = False
40 42
41 self.map = {} 43 self.map = {}
42 try: 44 try:
43 origmapfile = open(self.mapfile, 'r') 45 origmapfile = open(self.mapfile, 'r')
44 for l in origmapfile: 46 for l in origmapfile:
45 sv, dv = l[:-1].split() 47 sv, dv = l[:-1].split()
46 self.map[sv] = dv 48 self.map[sv] = dv
47 origmapfile.close() 49 origmapfile.close()
48 except IOError: 50 except IOError:
49 pass 51 pass
52
53 # Read first the dst author map if any
54 if hasattr(self.dest, 'authorfile'):
55 self.readauthormap(self.dest.authorfile())
56 # Extend/Override with new author map if necessary
57 if 'authors' in opts:
58 self.readauthormap(opts.get('authors'))
59 self.writeauthors = True
50 60
51 def walktree(self, heads): 61 def walktree(self, heads):
52 visit = heads 62 visit = heads
53 known = {} 63 known = {}
54 parents = {} 64 parents = {}
129 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.mapfile, errno, strerror)) 139 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.mapfile, errno, strerror))
130 self.map[src] = dst 140 self.map[src] = dst
131 self.mapfilefd.write("%s %s\n" % (src, dst)) 141 self.mapfilefd.write("%s %s\n" % (src, dst))
132 self.mapfilefd.flush() 142 self.mapfilefd.flush()
133 143
144 def writeauthormap(self):
145 if self.writeauthors == True and len(self.authors) > 0 and hasattr(self.dest, 'authorfile'):
146 authorfile = self.dest.authorfile()
147 self.ui.status('Writing author map file %s\n' % authorfile)
148 ofile = open(authorfile, 'w+')
149 for author in self.authors:
150 ofile.write("%s=%s\n" % (author, self.authors[author]))
151 ofile.close()
152
153 def readauthormap(self, authorfile):
154 try:
155 afile = open(authorfile, 'r')
156 for line in afile:
157 try:
158 srcauthor = line.split('=')[0].strip()
159 dstauthor = line.split('=')[1].strip()
160 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
161 self.ui.status(
162 'Overriding mapping for author %s, was %s, will be %s\n'
163 % (srcauthor, self.authors[srcauthor], dstauthor))
164 else:
165 self.ui.debug('Mapping author %s to %s\n'
166 % (srcauthor, dstauthor))
167 self.authors[srcauthor] = dstauthor
168
169 except IndexError:
170 self.ui.warn(
171 'Ignoring bad line in author file map %s: %s\n'
172 % (authorfile, line))
173 afile.close()
174 except IOError:
175 self.ui.warn('Error reading author file map %s.\n' % authorfile)
176
134 def copy(self, rev): 177 def copy(self, rev):
135 c = self.commitcache[rev] 178 c = self.commitcache[rev]
136 files = self.source.getchanges(rev) 179 files = self.source.getchanges(rev)
137 180
138 for f, v in files: 181 for f, v in files:
163 for c in t: 206 for c in t:
164 num -= 1 207 num -= 1
165 desc = self.commitcache[c].desc 208 desc = self.commitcache[c].desc
166 if "\n" in desc: 209 if "\n" in desc:
167 desc = desc.splitlines()[0] 210 desc = desc.splitlines()[0]
211 author = self.commitcache[c].author
212 author = self.authors.get(author, author)
213 self.commitcache[c].author = author
168 self.ui.status("%d %s\n" % (num, desc)) 214 self.ui.status("%d %s\n" % (num, desc))
169 self.copy(c) 215 self.copy(c)
170 216
171 tags = self.source.gettags() 217 tags = self.source.gettags()
172 ctags = {} 218 ctags = {}
179 nrev = self.dest.puttags(ctags) 225 nrev = self.dest.puttags(ctags)
180 # write another hash correspondence to override the previous 226 # write another hash correspondence to override the previous
181 # one so we don't end up with extra tag heads 227 # one so we don't end up with extra tag heads
182 if nrev: 228 if nrev:
183 self.mapentry(c, nrev) 229 self.mapentry(c, nrev)
230
231 self.writeauthormap()
184 finally: 232 finally:
185 self.cleanup() 233 self.cleanup()
186 234
187 def cleanup(self): 235 def cleanup(self):
188 if self.mapfilefd: 236 if self.mapfilefd:
202 be created. If <mapfile> isn't given, it will be put in a default 250 be created. If <mapfile> isn't given, it will be put in a default
203 location (<dest>/.hg/shamap by default) 251 location (<dest>/.hg/shamap by default)
204 252
205 The <mapfile> is a simple text file that maps each source commit ID to 253 The <mapfile> is a simple text file that maps each source commit ID to
206 the destination ID for that revision, like so: 254 the destination ID for that revision, like so:
207
208 <source ID> <destination ID> 255 <source ID> <destination ID>
209 256
210 If the file doesn't exist, it's automatically created. It's updated 257 If the file doesn't exist, it's automatically created. It's updated
211 on each commit copied, so convert-repo can be interrupted and can 258 on each commit copied, so convert-repo can be interrupted and can
212 be run repeatedly to copy new commits. 259 be run repeatedly to copy new commits.
260
261 The [username mapping] file is a simple text file that maps each source
262 commit author to a destination commit author. It is handy for source SCMs
263 that use unix logins to identify authors (eg: CVS). One line per author
264 mapping and the line format is:
265 srcauthor=whatever string you want
213 ''' 266 '''
214 267
215 srcc = converter(ui, src) 268 srcc = converter(ui, src)
216 if not hasattr(srcc, "getcommit"): 269 if not hasattr(srcc, "getcommit"):
217 raise util.Abort("%s: can't read from this repo type" % src) 270 raise util.Abort("%s: can't read from this repo type" % src)
255 c.convert() 308 c.convert()
256 309
257 cmdtable = { 310 cmdtable = {
258 "convert": 311 "convert":
259 (_convert, 312 (_convert,
260 [('', 'datesort', None, 'try to sort changesets by date')], 313 [('A', 'authors', '', 'username mapping filename'),
314 ('', 'datesort', None, 'try to sort changesets by date')],
261 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'), 315 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
262 } 316 }