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