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