##// END OF EJS Templates
convert: Clean up authormap key=value splitting....
Marti Raudsepp -
r6186:aae4eb2f default
parent child Browse files
Show More
@@ -1,351 +1,352 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() == '':
194 if line.strip() == '':
195 continue
195 continue
196 try:
196 try:
197 srcauthor = line.split('=')[0].strip()
197 srcauthor, dstauthor = line.split('=', 1)
198 dstauthor = line.split('=')[1].strip()
198 srcauthor = srcauthor.strip()
199 dstauthor = dstauthor.strip()
199 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
200 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
200 self.ui.status(
201 self.ui.status(
201 'Overriding mapping for author %s, was %s, will be %s\n'
202 'Overriding mapping for author %s, was %s, will be %s\n'
202 % (srcauthor, self.authors[srcauthor], dstauthor))
203 % (srcauthor, self.authors[srcauthor], dstauthor))
203 else:
204 else:
204 self.ui.debug('Mapping author %s to %s\n'
205 self.ui.debug('Mapping author %s to %s\n'
205 % (srcauthor, dstauthor))
206 % (srcauthor, dstauthor))
206 self.authors[srcauthor] = dstauthor
207 self.authors[srcauthor] = dstauthor
207 except IndexError:
208 except IndexError:
208 self.ui.warn(
209 self.ui.warn(
209 'Ignoring bad line in author map file %s: %s\n'
210 'Ignoring bad line in author map file %s: %s\n'
210 % (authorfile, line.rstrip()))
211 % (authorfile, line.rstrip()))
211 afile.close()
212 afile.close()
212
213
213 def cachecommit(self, rev):
214 def cachecommit(self, rev):
214 commit = self.source.getcommit(rev)
215 commit = self.source.getcommit(rev)
215 commit.author = self.authors.get(commit.author, commit.author)
216 commit.author = self.authors.get(commit.author, commit.author)
216 self.commitcache[rev] = commit
217 self.commitcache[rev] = commit
217 return commit
218 return commit
218
219
219 def copy(self, rev):
220 def copy(self, rev):
220 commit = self.commitcache[rev]
221 commit = self.commitcache[rev]
221 do_copies = hasattr(self.dest, 'copyfile')
222 do_copies = hasattr(self.dest, 'copyfile')
222 filenames = []
223 filenames = []
223
224
224 changes = self.source.getchanges(rev)
225 changes = self.source.getchanges(rev)
225 if isinstance(changes, basestring):
226 if isinstance(changes, basestring):
226 if changes == SKIPREV:
227 if changes == SKIPREV:
227 dest = SKIPREV
228 dest = SKIPREV
228 else:
229 else:
229 dest = self.map[changes]
230 dest = self.map[changes]
230 self.map[rev] = dest
231 self.map[rev] = dest
231 return
232 return
232 files, copies = changes
233 files, copies = changes
233 pbranches = []
234 pbranches = []
234 if commit.parents:
235 if commit.parents:
235 for prev in commit.parents:
236 for prev in commit.parents:
236 if prev not in self.commitcache:
237 if prev not in self.commitcache:
237 self.cachecommit(prev)
238 self.cachecommit(prev)
238 pbranches.append((self.map[prev],
239 pbranches.append((self.map[prev],
239 self.commitcache[prev].branch))
240 self.commitcache[prev].branch))
240 self.dest.setbranch(commit.branch, pbranches)
241 self.dest.setbranch(commit.branch, pbranches)
241 for f, v in files:
242 for f, v in files:
242 filenames.append(f)
243 filenames.append(f)
243 try:
244 try:
244 data = self.source.getfile(f, v)
245 data = self.source.getfile(f, v)
245 except IOError, inst:
246 except IOError, inst:
246 self.dest.delfile(f)
247 self.dest.delfile(f)
247 else:
248 else:
248 e = self.source.getmode(f, v)
249 e = self.source.getmode(f, v)
249 self.dest.putfile(f, e, data)
250 self.dest.putfile(f, e, data)
250 if do_copies:
251 if do_copies:
251 if f in copies:
252 if f in copies:
252 copyf = copies[f]
253 copyf = copies[f]
253 # Merely marks that a copy happened.
254 # Merely marks that a copy happened.
254 self.dest.copyfile(copyf, f)
255 self.dest.copyfile(copyf, f)
255
256
256 try:
257 try:
257 parents = self.splicemap[rev].replace(',', ' ').split()
258 parents = self.splicemap[rev].replace(',', ' ').split()
258 self.ui.status('spliced in %s as parents of %s\n' %
259 self.ui.status('spliced in %s as parents of %s\n' %
259 (parents, rev))
260 (parents, rev))
260 parents = [self.map.get(p, p) for p in parents]
261 parents = [self.map.get(p, p) for p in parents]
261 except KeyError:
262 except KeyError:
262 parents = [b[0] for b in pbranches]
263 parents = [b[0] for b in pbranches]
263 newnode = self.dest.putcommit(filenames, parents, commit)
264 newnode = self.dest.putcommit(filenames, parents, commit)
264 self.source.converted(rev, newnode)
265 self.source.converted(rev, newnode)
265 self.map[rev] = newnode
266 self.map[rev] = newnode
266
267
267 def convert(self):
268 def convert(self):
268
269
269 try:
270 try:
270 self.source.before()
271 self.source.before()
271 self.dest.before()
272 self.dest.before()
272 self.source.setrevmap(self.map)
273 self.source.setrevmap(self.map)
273 self.ui.status("scanning source...\n")
274 self.ui.status("scanning source...\n")
274 heads = self.source.getheads()
275 heads = self.source.getheads()
275 parents = self.walktree(heads)
276 parents = self.walktree(heads)
276 self.ui.status("sorting...\n")
277 self.ui.status("sorting...\n")
277 t = self.toposort(parents)
278 t = self.toposort(parents)
278 num = len(t)
279 num = len(t)
279 c = None
280 c = None
280
281
281 self.ui.status("converting...\n")
282 self.ui.status("converting...\n")
282 for c in t:
283 for c in t:
283 num -= 1
284 num -= 1
284 desc = self.commitcache[c].desc
285 desc = self.commitcache[c].desc
285 if "\n" in desc:
286 if "\n" in desc:
286 desc = desc.splitlines()[0]
287 desc = desc.splitlines()[0]
287 # convert log message to local encoding without using
288 # convert log message to local encoding without using
288 # tolocal() because util._encoding conver() use it as
289 # tolocal() because util._encoding conver() use it as
289 # 'utf-8'
290 # 'utf-8'
290 self.ui.status("%d %s\n" % (num, recode(desc)))
291 self.ui.status("%d %s\n" % (num, recode(desc)))
291 self.ui.note(_("source: %s\n" % recode(c)))
292 self.ui.note(_("source: %s\n" % recode(c)))
292 self.copy(c)
293 self.copy(c)
293
294
294 tags = self.source.gettags()
295 tags = self.source.gettags()
295 ctags = {}
296 ctags = {}
296 for k in tags:
297 for k in tags:
297 v = tags[k]
298 v = tags[k]
298 if self.map.get(v, SKIPREV) != SKIPREV:
299 if self.map.get(v, SKIPREV) != SKIPREV:
299 ctags[k] = self.map[v]
300 ctags[k] = self.map[v]
300
301
301 if c and ctags:
302 if c and ctags:
302 nrev = self.dest.puttags(ctags)
303 nrev = self.dest.puttags(ctags)
303 # write another hash correspondence to override the previous
304 # write another hash correspondence to override the previous
304 # one so we don't end up with extra tag heads
305 # one so we don't end up with extra tag heads
305 if nrev:
306 if nrev:
306 self.map[c] = nrev
307 self.map[c] = nrev
307
308
308 self.writeauthormap()
309 self.writeauthormap()
309 finally:
310 finally:
310 self.cleanup()
311 self.cleanup()
311
312
312 def cleanup(self):
313 def cleanup(self):
313 try:
314 try:
314 self.dest.after()
315 self.dest.after()
315 finally:
316 finally:
316 self.source.after()
317 self.source.after()
317 self.map.close()
318 self.map.close()
318
319
319 def convert(ui, src, dest=None, revmapfile=None, **opts):
320 def convert(ui, src, dest=None, revmapfile=None, **opts):
320 global orig_encoding
321 global orig_encoding
321 orig_encoding = util._encoding
322 orig_encoding = util._encoding
322 util._encoding = 'UTF-8'
323 util._encoding = 'UTF-8'
323
324
324 if not dest:
325 if not dest:
325 dest = hg.defaultdest(src) + "-hg"
326 dest = hg.defaultdest(src) + "-hg"
326 ui.status("assuming destination %s\n" % dest)
327 ui.status("assuming destination %s\n" % dest)
327
328
328 destc = convertsink(ui, dest, opts.get('dest_type'))
329 destc = convertsink(ui, dest, opts.get('dest_type'))
329
330
330 try:
331 try:
331 srcc = convertsource(ui, src, opts.get('source_type'),
332 srcc = convertsource(ui, src, opts.get('source_type'),
332 opts.get('rev'))
333 opts.get('rev'))
333 except Exception:
334 except Exception:
334 for path in destc.created:
335 for path in destc.created:
335 shutil.rmtree(path, True)
336 shutil.rmtree(path, True)
336 raise
337 raise
337
338
338 fmap = opts.get('filemap')
339 fmap = opts.get('filemap')
339 if fmap:
340 if fmap:
340 srcc = filemap.filemap_source(ui, srcc, fmap)
341 srcc = filemap.filemap_source(ui, srcc, fmap)
341 destc.setfilemapmode(True)
342 destc.setfilemapmode(True)
342
343
343 if not revmapfile:
344 if not revmapfile:
344 try:
345 try:
345 revmapfile = destc.revmapfile()
346 revmapfile = destc.revmapfile()
346 except:
347 except:
347 revmapfile = os.path.join(destc, "map")
348 revmapfile = os.path.join(destc, "map")
348
349
349 c = converter(ui, srcc, destc, revmapfile, opts)
350 c = converter(ui, srcc, destc, revmapfile, opts)
350 c.convert()
351 c.convert()
351
352
General Comments 0
You need to be logged in to leave comments. Login now