Show More
@@ -11,8 +11,9 b' 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 |
|
12 | from subversion import convert_svn | |
13 |
|
13 | |||
14 | import os, 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 |
|
17 | |||
17 | commands.norepo += " convert" |
|
18 | commands.norepo += " convert" | |
18 |
|
19 | |||
@@ -42,7 +43,7 b' def convertsink(ui, path):' | |||||
42 | raise util.Abort('%s: unknown repository type' % path) |
|
43 | raise util.Abort('%s: unknown repository type' % path) | |
43 |
|
44 | |||
44 | class convert(object): |
|
45 | class convert(object): | |
45 | def __init__(self, ui, source, dest, revmapfile, opts): |
|
46 | def __init__(self, ui, source, dest, revmapfile, filemapper, opts): | |
46 |
|
47 | |||
47 | self.source = source |
|
48 | self.source = source | |
48 | self.dest = dest |
|
49 | self.dest = dest | |
@@ -53,6 +54,7 b' class convert(object):' | |||||
53 | self.revmapfilefd = None |
|
54 | self.revmapfilefd = None | |
54 | self.authors = {} |
|
55 | self.authors = {} | |
55 | self.authorfile = None |
|
56 | self.authorfile = None | |
|
57 | self.mapfile = filemapper | |||
56 |
|
58 | |||
57 | self.map = {} |
|
59 | self.map = {} | |
58 | try: |
|
60 | try: | |
@@ -191,28 +193,31 b' class convert(object):' | |||||
191 | afile.close() |
|
193 | afile.close() | |
192 |
|
194 | |||
193 | def copy(self, rev): |
|
195 | def copy(self, rev): | |
194 | c = self.commitcache[rev] |
|
196 | commit = self.commitcache[rev] | |
195 | files = self.source.getchanges(rev) |
|
197 | do_copies = hasattr(self.dest, 'copyfile') | |
|
198 | filenames = [] | |||
196 |
|
199 | |||
197 | do_copies = hasattr(self.dest, 'copyfile') |
|
200 | for f, v in self.source.getchanges(rev): | |
198 |
|
201 | newf = self.mapfile(f) | ||
199 |
f |
|
202 | if not newf: | |
|
203 | continue | |||
|
204 | filenames.append(newf) | |||
200 | try: |
|
205 | try: | |
201 | data = self.source.getfile(f, v) |
|
206 | data = self.source.getfile(f, v) | |
202 | except IOError, inst: |
|
207 | except IOError, inst: | |
203 | self.dest.delfile(f) |
|
208 | self.dest.delfile(newf) | |
204 | else: |
|
209 | else: | |
205 | e = self.source.getmode(f, v) |
|
210 | e = self.source.getmode(f, v) | |
206 | self.dest.putfile(f, e, data) |
|
211 | self.dest.putfile(newf, e, data) | |
207 | if do_copies: |
|
212 | if do_copies: | |
208 | if f in c.copies: |
|
213 | if f in commit.copies: | |
209 | # Merely marks that a copy happened. |
|
214 | copyf = self.mapfile(commit.copies[f]) | |
210 |
|
|
215 | if copyf: | |
|
216 | # Merely marks that a copy happened. | |||
|
217 | self.dest.copyfile(copyf, newf) | |||
211 |
|
218 | |||
212 |
|
219 | parents = [self.map[r] for r in commit.parents] | ||
213 | r = [self.map[v] for v in c.parents] |
|
220 | newnode = self.dest.putcommit(filenames, parents, commit) | |
214 | f = [f for f, v in files] |
|
|||
215 | newnode = self.dest.putcommit(f, r, c) |
|
|||
216 | self.mapentry(rev, newnode) |
|
221 | self.mapentry(rev, newnode) | |
217 |
|
222 | |||
218 | def convert(self): |
|
223 | def convert(self): | |
@@ -262,6 +267,87 b' class convert(object):' | |||||
262 | if self.revmapfilefd: |
|
267 | if self.revmapfilefd: | |
263 | self.revmapfilefd.close() |
|
268 | self.revmapfilefd.close() | |
264 |
|
269 | |||
|
270 | def rpairs(name): | |||
|
271 | e = len(name) | |||
|
272 | while e != -1: | |||
|
273 | yield name[:e], name[e+1:] | |||
|
274 | e = name.rfind('/', 0, e) | |||
|
275 | ||||
|
276 | class filemapper(object): | |||
|
277 | '''Map and filter filenames when importing. | |||
|
278 | A name can be mapped to itself, a new name, or None (omit from new | |||
|
279 | repository).''' | |||
|
280 | ||||
|
281 | def __init__(self, ui, path=None): | |||
|
282 | self.ui = ui | |||
|
283 | self.include = {} | |||
|
284 | self.exclude = {} | |||
|
285 | self.rename = {} | |||
|
286 | if path: | |||
|
287 | if self.parse(path): | |||
|
288 | raise util.Abort(_('errors in filemap')) | |||
|
289 | ||||
|
290 | def parse(self, path): | |||
|
291 | errs = 0 | |||
|
292 | def check(name, mapping, listname): | |||
|
293 | if name in mapping: | |||
|
294 | self.ui.warn(_('%s:%d: %r already in %s list\n') % | |||
|
295 | (lex.infile, lex.lineno, name, listname)) | |||
|
296 | return 1 | |||
|
297 | return 0 | |||
|
298 | lex = shlex.shlex(open(path), path, True) | |||
|
299 | lex.wordchars += '!@#$%^&*()-=+[]{}|;:,./<>?' | |||
|
300 | cmd = lex.get_token() | |||
|
301 | while cmd: | |||
|
302 | if cmd == 'include': | |||
|
303 | name = lex.get_token() | |||
|
304 | errs += check(name, self.exclude, 'exclude') | |||
|
305 | self.include[name] = name | |||
|
306 | elif cmd == 'exclude': | |||
|
307 | name = lex.get_token() | |||
|
308 | errs += check(name, self.include, 'include') | |||
|
309 | errs += check(name, self.rename, 'rename') | |||
|
310 | self.exclude[name] = name | |||
|
311 | elif cmd == 'rename': | |||
|
312 | src = lex.get_token() | |||
|
313 | dest = lex.get_token() | |||
|
314 | errs += check(src, self.exclude, 'exclude') | |||
|
315 | self.rename[src] = dest | |||
|
316 | elif cmd == 'source': | |||
|
317 | errs += self.parse(lex.get_token()) | |||
|
318 | else: | |||
|
319 | self.ui.warn(_('%s:%d: unknown directive %r\n') % | |||
|
320 | (lex.infile, lex.lineno, cmd)) | |||
|
321 | errs += 1 | |||
|
322 | cmd = lex.get_token() | |||
|
323 | return errs | |||
|
324 | ||||
|
325 | def lookup(self, name, mapping): | |||
|
326 | for pre, suf in rpairs(name): | |||
|
327 | try: | |||
|
328 | return mapping[pre], pre, suf | |||
|
329 | except KeyError, err: | |||
|
330 | pass | |||
|
331 | return '', name, '' | |||
|
332 | ||||
|
333 | def __call__(self, name): | |||
|
334 | if self.include: | |||
|
335 | inc = self.lookup(name, self.include)[0] | |||
|
336 | else: | |||
|
337 | inc = name | |||
|
338 | if self.exclude: | |||
|
339 | exc = self.lookup(name, self.exclude)[0] | |||
|
340 | else: | |||
|
341 | exc = '' | |||
|
342 | if not inc or exc: | |||
|
343 | return None | |||
|
344 | newpre, pre, suf = self.lookup(name, self.rename) | |||
|
345 | if newpre: | |||
|
346 | if suf: | |||
|
347 | return newpre + '/' + suf | |||
|
348 | return newpre | |||
|
349 | return name | |||
|
350 | ||||
265 | def _convert(ui, src, dest=None, revmapfile=None, **opts): |
|
351 | def _convert(ui, src, dest=None, revmapfile=None, **opts): | |
266 | """Convert a foreign SCM repository to a Mercurial one. |
|
352 | """Convert a foreign SCM repository to a Mercurial one. | |
267 |
|
353 | |||
@@ -343,13 +429,16 b' def _convert(ui, src, dest=None, revmapf' | |||||
343 | except: |
|
429 | except: | |
344 | revmapfile = os.path.join(destc, "map") |
|
430 | revmapfile = os.path.join(destc, "map") | |
345 |
|
431 | |||
346 | c = convert(ui, srcc, destc, revmapfile, opts) |
|
432 | ||
|
433 | c = convert(ui, srcc, destc, revmapfile, filemapper(ui, opts['filemap']), | |||
|
434 | opts) | |||
347 | c.convert() |
|
435 | c.convert() | |
348 |
|
436 | |||
349 | cmdtable = { |
|
437 | cmdtable = { | |
350 | "convert": |
|
438 | "convert": | |
351 | (_convert, |
|
439 | (_convert, | |
352 | [('A', 'authors', '', 'username mapping filename'), |
|
440 | [('A', 'authors', '', 'username mapping filename'), | |
|
441 | ('', 'filemap', '', 'remap file names using contents of file'), | |||
353 | ('r', 'rev', '', 'import up to target revision REV'), |
|
442 | ('r', 'rev', '', 'import up to target revision REV'), | |
354 | ('', 'datesort', None, 'try to sort changesets by date')], |
|
443 | ('', 'datesort', None, 'try to sort changesets by date')], | |
355 | 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'), |
|
444 | 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'), |
@@ -59,7 +59,10 b' class mercurial_sink(converter_sink):' | |||||
59 | pass |
|
59 | pass | |
60 |
|
60 | |||
61 | def putcommit(self, files, parents, commit): |
|
61 | def putcommit(self, files, parents, commit): | |
62 | seen = {} |
|
62 | if not files: | |
|
63 | return hex(self.repo.changelog.tip()) | |||
|
64 | ||||
|
65 | seen = {hex(nullid): 1} | |||
63 | pl = [] |
|
66 | pl = [] | |
64 | for p in parents: |
|
67 | for p in parents: | |
65 | if p not in seen: |
|
68 | if p not in seen: |
General Comments 0
You need to be logged in to leave comments.
Login now