88 # If branch is None or empty, this commit is coming from the source |
88 # If branch is None or empty, this commit is coming from the source |
89 # repository's default branch and destined for the default branch in the |
89 # repository's default branch and destined for the default branch in the |
90 # destination repository. For such commits, using a literal "default" |
90 # destination repository. For such commits, using a literal "default" |
91 # in branchmap below allows the user to map "default" to an alternate |
91 # in branchmap below allows the user to map "default" to an alternate |
92 # default branch in the destination repository. |
92 # default branch in the destination repository. |
93 branch = branchmap.get(branch or 'default', branch) |
93 branch = branchmap.get(branch or b'default', branch) |
94 # At some point we used "None" literal to denote the default branch, |
94 # At some point we used "None" literal to denote the default branch, |
95 # attempt to use that for backward compatibility. |
95 # attempt to use that for backward compatibility. |
96 if not branch: |
96 if not branch: |
97 branch = branchmap.get('None', branch) |
97 branch = branchmap.get(b'None', branch) |
98 return branch |
98 return branch |
99 |
99 |
100 |
100 |
101 source_converters = [ |
101 source_converters = [ |
102 ('cvs', convert_cvs, 'branchsort'), |
102 (b'cvs', convert_cvs, b'branchsort'), |
103 ('git', convert_git, 'branchsort'), |
103 (b'git', convert_git, b'branchsort'), |
104 ('svn', svn_source, 'branchsort'), |
104 (b'svn', svn_source, b'branchsort'), |
105 ('hg', mercurial_source, 'sourcesort'), |
105 (b'hg', mercurial_source, b'sourcesort'), |
106 ('darcs', darcs_source, 'branchsort'), |
106 (b'darcs', darcs_source, b'branchsort'), |
107 ('mtn', monotone_source, 'branchsort'), |
107 (b'mtn', monotone_source, b'branchsort'), |
108 ('gnuarch', gnuarch_source, 'branchsort'), |
108 (b'gnuarch', gnuarch_source, b'branchsort'), |
109 ('bzr', bzr_source, 'branchsort'), |
109 (b'bzr', bzr_source, b'branchsort'), |
110 ('p4', p4_source, 'branchsort'), |
110 (b'p4', p4_source, b'branchsort'), |
111 ] |
111 ] |
112 |
112 |
113 sink_converters = [ |
113 sink_converters = [ |
114 ('hg', mercurial_sink), |
114 (b'hg', mercurial_sink), |
115 ('svn', svn_sink), |
115 (b'svn', svn_sink), |
116 ] |
116 ] |
117 |
117 |
118 |
118 |
119 def convertsource(ui, path, type, revs): |
119 def convertsource(ui, path, type, revs): |
120 exceptions = [] |
120 exceptions = [] |
121 if type and type not in [s[0] for s in source_converters]: |
121 if type and type not in [s[0] for s in source_converters]: |
122 raise error.Abort(_('%s: invalid source repository type') % type) |
122 raise error.Abort(_(b'%s: invalid source repository type') % type) |
123 for name, source, sortmode in source_converters: |
123 for name, source, sortmode in source_converters: |
124 try: |
124 try: |
125 if not type or name == type: |
125 if not type or name == type: |
126 return source(ui, name, path, revs), sortmode |
126 return source(ui, name, path, revs), sortmode |
127 except (NoRepo, MissingTool) as inst: |
127 except (NoRepo, MissingTool) as inst: |
128 exceptions.append(inst) |
128 exceptions.append(inst) |
129 if not ui.quiet: |
129 if not ui.quiet: |
130 for inst in exceptions: |
130 for inst in exceptions: |
131 ui.write("%s\n" % pycompat.bytestr(inst.args[0])) |
131 ui.write(b"%s\n" % pycompat.bytestr(inst.args[0])) |
132 raise error.Abort(_('%s: missing or unsupported repository') % path) |
132 raise error.Abort(_(b'%s: missing or unsupported repository') % path) |
133 |
133 |
134 |
134 |
135 def convertsink(ui, path, type): |
135 def convertsink(ui, path, type): |
136 if type and type not in [s[0] for s in sink_converters]: |
136 if type and type not in [s[0] for s in sink_converters]: |
137 raise error.Abort(_('%s: invalid destination repository type') % type) |
137 raise error.Abort(_(b'%s: invalid destination repository type') % type) |
138 for name, sink in sink_converters: |
138 for name, sink in sink_converters: |
139 try: |
139 try: |
140 if not type or name == type: |
140 if not type or name == type: |
141 return sink(ui, name, path) |
141 return sink(ui, name, path) |
142 except NoRepo as inst: |
142 except NoRepo as inst: |
143 ui.note(_("convert: %s\n") % inst) |
143 ui.note(_(b"convert: %s\n") % inst) |
144 except MissingTool as inst: |
144 except MissingTool as inst: |
145 raise error.Abort('%s\n' % inst) |
145 raise error.Abort(b'%s\n' % inst) |
146 raise error.Abort(_('%s: unknown repository type') % path) |
146 raise error.Abort(_(b'%s: unknown repository type') % path) |
147 |
147 |
148 |
148 |
149 class progresssource(object): |
149 class progresssource(object): |
150 def __init__(self, ui, source, filecount): |
150 def __init__(self, ui, source, filecount): |
151 self.ui = ui |
151 self.ui = ui |
152 self.source = source |
152 self.source = source |
153 self.progress = ui.makeprogress( |
153 self.progress = ui.makeprogress( |
154 _('getting files'), unit=_('files'), total=filecount |
154 _(b'getting files'), unit=_(b'files'), total=filecount |
155 ) |
155 ) |
156 |
156 |
157 def getfile(self, file, rev): |
157 def getfile(self, file, rev): |
158 self.progress.increment(item=file) |
158 self.progress.increment(item=file) |
159 return self.source.getfile(file, rev) |
159 return self.source.getfile(file, rev) |
187 # Read first the dst author map if any |
187 # Read first the dst author map if any |
188 authorfile = self.dest.authorfile() |
188 authorfile = self.dest.authorfile() |
189 if authorfile and os.path.exists(authorfile): |
189 if authorfile and os.path.exists(authorfile): |
190 self.readauthormap(authorfile) |
190 self.readauthormap(authorfile) |
191 # Extend/Override with new author map if necessary |
191 # Extend/Override with new author map if necessary |
192 if opts.get('authormap'): |
192 if opts.get(b'authormap'): |
193 self.readauthormap(opts.get('authormap')) |
193 self.readauthormap(opts.get(b'authormap')) |
194 self.authorfile = self.dest.authorfile() |
194 self.authorfile = self.dest.authorfile() |
195 |
195 |
196 self.splicemap = self.parsesplicemap(opts.get('splicemap')) |
196 self.splicemap = self.parsesplicemap(opts.get(b'splicemap')) |
197 self.branchmap = mapfile(ui, opts.get('branchmap')) |
197 self.branchmap = mapfile(ui, opts.get(b'branchmap')) |
198 |
198 |
199 def parsesplicemap(self, path): |
199 def parsesplicemap(self, path): |
200 """ check and validate the splicemap format and |
200 """ check and validate the splicemap format and |
201 return a child/parents dictionary. |
201 return a child/parents dictionary. |
202 Format checking has two parts. |
202 Format checking has two parts. |
418 pendings[c] = [p for p in parents[c] if p not in self.map] |
418 pendings[c] = [p for p in parents[c] if p not in self.map] |
419 try: |
419 try: |
420 pendings[c].remove(n) |
420 pendings[c].remove(n) |
421 except ValueError: |
421 except ValueError: |
422 raise error.Abort( |
422 raise error.Abort( |
423 _('cycle detected between %s and %s') |
423 _(b'cycle detected between %s and %s') |
424 % (recode(c), recode(n)) |
424 % (recode(c), recode(n)) |
425 ) |
425 ) |
426 if not pendings[c]: |
426 if not pendings[c]: |
427 # Parents are converted, node is eligible |
427 # Parents are converted, node is eligible |
428 actives.insert(0, c) |
428 actives.insert(0, c) |
429 pendings[c] = None |
429 pendings[c] = None |
430 |
430 |
431 if len(s) != len(parents): |
431 if len(s) != len(parents): |
432 raise error.Abort(_("not all revisions were sorted")) |
432 raise error.Abort(_(b"not all revisions were sorted")) |
433 |
433 |
434 return s |
434 return s |
435 |
435 |
436 def writeauthormap(self): |
436 def writeauthormap(self): |
437 authorfile = self.authorfile |
437 authorfile = self.authorfile |
438 if authorfile: |
438 if authorfile: |
439 self.ui.status(_('writing author map file %s\n') % authorfile) |
439 self.ui.status(_(b'writing author map file %s\n') % authorfile) |
440 ofile = open(authorfile, 'wb+') |
440 ofile = open(authorfile, b'wb+') |
441 for author in self.authors: |
441 for author in self.authors: |
442 ofile.write( |
442 ofile.write( |
443 util.tonativeeol("%s=%s\n" % (author, self.authors[author])) |
443 util.tonativeeol( |
|
444 b"%s=%s\n" % (author, self.authors[author]) |
|
445 ) |
444 ) |
446 ) |
445 ofile.close() |
447 ofile.close() |
446 |
448 |
447 def readauthormap(self, authorfile): |
449 def readauthormap(self, authorfile): |
448 afile = open(authorfile, 'rb') |
450 afile = open(authorfile, b'rb') |
449 for line in afile: |
451 for line in afile: |
450 |
452 |
451 line = line.strip() |
453 line = line.strip() |
452 if not line or line.startswith('#'): |
454 if not line or line.startswith(b'#'): |
453 continue |
455 continue |
454 |
456 |
455 try: |
457 try: |
456 srcauthor, dstauthor = line.split('=', 1) |
458 srcauthor, dstauthor = line.split(b'=', 1) |
457 except ValueError: |
459 except ValueError: |
458 msg = _('ignoring bad line in author map file %s: %s\n') |
460 msg = _(b'ignoring bad line in author map file %s: %s\n') |
459 self.ui.warn(msg % (authorfile, line.rstrip())) |
461 self.ui.warn(msg % (authorfile, line.rstrip())) |
460 continue |
462 continue |
461 |
463 |
462 srcauthor = srcauthor.strip() |
464 srcauthor = srcauthor.strip() |
463 dstauthor = dstauthor.strip() |
465 dstauthor = dstauthor.strip() |
464 if self.authors.get(srcauthor) in (None, dstauthor): |
466 if self.authors.get(srcauthor) in (None, dstauthor): |
465 msg = _('mapping author %s to %s\n') |
467 msg = _(b'mapping author %s to %s\n') |
466 self.ui.debug(msg % (srcauthor, dstauthor)) |
468 self.ui.debug(msg % (srcauthor, dstauthor)) |
467 self.authors[srcauthor] = dstauthor |
469 self.authors[srcauthor] = dstauthor |
468 continue |
470 continue |
469 |
471 |
470 m = _('overriding mapping for author %s, was %s, will be %s\n') |
472 m = _(b'overriding mapping for author %s, was %s, will be %s\n') |
471 self.ui.status(m % (srcauthor, self.authors[srcauthor], dstauthor)) |
473 self.ui.status(m % (srcauthor, self.authors[srcauthor], dstauthor)) |
472 |
474 |
473 afile.close() |
475 afile.close() |
474 |
476 |
475 def cachecommit(self, rev): |
477 def cachecommit(self, rev): |
534 def convert(self, sortmode): |
536 def convert(self, sortmode): |
535 try: |
537 try: |
536 self.source.before() |
538 self.source.before() |
537 self.dest.before() |
539 self.dest.before() |
538 self.source.setrevmap(self.map) |
540 self.source.setrevmap(self.map) |
539 self.ui.status(_("scanning source...\n")) |
541 self.ui.status(_(b"scanning source...\n")) |
540 heads = self.source.getheads() |
542 heads = self.source.getheads() |
541 parents = self.walktree(heads) |
543 parents = self.walktree(heads) |
542 self.mergesplicemap(parents, self.splicemap) |
544 self.mergesplicemap(parents, self.splicemap) |
543 self.ui.status(_("sorting...\n")) |
545 self.ui.status(_(b"sorting...\n")) |
544 t = self.toposort(parents, sortmode) |
546 t = self.toposort(parents, sortmode) |
545 num = len(t) |
547 num = len(t) |
546 c = None |
548 c = None |
547 |
549 |
548 self.ui.status(_("converting...\n")) |
550 self.ui.status(_(b"converting...\n")) |
549 progress = self.ui.makeprogress( |
551 progress = self.ui.makeprogress( |
550 _('converting'), unit=_('revisions'), total=len(t) |
552 _(b'converting'), unit=_(b'revisions'), total=len(t) |
551 ) |
553 ) |
552 for i, c in enumerate(t): |
554 for i, c in enumerate(t): |
553 num -= 1 |
555 num -= 1 |
554 desc = self.commitcache[c].desc |
556 desc = self.commitcache[c].desc |
555 if "\n" in desc: |
557 if b"\n" in desc: |
556 desc = desc.splitlines()[0] |
558 desc = desc.splitlines()[0] |
557 # convert log message to local encoding without using |
559 # convert log message to local encoding without using |
558 # tolocal() because the encoding.encoding convert() |
560 # tolocal() because the encoding.encoding convert() |
559 # uses is 'utf-8' |
561 # uses is 'utf-8' |
560 self.ui.status("%d %s\n" % (num, recode(desc))) |
562 self.ui.status(b"%d %s\n" % (num, recode(desc))) |
561 self.ui.note(_("source: %s\n") % recode(c)) |
563 self.ui.note(_(b"source: %s\n") % recode(c)) |
562 progress.update(i) |
564 progress.update(i) |
563 self.copy(c) |
565 self.copy(c) |
564 progress.complete() |
566 progress.complete() |
565 |
567 |
566 if not self.ui.configbool('convert', 'skiptags'): |
568 if not self.ui.configbool(b'convert', b'skiptags'): |
567 tags = self.source.gettags() |
569 tags = self.source.gettags() |
568 ctags = {} |
570 ctags = {} |
569 for k in tags: |
571 for k in tags: |
570 v = tags[k] |
572 v = tags[k] |
571 if self.map.get(v, SKIPREV) != SKIPREV: |
573 if self.map.get(v, SKIPREV) != SKIPREV: |
608 |
610 |
609 def convert(ui, src, dest=None, revmapfile=None, **opts): |
611 def convert(ui, src, dest=None, revmapfile=None, **opts): |
610 opts = pycompat.byteskwargs(opts) |
612 opts = pycompat.byteskwargs(opts) |
611 global orig_encoding |
613 global orig_encoding |
612 orig_encoding = encoding.encoding |
614 orig_encoding = encoding.encoding |
613 encoding.encoding = 'UTF-8' |
615 encoding.encoding = b'UTF-8' |
614 |
616 |
615 # support --authors as an alias for --authormap |
617 # support --authors as an alias for --authormap |
616 if not opts.get('authormap'): |
618 if not opts.get(b'authormap'): |
617 opts['authormap'] = opts.get('authors') |
619 opts[b'authormap'] = opts.get(b'authors') |
618 |
620 |
619 if not dest: |
621 if not dest: |
620 dest = hg.defaultdest(src) + "-hg" |
622 dest = hg.defaultdest(src) + b"-hg" |
621 ui.status(_("assuming destination %s\n") % dest) |
623 ui.status(_(b"assuming destination %s\n") % dest) |
622 |
624 |
623 destc = convertsink(ui, dest, opts.get('dest_type')) |
625 destc = convertsink(ui, dest, opts.get(b'dest_type')) |
624 destc = scmutil.wrapconvertsink(destc) |
626 destc = scmutil.wrapconvertsink(destc) |
625 |
627 |
626 try: |
628 try: |
627 srcc, defaultsort = convertsource( |
629 srcc, defaultsort = convertsource( |
628 ui, src, opts.get('source_type'), opts.get('rev') |
630 ui, src, opts.get(b'source_type'), opts.get(b'rev') |
629 ) |
631 ) |
630 except Exception: |
632 except Exception: |
631 for path in destc.created: |
633 for path in destc.created: |
632 shutil.rmtree(path, True) |
634 shutil.rmtree(path, True) |
633 raise |
635 raise |
634 |
636 |
635 sortmodes = ('branchsort', 'datesort', 'sourcesort', 'closesort') |
637 sortmodes = (b'branchsort', b'datesort', b'sourcesort', b'closesort') |
636 sortmode = [m for m in sortmodes if opts.get(m)] |
638 sortmode = [m for m in sortmodes if opts.get(m)] |
637 if len(sortmode) > 1: |
639 if len(sortmode) > 1: |
638 raise error.Abort(_('more than one sort mode specified')) |
640 raise error.Abort(_(b'more than one sort mode specified')) |
639 if sortmode: |
641 if sortmode: |
640 sortmode = sortmode[0] |
642 sortmode = sortmode[0] |
641 else: |
643 else: |
642 sortmode = defaultsort |
644 sortmode = defaultsort |
643 |
645 |
644 if sortmode == 'sourcesort' and not srcc.hasnativeorder(): |
646 if sortmode == b'sourcesort' and not srcc.hasnativeorder(): |
645 raise error.Abort( |
647 raise error.Abort( |
646 _('--sourcesort is not supported by this data source') |
648 _(b'--sourcesort is not supported by this data source') |
647 ) |
649 ) |
648 if sortmode == 'closesort' and not srcc.hasnativeclose(): |
650 if sortmode == b'closesort' and not srcc.hasnativeclose(): |
649 raise error.Abort(_('--closesort is not supported by this data source')) |
651 raise error.Abort( |
650 |
652 _(b'--closesort is not supported by this data source') |
651 fmap = opts.get('filemap') |
653 ) |
|
654 |
|
655 fmap = opts.get(b'filemap') |
652 if fmap: |
656 if fmap: |
653 srcc = filemap.filemap_source(ui, srcc, fmap) |
657 srcc = filemap.filemap_source(ui, srcc, fmap) |
654 destc.setfilemapmode(True) |
658 destc.setfilemapmode(True) |
655 |
659 |
656 if not revmapfile: |
660 if not revmapfile: |