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