##// END OF EJS Templates
convert: fix mercurial_sink.putcommit...
Alexis S. L. Carvalho -
r5195:33015dac default
parent child Browse files
Show More
@@ -1,455 +1,461 b''
1 # convert.py Foreign SCM converter
1 # convert.py Foreign SCM converter
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, converter_source, converter_sink
8 from common import NoRepo, converter_source, converter_sink
9 from cvs import convert_cvs
9 from cvs import convert_cvs
10 from git import convert_git
10 from git import convert_git
11 from hg import mercurial_source, mercurial_sink
11 from hg import mercurial_source, mercurial_sink
12 from subversion import convert_svn, debugsvnlog
12 from subversion import convert_svn, debugsvnlog
13
13
14 import os, shlex, shutil
14 import os, shlex, shutil
15 from mercurial import hg, ui, util, commands
15 from mercurial import hg, ui, util, commands
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17
17
18 commands.norepo += " convert debugsvnlog"
18 commands.norepo += " convert debugsvnlog"
19
19
20 converters = [convert_cvs, convert_git, convert_svn, mercurial_source,
20 converters = [convert_cvs, convert_git, convert_svn, mercurial_source,
21 mercurial_sink]
21 mercurial_sink]
22
22
23 def convertsource(ui, path, **opts):
23 def convertsource(ui, path, **opts):
24 for c in converters:
24 for c in converters:
25 try:
25 try:
26 return c.getcommit and c(ui, path, **opts)
26 return c.getcommit and c(ui, path, **opts)
27 except (AttributeError, NoRepo):
27 except (AttributeError, NoRepo):
28 pass
28 pass
29 raise util.Abort('%s: unknown repository type' % path)
29 raise util.Abort('%s: unknown repository type' % path)
30
30
31 def convertsink(ui, path):
31 def convertsink(ui, path):
32 if not os.path.isdir(path):
32 if not os.path.isdir(path):
33 raise util.Abort("%s: not a directory" % path)
33 raise util.Abort("%s: not a directory" % path)
34 for c in converters:
34 for c in converters:
35 try:
35 try:
36 return c.putcommit and c(ui, path)
36 return c.putcommit and c(ui, path)
37 except (AttributeError, NoRepo):
37 except (AttributeError, NoRepo):
38 pass
38 pass
39 raise util.Abort('%s: unknown repository type' % path)
39 raise util.Abort('%s: unknown repository type' % path)
40
40
41 class convert(object):
41 class convert(object):
42 def __init__(self, ui, source, dest, revmapfile, filemapper, opts):
42 def __init__(self, ui, source, dest, revmapfile, filemapper, opts):
43
43
44 self.source = source
44 self.source = source
45 self.dest = dest
45 self.dest = dest
46 self.ui = ui
46 self.ui = ui
47 self.opts = opts
47 self.opts = opts
48 self.commitcache = {}
48 self.commitcache = {}
49 self.revmapfile = revmapfile
49 self.revmapfile = revmapfile
50 self.revmapfilefd = None
50 self.revmapfilefd = None
51 self.authors = {}
51 self.authors = {}
52 self.authorfile = None
52 self.authorfile = None
53 self.mapfile = filemapper
53 self.mapfile = filemapper
54
54
55 self.map = {}
55 self.map = {}
56 try:
56 try:
57 origrevmapfile = open(self.revmapfile, 'r')
57 origrevmapfile = open(self.revmapfile, 'r')
58 for l in origrevmapfile:
58 for l in origrevmapfile:
59 sv, dv = l[:-1].split()
59 sv, dv = l[:-1].split()
60 self.map[sv] = dv
60 self.map[sv] = dv
61 origrevmapfile.close()
61 origrevmapfile.close()
62 except IOError:
62 except IOError:
63 pass
63 pass
64
64
65 # Read first the dst author map if any
65 # Read first the dst author map if any
66 authorfile = self.dest.authorfile()
66 authorfile = self.dest.authorfile()
67 if authorfile and os.path.exists(authorfile):
67 if authorfile and os.path.exists(authorfile):
68 self.readauthormap(authorfile)
68 self.readauthormap(authorfile)
69 # Extend/Override with new author map if necessary
69 # Extend/Override with new author map if necessary
70 if opts.get('authors'):
70 if opts.get('authors'):
71 self.readauthormap(opts.get('authors'))
71 self.readauthormap(opts.get('authors'))
72 self.authorfile = self.dest.authorfile()
72 self.authorfile = self.dest.authorfile()
73
73
74 def walktree(self, heads):
74 def walktree(self, heads):
75 '''Return a mapping that identifies the uncommitted parents of every
75 '''Return a mapping that identifies the uncommitted parents of every
76 uncommitted changeset.'''
76 uncommitted changeset.'''
77 visit = heads
77 visit = heads
78 known = {}
78 known = {}
79 parents = {}
79 parents = {}
80 while visit:
80 while visit:
81 n = visit.pop(0)
81 n = visit.pop(0)
82 if n in known or n in self.map: continue
82 if n in known or n in self.map: continue
83 known[n] = 1
83 known[n] = 1
84 self.commitcache[n] = self.source.getcommit(n)
84 self.commitcache[n] = self.source.getcommit(n)
85 cp = self.commitcache[n].parents
85 cp = self.commitcache[n].parents
86 parents[n] = []
86 parents[n] = []
87 for p in cp:
87 for p in cp:
88 parents[n].append(p)
88 parents[n].append(p)
89 visit.append(p)
89 visit.append(p)
90
90
91 return parents
91 return parents
92
92
93 def toposort(self, parents):
93 def toposort(self, parents):
94 '''Return an ordering such that every uncommitted changeset is
94 '''Return an ordering such that every uncommitted changeset is
95 preceeded by all its uncommitted ancestors.'''
95 preceeded by all its uncommitted ancestors.'''
96 visit = parents.keys()
96 visit = parents.keys()
97 seen = {}
97 seen = {}
98 children = {}
98 children = {}
99
99
100 while visit:
100 while visit:
101 n = visit.pop(0)
101 n = visit.pop(0)
102 if n in seen: continue
102 if n in seen: continue
103 seen[n] = 1
103 seen[n] = 1
104 # Ensure that nodes without parents are present in the 'children'
104 # Ensure that nodes without parents are present in the 'children'
105 # mapping.
105 # mapping.
106 children.setdefault(n, [])
106 children.setdefault(n, [])
107 for p in parents[n]:
107 for p in parents[n]:
108 if not p in self.map:
108 if not p in self.map:
109 visit.append(p)
109 visit.append(p)
110 children.setdefault(p, []).append(n)
110 children.setdefault(p, []).append(n)
111
111
112 s = []
112 s = []
113 removed = {}
113 removed = {}
114 visit = children.keys()
114 visit = children.keys()
115 while visit:
115 while visit:
116 n = visit.pop(0)
116 n = visit.pop(0)
117 if n in removed: continue
117 if n in removed: continue
118 dep = 0
118 dep = 0
119 if n in parents:
119 if n in parents:
120 for p in parents[n]:
120 for p in parents[n]:
121 if p in self.map: continue
121 if p in self.map: continue
122 if p not in removed:
122 if p not in removed:
123 # we're still dependent
123 # we're still dependent
124 visit.append(n)
124 visit.append(n)
125 dep = 1
125 dep = 1
126 break
126 break
127
127
128 if not dep:
128 if not dep:
129 # all n's parents are in the list
129 # all n's parents are in the list
130 removed[n] = 1
130 removed[n] = 1
131 if n not in self.map:
131 if n not in self.map:
132 s.append(n)
132 s.append(n)
133 if n in children:
133 if n in children:
134 for c in children[n]:
134 for c in children[n]:
135 visit.insert(0, c)
135 visit.insert(0, c)
136
136
137 if self.opts.get('datesort'):
137 if self.opts.get('datesort'):
138 depth = {}
138 depth = {}
139 for n in s:
139 for n in s:
140 depth[n] = 0
140 depth[n] = 0
141 pl = [p for p in self.commitcache[n].parents
141 pl = [p for p in self.commitcache[n].parents
142 if p not in self.map]
142 if p not in self.map]
143 if pl:
143 if pl:
144 depth[n] = max([depth[p] for p in pl]) + 1
144 depth[n] = max([depth[p] for p in pl]) + 1
145
145
146 s = [(depth[n], self.commitcache[n].date, n) for n in s]
146 s = [(depth[n], self.commitcache[n].date, n) for n in s]
147 s.sort()
147 s.sort()
148 s = [e[2] for e in s]
148 s = [e[2] for e in s]
149
149
150 return s
150 return s
151
151
152 def mapentry(self, src, dst):
152 def mapentry(self, src, dst):
153 if self.revmapfilefd is None:
153 if self.revmapfilefd is None:
154 try:
154 try:
155 self.revmapfilefd = open(self.revmapfile, "a")
155 self.revmapfilefd = open(self.revmapfile, "a")
156 except IOError, (errno, strerror):
156 except IOError, (errno, strerror):
157 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.revmapfile, errno, strerror))
157 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.revmapfile, errno, strerror))
158 self.map[src] = dst
158 self.map[src] = dst
159 self.revmapfilefd.write("%s %s\n" % (src, dst))
159 self.revmapfilefd.write("%s %s\n" % (src, dst))
160 self.revmapfilefd.flush()
160 self.revmapfilefd.flush()
161
161
162 def writeauthormap(self):
162 def writeauthormap(self):
163 authorfile = self.authorfile
163 authorfile = self.authorfile
164 if authorfile:
164 if authorfile:
165 self.ui.status('Writing author map file %s\n' % authorfile)
165 self.ui.status('Writing author map file %s\n' % authorfile)
166 ofile = open(authorfile, 'w+')
166 ofile = open(authorfile, 'w+')
167 for author in self.authors:
167 for author in self.authors:
168 ofile.write("%s=%s\n" % (author, self.authors[author]))
168 ofile.write("%s=%s\n" % (author, self.authors[author]))
169 ofile.close()
169 ofile.close()
170
170
171 def readauthormap(self, authorfile):
171 def readauthormap(self, authorfile):
172 afile = open(authorfile, 'r')
172 afile = open(authorfile, 'r')
173 for line in afile:
173 for line in afile:
174 try:
174 try:
175 srcauthor = line.split('=')[0].strip()
175 srcauthor = line.split('=')[0].strip()
176 dstauthor = line.split('=')[1].strip()
176 dstauthor = line.split('=')[1].strip()
177 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
177 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
178 self.ui.status(
178 self.ui.status(
179 'Overriding mapping for author %s, was %s, will be %s\n'
179 'Overriding mapping for author %s, was %s, will be %s\n'
180 % (srcauthor, self.authors[srcauthor], dstauthor))
180 % (srcauthor, self.authors[srcauthor], dstauthor))
181 else:
181 else:
182 self.ui.debug('Mapping author %s to %s\n'
182 self.ui.debug('Mapping author %s to %s\n'
183 % (srcauthor, dstauthor))
183 % (srcauthor, dstauthor))
184 self.authors[srcauthor] = dstauthor
184 self.authors[srcauthor] = dstauthor
185 except IndexError:
185 except IndexError:
186 self.ui.warn(
186 self.ui.warn(
187 'Ignoring bad line in author file map %s: %s\n'
187 'Ignoring bad line in author file map %s: %s\n'
188 % (authorfile, line))
188 % (authorfile, line))
189 afile.close()
189 afile.close()
190
190
191 def copy(self, rev):
191 def copy(self, rev):
192 commit = self.commitcache[rev]
192 commit = self.commitcache[rev]
193 do_copies = hasattr(self.dest, 'copyfile')
193 do_copies = hasattr(self.dest, 'copyfile')
194 filenames = []
194 filenames = []
195
195
196 files, copies = self.source.getchanges(rev)
196 files, copies = self.source.getchanges(rev)
197 parents = [self.map[r] for r in commit.parents]
197 parents = [self.map[r] for r in commit.parents]
198 if commit.parents:
198 if commit.parents:
199 pbranch = self.commitcache[commit.parents[0]].branch
199 pbranch = self.commitcache[commit.parents[0]].branch
200 else:
200 else:
201 pbranch = None
201 pbranch = None
202 self.dest.setbranch(commit.branch, pbranch, parents)
202 self.dest.setbranch(commit.branch, pbranch, parents)
203 for f, v in files:
203 for f, v in files:
204 newf = self.mapfile(f)
204 newf = self.mapfile(f)
205 if not newf:
205 if not newf:
206 continue
206 continue
207 filenames.append(newf)
207 filenames.append(newf)
208 try:
208 try:
209 data = self.source.getfile(f, v)
209 data = self.source.getfile(f, v)
210 except IOError, inst:
210 except IOError, inst:
211 self.dest.delfile(newf)
211 self.dest.delfile(newf)
212 else:
212 else:
213 e = self.source.getmode(f, v)
213 e = self.source.getmode(f, v)
214 self.dest.putfile(newf, e, data)
214 self.dest.putfile(newf, e, data)
215 if do_copies:
215 if do_copies:
216 if f in copies:
216 if f in copies:
217 copyf = self.mapfile(copies[f])
217 copyf = self.mapfile(copies[f])
218 if copyf:
218 if copyf:
219 # Merely marks that a copy happened.
219 # Merely marks that a copy happened.
220 self.dest.copyfile(copyf, newf)
220 self.dest.copyfile(copyf, newf)
221
221
222 newnode = self.dest.putcommit(filenames, parents, commit)
222 if not filenames and self.mapfile.active():
223 newnode = parents[0]
224 else:
225 newnode = self.dest.putcommit(filenames, parents, commit)
223 self.mapentry(rev, newnode)
226 self.mapentry(rev, newnode)
224
227
225 def convert(self):
228 def convert(self):
226 try:
229 try:
227 self.dest.before()
230 self.dest.before()
228 self.source.setrevmap(self.map)
231 self.source.setrevmap(self.map)
229 self.ui.status("scanning source...\n")
232 self.ui.status("scanning source...\n")
230 heads = self.source.getheads()
233 heads = self.source.getheads()
231 parents = self.walktree(heads)
234 parents = self.walktree(heads)
232 self.ui.status("sorting...\n")
235 self.ui.status("sorting...\n")
233 t = self.toposort(parents)
236 t = self.toposort(parents)
234 num = len(t)
237 num = len(t)
235 c = None
238 c = None
236
239
237 self.ui.status("converting...\n")
240 self.ui.status("converting...\n")
238 for c in t:
241 for c in t:
239 num -= 1
242 num -= 1
240 desc = self.commitcache[c].desc
243 desc = self.commitcache[c].desc
241 if "\n" in desc:
244 if "\n" in desc:
242 desc = desc.splitlines()[0]
245 desc = desc.splitlines()[0]
243 author = self.commitcache[c].author
246 author = self.commitcache[c].author
244 author = self.authors.get(author, author)
247 author = self.authors.get(author, author)
245 self.commitcache[c].author = author
248 self.commitcache[c].author = author
246 self.ui.status("%d %s\n" % (num, desc))
249 self.ui.status("%d %s\n" % (num, desc))
247 self.copy(c)
250 self.copy(c)
248
251
249 tags = self.source.gettags()
252 tags = self.source.gettags()
250 ctags = {}
253 ctags = {}
251 for k in tags:
254 for k in tags:
252 v = tags[k]
255 v = tags[k]
253 if v in self.map:
256 if v in self.map:
254 ctags[k] = self.map[v]
257 ctags[k] = self.map[v]
255
258
256 if c and ctags:
259 if c and ctags:
257 nrev = self.dest.puttags(ctags)
260 nrev = self.dest.puttags(ctags)
258 # write another hash correspondence to override the previous
261 # write another hash correspondence to override the previous
259 # one so we don't end up with extra tag heads
262 # one so we don't end up with extra tag heads
260 if nrev:
263 if nrev:
261 self.mapentry(c, nrev)
264 self.mapentry(c, nrev)
262
265
263 self.writeauthormap()
266 self.writeauthormap()
264 finally:
267 finally:
265 self.cleanup()
268 self.cleanup()
266
269
267 def cleanup(self):
270 def cleanup(self):
268 self.dest.after()
271 self.dest.after()
269 if self.revmapfilefd:
272 if self.revmapfilefd:
270 self.revmapfilefd.close()
273 self.revmapfilefd.close()
271
274
272 def rpairs(name):
275 def rpairs(name):
273 e = len(name)
276 e = len(name)
274 while e != -1:
277 while e != -1:
275 yield name[:e], name[e+1:]
278 yield name[:e], name[e+1:]
276 e = name.rfind('/', 0, e)
279 e = name.rfind('/', 0, e)
277
280
278 class filemapper(object):
281 class filemapper(object):
279 '''Map and filter filenames when importing.
282 '''Map and filter filenames when importing.
280 A name can be mapped to itself, a new name, or None (omit from new
283 A name can be mapped to itself, a new name, or None (omit from new
281 repository).'''
284 repository).'''
282
285
283 def __init__(self, ui, path=None):
286 def __init__(self, ui, path=None):
284 self.ui = ui
287 self.ui = ui
285 self.include = {}
288 self.include = {}
286 self.exclude = {}
289 self.exclude = {}
287 self.rename = {}
290 self.rename = {}
288 if path:
291 if path:
289 if self.parse(path):
292 if self.parse(path):
290 raise util.Abort(_('errors in filemap'))
293 raise util.Abort(_('errors in filemap'))
291
294
292 def parse(self, path):
295 def parse(self, path):
293 errs = 0
296 errs = 0
294 def check(name, mapping, listname):
297 def check(name, mapping, listname):
295 if name in mapping:
298 if name in mapping:
296 self.ui.warn(_('%s:%d: %r already in %s list\n') %
299 self.ui.warn(_('%s:%d: %r already in %s list\n') %
297 (lex.infile, lex.lineno, name, listname))
300 (lex.infile, lex.lineno, name, listname))
298 return 1
301 return 1
299 return 0
302 return 0
300 lex = shlex.shlex(open(path), path, True)
303 lex = shlex.shlex(open(path), path, True)
301 lex.wordchars += '!@#$%^&*()-=+[]{}|;:,./<>?'
304 lex.wordchars += '!@#$%^&*()-=+[]{}|;:,./<>?'
302 cmd = lex.get_token()
305 cmd = lex.get_token()
303 while cmd:
306 while cmd:
304 if cmd == 'include':
307 if cmd == 'include':
305 name = lex.get_token()
308 name = lex.get_token()
306 errs += check(name, self.exclude, 'exclude')
309 errs += check(name, self.exclude, 'exclude')
307 self.include[name] = name
310 self.include[name] = name
308 elif cmd == 'exclude':
311 elif cmd == 'exclude':
309 name = lex.get_token()
312 name = lex.get_token()
310 errs += check(name, self.include, 'include')
313 errs += check(name, self.include, 'include')
311 errs += check(name, self.rename, 'rename')
314 errs += check(name, self.rename, 'rename')
312 self.exclude[name] = name
315 self.exclude[name] = name
313 elif cmd == 'rename':
316 elif cmd == 'rename':
314 src = lex.get_token()
317 src = lex.get_token()
315 dest = lex.get_token()
318 dest = lex.get_token()
316 errs += check(src, self.exclude, 'exclude')
319 errs += check(src, self.exclude, 'exclude')
317 self.rename[src] = dest
320 self.rename[src] = dest
318 elif cmd == 'source':
321 elif cmd == 'source':
319 errs += self.parse(lex.get_token())
322 errs += self.parse(lex.get_token())
320 else:
323 else:
321 self.ui.warn(_('%s:%d: unknown directive %r\n') %
324 self.ui.warn(_('%s:%d: unknown directive %r\n') %
322 (lex.infile, lex.lineno, cmd))
325 (lex.infile, lex.lineno, cmd))
323 errs += 1
326 errs += 1
324 cmd = lex.get_token()
327 cmd = lex.get_token()
325 return errs
328 return errs
326
329
327 def lookup(self, name, mapping):
330 def lookup(self, name, mapping):
328 for pre, suf in rpairs(name):
331 for pre, suf in rpairs(name):
329 try:
332 try:
330 return mapping[pre], pre, suf
333 return mapping[pre], pre, suf
331 except KeyError, err:
334 except KeyError, err:
332 pass
335 pass
333 return '', name, ''
336 return '', name, ''
334
337
335 def __call__(self, name):
338 def __call__(self, name):
336 if self.include:
339 if self.include:
337 inc = self.lookup(name, self.include)[0]
340 inc = self.lookup(name, self.include)[0]
338 else:
341 else:
339 inc = name
342 inc = name
340 if self.exclude:
343 if self.exclude:
341 exc = self.lookup(name, self.exclude)[0]
344 exc = self.lookup(name, self.exclude)[0]
342 else:
345 else:
343 exc = ''
346 exc = ''
344 if not inc or exc:
347 if not inc or exc:
345 return None
348 return None
346 newpre, pre, suf = self.lookup(name, self.rename)
349 newpre, pre, suf = self.lookup(name, self.rename)
347 if newpre:
350 if newpre:
348 if newpre == '.':
351 if newpre == '.':
349 return suf
352 return suf
350 if suf:
353 if suf:
351 return newpre + '/' + suf
354 return newpre + '/' + suf
352 return newpre
355 return newpre
353 return name
356 return name
354
357
358 def active(self):
359 return bool(self.include or self.exclude or self.rename)
360
355 def _convert(ui, src, dest=None, revmapfile=None, **opts):
361 def _convert(ui, src, dest=None, revmapfile=None, **opts):
356 """Convert a foreign SCM repository to a Mercurial one.
362 """Convert a foreign SCM repository to a Mercurial one.
357
363
358 Accepted source formats:
364 Accepted source formats:
359 - GIT
365 - GIT
360 - CVS
366 - CVS
361 - SVN
367 - SVN
362
368
363 Accepted destination formats:
369 Accepted destination formats:
364 - Mercurial
370 - Mercurial
365
371
366 If no revision is given, all revisions will be converted. Otherwise,
372 If no revision is given, all revisions will be converted. Otherwise,
367 convert will only import up to the named revision (given in a format
373 convert will only import up to the named revision (given in a format
368 understood by the source).
374 understood by the source).
369
375
370 If no destination directory name is specified, it defaults to the
376 If no destination directory name is specified, it defaults to the
371 basename of the source with '-hg' appended. If the destination
377 basename of the source with '-hg' appended. If the destination
372 repository doesn't exist, it will be created.
378 repository doesn't exist, it will be created.
373
379
374 If <revmapfile> isn't given, it will be put in a default location
380 If <revmapfile> isn't given, it will be put in a default location
375 (<dest>/.hg/shamap by default). The <revmapfile> is a simple text
381 (<dest>/.hg/shamap by default). The <revmapfile> is a simple text
376 file that maps each source commit ID to the destination ID for
382 file that maps each source commit ID to the destination ID for
377 that revision, like so:
383 that revision, like so:
378 <source ID> <destination ID>
384 <source ID> <destination ID>
379
385
380 If the file doesn't exist, it's automatically created. It's updated
386 If the file doesn't exist, it's automatically created. It's updated
381 on each commit copied, so convert-repo can be interrupted and can
387 on each commit copied, so convert-repo can be interrupted and can
382 be run repeatedly to copy new commits.
388 be run repeatedly to copy new commits.
383
389
384 The [username mapping] file is a simple text file that maps each source
390 The [username mapping] file is a simple text file that maps each source
385 commit author to a destination commit author. It is handy for source SCMs
391 commit author to a destination commit author. It is handy for source SCMs
386 that use unix logins to identify authors (eg: CVS). One line per author
392 that use unix logins to identify authors (eg: CVS). One line per author
387 mapping and the line format is:
393 mapping and the line format is:
388 srcauthor=whatever string you want
394 srcauthor=whatever string you want
389 """
395 """
390
396
391 util._encoding = 'UTF-8'
397 util._encoding = 'UTF-8'
392
398
393 if not dest:
399 if not dest:
394 dest = hg.defaultdest(src) + "-hg"
400 dest = hg.defaultdest(src) + "-hg"
395 ui.status("assuming destination %s\n" % dest)
401 ui.status("assuming destination %s\n" % dest)
396
402
397 # Try to be smart and initalize things when required
403 # Try to be smart and initalize things when required
398 created = False
404 created = False
399 if os.path.isdir(dest):
405 if os.path.isdir(dest):
400 if len(os.listdir(dest)) > 0:
406 if len(os.listdir(dest)) > 0:
401 try:
407 try:
402 hg.repository(ui, dest)
408 hg.repository(ui, dest)
403 ui.status("destination %s is a Mercurial repository\n" % dest)
409 ui.status("destination %s is a Mercurial repository\n" % dest)
404 except hg.RepoError:
410 except hg.RepoError:
405 raise util.Abort(
411 raise util.Abort(
406 "destination directory %s is not empty.\n"
412 "destination directory %s is not empty.\n"
407 "Please specify an empty directory to be initialized\n"
413 "Please specify an empty directory to be initialized\n"
408 "or an already initialized mercurial repository"
414 "or an already initialized mercurial repository"
409 % dest)
415 % dest)
410 else:
416 else:
411 ui.status("initializing destination %s repository\n" % dest)
417 ui.status("initializing destination %s repository\n" % dest)
412 hg.repository(ui, dest, create=True)
418 hg.repository(ui, dest, create=True)
413 created = True
419 created = True
414 elif os.path.exists(dest):
420 elif os.path.exists(dest):
415 raise util.Abort("destination %s exists and is not a directory" % dest)
421 raise util.Abort("destination %s exists and is not a directory" % dest)
416 else:
422 else:
417 ui.status("initializing destination %s repository\n" % dest)
423 ui.status("initializing destination %s repository\n" % dest)
418 hg.repository(ui, dest, create=True)
424 hg.repository(ui, dest, create=True)
419 created = True
425 created = True
420
426
421 destc = convertsink(ui, dest)
427 destc = convertsink(ui, dest)
422
428
423 try:
429 try:
424 srcc = convertsource(ui, src, rev=opts.get('rev'))
430 srcc = convertsource(ui, src, rev=opts.get('rev'))
425 except Exception:
431 except Exception:
426 if created:
432 if created:
427 shutil.rmtree(dest, True)
433 shutil.rmtree(dest, True)
428 raise
434 raise
429
435
430 if not revmapfile:
436 if not revmapfile:
431 try:
437 try:
432 revmapfile = destc.revmapfile()
438 revmapfile = destc.revmapfile()
433 except:
439 except:
434 revmapfile = os.path.join(destc, "map")
440 revmapfile = os.path.join(destc, "map")
435
441
436
442
437 c = convert(ui, srcc, destc, revmapfile, filemapper(ui, opts['filemap']),
443 c = convert(ui, srcc, destc, revmapfile, filemapper(ui, opts['filemap']),
438 opts)
444 opts)
439 c.convert()
445 c.convert()
440
446
441
447
442 cmdtable = {
448 cmdtable = {
443 "convert":
449 "convert":
444 (_convert,
450 (_convert,
445 [('A', 'authors', '', 'username mapping filename'),
451 [('A', 'authors', '', 'username mapping filename'),
446 ('', 'filemap', '', 'remap file names using contents of file'),
452 ('', 'filemap', '', 'remap file names using contents of file'),
447 ('r', 'rev', '', 'import up to target revision REV'),
453 ('r', 'rev', '', 'import up to target revision REV'),
448 ('', 'datesort', None, 'try to sort changesets by date')],
454 ('', 'datesort', None, 'try to sort changesets by date')],
449 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
455 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
450 "debugsvnlog":
456 "debugsvnlog":
451 (debugsvnlog,
457 (debugsvnlog,
452 [],
458 [],
453 'hg debugsvnlog'),
459 'hg debugsvnlog'),
454 }
460 }
455
461
@@ -1,204 +1,201 b''
1 # hg backend for convert extension
1 # hg backend for convert extension
2
2
3 # Note for hg->hg conversion: Old versions of Mercurial didn't trim
3 # Note for hg->hg conversion: Old versions of Mercurial didn't trim
4 # the whitespace from the ends of commit messages, but new versions
4 # the whitespace from the ends of commit messages, but new versions
5 # do. Changesets created by those older versions, then converted, may
5 # do. Changesets created by those older versions, then converted, may
6 # thus have different hashes for changesets that are otherwise
6 # thus have different hashes for changesets that are otherwise
7 # identical.
7 # identical.
8
8
9
9
10 import os, time
10 import os, time
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 from mercurial.node import *
12 from mercurial.node import *
13 from mercurial import hg, lock, revlog, util
13 from mercurial import hg, lock, revlog, util
14
14
15 from common import NoRepo, commit, converter_source, converter_sink
15 from common import NoRepo, commit, converter_source, converter_sink
16
16
17 class mercurial_sink(converter_sink):
17 class mercurial_sink(converter_sink):
18 def __init__(self, ui, path):
18 def __init__(self, ui, path):
19 self.path = path
19 self.path = path
20 self.ui = ui
20 self.ui = ui
21 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
21 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
22 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
22 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
23 self.lastbranch = None
23 self.lastbranch = None
24 try:
24 try:
25 self.repo = hg.repository(self.ui, path)
25 self.repo = hg.repository(self.ui, path)
26 except:
26 except:
27 raise NoRepo("could not open hg repo %s as sink" % path)
27 raise NoRepo("could not open hg repo %s as sink" % path)
28 self.lock = None
28 self.lock = None
29 self.wlock = None
29 self.wlock = None
30
30
31 def before(self):
31 def before(self):
32 self.wlock = self.repo.wlock()
32 self.wlock = self.repo.wlock()
33 self.lock = self.repo.lock()
33 self.lock = self.repo.lock()
34
34
35 def after(self):
35 def after(self):
36 self.lock = None
36 self.lock = None
37 self.wlock = None
37 self.wlock = None
38
38
39 def revmapfile(self):
39 def revmapfile(self):
40 return os.path.join(self.path, ".hg", "shamap")
40 return os.path.join(self.path, ".hg", "shamap")
41
41
42 def authorfile(self):
42 def authorfile(self):
43 return os.path.join(self.path, ".hg", "authormap")
43 return os.path.join(self.path, ".hg", "authormap")
44
44
45 def getheads(self):
45 def getheads(self):
46 h = self.repo.changelog.heads()
46 h = self.repo.changelog.heads()
47 return [ hex(x) for x in h ]
47 return [ hex(x) for x in h ]
48
48
49 def putfile(self, f, e, data):
49 def putfile(self, f, e, data):
50 self.repo.wwrite(f, data, e)
50 self.repo.wwrite(f, data, e)
51 if f not in self.repo.dirstate:
51 if f not in self.repo.dirstate:
52 self.repo.dirstate.add(f)
52 self.repo.dirstate.add(f)
53
53
54 def copyfile(self, source, dest):
54 def copyfile(self, source, dest):
55 self.repo.copy(source, dest)
55 self.repo.copy(source, dest)
56
56
57 def delfile(self, f):
57 def delfile(self, f):
58 try:
58 try:
59 os.unlink(self.repo.wjoin(f))
59 os.unlink(self.repo.wjoin(f))
60 #self.repo.remove([f])
60 #self.repo.remove([f])
61 except:
61 except:
62 pass
62 pass
63
63
64 def setbranch(self, branch, pbranch, parents):
64 def setbranch(self, branch, pbranch, parents):
65 if (not self.clonebranches) or (branch == self.lastbranch):
65 if (not self.clonebranches) or (branch == self.lastbranch):
66 return
66 return
67
67
68 self.lastbranch = branch
68 self.lastbranch = branch
69 self.after()
69 self.after()
70 if not branch:
70 if not branch:
71 branch = 'default'
71 branch = 'default'
72 if not pbranch:
72 if not pbranch:
73 pbranch = 'default'
73 pbranch = 'default'
74
74
75 branchpath = os.path.join(self.path, branch)
75 branchpath = os.path.join(self.path, branch)
76 try:
76 try:
77 self.repo = hg.repository(self.ui, branchpath)
77 self.repo = hg.repository(self.ui, branchpath)
78 except:
78 except:
79 if not parents:
79 if not parents:
80 self.repo = hg.repository(self.ui, branchpath, create=True)
80 self.repo = hg.repository(self.ui, branchpath, create=True)
81 else:
81 else:
82 self.ui.note(_('cloning branch %s to %s\n') % (pbranch, branch))
82 self.ui.note(_('cloning branch %s to %s\n') % (pbranch, branch))
83 hg.clone(self.ui, os.path.join(self.path, pbranch),
83 hg.clone(self.ui, os.path.join(self.path, pbranch),
84 branchpath, rev=parents, update=False,
84 branchpath, rev=parents, update=False,
85 stream=True)
85 stream=True)
86 self.repo = hg.repository(self.ui, branchpath)
86 self.repo = hg.repository(self.ui, branchpath)
87
87
88 def putcommit(self, files, parents, commit):
88 def putcommit(self, files, parents, commit):
89 if not files:
89 seen = {}
90 return hex(self.repo.changelog.tip())
91
92 seen = {hex(nullid): 1}
93 pl = []
90 pl = []
94 for p in parents:
91 for p in parents:
95 if p not in seen:
92 if p not in seen:
96 pl.append(p)
93 pl.append(p)
97 seen[p] = 1
94 seen[p] = 1
98 parents = pl
95 parents = pl
99
96
100 if len(parents) < 2: parents.append("0" * 40)
97 if len(parents) < 2: parents.append("0" * 40)
101 if len(parents) < 2: parents.append("0" * 40)
98 if len(parents) < 2: parents.append("0" * 40)
102 p2 = parents.pop(0)
99 p2 = parents.pop(0)
103
100
104 text = commit.desc
101 text = commit.desc
105 extra = {}
102 extra = {}
106 if self.branchnames and commit.branch:
103 if self.branchnames and commit.branch:
107 extra['branch'] = commit.branch
104 extra['branch'] = commit.branch
108 if commit.rev:
105 if commit.rev:
109 extra['convert_revision'] = commit.rev
106 extra['convert_revision'] = commit.rev
110
107
111 while parents:
108 while parents:
112 p1 = p2
109 p1 = p2
113 p2 = parents.pop(0)
110 p2 = parents.pop(0)
114 a = self.repo.rawcommit(files, text, commit.author, commit.date,
111 a = self.repo.rawcommit(files, text, commit.author, commit.date,
115 bin(p1), bin(p2), extra=extra)
112 bin(p1), bin(p2), extra=extra)
116 self.repo.dirstate.invalidate()
113 self.repo.dirstate.invalidate()
117 text = "(octopus merge fixup)\n"
114 text = "(octopus merge fixup)\n"
118 p2 = hg.hex(self.repo.changelog.tip())
115 p2 = hg.hex(self.repo.changelog.tip())
119
116
120 return p2
117 return p2
121
118
122 def puttags(self, tags):
119 def puttags(self, tags):
123 try:
120 try:
124 old = self.repo.wfile(".hgtags").read()
121 old = self.repo.wfile(".hgtags").read()
125 oldlines = old.splitlines(1)
122 oldlines = old.splitlines(1)
126 oldlines.sort()
123 oldlines.sort()
127 except:
124 except:
128 oldlines = []
125 oldlines = []
129
126
130 k = tags.keys()
127 k = tags.keys()
131 k.sort()
128 k.sort()
132 newlines = []
129 newlines = []
133 for tag in k:
130 for tag in k:
134 newlines.append("%s %s\n" % (tags[tag], tag))
131 newlines.append("%s %s\n" % (tags[tag], tag))
135
132
136 newlines.sort()
133 newlines.sort()
137
134
138 if newlines != oldlines:
135 if newlines != oldlines:
139 self.ui.status("updating tags\n")
136 self.ui.status("updating tags\n")
140 f = self.repo.wfile(".hgtags", "w")
137 f = self.repo.wfile(".hgtags", "w")
141 f.write("".join(newlines))
138 f.write("".join(newlines))
142 f.close()
139 f.close()
143 if not oldlines: self.repo.add([".hgtags"])
140 if not oldlines: self.repo.add([".hgtags"])
144 date = "%s 0" % int(time.mktime(time.gmtime()))
141 date = "%s 0" % int(time.mktime(time.gmtime()))
145 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
142 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
146 date, self.repo.changelog.tip(), nullid)
143 date, self.repo.changelog.tip(), nullid)
147 return hex(self.repo.changelog.tip())
144 return hex(self.repo.changelog.tip())
148
145
149 class mercurial_source(converter_source):
146 class mercurial_source(converter_source):
150 def __init__(self, ui, path, rev=None):
147 def __init__(self, ui, path, rev=None):
151 converter_source.__init__(self, ui, path, rev)
148 converter_source.__init__(self, ui, path, rev)
152 self.repo = hg.repository(self.ui, path)
149 self.repo = hg.repository(self.ui, path)
153 self.lastrev = None
150 self.lastrev = None
154 self.lastctx = None
151 self.lastctx = None
155
152
156 def changectx(self, rev):
153 def changectx(self, rev):
157 if self.lastrev != rev:
154 if self.lastrev != rev:
158 self.lastctx = self.repo.changectx(rev)
155 self.lastctx = self.repo.changectx(rev)
159 self.lastrev = rev
156 self.lastrev = rev
160 return self.lastctx
157 return self.lastctx
161
158
162 def getheads(self):
159 def getheads(self):
163 if self.rev:
160 if self.rev:
164 return [hex(self.repo.changectx(self.rev).node())]
161 return [hex(self.repo.changectx(self.rev).node())]
165 else:
162 else:
166 return [hex(node) for node in self.repo.heads()]
163 return [hex(node) for node in self.repo.heads()]
167
164
168 def getfile(self, name, rev):
165 def getfile(self, name, rev):
169 try:
166 try:
170 return self.changectx(rev).filectx(name).data()
167 return self.changectx(rev).filectx(name).data()
171 except revlog.LookupError, err:
168 except revlog.LookupError, err:
172 raise IOError(err)
169 raise IOError(err)
173
170
174 def getmode(self, name, rev):
171 def getmode(self, name, rev):
175 m = self.changectx(rev).manifest()
172 m = self.changectx(rev).manifest()
176 return (m.execf(name) and 'x' or '') + (m.linkf(name) and 'l' or '')
173 return (m.execf(name) and 'x' or '') + (m.linkf(name) and 'l' or '')
177
174
178 def getchanges(self, rev):
175 def getchanges(self, rev):
179 ctx = self.changectx(rev)
176 ctx = self.changectx(rev)
180 m, a, r = self.repo.status(ctx.parents()[0].node(), ctx.node())[:3]
177 m, a, r = self.repo.status(ctx.parents()[0].node(), ctx.node())[:3]
181 changes = [(name, rev) for name in m + a + r]
178 changes = [(name, rev) for name in m + a + r]
182 changes.sort()
179 changes.sort()
183 return (changes, self.getcopies(ctx))
180 return (changes, self.getcopies(ctx))
184
181
185 def getcopies(self, ctx):
182 def getcopies(self, ctx):
186 added = self.repo.status(ctx.parents()[0].node(), ctx.node())[1]
183 added = self.repo.status(ctx.parents()[0].node(), ctx.node())[1]
187 copies = {}
184 copies = {}
188 for name in added:
185 for name in added:
189 try:
186 try:
190 copies[name] = ctx.filectx(name).renamed()[0]
187 copies[name] = ctx.filectx(name).renamed()[0]
191 except TypeError:
188 except TypeError:
192 pass
189 pass
193 return copies
190 return copies
194
191
195 def getcommit(self, rev):
192 def getcommit(self, rev):
196 ctx = self.changectx(rev)
193 ctx = self.changectx(rev)
197 parents = [hex(p.node()) for p in ctx.parents() if p.node() != nullid]
194 parents = [hex(p.node()) for p in ctx.parents() if p.node() != nullid]
198 return commit(author=ctx.user(), date=util.datestr(ctx.date()),
195 return commit(author=ctx.user(), date=util.datestr(ctx.date()),
199 desc=ctx.description(), parents=parents,
196 desc=ctx.description(), parents=parents,
200 branch=ctx.branch())
197 branch=ctx.branch())
201
198
202 def gettags(self):
199 def gettags(self):
203 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
200 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
204 return dict([(name, hex(node)) for name, node in tags])
201 return dict([(name, hex(node)) for name, node in tags])
General Comments 0
You need to be logged in to leave comments. Login now