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