##// END OF EJS Templates
convert: Ignore empty lines in authormap file.
Marti Raudsepp -
r6184:9d13e712 default
parent child Browse files
Show More
@@ -1,349 +1,351 b''
1 # convcmd - convert extension commands definition
1 # convcmd - convert extension commands definition
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from common import NoRepo, SKIPREV, converter_source, converter_sink, mapfile
8 from common import NoRepo, SKIPREV, converter_source, converter_sink, mapfile
9 from cvs import convert_cvs
9 from cvs import convert_cvs
10 from darcs import darcs_source
10 from darcs import darcs_source
11 from git import convert_git
11 from git import convert_git
12 from hg import mercurial_source, mercurial_sink
12 from hg import mercurial_source, mercurial_sink
13 from subversion import debugsvnlog, svn_source, svn_sink
13 from subversion import debugsvnlog, svn_source, svn_sink
14 from gnuarch import gnuarch_source
14 from gnuarch import gnuarch_source
15 import filemap
15 import filemap
16
16
17 import os, shutil
17 import os, shutil
18 from mercurial import hg, util
18 from mercurial import hg, util
19 from mercurial.i18n import _
19 from mercurial.i18n import _
20
20
21 orig_encoding = 'ascii'
21 orig_encoding = 'ascii'
22
22
23 def recode(s):
23 def recode(s):
24 if isinstance(s, unicode):
24 if isinstance(s, unicode):
25 return s.encode(orig_encoding, 'replace')
25 return s.encode(orig_encoding, 'replace')
26 else:
26 else:
27 return s.decode('utf-8').encode(orig_encoding, 'replace')
27 return s.decode('utf-8').encode(orig_encoding, 'replace')
28
28
29 source_converters = [
29 source_converters = [
30 ('cvs', convert_cvs),
30 ('cvs', convert_cvs),
31 ('git', convert_git),
31 ('git', convert_git),
32 ('svn', svn_source),
32 ('svn', svn_source),
33 ('hg', mercurial_source),
33 ('hg', mercurial_source),
34 ('darcs', darcs_source),
34 ('darcs', darcs_source),
35 ('gnuarch', gnuarch_source),
35 ('gnuarch', gnuarch_source),
36 ]
36 ]
37
37
38 sink_converters = [
38 sink_converters = [
39 ('hg', mercurial_sink),
39 ('hg', mercurial_sink),
40 ('svn', svn_sink),
40 ('svn', svn_sink),
41 ]
41 ]
42
42
43 def convertsource(ui, path, type, rev):
43 def convertsource(ui, path, type, rev):
44 exceptions = []
44 exceptions = []
45 for name, source in source_converters:
45 for name, source in source_converters:
46 try:
46 try:
47 if not type or name == type:
47 if not type or name == type:
48 return source(ui, path, rev)
48 return source(ui, path, rev)
49 except NoRepo, inst:
49 except NoRepo, inst:
50 exceptions.append(inst)
50 exceptions.append(inst)
51 if not ui.quiet:
51 if not ui.quiet:
52 for inst in exceptions:
52 for inst in exceptions:
53 ui.write(_("%s\n") % inst)
53 ui.write(_("%s\n") % inst)
54 raise util.Abort('%s: unknown repository type' % path)
54 raise util.Abort('%s: unknown repository type' % path)
55
55
56 def convertsink(ui, path, type):
56 def convertsink(ui, path, type):
57 for name, sink in sink_converters:
57 for name, sink in sink_converters:
58 try:
58 try:
59 if not type or name == type:
59 if not type or name == type:
60 return sink(ui, path)
60 return sink(ui, path)
61 except NoRepo, inst:
61 except NoRepo, inst:
62 ui.note(_("convert: %s\n") % inst)
62 ui.note(_("convert: %s\n") % inst)
63 raise util.Abort('%s: unknown repository type' % path)
63 raise util.Abort('%s: unknown repository type' % path)
64
64
65 class converter(object):
65 class converter(object):
66 def __init__(self, ui, source, dest, revmapfile, opts):
66 def __init__(self, ui, source, dest, revmapfile, opts):
67
67
68 self.source = source
68 self.source = source
69 self.dest = dest
69 self.dest = dest
70 self.ui = ui
70 self.ui = ui
71 self.opts = opts
71 self.opts = opts
72 self.commitcache = {}
72 self.commitcache = {}
73 self.authors = {}
73 self.authors = {}
74 self.authorfile = None
74 self.authorfile = None
75
75
76 self.map = mapfile(ui, revmapfile)
76 self.map = mapfile(ui, revmapfile)
77
77
78 # Read first the dst author map if any
78 # Read first the dst author map if any
79 authorfile = self.dest.authorfile()
79 authorfile = self.dest.authorfile()
80 if authorfile and os.path.exists(authorfile):
80 if authorfile and os.path.exists(authorfile):
81 self.readauthormap(authorfile)
81 self.readauthormap(authorfile)
82 # Extend/Override with new author map if necessary
82 # Extend/Override with new author map if necessary
83 if opts.get('authors'):
83 if opts.get('authors'):
84 self.readauthormap(opts.get('authors'))
84 self.readauthormap(opts.get('authors'))
85 self.authorfile = self.dest.authorfile()
85 self.authorfile = self.dest.authorfile()
86
86
87 self.splicemap = mapfile(ui, opts.get('splicemap'))
87 self.splicemap = mapfile(ui, opts.get('splicemap'))
88
88
89 def walktree(self, heads):
89 def walktree(self, heads):
90 '''Return a mapping that identifies the uncommitted parents of every
90 '''Return a mapping that identifies the uncommitted parents of every
91 uncommitted changeset.'''
91 uncommitted changeset.'''
92 visit = heads
92 visit = heads
93 known = {}
93 known = {}
94 parents = {}
94 parents = {}
95 while visit:
95 while visit:
96 n = visit.pop(0)
96 n = visit.pop(0)
97 if n in known or n in self.map: continue
97 if n in known or n in self.map: continue
98 known[n] = 1
98 known[n] = 1
99 commit = self.cachecommit(n)
99 commit = self.cachecommit(n)
100 parents[n] = []
100 parents[n] = []
101 for p in commit.parents:
101 for p in commit.parents:
102 parents[n].append(p)
102 parents[n].append(p)
103 visit.append(p)
103 visit.append(p)
104
104
105 return parents
105 return parents
106
106
107 def toposort(self, parents):
107 def toposort(self, parents):
108 '''Return an ordering such that every uncommitted changeset is
108 '''Return an ordering such that every uncommitted changeset is
109 preceeded by all its uncommitted ancestors.'''
109 preceeded by all its uncommitted ancestors.'''
110 visit = parents.keys()
110 visit = parents.keys()
111 seen = {}
111 seen = {}
112 children = {}
112 children = {}
113 actives = []
113 actives = []
114
114
115 while visit:
115 while visit:
116 n = visit.pop(0)
116 n = visit.pop(0)
117 if n in seen: continue
117 if n in seen: continue
118 seen[n] = 1
118 seen[n] = 1
119 # Ensure that nodes without parents are present in the 'children'
119 # Ensure that nodes without parents are present in the 'children'
120 # mapping.
120 # mapping.
121 children.setdefault(n, [])
121 children.setdefault(n, [])
122 hasparent = False
122 hasparent = False
123 for p in parents[n]:
123 for p in parents[n]:
124 if not p in self.map:
124 if not p in self.map:
125 visit.append(p)
125 visit.append(p)
126 hasparent = True
126 hasparent = True
127 children.setdefault(p, []).append(n)
127 children.setdefault(p, []).append(n)
128 if not hasparent:
128 if not hasparent:
129 actives.append(n)
129 actives.append(n)
130
130
131 del seen
131 del seen
132 del visit
132 del visit
133
133
134 if self.opts.get('datesort'):
134 if self.opts.get('datesort'):
135 dates = {}
135 dates = {}
136 def getdate(n):
136 def getdate(n):
137 if n not in dates:
137 if n not in dates:
138 dates[n] = util.parsedate(self.commitcache[n].date)
138 dates[n] = util.parsedate(self.commitcache[n].date)
139 return dates[n]
139 return dates[n]
140
140
141 def picknext(nodes):
141 def picknext(nodes):
142 return min([(getdate(n), n) for n in nodes])[1]
142 return min([(getdate(n), n) for n in nodes])[1]
143 else:
143 else:
144 prev = [None]
144 prev = [None]
145 def picknext(nodes):
145 def picknext(nodes):
146 # Return the first eligible child of the previously converted
146 # Return the first eligible child of the previously converted
147 # revision, or any of them.
147 # revision, or any of them.
148 next = nodes[0]
148 next = nodes[0]
149 for n in nodes:
149 for n in nodes:
150 if prev[0] in parents[n]:
150 if prev[0] in parents[n]:
151 next = n
151 next = n
152 break
152 break
153 prev[0] = next
153 prev[0] = next
154 return next
154 return next
155
155
156 s = []
156 s = []
157 pendings = {}
157 pendings = {}
158 while actives:
158 while actives:
159 n = picknext(actives)
159 n = picknext(actives)
160 actives.remove(n)
160 actives.remove(n)
161 s.append(n)
161 s.append(n)
162
162
163 # Update dependents list
163 # Update dependents list
164 for c in children.get(n, []):
164 for c in children.get(n, []):
165 if c not in pendings:
165 if c not in pendings:
166 pendings[c] = [p for p in parents[c] if p not in self.map]
166 pendings[c] = [p for p in parents[c] if p not in self.map]
167 try:
167 try:
168 pendings[c].remove(n)
168 pendings[c].remove(n)
169 except ValueError:
169 except ValueError:
170 raise util.Abort(_('cycle detected between %s and %s')
170 raise util.Abort(_('cycle detected between %s and %s')
171 % (recode(c), recode(n)))
171 % (recode(c), recode(n)))
172 if not pendings[c]:
172 if not pendings[c]:
173 # Parents are converted, node is eligible
173 # Parents are converted, node is eligible
174 actives.insert(0, c)
174 actives.insert(0, c)
175 pendings[c] = None
175 pendings[c] = None
176
176
177 if len(s) != len(parents):
177 if len(s) != len(parents):
178 raise util.Abort(_("not all revisions were sorted"))
178 raise util.Abort(_("not all revisions were sorted"))
179
179
180 return s
180 return s
181
181
182 def writeauthormap(self):
182 def writeauthormap(self):
183 authorfile = self.authorfile
183 authorfile = self.authorfile
184 if authorfile:
184 if authorfile:
185 self.ui.status('Writing author map file %s\n' % authorfile)
185 self.ui.status('Writing author map file %s\n' % authorfile)
186 ofile = open(authorfile, 'w+')
186 ofile = open(authorfile, 'w+')
187 for author in self.authors:
187 for author in self.authors:
188 ofile.write("%s=%s\n" % (author, self.authors[author]))
188 ofile.write("%s=%s\n" % (author, self.authors[author]))
189 ofile.close()
189 ofile.close()
190
190
191 def readauthormap(self, authorfile):
191 def readauthormap(self, authorfile):
192 afile = open(authorfile, 'r')
192 afile = open(authorfile, 'r')
193 for line in afile:
193 for line in afile:
194 if line.strip() == '':
195 continue
194 try:
196 try:
195 srcauthor = line.split('=')[0].strip()
197 srcauthor = line.split('=')[0].strip()
196 dstauthor = line.split('=')[1].strip()
198 dstauthor = line.split('=')[1].strip()
197 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
199 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
198 self.ui.status(
200 self.ui.status(
199 'Overriding mapping for author %s, was %s, will be %s\n'
201 'Overriding mapping for author %s, was %s, will be %s\n'
200 % (srcauthor, self.authors[srcauthor], dstauthor))
202 % (srcauthor, self.authors[srcauthor], dstauthor))
201 else:
203 else:
202 self.ui.debug('Mapping author %s to %s\n'
204 self.ui.debug('Mapping author %s to %s\n'
203 % (srcauthor, dstauthor))
205 % (srcauthor, dstauthor))
204 self.authors[srcauthor] = dstauthor
206 self.authors[srcauthor] = dstauthor
205 except IndexError:
207 except IndexError:
206 self.ui.warn(
208 self.ui.warn(
207 'Ignoring bad line in author file map %s: %s\n'
209 'Ignoring bad line in author file map %s: %s\n'
208 % (authorfile, line))
210 % (authorfile, line))
209 afile.close()
211 afile.close()
210
212
211 def cachecommit(self, rev):
213 def cachecommit(self, rev):
212 commit = self.source.getcommit(rev)
214 commit = self.source.getcommit(rev)
213 commit.author = self.authors.get(commit.author, commit.author)
215 commit.author = self.authors.get(commit.author, commit.author)
214 self.commitcache[rev] = commit
216 self.commitcache[rev] = commit
215 return commit
217 return commit
216
218
217 def copy(self, rev):
219 def copy(self, rev):
218 commit = self.commitcache[rev]
220 commit = self.commitcache[rev]
219 do_copies = hasattr(self.dest, 'copyfile')
221 do_copies = hasattr(self.dest, 'copyfile')
220 filenames = []
222 filenames = []
221
223
222 changes = self.source.getchanges(rev)
224 changes = self.source.getchanges(rev)
223 if isinstance(changes, basestring):
225 if isinstance(changes, basestring):
224 if changes == SKIPREV:
226 if changes == SKIPREV:
225 dest = SKIPREV
227 dest = SKIPREV
226 else:
228 else:
227 dest = self.map[changes]
229 dest = self.map[changes]
228 self.map[rev] = dest
230 self.map[rev] = dest
229 return
231 return
230 files, copies = changes
232 files, copies = changes
231 pbranches = []
233 pbranches = []
232 if commit.parents:
234 if commit.parents:
233 for prev in commit.parents:
235 for prev in commit.parents:
234 if prev not in self.commitcache:
236 if prev not in self.commitcache:
235 self.cachecommit(prev)
237 self.cachecommit(prev)
236 pbranches.append((self.map[prev],
238 pbranches.append((self.map[prev],
237 self.commitcache[prev].branch))
239 self.commitcache[prev].branch))
238 self.dest.setbranch(commit.branch, pbranches)
240 self.dest.setbranch(commit.branch, pbranches)
239 for f, v in files:
241 for f, v in files:
240 filenames.append(f)
242 filenames.append(f)
241 try:
243 try:
242 data = self.source.getfile(f, v)
244 data = self.source.getfile(f, v)
243 except IOError, inst:
245 except IOError, inst:
244 self.dest.delfile(f)
246 self.dest.delfile(f)
245 else:
247 else:
246 e = self.source.getmode(f, v)
248 e = self.source.getmode(f, v)
247 self.dest.putfile(f, e, data)
249 self.dest.putfile(f, e, data)
248 if do_copies:
250 if do_copies:
249 if f in copies:
251 if f in copies:
250 copyf = copies[f]
252 copyf = copies[f]
251 # Merely marks that a copy happened.
253 # Merely marks that a copy happened.
252 self.dest.copyfile(copyf, f)
254 self.dest.copyfile(copyf, f)
253
255
254 try:
256 try:
255 parents = self.splicemap[rev].replace(',', ' ').split()
257 parents = self.splicemap[rev].replace(',', ' ').split()
256 self.ui.status('spliced in %s as parents of %s\n' %
258 self.ui.status('spliced in %s as parents of %s\n' %
257 (parents, rev))
259 (parents, rev))
258 parents = [self.map.get(p, p) for p in parents]
260 parents = [self.map.get(p, p) for p in parents]
259 except KeyError:
261 except KeyError:
260 parents = [b[0] for b in pbranches]
262 parents = [b[0] for b in pbranches]
261 newnode = self.dest.putcommit(filenames, parents, commit)
263 newnode = self.dest.putcommit(filenames, parents, commit)
262 self.source.converted(rev, newnode)
264 self.source.converted(rev, newnode)
263 self.map[rev] = newnode
265 self.map[rev] = newnode
264
266
265 def convert(self):
267 def convert(self):
266
268
267 try:
269 try:
268 self.source.before()
270 self.source.before()
269 self.dest.before()
271 self.dest.before()
270 self.source.setrevmap(self.map)
272 self.source.setrevmap(self.map)
271 self.ui.status("scanning source...\n")
273 self.ui.status("scanning source...\n")
272 heads = self.source.getheads()
274 heads = self.source.getheads()
273 parents = self.walktree(heads)
275 parents = self.walktree(heads)
274 self.ui.status("sorting...\n")
276 self.ui.status("sorting...\n")
275 t = self.toposort(parents)
277 t = self.toposort(parents)
276 num = len(t)
278 num = len(t)
277 c = None
279 c = None
278
280
279 self.ui.status("converting...\n")
281 self.ui.status("converting...\n")
280 for c in t:
282 for c in t:
281 num -= 1
283 num -= 1
282 desc = self.commitcache[c].desc
284 desc = self.commitcache[c].desc
283 if "\n" in desc:
285 if "\n" in desc:
284 desc = desc.splitlines()[0]
286 desc = desc.splitlines()[0]
285 # convert log message to local encoding without using
287 # convert log message to local encoding without using
286 # tolocal() because util._encoding conver() use it as
288 # tolocal() because util._encoding conver() use it as
287 # 'utf-8'
289 # 'utf-8'
288 self.ui.status("%d %s\n" % (num, recode(desc)))
290 self.ui.status("%d %s\n" % (num, recode(desc)))
289 self.ui.note(_("source: %s\n" % recode(c)))
291 self.ui.note(_("source: %s\n" % recode(c)))
290 self.copy(c)
292 self.copy(c)
291
293
292 tags = self.source.gettags()
294 tags = self.source.gettags()
293 ctags = {}
295 ctags = {}
294 for k in tags:
296 for k in tags:
295 v = tags[k]
297 v = tags[k]
296 if self.map.get(v, SKIPREV) != SKIPREV:
298 if self.map.get(v, SKIPREV) != SKIPREV:
297 ctags[k] = self.map[v]
299 ctags[k] = self.map[v]
298
300
299 if c and ctags:
301 if c and ctags:
300 nrev = self.dest.puttags(ctags)
302 nrev = self.dest.puttags(ctags)
301 # write another hash correspondence to override the previous
303 # write another hash correspondence to override the previous
302 # one so we don't end up with extra tag heads
304 # one so we don't end up with extra tag heads
303 if nrev:
305 if nrev:
304 self.map[c] = nrev
306 self.map[c] = nrev
305
307
306 self.writeauthormap()
308 self.writeauthormap()
307 finally:
309 finally:
308 self.cleanup()
310 self.cleanup()
309
311
310 def cleanup(self):
312 def cleanup(self):
311 try:
313 try:
312 self.dest.after()
314 self.dest.after()
313 finally:
315 finally:
314 self.source.after()
316 self.source.after()
315 self.map.close()
317 self.map.close()
316
318
317 def convert(ui, src, dest=None, revmapfile=None, **opts):
319 def convert(ui, src, dest=None, revmapfile=None, **opts):
318 global orig_encoding
320 global orig_encoding
319 orig_encoding = util._encoding
321 orig_encoding = util._encoding
320 util._encoding = 'UTF-8'
322 util._encoding = 'UTF-8'
321
323
322 if not dest:
324 if not dest:
323 dest = hg.defaultdest(src) + "-hg"
325 dest = hg.defaultdest(src) + "-hg"
324 ui.status("assuming destination %s\n" % dest)
326 ui.status("assuming destination %s\n" % dest)
325
327
326 destc = convertsink(ui, dest, opts.get('dest_type'))
328 destc = convertsink(ui, dest, opts.get('dest_type'))
327
329
328 try:
330 try:
329 srcc = convertsource(ui, src, opts.get('source_type'),
331 srcc = convertsource(ui, src, opts.get('source_type'),
330 opts.get('rev'))
332 opts.get('rev'))
331 except Exception:
333 except Exception:
332 for path in destc.created:
334 for path in destc.created:
333 shutil.rmtree(path, True)
335 shutil.rmtree(path, True)
334 raise
336 raise
335
337
336 fmap = opts.get('filemap')
338 fmap = opts.get('filemap')
337 if fmap:
339 if fmap:
338 srcc = filemap.filemap_source(ui, srcc, fmap)
340 srcc = filemap.filemap_source(ui, srcc, fmap)
339 destc.setfilemapmode(True)
341 destc.setfilemapmode(True)
340
342
341 if not revmapfile:
343 if not revmapfile:
342 try:
344 try:
343 revmapfile = destc.revmapfile()
345 revmapfile = destc.revmapfile()
344 except:
346 except:
345 revmapfile = os.path.join(destc, "map")
347 revmapfile = os.path.join(destc, "map")
346
348
347 c = converter(ui, srcc, destc, revmapfile, opts)
349 c = converter(ui, srcc, destc, revmapfile, opts)
348 c.convert()
350 c.convert()
349
351
General Comments 0
You need to be logged in to leave comments. Login now