##// END OF EJS Templates
convert: handle comments starting with '#' in authormap files
Marti Raudsepp -
r7968:43b70a96 default
parent child Browse files
Show More
@@ -1,345 +1,346 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
201 if line.strip() == '':
201 line = line.strip()
202 if not line or line.startswith('#'):
202 continue
203 continue
203
204
204 try:
205 try:
205 srcauthor, dstauthor = line.split('=', 1)
206 srcauthor, dstauthor = line.split('=', 1)
206 except ValueError:
207 except ValueError:
207 msg = _('Ignoring bad line in author map file %s: %s\n')
208 msg = _('Ignoring bad line in author map file %s: %s\n')
208 self.ui.warn(msg % (authorfile, line.rstrip()))
209 self.ui.warn(msg % (authorfile, line.rstrip()))
209 continue
210 continue
210
211
211 srcauthor = srcauthor.strip()
212 srcauthor = srcauthor.strip()
212 dstauthor = dstauthor.strip()
213 dstauthor = dstauthor.strip()
213 if self.authors.get(srcauthor) in (None, dstauthor):
214 if self.authors.get(srcauthor) in (None, dstauthor):
214 msg = _('mapping author %s to %s\n')
215 msg = _('mapping author %s to %s\n')
215 self.ui.debug(msg % (srcauthor, dstauthor))
216 self.ui.debug(msg % (srcauthor, dstauthor))
216 self.authors[srcauthor] = dstauthor
217 self.authors[srcauthor] = dstauthor
217 continue
218 continue
218
219
219 m = _('overriding mapping for author %s, was %s, will be %s\n')
220 m = _('overriding mapping for author %s, was %s, will be %s\n')
220 self.ui.status(m % (srcauthor, self.authors[srcauthor], dstauthor))
221 self.ui.status(m % (srcauthor, self.authors[srcauthor], dstauthor))
221
222
222 afile.close()
223 afile.close()
223
224
224 def cachecommit(self, rev):
225 def cachecommit(self, rev):
225 commit = self.source.getcommit(rev)
226 commit = self.source.getcommit(rev)
226 commit.author = self.authors.get(commit.author, commit.author)
227 commit.author = self.authors.get(commit.author, commit.author)
227 self.commitcache[rev] = commit
228 self.commitcache[rev] = commit
228 return commit
229 return commit
229
230
230 def copy(self, rev):
231 def copy(self, rev):
231 commit = self.commitcache[rev]
232 commit = self.commitcache[rev]
232
233
233 changes = self.source.getchanges(rev)
234 changes = self.source.getchanges(rev)
234 if isinstance(changes, basestring):
235 if isinstance(changes, basestring):
235 if changes == SKIPREV:
236 if changes == SKIPREV:
236 dest = SKIPREV
237 dest = SKIPREV
237 else:
238 else:
238 dest = self.map[changes]
239 dest = self.map[changes]
239 self.map[rev] = dest
240 self.map[rev] = dest
240 return
241 return
241 files, copies = changes
242 files, copies = changes
242 pbranches = []
243 pbranches = []
243 if commit.parents:
244 if commit.parents:
244 for prev in commit.parents:
245 for prev in commit.parents:
245 if prev not in self.commitcache:
246 if prev not in self.commitcache:
246 self.cachecommit(prev)
247 self.cachecommit(prev)
247 pbranches.append((self.map[prev],
248 pbranches.append((self.map[prev],
248 self.commitcache[prev].branch))
249 self.commitcache[prev].branch))
249 self.dest.setbranch(commit.branch, pbranches)
250 self.dest.setbranch(commit.branch, pbranches)
250 try:
251 try:
251 parents = self.splicemap[rev].replace(',', ' ').split()
252 parents = self.splicemap[rev].replace(',', ' ').split()
252 self.ui.status(_('spliced in %s as parents of %s\n') %
253 self.ui.status(_('spliced in %s as parents of %s\n') %
253 (parents, rev))
254 (parents, rev))
254 parents = [self.map.get(p, p) for p in parents]
255 parents = [self.map.get(p, p) for p in parents]
255 except KeyError:
256 except KeyError:
256 parents = [b[0] for b in pbranches]
257 parents = [b[0] for b in pbranches]
257 newnode = self.dest.putcommit(files, copies, parents, commit, self.source)
258 newnode = self.dest.putcommit(files, copies, parents, commit, self.source)
258 self.source.converted(rev, newnode)
259 self.source.converted(rev, newnode)
259 self.map[rev] = newnode
260 self.map[rev] = newnode
260
261
261 def convert(self):
262 def convert(self):
262
263
263 try:
264 try:
264 self.source.before()
265 self.source.before()
265 self.dest.before()
266 self.dest.before()
266 self.source.setrevmap(self.map)
267 self.source.setrevmap(self.map)
267 self.ui.status(_("scanning source...\n"))
268 self.ui.status(_("scanning source...\n"))
268 heads = self.source.getheads()
269 heads = self.source.getheads()
269 parents = self.walktree(heads)
270 parents = self.walktree(heads)
270 self.ui.status(_("sorting...\n"))
271 self.ui.status(_("sorting...\n"))
271 t = self.toposort(parents)
272 t = self.toposort(parents)
272 num = len(t)
273 num = len(t)
273 c = None
274 c = None
274
275
275 self.ui.status(_("converting...\n"))
276 self.ui.status(_("converting...\n"))
276 for c in t:
277 for c in t:
277 num -= 1
278 num -= 1
278 desc = self.commitcache[c].desc
279 desc = self.commitcache[c].desc
279 if "\n" in desc:
280 if "\n" in desc:
280 desc = desc.splitlines()[0]
281 desc = desc.splitlines()[0]
281 # convert log message to local encoding without using
282 # convert log message to local encoding without using
282 # tolocal() because encoding.encoding conver() use it as
283 # tolocal() because encoding.encoding conver() use it as
283 # 'utf-8'
284 # 'utf-8'
284 self.ui.status("%d %s\n" % (num, recode(desc)))
285 self.ui.status("%d %s\n" % (num, recode(desc)))
285 self.ui.note(_("source: %s\n") % recode(c))
286 self.ui.note(_("source: %s\n") % recode(c))
286 self.copy(c)
287 self.copy(c)
287
288
288 tags = self.source.gettags()
289 tags = self.source.gettags()
289 ctags = {}
290 ctags = {}
290 for k in tags:
291 for k in tags:
291 v = tags[k]
292 v = tags[k]
292 if self.map.get(v, SKIPREV) != SKIPREV:
293 if self.map.get(v, SKIPREV) != SKIPREV:
293 ctags[k] = self.map[v]
294 ctags[k] = self.map[v]
294
295
295 if c and ctags:
296 if c and ctags:
296 nrev = self.dest.puttags(ctags)
297 nrev = self.dest.puttags(ctags)
297 # write another hash correspondence to override the previous
298 # write another hash correspondence to override the previous
298 # one so we don't end up with extra tag heads
299 # one so we don't end up with extra tag heads
299 if nrev:
300 if nrev:
300 self.map[c] = nrev
301 self.map[c] = nrev
301
302
302 self.writeauthormap()
303 self.writeauthormap()
303 finally:
304 finally:
304 self.cleanup()
305 self.cleanup()
305
306
306 def cleanup(self):
307 def cleanup(self):
307 try:
308 try:
308 self.dest.after()
309 self.dest.after()
309 finally:
310 finally:
310 self.source.after()
311 self.source.after()
311 self.map.close()
312 self.map.close()
312
313
313 def convert(ui, src, dest=None, revmapfile=None, **opts):
314 def convert(ui, src, dest=None, revmapfile=None, **opts):
314 global orig_encoding
315 global orig_encoding
315 orig_encoding = encoding.encoding
316 orig_encoding = encoding.encoding
316 encoding.encoding = 'UTF-8'
317 encoding.encoding = 'UTF-8'
317
318
318 if not dest:
319 if not dest:
319 dest = hg.defaultdest(src) + "-hg"
320 dest = hg.defaultdest(src) + "-hg"
320 ui.status(_("assuming destination %s\n") % dest)
321 ui.status(_("assuming destination %s\n") % dest)
321
322
322 destc = convertsink(ui, dest, opts.get('dest_type'))
323 destc = convertsink(ui, dest, opts.get('dest_type'))
323
324
324 try:
325 try:
325 srcc = convertsource(ui, src, opts.get('source_type'),
326 srcc = convertsource(ui, src, opts.get('source_type'),
326 opts.get('rev'))
327 opts.get('rev'))
327 except Exception:
328 except Exception:
328 for path in destc.created:
329 for path in destc.created:
329 shutil.rmtree(path, True)
330 shutil.rmtree(path, True)
330 raise
331 raise
331
332
332 fmap = opts.get('filemap')
333 fmap = opts.get('filemap')
333 if fmap:
334 if fmap:
334 srcc = filemap.filemap_source(ui, srcc, fmap)
335 srcc = filemap.filemap_source(ui, srcc, fmap)
335 destc.setfilemapmode(True)
336 destc.setfilemapmode(True)
336
337
337 if not revmapfile:
338 if not revmapfile:
338 try:
339 try:
339 revmapfile = destc.revmapfile()
340 revmapfile = destc.revmapfile()
340 except:
341 except:
341 revmapfile = os.path.join(destc, "map")
342 revmapfile = os.path.join(destc, "map")
342
343
343 c = converter(ui, srcc, destc, revmapfile, opts)
344 c = converter(ui, srcc, destc, revmapfile, opts)
344 c.convert()
345 c.convert()
345
346
@@ -1,36 +1,37 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
18
19 # comment
19 this line is ignored
20 this line is ignored
20 EOF
21 EOF
21
22
22 hg convert --authors authormap.txt orig new
23 hg convert --authors authormap.txt orig new
23 echo $?
24 echo $?
24 cat new/.hg/authormap
25 cat new/.hg/authormap
25
26
26 hg -Rnew log
27 hg -Rnew log
27 rm -rf new
28 rm -rf new
28
29
29 # Implicit .hg/authormap
30 # Implicit .hg/authormap
30 hg init new
31 hg init new
31 mv authormap.txt new/.hg/authormap
32 mv authormap.txt new/.hg/authormap
32
33
33 hg convert orig new
34 hg convert orig new
34 echo $?
35 echo $?
35
36
36 hg -Rnew log
37 hg -Rnew log
General Comments 0
You need to be logged in to leave comments. Login now