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