##// END OF EJS Templates
py3: replace str(None) with literal in convcmd.py
Yuya Nishihara -
r34355:ee10eb66 default
parent child Browse files
Show More
@@ -1,611 +1,611 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 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import os
9 import os
10 import shlex
10 import shlex
11 import shutil
11 import shutil
12
12
13 from mercurial.i18n import _
13 from mercurial.i18n import _
14 from mercurial import (
14 from mercurial import (
15 encoding,
15 encoding,
16 error,
16 error,
17 hg,
17 hg,
18 util,
18 util,
19 )
19 )
20
20
21 from . import (
21 from . import (
22 bzr,
22 bzr,
23 common,
23 common,
24 cvs,
24 cvs,
25 darcs,
25 darcs,
26 filemap,
26 filemap,
27 git,
27 git,
28 gnuarch,
28 gnuarch,
29 hg as hgconvert,
29 hg as hgconvert,
30 monotone,
30 monotone,
31 p4,
31 p4,
32 subversion,
32 subversion,
33 )
33 )
34
34
35 mapfile = common.mapfile
35 mapfile = common.mapfile
36 MissingTool = common.MissingTool
36 MissingTool = common.MissingTool
37 NoRepo = common.NoRepo
37 NoRepo = common.NoRepo
38 SKIPREV = common.SKIPREV
38 SKIPREV = common.SKIPREV
39
39
40 bzr_source = bzr.bzr_source
40 bzr_source = bzr.bzr_source
41 convert_cvs = cvs.convert_cvs
41 convert_cvs = cvs.convert_cvs
42 convert_git = git.convert_git
42 convert_git = git.convert_git
43 darcs_source = darcs.darcs_source
43 darcs_source = darcs.darcs_source
44 gnuarch_source = gnuarch.gnuarch_source
44 gnuarch_source = gnuarch.gnuarch_source
45 mercurial_sink = hgconvert.mercurial_sink
45 mercurial_sink = hgconvert.mercurial_sink
46 mercurial_source = hgconvert.mercurial_source
46 mercurial_source = hgconvert.mercurial_source
47 monotone_source = monotone.monotone_source
47 monotone_source = monotone.monotone_source
48 p4_source = p4.p4_source
48 p4_source = p4.p4_source
49 svn_sink = subversion.svn_sink
49 svn_sink = subversion.svn_sink
50 svn_source = subversion.svn_source
50 svn_source = subversion.svn_source
51
51
52 orig_encoding = 'ascii'
52 orig_encoding = 'ascii'
53
53
54 def recode(s):
54 def recode(s):
55 if isinstance(s, unicode):
55 if isinstance(s, unicode):
56 return s.encode(orig_encoding, 'replace')
56 return s.encode(orig_encoding, 'replace')
57 else:
57 else:
58 return s.decode('utf-8').encode(orig_encoding, 'replace')
58 return s.decode('utf-8').encode(orig_encoding, 'replace')
59
59
60 def mapbranch(branch, branchmap):
60 def mapbranch(branch, branchmap):
61 '''
61 '''
62 >>> bmap = {b'default': b'branch1'}
62 >>> bmap = {b'default': b'branch1'}
63 >>> for i in [b'', None]:
63 >>> for i in [b'', None]:
64 ... mapbranch(i, bmap)
64 ... mapbranch(i, bmap)
65 'branch1'
65 'branch1'
66 'branch1'
66 'branch1'
67 >>> bmap = {b'None': b'branch2'}
67 >>> bmap = {b'None': b'branch2'}
68 >>> for i in [b'', None]:
68 >>> for i in [b'', None]:
69 ... mapbranch(i, bmap)
69 ... mapbranch(i, bmap)
70 'branch2'
70 'branch2'
71 'branch2'
71 'branch2'
72 >>> bmap = {b'None': b'branch3', b'default': b'branch4'}
72 >>> bmap = {b'None': b'branch3', b'default': b'branch4'}
73 >>> for i in [b'None', b'', None, b'default', b'branch5']:
73 >>> for i in [b'None', b'', None, b'default', b'branch5']:
74 ... mapbranch(i, bmap)
74 ... mapbranch(i, bmap)
75 'branch3'
75 'branch3'
76 'branch4'
76 'branch4'
77 'branch4'
77 'branch4'
78 'branch4'
78 'branch4'
79 'branch5'
79 'branch5'
80 '''
80 '''
81 # If branch is None or empty, this commit is coming from the source
81 # If branch is None or empty, this commit is coming from the source
82 # repository's default branch and destined for the default branch in the
82 # repository's default branch and destined for the default branch in the
83 # destination repository. For such commits, using a literal "default"
83 # destination repository. For such commits, using a literal "default"
84 # in branchmap below allows the user to map "default" to an alternate
84 # in branchmap below allows the user to map "default" to an alternate
85 # default branch in the destination repository.
85 # default branch in the destination repository.
86 branch = branchmap.get(branch or 'default', branch)
86 branch = branchmap.get(branch or 'default', branch)
87 # At some point we used "None" literal to denote the default branch,
87 # At some point we used "None" literal to denote the default branch,
88 # attempt to use that for backward compatibility.
88 # attempt to use that for backward compatibility.
89 if (not branch):
89 if (not branch):
90 branch = branchmap.get(str(None), branch)
90 branch = branchmap.get('None', branch)
91 return branch
91 return branch
92
92
93 source_converters = [
93 source_converters = [
94 ('cvs', convert_cvs, 'branchsort'),
94 ('cvs', convert_cvs, 'branchsort'),
95 ('git', convert_git, 'branchsort'),
95 ('git', convert_git, 'branchsort'),
96 ('svn', svn_source, 'branchsort'),
96 ('svn', svn_source, 'branchsort'),
97 ('hg', mercurial_source, 'sourcesort'),
97 ('hg', mercurial_source, 'sourcesort'),
98 ('darcs', darcs_source, 'branchsort'),
98 ('darcs', darcs_source, 'branchsort'),
99 ('mtn', monotone_source, 'branchsort'),
99 ('mtn', monotone_source, 'branchsort'),
100 ('gnuarch', gnuarch_source, 'branchsort'),
100 ('gnuarch', gnuarch_source, 'branchsort'),
101 ('bzr', bzr_source, 'branchsort'),
101 ('bzr', bzr_source, 'branchsort'),
102 ('p4', p4_source, 'branchsort'),
102 ('p4', p4_source, 'branchsort'),
103 ]
103 ]
104
104
105 sink_converters = [
105 sink_converters = [
106 ('hg', mercurial_sink),
106 ('hg', mercurial_sink),
107 ('svn', svn_sink),
107 ('svn', svn_sink),
108 ]
108 ]
109
109
110 def convertsource(ui, path, type, revs):
110 def convertsource(ui, path, type, revs):
111 exceptions = []
111 exceptions = []
112 if type and type not in [s[0] for s in source_converters]:
112 if type and type not in [s[0] for s in source_converters]:
113 raise error.Abort(_('%s: invalid source repository type') % type)
113 raise error.Abort(_('%s: invalid source repository type') % type)
114 for name, source, sortmode in source_converters:
114 for name, source, sortmode in source_converters:
115 try:
115 try:
116 if not type or name == type:
116 if not type or name == type:
117 return source(ui, path, revs), sortmode
117 return source(ui, path, revs), sortmode
118 except (NoRepo, MissingTool) as inst:
118 except (NoRepo, MissingTool) as inst:
119 exceptions.append(inst)
119 exceptions.append(inst)
120 if not ui.quiet:
120 if not ui.quiet:
121 for inst in exceptions:
121 for inst in exceptions:
122 ui.write("%s\n" % inst)
122 ui.write("%s\n" % inst)
123 raise error.Abort(_('%s: missing or unsupported repository') % path)
123 raise error.Abort(_('%s: missing or unsupported repository') % path)
124
124
125 def convertsink(ui, path, type):
125 def convertsink(ui, path, type):
126 if type and type not in [s[0] for s in sink_converters]:
126 if type and type not in [s[0] for s in sink_converters]:
127 raise error.Abort(_('%s: invalid destination repository type') % type)
127 raise error.Abort(_('%s: invalid destination repository type') % type)
128 for name, sink in sink_converters:
128 for name, sink in sink_converters:
129 try:
129 try:
130 if not type or name == type:
130 if not type or name == type:
131 return sink(ui, path)
131 return sink(ui, path)
132 except NoRepo as inst:
132 except NoRepo as inst:
133 ui.note(_("convert: %s\n") % inst)
133 ui.note(_("convert: %s\n") % inst)
134 except MissingTool as inst:
134 except MissingTool as inst:
135 raise error.Abort('%s\n' % inst)
135 raise error.Abort('%s\n' % inst)
136 raise error.Abort(_('%s: unknown repository type') % path)
136 raise error.Abort(_('%s: unknown repository type') % path)
137
137
138 class progresssource(object):
138 class progresssource(object):
139 def __init__(self, ui, source, filecount):
139 def __init__(self, ui, source, filecount):
140 self.ui = ui
140 self.ui = ui
141 self.source = source
141 self.source = source
142 self.filecount = filecount
142 self.filecount = filecount
143 self.retrieved = 0
143 self.retrieved = 0
144
144
145 def getfile(self, file, rev):
145 def getfile(self, file, rev):
146 self.retrieved += 1
146 self.retrieved += 1
147 self.ui.progress(_('getting files'), self.retrieved,
147 self.ui.progress(_('getting files'), self.retrieved,
148 item=file, total=self.filecount, unit=_('files'))
148 item=file, total=self.filecount, unit=_('files'))
149 return self.source.getfile(file, rev)
149 return self.source.getfile(file, rev)
150
150
151 def targetfilebelongstosource(self, targetfilename):
151 def targetfilebelongstosource(self, targetfilename):
152 return self.source.targetfilebelongstosource(targetfilename)
152 return self.source.targetfilebelongstosource(targetfilename)
153
153
154 def lookuprev(self, rev):
154 def lookuprev(self, rev):
155 return self.source.lookuprev(rev)
155 return self.source.lookuprev(rev)
156
156
157 def close(self):
157 def close(self):
158 self.ui.progress(_('getting files'), None)
158 self.ui.progress(_('getting files'), None)
159
159
160 class converter(object):
160 class converter(object):
161 def __init__(self, ui, source, dest, revmapfile, opts):
161 def __init__(self, ui, source, dest, revmapfile, opts):
162
162
163 self.source = source
163 self.source = source
164 self.dest = dest
164 self.dest = dest
165 self.ui = ui
165 self.ui = ui
166 self.opts = opts
166 self.opts = opts
167 self.commitcache = {}
167 self.commitcache = {}
168 self.authors = {}
168 self.authors = {}
169 self.authorfile = None
169 self.authorfile = None
170
170
171 # Record converted revisions persistently: maps source revision
171 # Record converted revisions persistently: maps source revision
172 # ID to target revision ID (both strings). (This is how
172 # ID to target revision ID (both strings). (This is how
173 # incremental conversions work.)
173 # incremental conversions work.)
174 self.map = mapfile(ui, revmapfile)
174 self.map = mapfile(ui, revmapfile)
175
175
176 # Read first the dst author map if any
176 # Read first the dst author map if any
177 authorfile = self.dest.authorfile()
177 authorfile = self.dest.authorfile()
178 if authorfile and os.path.exists(authorfile):
178 if authorfile and os.path.exists(authorfile):
179 self.readauthormap(authorfile)
179 self.readauthormap(authorfile)
180 # Extend/Override with new author map if necessary
180 # Extend/Override with new author map if necessary
181 if opts.get('authormap'):
181 if opts.get('authormap'):
182 self.readauthormap(opts.get('authormap'))
182 self.readauthormap(opts.get('authormap'))
183 self.authorfile = self.dest.authorfile()
183 self.authorfile = self.dest.authorfile()
184
184
185 self.splicemap = self.parsesplicemap(opts.get('splicemap'))
185 self.splicemap = self.parsesplicemap(opts.get('splicemap'))
186 self.branchmap = mapfile(ui, opts.get('branchmap'))
186 self.branchmap = mapfile(ui, opts.get('branchmap'))
187
187
188 def parsesplicemap(self, path):
188 def parsesplicemap(self, path):
189 """ check and validate the splicemap format and
189 """ check and validate the splicemap format and
190 return a child/parents dictionary.
190 return a child/parents dictionary.
191 Format checking has two parts.
191 Format checking has two parts.
192 1. generic format which is same across all source types
192 1. generic format which is same across all source types
193 2. specific format checking which may be different for
193 2. specific format checking which may be different for
194 different source type. This logic is implemented in
194 different source type. This logic is implemented in
195 checkrevformat function in source files like
195 checkrevformat function in source files like
196 hg.py, subversion.py etc.
196 hg.py, subversion.py etc.
197 """
197 """
198
198
199 if not path:
199 if not path:
200 return {}
200 return {}
201 m = {}
201 m = {}
202 try:
202 try:
203 fp = open(path, 'r')
203 fp = open(path, 'r')
204 for i, line in enumerate(util.iterfile(fp)):
204 for i, line in enumerate(util.iterfile(fp)):
205 line = line.splitlines()[0].rstrip()
205 line = line.splitlines()[0].rstrip()
206 if not line:
206 if not line:
207 # Ignore blank lines
207 # Ignore blank lines
208 continue
208 continue
209 # split line
209 # split line
210 lex = shlex.shlex(line, posix=True)
210 lex = shlex.shlex(line, posix=True)
211 lex.whitespace_split = True
211 lex.whitespace_split = True
212 lex.whitespace += ','
212 lex.whitespace += ','
213 line = list(lex)
213 line = list(lex)
214 # check number of parents
214 # check number of parents
215 if not (2 <= len(line) <= 3):
215 if not (2 <= len(line) <= 3):
216 raise error.Abort(_('syntax error in %s(%d): child parent1'
216 raise error.Abort(_('syntax error in %s(%d): child parent1'
217 '[,parent2] expected') % (path, i + 1))
217 '[,parent2] expected') % (path, i + 1))
218 for part in line:
218 for part in line:
219 self.source.checkrevformat(part)
219 self.source.checkrevformat(part)
220 child, p1, p2 = line[0], line[1:2], line[2:]
220 child, p1, p2 = line[0], line[1:2], line[2:]
221 if p1 == p2:
221 if p1 == p2:
222 m[child] = p1
222 m[child] = p1
223 else:
223 else:
224 m[child] = p1 + p2
224 m[child] = p1 + p2
225 # if file does not exist or error reading, exit
225 # if file does not exist or error reading, exit
226 except IOError:
226 except IOError:
227 raise error.Abort(_('splicemap file not found or error reading %s:')
227 raise error.Abort(_('splicemap file not found or error reading %s:')
228 % path)
228 % path)
229 return m
229 return m
230
230
231
231
232 def walktree(self, heads):
232 def walktree(self, heads):
233 '''Return a mapping that identifies the uncommitted parents of every
233 '''Return a mapping that identifies the uncommitted parents of every
234 uncommitted changeset.'''
234 uncommitted changeset.'''
235 visit = heads
235 visit = heads
236 known = set()
236 known = set()
237 parents = {}
237 parents = {}
238 numcommits = self.source.numcommits()
238 numcommits = self.source.numcommits()
239 while visit:
239 while visit:
240 n = visit.pop(0)
240 n = visit.pop(0)
241 if n in known:
241 if n in known:
242 continue
242 continue
243 if n in self.map:
243 if n in self.map:
244 m = self.map[n]
244 m = self.map[n]
245 if m == SKIPREV or self.dest.hascommitfrommap(m):
245 if m == SKIPREV or self.dest.hascommitfrommap(m):
246 continue
246 continue
247 known.add(n)
247 known.add(n)
248 self.ui.progress(_('scanning'), len(known), unit=_('revisions'),
248 self.ui.progress(_('scanning'), len(known), unit=_('revisions'),
249 total=numcommits)
249 total=numcommits)
250 commit = self.cachecommit(n)
250 commit = self.cachecommit(n)
251 parents[n] = []
251 parents[n] = []
252 for p in commit.parents:
252 for p in commit.parents:
253 parents[n].append(p)
253 parents[n].append(p)
254 visit.append(p)
254 visit.append(p)
255 self.ui.progress(_('scanning'), None)
255 self.ui.progress(_('scanning'), None)
256
256
257 return parents
257 return parents
258
258
259 def mergesplicemap(self, parents, splicemap):
259 def mergesplicemap(self, parents, splicemap):
260 """A splicemap redefines child/parent relationships. Check the
260 """A splicemap redefines child/parent relationships. Check the
261 map contains valid revision identifiers and merge the new
261 map contains valid revision identifiers and merge the new
262 links in the source graph.
262 links in the source graph.
263 """
263 """
264 for c in sorted(splicemap):
264 for c in sorted(splicemap):
265 if c not in parents:
265 if c not in parents:
266 if not self.dest.hascommitforsplicemap(self.map.get(c, c)):
266 if not self.dest.hascommitforsplicemap(self.map.get(c, c)):
267 # Could be in source but not converted during this run
267 # Could be in source but not converted during this run
268 self.ui.warn(_('splice map revision %s is not being '
268 self.ui.warn(_('splice map revision %s is not being '
269 'converted, ignoring\n') % c)
269 'converted, ignoring\n') % c)
270 continue
270 continue
271 pc = []
271 pc = []
272 for p in splicemap[c]:
272 for p in splicemap[c]:
273 # We do not have to wait for nodes already in dest.
273 # We do not have to wait for nodes already in dest.
274 if self.dest.hascommitforsplicemap(self.map.get(p, p)):
274 if self.dest.hascommitforsplicemap(self.map.get(p, p)):
275 continue
275 continue
276 # Parent is not in dest and not being converted, not good
276 # Parent is not in dest and not being converted, not good
277 if p not in parents:
277 if p not in parents:
278 raise error.Abort(_('unknown splice map parent: %s') % p)
278 raise error.Abort(_('unknown splice map parent: %s') % p)
279 pc.append(p)
279 pc.append(p)
280 parents[c] = pc
280 parents[c] = pc
281
281
282 def toposort(self, parents, sortmode):
282 def toposort(self, parents, sortmode):
283 '''Return an ordering such that every uncommitted changeset is
283 '''Return an ordering such that every uncommitted changeset is
284 preceded by all its uncommitted ancestors.'''
284 preceded by all its uncommitted ancestors.'''
285
285
286 def mapchildren(parents):
286 def mapchildren(parents):
287 """Return a (children, roots) tuple where 'children' maps parent
287 """Return a (children, roots) tuple where 'children' maps parent
288 revision identifiers to children ones, and 'roots' is the list of
288 revision identifiers to children ones, and 'roots' is the list of
289 revisions without parents. 'parents' must be a mapping of revision
289 revisions without parents. 'parents' must be a mapping of revision
290 identifier to its parents ones.
290 identifier to its parents ones.
291 """
291 """
292 visit = sorted(parents)
292 visit = sorted(parents)
293 seen = set()
293 seen = set()
294 children = {}
294 children = {}
295 roots = []
295 roots = []
296
296
297 while visit:
297 while visit:
298 n = visit.pop(0)
298 n = visit.pop(0)
299 if n in seen:
299 if n in seen:
300 continue
300 continue
301 seen.add(n)
301 seen.add(n)
302 # Ensure that nodes without parents are present in the
302 # Ensure that nodes without parents are present in the
303 # 'children' mapping.
303 # 'children' mapping.
304 children.setdefault(n, [])
304 children.setdefault(n, [])
305 hasparent = False
305 hasparent = False
306 for p in parents[n]:
306 for p in parents[n]:
307 if p not in self.map:
307 if p not in self.map:
308 visit.append(p)
308 visit.append(p)
309 hasparent = True
309 hasparent = True
310 children.setdefault(p, []).append(n)
310 children.setdefault(p, []).append(n)
311 if not hasparent:
311 if not hasparent:
312 roots.append(n)
312 roots.append(n)
313
313
314 return children, roots
314 return children, roots
315
315
316 # Sort functions are supposed to take a list of revisions which
316 # Sort functions are supposed to take a list of revisions which
317 # can be converted immediately and pick one
317 # can be converted immediately and pick one
318
318
319 def makebranchsorter():
319 def makebranchsorter():
320 """If the previously converted revision has a child in the
320 """If the previously converted revision has a child in the
321 eligible revisions list, pick it. Return the list head
321 eligible revisions list, pick it. Return the list head
322 otherwise. Branch sort attempts to minimize branch
322 otherwise. Branch sort attempts to minimize branch
323 switching, which is harmful for Mercurial backend
323 switching, which is harmful for Mercurial backend
324 compression.
324 compression.
325 """
325 """
326 prev = [None]
326 prev = [None]
327 def picknext(nodes):
327 def picknext(nodes):
328 next = nodes[0]
328 next = nodes[0]
329 for n in nodes:
329 for n in nodes:
330 if prev[0] in parents[n]:
330 if prev[0] in parents[n]:
331 next = n
331 next = n
332 break
332 break
333 prev[0] = next
333 prev[0] = next
334 return next
334 return next
335 return picknext
335 return picknext
336
336
337 def makesourcesorter():
337 def makesourcesorter():
338 """Source specific sort."""
338 """Source specific sort."""
339 keyfn = lambda n: self.commitcache[n].sortkey
339 keyfn = lambda n: self.commitcache[n].sortkey
340 def picknext(nodes):
340 def picknext(nodes):
341 return sorted(nodes, key=keyfn)[0]
341 return sorted(nodes, key=keyfn)[0]
342 return picknext
342 return picknext
343
343
344 def makeclosesorter():
344 def makeclosesorter():
345 """Close order sort."""
345 """Close order sort."""
346 keyfn = lambda n: ('close' not in self.commitcache[n].extra,
346 keyfn = lambda n: ('close' not in self.commitcache[n].extra,
347 self.commitcache[n].sortkey)
347 self.commitcache[n].sortkey)
348 def picknext(nodes):
348 def picknext(nodes):
349 return sorted(nodes, key=keyfn)[0]
349 return sorted(nodes, key=keyfn)[0]
350 return picknext
350 return picknext
351
351
352 def makedatesorter():
352 def makedatesorter():
353 """Sort revisions by date."""
353 """Sort revisions by date."""
354 dates = {}
354 dates = {}
355 def getdate(n):
355 def getdate(n):
356 if n not in dates:
356 if n not in dates:
357 dates[n] = util.parsedate(self.commitcache[n].date)
357 dates[n] = util.parsedate(self.commitcache[n].date)
358 return dates[n]
358 return dates[n]
359
359
360 def picknext(nodes):
360 def picknext(nodes):
361 return min([(getdate(n), n) for n in nodes])[1]
361 return min([(getdate(n), n) for n in nodes])[1]
362
362
363 return picknext
363 return picknext
364
364
365 if sortmode == 'branchsort':
365 if sortmode == 'branchsort':
366 picknext = makebranchsorter()
366 picknext = makebranchsorter()
367 elif sortmode == 'datesort':
367 elif sortmode == 'datesort':
368 picknext = makedatesorter()
368 picknext = makedatesorter()
369 elif sortmode == 'sourcesort':
369 elif sortmode == 'sourcesort':
370 picknext = makesourcesorter()
370 picknext = makesourcesorter()
371 elif sortmode == 'closesort':
371 elif sortmode == 'closesort':
372 picknext = makeclosesorter()
372 picknext = makeclosesorter()
373 else:
373 else:
374 raise error.Abort(_('unknown sort mode: %s') % sortmode)
374 raise error.Abort(_('unknown sort mode: %s') % sortmode)
375
375
376 children, actives = mapchildren(parents)
376 children, actives = mapchildren(parents)
377
377
378 s = []
378 s = []
379 pendings = {}
379 pendings = {}
380 while actives:
380 while actives:
381 n = picknext(actives)
381 n = picknext(actives)
382 actives.remove(n)
382 actives.remove(n)
383 s.append(n)
383 s.append(n)
384
384
385 # Update dependents list
385 # Update dependents list
386 for c in children.get(n, []):
386 for c in children.get(n, []):
387 if c not in pendings:
387 if c not in pendings:
388 pendings[c] = [p for p in parents[c] if p not in self.map]
388 pendings[c] = [p for p in parents[c] if p not in self.map]
389 try:
389 try:
390 pendings[c].remove(n)
390 pendings[c].remove(n)
391 except ValueError:
391 except ValueError:
392 raise error.Abort(_('cycle detected between %s and %s')
392 raise error.Abort(_('cycle detected between %s and %s')
393 % (recode(c), recode(n)))
393 % (recode(c), recode(n)))
394 if not pendings[c]:
394 if not pendings[c]:
395 # Parents are converted, node is eligible
395 # Parents are converted, node is eligible
396 actives.insert(0, c)
396 actives.insert(0, c)
397 pendings[c] = None
397 pendings[c] = None
398
398
399 if len(s) != len(parents):
399 if len(s) != len(parents):
400 raise error.Abort(_("not all revisions were sorted"))
400 raise error.Abort(_("not all revisions were sorted"))
401
401
402 return s
402 return s
403
403
404 def writeauthormap(self):
404 def writeauthormap(self):
405 authorfile = self.authorfile
405 authorfile = self.authorfile
406 if authorfile:
406 if authorfile:
407 self.ui.status(_('writing author map file %s\n') % authorfile)
407 self.ui.status(_('writing author map file %s\n') % authorfile)
408 ofile = open(authorfile, 'w+')
408 ofile = open(authorfile, 'w+')
409 for author in self.authors:
409 for author in self.authors:
410 ofile.write("%s=%s\n" % (author, self.authors[author]))
410 ofile.write("%s=%s\n" % (author, self.authors[author]))
411 ofile.close()
411 ofile.close()
412
412
413 def readauthormap(self, authorfile):
413 def readauthormap(self, authorfile):
414 afile = open(authorfile, 'r')
414 afile = open(authorfile, 'r')
415 for line in afile:
415 for line in afile:
416
416
417 line = line.strip()
417 line = line.strip()
418 if not line or line.startswith('#'):
418 if not line or line.startswith('#'):
419 continue
419 continue
420
420
421 try:
421 try:
422 srcauthor, dstauthor = line.split('=', 1)
422 srcauthor, dstauthor = line.split('=', 1)
423 except ValueError:
423 except ValueError:
424 msg = _('ignoring bad line in author map file %s: %s\n')
424 msg = _('ignoring bad line in author map file %s: %s\n')
425 self.ui.warn(msg % (authorfile, line.rstrip()))
425 self.ui.warn(msg % (authorfile, line.rstrip()))
426 continue
426 continue
427
427
428 srcauthor = srcauthor.strip()
428 srcauthor = srcauthor.strip()
429 dstauthor = dstauthor.strip()
429 dstauthor = dstauthor.strip()
430 if self.authors.get(srcauthor) in (None, dstauthor):
430 if self.authors.get(srcauthor) in (None, dstauthor):
431 msg = _('mapping author %s to %s\n')
431 msg = _('mapping author %s to %s\n')
432 self.ui.debug(msg % (srcauthor, dstauthor))
432 self.ui.debug(msg % (srcauthor, dstauthor))
433 self.authors[srcauthor] = dstauthor
433 self.authors[srcauthor] = dstauthor
434 continue
434 continue
435
435
436 m = _('overriding mapping for author %s, was %s, will be %s\n')
436 m = _('overriding mapping for author %s, was %s, will be %s\n')
437 self.ui.status(m % (srcauthor, self.authors[srcauthor], dstauthor))
437 self.ui.status(m % (srcauthor, self.authors[srcauthor], dstauthor))
438
438
439 afile.close()
439 afile.close()
440
440
441 def cachecommit(self, rev):
441 def cachecommit(self, rev):
442 commit = self.source.getcommit(rev)
442 commit = self.source.getcommit(rev)
443 commit.author = self.authors.get(commit.author, commit.author)
443 commit.author = self.authors.get(commit.author, commit.author)
444 commit.branch = mapbranch(commit.branch, self.branchmap)
444 commit.branch = mapbranch(commit.branch, self.branchmap)
445 self.commitcache[rev] = commit
445 self.commitcache[rev] = commit
446 return commit
446 return commit
447
447
448 def copy(self, rev):
448 def copy(self, rev):
449 commit = self.commitcache[rev]
449 commit = self.commitcache[rev]
450 full = self.opts.get('full')
450 full = self.opts.get('full')
451 changes = self.source.getchanges(rev, full)
451 changes = self.source.getchanges(rev, full)
452 if isinstance(changes, basestring):
452 if isinstance(changes, basestring):
453 if changes == SKIPREV:
453 if changes == SKIPREV:
454 dest = SKIPREV
454 dest = SKIPREV
455 else:
455 else:
456 dest = self.map[changes]
456 dest = self.map[changes]
457 self.map[rev] = dest
457 self.map[rev] = dest
458 return
458 return
459 files, copies, cleanp2 = changes
459 files, copies, cleanp2 = changes
460 pbranches = []
460 pbranches = []
461 if commit.parents:
461 if commit.parents:
462 for prev in commit.parents:
462 for prev in commit.parents:
463 if prev not in self.commitcache:
463 if prev not in self.commitcache:
464 self.cachecommit(prev)
464 self.cachecommit(prev)
465 pbranches.append((self.map[prev],
465 pbranches.append((self.map[prev],
466 self.commitcache[prev].branch))
466 self.commitcache[prev].branch))
467 self.dest.setbranch(commit.branch, pbranches)
467 self.dest.setbranch(commit.branch, pbranches)
468 try:
468 try:
469 parents = self.splicemap[rev]
469 parents = self.splicemap[rev]
470 self.ui.status(_('spliced in %s as parents of %s\n') %
470 self.ui.status(_('spliced in %s as parents of %s\n') %
471 (_(' and ').join(parents), rev))
471 (_(' and ').join(parents), rev))
472 parents = [self.map.get(p, p) for p in parents]
472 parents = [self.map.get(p, p) for p in parents]
473 except KeyError:
473 except KeyError:
474 parents = [b[0] for b in pbranches]
474 parents = [b[0] for b in pbranches]
475 parents.extend(self.map[x]
475 parents.extend(self.map[x]
476 for x in commit.optparents
476 for x in commit.optparents
477 if x in self.map)
477 if x in self.map)
478 if len(pbranches) != 2:
478 if len(pbranches) != 2:
479 cleanp2 = set()
479 cleanp2 = set()
480 if len(parents) < 3:
480 if len(parents) < 3:
481 source = progresssource(self.ui, self.source, len(files))
481 source = progresssource(self.ui, self.source, len(files))
482 else:
482 else:
483 # For an octopus merge, we end up traversing the list of
483 # For an octopus merge, we end up traversing the list of
484 # changed files N-1 times. This tweak to the number of
484 # changed files N-1 times. This tweak to the number of
485 # files makes it so the progress bar doesn't overflow
485 # files makes it so the progress bar doesn't overflow
486 # itself.
486 # itself.
487 source = progresssource(self.ui, self.source,
487 source = progresssource(self.ui, self.source,
488 len(files) * (len(parents) - 1))
488 len(files) * (len(parents) - 1))
489 newnode = self.dest.putcommit(files, copies, parents, commit,
489 newnode = self.dest.putcommit(files, copies, parents, commit,
490 source, self.map, full, cleanp2)
490 source, self.map, full, cleanp2)
491 source.close()
491 source.close()
492 self.source.converted(rev, newnode)
492 self.source.converted(rev, newnode)
493 self.map[rev] = newnode
493 self.map[rev] = newnode
494
494
495 def convert(self, sortmode):
495 def convert(self, sortmode):
496 try:
496 try:
497 self.source.before()
497 self.source.before()
498 self.dest.before()
498 self.dest.before()
499 self.source.setrevmap(self.map)
499 self.source.setrevmap(self.map)
500 self.ui.status(_("scanning source...\n"))
500 self.ui.status(_("scanning source...\n"))
501 heads = self.source.getheads()
501 heads = self.source.getheads()
502 parents = self.walktree(heads)
502 parents = self.walktree(heads)
503 self.mergesplicemap(parents, self.splicemap)
503 self.mergesplicemap(parents, self.splicemap)
504 self.ui.status(_("sorting...\n"))
504 self.ui.status(_("sorting...\n"))
505 t = self.toposort(parents, sortmode)
505 t = self.toposort(parents, sortmode)
506 num = len(t)
506 num = len(t)
507 c = None
507 c = None
508
508
509 self.ui.status(_("converting...\n"))
509 self.ui.status(_("converting...\n"))
510 for i, c in enumerate(t):
510 for i, c in enumerate(t):
511 num -= 1
511 num -= 1
512 desc = self.commitcache[c].desc
512 desc = self.commitcache[c].desc
513 if "\n" in desc:
513 if "\n" in desc:
514 desc = desc.splitlines()[0]
514 desc = desc.splitlines()[0]
515 # convert log message to local encoding without using
515 # convert log message to local encoding without using
516 # tolocal() because the encoding.encoding convert()
516 # tolocal() because the encoding.encoding convert()
517 # uses is 'utf-8'
517 # uses is 'utf-8'
518 self.ui.status("%d %s\n" % (num, recode(desc)))
518 self.ui.status("%d %s\n" % (num, recode(desc)))
519 self.ui.note(_("source: %s\n") % recode(c))
519 self.ui.note(_("source: %s\n") % recode(c))
520 self.ui.progress(_('converting'), i, unit=_('revisions'),
520 self.ui.progress(_('converting'), i, unit=_('revisions'),
521 total=len(t))
521 total=len(t))
522 self.copy(c)
522 self.copy(c)
523 self.ui.progress(_('converting'), None)
523 self.ui.progress(_('converting'), None)
524
524
525 if not self.ui.configbool('convert', 'skiptags'):
525 if not self.ui.configbool('convert', 'skiptags'):
526 tags = self.source.gettags()
526 tags = self.source.gettags()
527 ctags = {}
527 ctags = {}
528 for k in tags:
528 for k in tags:
529 v = tags[k]
529 v = tags[k]
530 if self.map.get(v, SKIPREV) != SKIPREV:
530 if self.map.get(v, SKIPREV) != SKIPREV:
531 ctags[k] = self.map[v]
531 ctags[k] = self.map[v]
532
532
533 if c and ctags:
533 if c and ctags:
534 nrev, tagsparent = self.dest.puttags(ctags)
534 nrev, tagsparent = self.dest.puttags(ctags)
535 if nrev and tagsparent:
535 if nrev and tagsparent:
536 # write another hash correspondence to override the
536 # write another hash correspondence to override the
537 # previous one so we don't end up with extra tag heads
537 # previous one so we don't end up with extra tag heads
538 tagsparents = [e for e in self.map.iteritems()
538 tagsparents = [e for e in self.map.iteritems()
539 if e[1] == tagsparent]
539 if e[1] == tagsparent]
540 if tagsparents:
540 if tagsparents:
541 self.map[tagsparents[0][0]] = nrev
541 self.map[tagsparents[0][0]] = nrev
542
542
543 bookmarks = self.source.getbookmarks()
543 bookmarks = self.source.getbookmarks()
544 cbookmarks = {}
544 cbookmarks = {}
545 for k in bookmarks:
545 for k in bookmarks:
546 v = bookmarks[k]
546 v = bookmarks[k]
547 if self.map.get(v, SKIPREV) != SKIPREV:
547 if self.map.get(v, SKIPREV) != SKIPREV:
548 cbookmarks[k] = self.map[v]
548 cbookmarks[k] = self.map[v]
549
549
550 if c and cbookmarks:
550 if c and cbookmarks:
551 self.dest.putbookmarks(cbookmarks)
551 self.dest.putbookmarks(cbookmarks)
552
552
553 self.writeauthormap()
553 self.writeauthormap()
554 finally:
554 finally:
555 self.cleanup()
555 self.cleanup()
556
556
557 def cleanup(self):
557 def cleanup(self):
558 try:
558 try:
559 self.dest.after()
559 self.dest.after()
560 finally:
560 finally:
561 self.source.after()
561 self.source.after()
562 self.map.close()
562 self.map.close()
563
563
564 def convert(ui, src, dest=None, revmapfile=None, **opts):
564 def convert(ui, src, dest=None, revmapfile=None, **opts):
565 global orig_encoding
565 global orig_encoding
566 orig_encoding = encoding.encoding
566 orig_encoding = encoding.encoding
567 encoding.encoding = 'UTF-8'
567 encoding.encoding = 'UTF-8'
568
568
569 # support --authors as an alias for --authormap
569 # support --authors as an alias for --authormap
570 if not opts.get('authormap'):
570 if not opts.get('authormap'):
571 opts['authormap'] = opts.get('authors')
571 opts['authormap'] = opts.get('authors')
572
572
573 if not dest:
573 if not dest:
574 dest = hg.defaultdest(src) + "-hg"
574 dest = hg.defaultdest(src) + "-hg"
575 ui.status(_("assuming destination %s\n") % dest)
575 ui.status(_("assuming destination %s\n") % dest)
576
576
577 destc = convertsink(ui, dest, opts.get('dest_type'))
577 destc = convertsink(ui, dest, opts.get('dest_type'))
578
578
579 try:
579 try:
580 srcc, defaultsort = convertsource(ui, src, opts.get('source_type'),
580 srcc, defaultsort = convertsource(ui, src, opts.get('source_type'),
581 opts.get('rev'))
581 opts.get('rev'))
582 except Exception:
582 except Exception:
583 for path in destc.created:
583 for path in destc.created:
584 shutil.rmtree(path, True)
584 shutil.rmtree(path, True)
585 raise
585 raise
586
586
587 sortmodes = ('branchsort', 'datesort', 'sourcesort', 'closesort')
587 sortmodes = ('branchsort', 'datesort', 'sourcesort', 'closesort')
588 sortmode = [m for m in sortmodes if opts.get(m)]
588 sortmode = [m for m in sortmodes if opts.get(m)]
589 if len(sortmode) > 1:
589 if len(sortmode) > 1:
590 raise error.Abort(_('more than one sort mode specified'))
590 raise error.Abort(_('more than one sort mode specified'))
591 if sortmode:
591 if sortmode:
592 sortmode = sortmode[0]
592 sortmode = sortmode[0]
593 else:
593 else:
594 sortmode = defaultsort
594 sortmode = defaultsort
595
595
596 if sortmode == 'sourcesort' and not srcc.hasnativeorder():
596 if sortmode == 'sourcesort' and not srcc.hasnativeorder():
597 raise error.Abort(_('--sourcesort is not supported by this data source')
597 raise error.Abort(_('--sourcesort is not supported by this data source')
598 )
598 )
599 if sortmode == 'closesort' and not srcc.hasnativeclose():
599 if sortmode == 'closesort' and not srcc.hasnativeclose():
600 raise error.Abort(_('--closesort is not supported by this data source'))
600 raise error.Abort(_('--closesort is not supported by this data source'))
601
601
602 fmap = opts.get('filemap')
602 fmap = opts.get('filemap')
603 if fmap:
603 if fmap:
604 srcc = filemap.filemap_source(ui, srcc, fmap)
604 srcc = filemap.filemap_source(ui, srcc, fmap)
605 destc.setfilemapmode(True)
605 destc.setfilemapmode(True)
606
606
607 if not revmapfile:
607 if not revmapfile:
608 revmapfile = destc.revmapfile()
608 revmapfile = destc.revmapfile()
609
609
610 c = converter(ui, srcc, destc, revmapfile, opts)
610 c = converter(ui, srcc, destc, revmapfile, opts)
611 c.convert(sortmode)
611 c.convert(sortmode)
@@ -1,81 +1,81 b''
1 # this is hack to make sure no escape characters are inserted into the output
1 # this is hack to make sure no escape characters are inserted into the output
2
2
3 from __future__ import absolute_import
3 from __future__ import absolute_import
4
4
5 import doctest
5 import doctest
6 import os
6 import os
7 import re
7 import re
8 import sys
8 import sys
9
9
10 ispy3 = (sys.version_info[0] >= 3)
10 ispy3 = (sys.version_info[0] >= 3)
11
11
12 if 'TERM' in os.environ:
12 if 'TERM' in os.environ:
13 del os.environ['TERM']
13 del os.environ['TERM']
14
14
15 class py3docchecker(doctest.OutputChecker):
15 class py3docchecker(doctest.OutputChecker):
16 def check_output(self, want, got, optionflags):
16 def check_output(self, want, got, optionflags):
17 want2 = re.sub(r'''\bu(['"])(.*?)\1''', r'\1\2\1', want) # py2: u''
17 want2 = re.sub(r'''\bu(['"])(.*?)\1''', r'\1\2\1', want) # py2: u''
18 got2 = re.sub(r'''\bb(['"])(.*?)\1''', r'\1\2\1', got) # py3: b''
18 got2 = re.sub(r'''\bb(['"])(.*?)\1''', r'\1\2\1', got) # py3: b''
19 # py3: <exc.name>: b'<msg>' -> <name>: <msg>
19 # py3: <exc.name>: b'<msg>' -> <name>: <msg>
20 # <exc.name>: <others> -> <name>: <others>
20 # <exc.name>: <others> -> <name>: <others>
21 got2 = re.sub(r'''^mercurial\.\w+\.(\w+): (['"])(.*?)\2''', r'\1: \3',
21 got2 = re.sub(r'''^mercurial\.\w+\.(\w+): (['"])(.*?)\2''', r'\1: \3',
22 got2, re.MULTILINE)
22 got2, re.MULTILINE)
23 got2 = re.sub(r'^mercurial\.\w+\.(\w+): ', r'\1: ', got2, re.MULTILINE)
23 got2 = re.sub(r'^mercurial\.\w+\.(\w+): ', r'\1: ', got2, re.MULTILINE)
24 return any(doctest.OutputChecker.check_output(self, w, g, optionflags)
24 return any(doctest.OutputChecker.check_output(self, w, g, optionflags)
25 for w, g in [(want, got), (want2, got2)])
25 for w, g in [(want, got), (want2, got2)])
26
26
27 # TODO: migrate doctests to py3 and enable them on both versions
27 # TODO: migrate doctests to py3 and enable them on both versions
28 def testmod(name, optionflags=0, testtarget=None, py2=True, py3=True):
28 def testmod(name, optionflags=0, testtarget=None, py2=True, py3=True):
29 if not (not ispy3 and py2 or ispy3 and py3):
29 if not (not ispy3 and py2 or ispy3 and py3):
30 return
30 return
31 __import__(name)
31 __import__(name)
32 mod = sys.modules[name]
32 mod = sys.modules[name]
33 if testtarget is not None:
33 if testtarget is not None:
34 mod = getattr(mod, testtarget)
34 mod = getattr(mod, testtarget)
35
35
36 # minimal copy of doctest.testmod()
36 # minimal copy of doctest.testmod()
37 finder = doctest.DocTestFinder()
37 finder = doctest.DocTestFinder()
38 checker = None
38 checker = None
39 if ispy3:
39 if ispy3:
40 checker = py3docchecker()
40 checker = py3docchecker()
41 runner = doctest.DocTestRunner(checker=checker, optionflags=optionflags)
41 runner = doctest.DocTestRunner(checker=checker, optionflags=optionflags)
42 for test in finder.find(mod, name):
42 for test in finder.find(mod, name):
43 runner.run(test)
43 runner.run(test)
44 runner.summarize()
44 runner.summarize()
45
45
46 testmod('mercurial.changegroup')
46 testmod('mercurial.changegroup')
47 testmod('mercurial.changelog')
47 testmod('mercurial.changelog')
48 testmod('mercurial.color')
48 testmod('mercurial.color')
49 testmod('mercurial.config')
49 testmod('mercurial.config')
50 testmod('mercurial.context')
50 testmod('mercurial.context')
51 testmod('mercurial.dagparser', optionflags=doctest.NORMALIZE_WHITESPACE)
51 testmod('mercurial.dagparser', optionflags=doctest.NORMALIZE_WHITESPACE)
52 testmod('mercurial.dispatch')
52 testmod('mercurial.dispatch')
53 testmod('mercurial.encoding')
53 testmod('mercurial.encoding')
54 testmod('mercurial.formatter')
54 testmod('mercurial.formatter')
55 testmod('mercurial.hg')
55 testmod('mercurial.hg')
56 testmod('mercurial.hgweb.hgwebdir_mod')
56 testmod('mercurial.hgweb.hgwebdir_mod')
57 testmod('mercurial.match')
57 testmod('mercurial.match')
58 testmod('mercurial.mdiff')
58 testmod('mercurial.mdiff')
59 testmod('mercurial.minirst')
59 testmod('mercurial.minirst')
60 testmod('mercurial.patch')
60 testmod('mercurial.patch')
61 testmod('mercurial.pathutil')
61 testmod('mercurial.pathutil')
62 testmod('mercurial.parser')
62 testmod('mercurial.parser')
63 testmod('mercurial.pycompat')
63 testmod('mercurial.pycompat')
64 testmod('mercurial.revsetlang')
64 testmod('mercurial.revsetlang')
65 testmod('mercurial.smartset')
65 testmod('mercurial.smartset')
66 testmod('mercurial.store')
66 testmod('mercurial.store')
67 testmod('mercurial.subrepo')
67 testmod('mercurial.subrepo')
68 testmod('mercurial.templatefilters')
68 testmod('mercurial.templatefilters')
69 testmod('mercurial.templater')
69 testmod('mercurial.templater')
70 testmod('mercurial.ui')
70 testmod('mercurial.ui')
71 testmod('mercurial.url')
71 testmod('mercurial.url')
72 testmod('mercurial.util', py3=False) # py3: multiple bytes/unicode issues
72 testmod('mercurial.util', py3=False) # py3: multiple bytes/unicode issues
73 testmod('mercurial.util', testtarget='platform')
73 testmod('mercurial.util', testtarget='platform')
74 testmod('hgext.convert.convcmd', py3=False) # py3: use of str() ?
74 testmod('hgext.convert.convcmd')
75 testmod('hgext.convert.cvsps')
75 testmod('hgext.convert.cvsps')
76 testmod('hgext.convert.filemap')
76 testmod('hgext.convert.filemap')
77 testmod('hgext.convert.p4')
77 testmod('hgext.convert.p4')
78 testmod('hgext.convert.subversion')
78 testmod('hgext.convert.subversion')
79 testmod('hgext.mq')
79 testmod('hgext.mq')
80 # Helper scripts in tests/ that have doctests:
80 # Helper scripts in tests/ that have doctests:
81 testmod('drawdag')
81 testmod('drawdag')
General Comments 0
You need to be logged in to leave comments. Login now