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