##// END OF EJS Templates
convert/svn: handle MissingTool exception from converter_sink class...
Azhagu Selvan SP -
r13479:b14ed169 stable
parent child Browse files
Show More
@@ -1,434 +1,436
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 of the
6 6 # GNU General Public License version 2 or any later version.
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, 'branchsort'),
34 34 ('git', convert_git, 'branchsort'),
35 35 ('svn', svn_source, 'branchsort'),
36 36 ('hg', mercurial_source, 'sourcesort'),
37 37 ('darcs', darcs_source, 'branchsort'),
38 38 ('mtn', monotone_source, 'branchsort'),
39 39 ('gnuarch', gnuarch_source, 'branchsort'),
40 40 ('bzr', bzr_source, 'branchsort'),
41 41 ('p4', p4_source, 'branchsort'),
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 if type and type not in [s[0] for s in source_converters]:
52 52 raise util.Abort(_('%s: invalid source repository type') % type)
53 53 for name, source, sortmode in source_converters:
54 54 try:
55 55 if not type or name == type:
56 56 return source(ui, path, rev), sortmode
57 57 except (NoRepo, MissingTool), inst:
58 58 exceptions.append(inst)
59 59 if not ui.quiet:
60 60 for inst in exceptions:
61 61 ui.write("%s\n" % inst)
62 62 raise util.Abort(_('%s: missing or unsupported repository') % path)
63 63
64 64 def convertsink(ui, path, type):
65 65 if type and type not in [s[0] for s in sink_converters]:
66 66 raise util.Abort(_('%s: invalid destination repository type') % type)
67 67 for name, sink in sink_converters:
68 68 try:
69 69 if not type or name == type:
70 70 return sink(ui, path)
71 71 except NoRepo, inst:
72 72 ui.note(_("convert: %s\n") % inst)
73 except MissingTool, inst:
74 raise util.Abort(_('%s\n') % inst)
73 75 raise util.Abort(_('%s: unknown repository type') % path)
74 76
75 77 class progresssource(object):
76 78 def __init__(self, ui, source, filecount):
77 79 self.ui = ui
78 80 self.source = source
79 81 self.filecount = filecount
80 82 self.retrieved = 0
81 83
82 84 def getfile(self, file, rev):
83 85 self.retrieved += 1
84 86 self.ui.progress(_('getting files'), self.retrieved,
85 87 item=file, total=self.filecount)
86 88 return self.source.getfile(file, rev)
87 89
88 90 def lookuprev(self, rev):
89 91 return self.source.lookuprev(rev)
90 92
91 93 def close(self):
92 94 self.ui.progress(_('getting files'), None)
93 95
94 96 class converter(object):
95 97 def __init__(self, ui, source, dest, revmapfile, opts):
96 98
97 99 self.source = source
98 100 self.dest = dest
99 101 self.ui = ui
100 102 self.opts = opts
101 103 self.commitcache = {}
102 104 self.authors = {}
103 105 self.authorfile = None
104 106
105 107 # Record converted revisions persistently: maps source revision
106 108 # ID to target revision ID (both strings). (This is how
107 109 # incremental conversions work.)
108 110 self.map = mapfile(ui, revmapfile)
109 111
110 112 # Read first the dst author map if any
111 113 authorfile = self.dest.authorfile()
112 114 if authorfile and os.path.exists(authorfile):
113 115 self.readauthormap(authorfile)
114 116 # Extend/Override with new author map if necessary
115 117 if opts.get('authormap'):
116 118 self.readauthormap(opts.get('authormap'))
117 119 self.authorfile = self.dest.authorfile()
118 120
119 121 self.splicemap = mapfile(ui, opts.get('splicemap'))
120 122 self.branchmap = mapfile(ui, opts.get('branchmap'))
121 123
122 124 def walktree(self, heads):
123 125 '''Return a mapping that identifies the uncommitted parents of every
124 126 uncommitted changeset.'''
125 127 visit = heads
126 128 known = set()
127 129 parents = {}
128 130 while visit:
129 131 n = visit.pop(0)
130 132 if n in known or n in self.map:
131 133 continue
132 134 known.add(n)
133 135 self.ui.progress(_('scanning'), len(known), unit=_('revisions'))
134 136 commit = self.cachecommit(n)
135 137 parents[n] = []
136 138 for p in commit.parents:
137 139 parents[n].append(p)
138 140 visit.append(p)
139 141 self.ui.progress(_('scanning'), None)
140 142
141 143 return parents
142 144
143 145 def toposort(self, parents, sortmode):
144 146 '''Return an ordering such that every uncommitted changeset is
145 147 preceeded by all its uncommitted ancestors.'''
146 148
147 149 def mapchildren(parents):
148 150 """Return a (children, roots) tuple where 'children' maps parent
149 151 revision identifiers to children ones, and 'roots' is the list of
150 152 revisions without parents. 'parents' must be a mapping of revision
151 153 identifier to its parents ones.
152 154 """
153 155 visit = parents.keys()
154 156 seen = set()
155 157 children = {}
156 158 roots = []
157 159
158 160 while visit:
159 161 n = visit.pop(0)
160 162 if n in seen:
161 163 continue
162 164 seen.add(n)
163 165 # Ensure that nodes without parents are present in the
164 166 # 'children' mapping.
165 167 children.setdefault(n, [])
166 168 hasparent = False
167 169 for p in parents[n]:
168 170 if not p in self.map:
169 171 visit.append(p)
170 172 hasparent = True
171 173 children.setdefault(p, []).append(n)
172 174 if not hasparent:
173 175 roots.append(n)
174 176
175 177 return children, roots
176 178
177 179 # Sort functions are supposed to take a list of revisions which
178 180 # can be converted immediately and pick one
179 181
180 182 def makebranchsorter():
181 183 """If the previously converted revision has a child in the
182 184 eligible revisions list, pick it. Return the list head
183 185 otherwise. Branch sort attempts to minimize branch
184 186 switching, which is harmful for Mercurial backend
185 187 compression.
186 188 """
187 189 prev = [None]
188 190 def picknext(nodes):
189 191 next = nodes[0]
190 192 for n in nodes:
191 193 if prev[0] in parents[n]:
192 194 next = n
193 195 break
194 196 prev[0] = next
195 197 return next
196 198 return picknext
197 199
198 200 def makesourcesorter():
199 201 """Source specific sort."""
200 202 keyfn = lambda n: self.commitcache[n].sortkey
201 203 def picknext(nodes):
202 204 return sorted(nodes, key=keyfn)[0]
203 205 return picknext
204 206
205 207 def makedatesorter():
206 208 """Sort revisions by date."""
207 209 dates = {}
208 210 def getdate(n):
209 211 if n not in dates:
210 212 dates[n] = util.parsedate(self.commitcache[n].date)
211 213 return dates[n]
212 214
213 215 def picknext(nodes):
214 216 return min([(getdate(n), n) for n in nodes])[1]
215 217
216 218 return picknext
217 219
218 220 if sortmode == 'branchsort':
219 221 picknext = makebranchsorter()
220 222 elif sortmode == 'datesort':
221 223 picknext = makedatesorter()
222 224 elif sortmode == 'sourcesort':
223 225 picknext = makesourcesorter()
224 226 else:
225 227 raise util.Abort(_('unknown sort mode: %s') % sortmode)
226 228
227 229 children, actives = mapchildren(parents)
228 230
229 231 s = []
230 232 pendings = {}
231 233 while actives:
232 234 n = picknext(actives)
233 235 actives.remove(n)
234 236 s.append(n)
235 237
236 238 # Update dependents list
237 239 for c in children.get(n, []):
238 240 if c not in pendings:
239 241 pendings[c] = [p for p in parents[c] if p not in self.map]
240 242 try:
241 243 pendings[c].remove(n)
242 244 except ValueError:
243 245 raise util.Abort(_('cycle detected between %s and %s')
244 246 % (recode(c), recode(n)))
245 247 if not pendings[c]:
246 248 # Parents are converted, node is eligible
247 249 actives.insert(0, c)
248 250 pendings[c] = None
249 251
250 252 if len(s) != len(parents):
251 253 raise util.Abort(_("not all revisions were sorted"))
252 254
253 255 return s
254 256
255 257 def writeauthormap(self):
256 258 authorfile = self.authorfile
257 259 if authorfile:
258 260 self.ui.status(_('Writing author map file %s\n') % authorfile)
259 261 ofile = open(authorfile, 'w+')
260 262 for author in self.authors:
261 263 ofile.write("%s=%s\n" % (author, self.authors[author]))
262 264 ofile.close()
263 265
264 266 def readauthormap(self, authorfile):
265 267 afile = open(authorfile, 'r')
266 268 for line in afile:
267 269
268 270 line = line.strip()
269 271 if not line or line.startswith('#'):
270 272 continue
271 273
272 274 try:
273 275 srcauthor, dstauthor = line.split('=', 1)
274 276 except ValueError:
275 277 msg = _('Ignoring bad line in author map file %s: %s\n')
276 278 self.ui.warn(msg % (authorfile, line.rstrip()))
277 279 continue
278 280
279 281 srcauthor = srcauthor.strip()
280 282 dstauthor = dstauthor.strip()
281 283 if self.authors.get(srcauthor) in (None, dstauthor):
282 284 msg = _('mapping author %s to %s\n')
283 285 self.ui.debug(msg % (srcauthor, dstauthor))
284 286 self.authors[srcauthor] = dstauthor
285 287 continue
286 288
287 289 m = _('overriding mapping for author %s, was %s, will be %s\n')
288 290 self.ui.status(m % (srcauthor, self.authors[srcauthor], dstauthor))
289 291
290 292 afile.close()
291 293
292 294 def cachecommit(self, rev):
293 295 commit = self.source.getcommit(rev)
294 296 commit.author = self.authors.get(commit.author, commit.author)
295 297 commit.branch = self.branchmap.get(commit.branch, commit.branch)
296 298 self.commitcache[rev] = commit
297 299 return commit
298 300
299 301 def copy(self, rev):
300 302 commit = self.commitcache[rev]
301 303
302 304 changes = self.source.getchanges(rev)
303 305 if isinstance(changes, basestring):
304 306 if changes == SKIPREV:
305 307 dest = SKIPREV
306 308 else:
307 309 dest = self.map[changes]
308 310 self.map[rev] = dest
309 311 return
310 312 files, copies = changes
311 313 pbranches = []
312 314 if commit.parents:
313 315 for prev in commit.parents:
314 316 if prev not in self.commitcache:
315 317 self.cachecommit(prev)
316 318 pbranches.append((self.map[prev],
317 319 self.commitcache[prev].branch))
318 320 self.dest.setbranch(commit.branch, pbranches)
319 321 try:
320 322 parents = self.splicemap[rev].replace(',', ' ').split()
321 323 self.ui.status(_('spliced in %s as parents of %s\n') %
322 324 (parents, rev))
323 325 parents = [self.map.get(p, p) for p in parents]
324 326 except KeyError:
325 327 parents = [b[0] for b in pbranches]
326 328 source = progresssource(self.ui, self.source, len(files))
327 329 newnode = self.dest.putcommit(files, copies, parents, commit,
328 330 source, self.map)
329 331 source.close()
330 332 self.source.converted(rev, newnode)
331 333 self.map[rev] = newnode
332 334
333 335 def convert(self, sortmode):
334 336 try:
335 337 self.source.before()
336 338 self.dest.before()
337 339 self.source.setrevmap(self.map)
338 340 self.ui.status(_("scanning source...\n"))
339 341 heads = self.source.getheads()
340 342 parents = self.walktree(heads)
341 343 self.ui.status(_("sorting...\n"))
342 344 t = self.toposort(parents, sortmode)
343 345 num = len(t)
344 346 c = None
345 347
346 348 self.ui.status(_("converting...\n"))
347 349 for i, c in enumerate(t):
348 350 num -= 1
349 351 desc = self.commitcache[c].desc
350 352 if "\n" in desc:
351 353 desc = desc.splitlines()[0]
352 354 # convert log message to local encoding without using
353 355 # tolocal() because the encoding.encoding convert()
354 356 # uses is 'utf-8'
355 357 self.ui.status("%d %s\n" % (num, recode(desc)))
356 358 self.ui.note(_("source: %s\n") % recode(c))
357 359 self.ui.progress(_('converting'), i, unit=_('revisions'),
358 360 total=len(t))
359 361 self.copy(c)
360 362 self.ui.progress(_('converting'), None)
361 363
362 364 tags = self.source.gettags()
363 365 ctags = {}
364 366 for k in tags:
365 367 v = tags[k]
366 368 if self.map.get(v, SKIPREV) != SKIPREV:
367 369 ctags[k] = self.map[v]
368 370
369 371 if c and ctags:
370 372 nrev, tagsparent = self.dest.puttags(ctags)
371 373 if nrev and tagsparent:
372 374 # write another hash correspondence to override the previous
373 375 # one so we don't end up with extra tag heads
374 376 tagsparents = [e for e in self.map.iteritems()
375 377 if e[1] == tagsparent]
376 378 if tagsparents:
377 379 self.map[tagsparents[0][0]] = nrev
378 380
379 381 self.writeauthormap()
380 382 finally:
381 383 self.cleanup()
382 384
383 385 def cleanup(self):
384 386 try:
385 387 self.dest.after()
386 388 finally:
387 389 self.source.after()
388 390 self.map.close()
389 391
390 392 def convert(ui, src, dest=None, revmapfile=None, **opts):
391 393 global orig_encoding
392 394 orig_encoding = encoding.encoding
393 395 encoding.encoding = 'UTF-8'
394 396
395 397 # support --authors as an alias for --authormap
396 398 if not opts.get('authormap'):
397 399 opts['authormap'] = opts.get('authors')
398 400
399 401 if not dest:
400 402 dest = hg.defaultdest(src) + "-hg"
401 403 ui.status(_("assuming destination %s\n") % dest)
402 404
403 405 destc = convertsink(ui, dest, opts.get('dest_type'))
404 406
405 407 try:
406 408 srcc, defaultsort = convertsource(ui, src, opts.get('source_type'),
407 409 opts.get('rev'))
408 410 except Exception:
409 411 for path in destc.created:
410 412 shutil.rmtree(path, True)
411 413 raise
412 414
413 415 sortmodes = ('branchsort', 'datesort', 'sourcesort')
414 416 sortmode = [m for m in sortmodes if opts.get(m)]
415 417 if len(sortmode) > 1:
416 418 raise util.Abort(_('more than one sort mode specified'))
417 419 sortmode = sortmode and sortmode[0] or defaultsort
418 420 if sortmode == 'sourcesort' and not srcc.hasnativeorder():
419 421 raise util.Abort(_('--sourcesort is not supported by this data source'))
420 422
421 423 fmap = opts.get('filemap')
422 424 if fmap:
423 425 srcc = filemap.filemap_source(ui, srcc, fmap)
424 426 destc.setfilemapmode(True)
425 427
426 428 if not revmapfile:
427 429 try:
428 430 revmapfile = destc.revmapfile()
429 431 except:
430 432 revmapfile = os.path.join(destc, "map")
431 433
432 434 c = converter(ui, srcc, destc, revmapfile, opts)
433 435 c.convert(sortmode)
434 436
General Comments 0
You need to be logged in to leave comments. Login now