hgext/convert/convcmd.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43085 eef9a2d67051
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    50 monotone_source = monotone.monotone_source
    50 monotone_source = monotone.monotone_source
    51 p4_source = p4.p4_source
    51 p4_source = p4.p4_source
    52 svn_sink = subversion.svn_sink
    52 svn_sink = subversion.svn_sink
    53 svn_source = subversion.svn_source
    53 svn_source = subversion.svn_source
    54 
    54 
    55 orig_encoding = 'ascii'
    55 orig_encoding = b'ascii'
    56 
    56 
    57 
    57 
    58 def recode(s):
    58 def recode(s):
    59     if isinstance(s, pycompat.unicode):
    59     if isinstance(s, pycompat.unicode):
    60         return s.encode(pycompat.sysstr(orig_encoding), 'replace')
    60         return s.encode(pycompat.sysstr(orig_encoding), 'replace')
    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.
   209 
   209 
   210         if not path:
   210         if not path:
   211             return {}
   211             return {}
   212         m = {}
   212         m = {}
   213         try:
   213         try:
   214             fp = open(path, 'rb')
   214             fp = open(path, b'rb')
   215             for i, line in enumerate(util.iterfile(fp)):
   215             for i, line in enumerate(util.iterfile(fp)):
   216                 line = line.splitlines()[0].rstrip()
   216                 line = line.splitlines()[0].rstrip()
   217                 if not line:
   217                 if not line:
   218                     # Ignore blank lines
   218                     # Ignore blank lines
   219                     continue
   219                     continue
   220                 # split line
   220                 # split line
   221                 lex = common.shlexer(data=line, whitespace=',')
   221                 lex = common.shlexer(data=line, whitespace=b',')
   222                 line = list(lex)
   222                 line = list(lex)
   223                 # check number of parents
   223                 # check number of parents
   224                 if not (2 <= len(line) <= 3):
   224                 if not (2 <= len(line) <= 3):
   225                     raise error.Abort(
   225                     raise error.Abort(
   226                         _(
   226                         _(
   227                             'syntax error in %s(%d): child parent1'
   227                             b'syntax error in %s(%d): child parent1'
   228                             '[,parent2] expected'
   228                             b'[,parent2] expected'
   229                         )
   229                         )
   230                         % (path, i + 1)
   230                         % (path, i + 1)
   231                     )
   231                     )
   232                 for part in line:
   232                 for part in line:
   233                     self.source.checkrevformat(part)
   233                     self.source.checkrevformat(part)
   237                 else:
   237                 else:
   238                     m[child] = p1 + p2
   238                     m[child] = p1 + p2
   239         # if file does not exist or error reading, exit
   239         # if file does not exist or error reading, exit
   240         except IOError:
   240         except IOError:
   241             raise error.Abort(
   241             raise error.Abort(
   242                 _('splicemap file not found or error reading %s:') % path
   242                 _(b'splicemap file not found or error reading %s:') % path
   243             )
   243             )
   244         return m
   244         return m
   245 
   245 
   246     def walktree(self, heads):
   246     def walktree(self, heads):
   247         '''Return a mapping that identifies the uncommitted parents of every
   247         '''Return a mapping that identifies the uncommitted parents of every
   249         visit = list(heads)
   249         visit = list(heads)
   250         known = set()
   250         known = set()
   251         parents = {}
   251         parents = {}
   252         numcommits = self.source.numcommits()
   252         numcommits = self.source.numcommits()
   253         progress = self.ui.makeprogress(
   253         progress = self.ui.makeprogress(
   254             _('scanning'), unit=_('revisions'), total=numcommits
   254             _(b'scanning'), unit=_(b'revisions'), total=numcommits
   255         )
   255         )
   256         while visit:
   256         while visit:
   257             n = visit.pop(0)
   257             n = visit.pop(0)
   258             if n in known:
   258             if n in known:
   259                 continue
   259                 continue
   281             if c not in parents:
   281             if c not in parents:
   282                 if not self.dest.hascommitforsplicemap(self.map.get(c, c)):
   282                 if not self.dest.hascommitforsplicemap(self.map.get(c, c)):
   283                     # Could be in source but not converted during this run
   283                     # Could be in source but not converted during this run
   284                     self.ui.warn(
   284                     self.ui.warn(
   285                         _(
   285                         _(
   286                             'splice map revision %s is not being '
   286                             b'splice map revision %s is not being '
   287                             'converted, ignoring\n'
   287                             b'converted, ignoring\n'
   288                         )
   288                         )
   289                         % c
   289                         % c
   290                     )
   290                     )
   291                 continue
   291                 continue
   292             pc = []
   292             pc = []
   294                 # We do not have to wait for nodes already in dest.
   294                 # We do not have to wait for nodes already in dest.
   295                 if self.dest.hascommitforsplicemap(self.map.get(p, p)):
   295                 if self.dest.hascommitforsplicemap(self.map.get(p, p)):
   296                     continue
   296                     continue
   297                 # Parent is not in dest and not being converted, not good
   297                 # Parent is not in dest and not being converted, not good
   298                 if p not in parents:
   298                 if p not in parents:
   299                     raise error.Abort(_('unknown splice map parent: %s') % p)
   299                     raise error.Abort(_(b'unknown splice map parent: %s') % p)
   300                 pc.append(p)
   300                 pc.append(p)
   301             parents[c] = pc
   301             parents[c] = pc
   302 
   302 
   303     def toposort(self, parents, sortmode):
   303     def toposort(self, parents, sortmode):
   304         '''Return an ordering such that every uncommitted changeset is
   304         '''Return an ordering such that every uncommitted changeset is
   367             return picknext
   367             return picknext
   368 
   368 
   369         def makeclosesorter():
   369         def makeclosesorter():
   370             """Close order sort."""
   370             """Close order sort."""
   371             keyfn = lambda n: (
   371             keyfn = lambda n: (
   372                 'close' not in self.commitcache[n].extra,
   372                 b'close' not in self.commitcache[n].extra,
   373                 self.commitcache[n].sortkey,
   373                 self.commitcache[n].sortkey,
   374             )
   374             )
   375 
   375 
   376             def picknext(nodes):
   376             def picknext(nodes):
   377                 return sorted(nodes, key=keyfn)[0]
   377                 return sorted(nodes, key=keyfn)[0]
   390             def picknext(nodes):
   390             def picknext(nodes):
   391                 return min([(getdate(n), n) for n in nodes])[1]
   391                 return min([(getdate(n), n) for n in nodes])[1]
   392 
   392 
   393             return picknext
   393             return picknext
   394 
   394 
   395         if sortmode == 'branchsort':
   395         if sortmode == b'branchsort':
   396             picknext = makebranchsorter()
   396             picknext = makebranchsorter()
   397         elif sortmode == 'datesort':
   397         elif sortmode == b'datesort':
   398             picknext = makedatesorter()
   398             picknext = makedatesorter()
   399         elif sortmode == 'sourcesort':
   399         elif sortmode == b'sourcesort':
   400             picknext = makesourcesorter()
   400             picknext = makesourcesorter()
   401         elif sortmode == 'closesort':
   401         elif sortmode == b'closesort':
   402             picknext = makeclosesorter()
   402             picknext = makeclosesorter()
   403         else:
   403         else:
   404             raise error.Abort(_('unknown sort mode: %s') % sortmode)
   404             raise error.Abort(_(b'unknown sort mode: %s') % sortmode)
   405 
   405 
   406         children, actives = mapchildren(parents)
   406         children, actives = mapchildren(parents)
   407 
   407 
   408         s = []
   408         s = []
   409         pendings = {}
   409         pendings = {}
   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):
   479         self.commitcache[rev] = commit
   481         self.commitcache[rev] = commit
   480         return commit
   482         return commit
   481 
   483 
   482     def copy(self, rev):
   484     def copy(self, rev):
   483         commit = self.commitcache[rev]
   485         commit = self.commitcache[rev]
   484         full = self.opts.get('full')
   486         full = self.opts.get(b'full')
   485         changes = self.source.getchanges(rev, full)
   487         changes = self.source.getchanges(rev, full)
   486         if isinstance(changes, bytes):
   488         if isinstance(changes, bytes):
   487             if changes == SKIPREV:
   489             if changes == SKIPREV:
   488                 dest = SKIPREV
   490                 dest = SKIPREV
   489             else:
   491             else:
   501                 )
   503                 )
   502         self.dest.setbranch(commit.branch, pbranches)
   504         self.dest.setbranch(commit.branch, pbranches)
   503         try:
   505         try:
   504             parents = self.splicemap[rev]
   506             parents = self.splicemap[rev]
   505             self.ui.status(
   507             self.ui.status(
   506                 _('spliced in %s as parents of %s\n')
   508                 _(b'spliced in %s as parents of %s\n')
   507                 % (_(' and ').join(parents), rev)
   509                 % (_(b' and ').join(parents), rev)
   508             )
   510             )
   509             parents = [self.map.get(p, p) for p in parents]
   511             parents = [self.map.get(p, p) for p in parents]
   510         except KeyError:
   512         except KeyError:
   511             parents = [b[0] for b in pbranches]
   513             parents = [b[0] for b in pbranches]
   512             parents.extend(
   514             parents.extend(
   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: