hgext/convert/filemap.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43105 649d3ac37a12
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    28     ('.', 'foo/bar/baz')
    28     ('.', 'foo/bar/baz')
    29     '''
    29     '''
    30     i = len(path)
    30     i = len(path)
    31     while i != -1:
    31     while i != -1:
    32         yield path[:i], path[i + 1 :]
    32         yield path[:i], path[i + 1 :]
    33         i = path.rfind('/', 0, i)
    33         i = path.rfind(b'/', 0, i)
    34     yield '.', path
    34     yield b'.', path
    35 
    35 
    36 
    36 
    37 def normalize(path):
    37 def normalize(path):
    38     ''' We use posixpath.normpath to support cross-platform path format.
    38     ''' We use posixpath.normpath to support cross-platform path format.
    39     However, it doesn't handle None input. So we wrap it up. '''
    39     However, it doesn't handle None input. So we wrap it up. '''
    53         self.exclude = {}
    53         self.exclude = {}
    54         self.rename = {}
    54         self.rename = {}
    55         self.targetprefixes = None
    55         self.targetprefixes = None
    56         if path:
    56         if path:
    57             if self.parse(path):
    57             if self.parse(path):
    58                 raise error.Abort(_('errors in filemap'))
    58                 raise error.Abort(_(b'errors in filemap'))
    59 
    59 
    60     def parse(self, path):
    60     def parse(self, path):
    61         errs = 0
    61         errs = 0
    62 
    62 
    63         def check(name, mapping, listname):
    63         def check(name, mapping, listname):
    64             if not name:
    64             if not name:
    65                 self.ui.warn(
    65                 self.ui.warn(
    66                     _('%s:%d: path to %s is missing\n')
    66                     _(b'%s:%d: path to %s is missing\n')
    67                     % (lex.infile, lex.lineno, listname)
    67                     % (lex.infile, lex.lineno, listname)
    68                 )
    68                 )
    69                 return 1
    69                 return 1
    70             if name in mapping:
    70             if name in mapping:
    71                 self.ui.warn(
    71                 self.ui.warn(
    72                     _('%s:%d: %r already in %s list\n')
    72                     _(b'%s:%d: %r already in %s list\n')
    73                     % (lex.infile, lex.lineno, name, listname)
    73                     % (lex.infile, lex.lineno, name, listname)
    74                 )
    74                 )
    75                 return 1
    75                 return 1
    76             if name.startswith('/') or name.endswith('/') or '//' in name:
    76             if name.startswith(b'/') or name.endswith(b'/') or b'//' in name:
    77                 self.ui.warn(
    77                 self.ui.warn(
    78                     _('%s:%d: superfluous / in %s %r\n')
    78                     _(b'%s:%d: superfluous / in %s %r\n')
    79                     % (lex.infile, lex.lineno, listname, pycompat.bytestr(name))
    79                     % (lex.infile, lex.lineno, listname, pycompat.bytestr(name))
    80                 )
    80                 )
    81                 return 1
    81                 return 1
    82             return 0
    82             return 0
    83 
    83 
    84         lex = common.shlexer(
    84         lex = common.shlexer(
    85             filepath=path, wordchars='!@#$%^&*()-=+[]{}|;:,./<>?'
    85             filepath=path, wordchars=b'!@#$%^&*()-=+[]{}|;:,./<>?'
    86         )
    86         )
    87         cmd = lex.get_token()
    87         cmd = lex.get_token()
    88         while cmd:
    88         while cmd:
    89             if cmd == 'include':
    89             if cmd == b'include':
    90                 name = normalize(lex.get_token())
    90                 name = normalize(lex.get_token())
    91                 errs += check(name, self.exclude, 'exclude')
    91                 errs += check(name, self.exclude, b'exclude')
    92                 self.include[name] = name
    92                 self.include[name] = name
    93             elif cmd == 'exclude':
    93             elif cmd == b'exclude':
    94                 name = normalize(lex.get_token())
    94                 name = normalize(lex.get_token())
    95                 errs += check(name, self.include, 'include')
    95                 errs += check(name, self.include, b'include')
    96                 errs += check(name, self.rename, 'rename')
    96                 errs += check(name, self.rename, b'rename')
    97                 self.exclude[name] = name
    97                 self.exclude[name] = name
    98             elif cmd == 'rename':
    98             elif cmd == b'rename':
    99                 src = normalize(lex.get_token())
    99                 src = normalize(lex.get_token())
   100                 dest = normalize(lex.get_token())
   100                 dest = normalize(lex.get_token())
   101                 errs += check(src, self.exclude, 'exclude')
   101                 errs += check(src, self.exclude, b'exclude')
   102                 self.rename[src] = dest
   102                 self.rename[src] = dest
   103             elif cmd == 'source':
   103             elif cmd == b'source':
   104                 errs += self.parse(normalize(lex.get_token()))
   104                 errs += self.parse(normalize(lex.get_token()))
   105             else:
   105             else:
   106                 self.ui.warn(
   106                 self.ui.warn(
   107                     _('%s:%d: unknown directive %r\n')
   107                     _(b'%s:%d: unknown directive %r\n')
   108                     % (lex.infile, lex.lineno, pycompat.bytestr(cmd))
   108                     % (lex.infile, lex.lineno, pycompat.bytestr(cmd))
   109                 )
   109                 )
   110                 errs += 1
   110                 errs += 1
   111             cmd = lex.get_token()
   111             cmd = lex.get_token()
   112         return errs
   112         return errs
   116         for pre, suf in rpairs(name):
   116         for pre, suf in rpairs(name):
   117             try:
   117             try:
   118                 return mapping[pre], pre, suf
   118                 return mapping[pre], pre, suf
   119             except KeyError:
   119             except KeyError:
   120                 pass
   120                 pass
   121         return '', name, ''
   121         return b'', name, b''
   122 
   122 
   123     def istargetfile(self, filename):
   123     def istargetfile(self, filename):
   124         """Return true if the given target filename is covered as a destination
   124         """Return true if the given target filename is covered as a destination
   125         of the filemap. This is useful for identifying what parts of the target
   125         of the filemap. This is useful for identifying what parts of the target
   126         repo belong to the source repo and what parts don't."""
   126         repo belong to the source repo and what parts don't."""
   129             for before, after in self.rename.iteritems():
   129             for before, after in self.rename.iteritems():
   130                 self.targetprefixes.add(after)
   130                 self.targetprefixes.add(after)
   131 
   131 
   132         # If "." is a target, then all target files are considered from the
   132         # If "." is a target, then all target files are considered from the
   133         # source.
   133         # source.
   134         if not self.targetprefixes or '.' in self.targetprefixes:
   134         if not self.targetprefixes or b'.' in self.targetprefixes:
   135             return True
   135             return True
   136 
   136 
   137         filename = normalize(filename)
   137         filename = normalize(filename)
   138         for pre, suf in rpairs(filename):
   138         for pre, suf in rpairs(filename):
   139             # This check is imperfect since it doesn't account for the
   139             # This check is imperfect since it doesn't account for the
   150         else:
   150         else:
   151             inc = name
   151             inc = name
   152         if self.exclude:
   152         if self.exclude:
   153             exc = self.lookup(name, self.exclude)[0]
   153             exc = self.lookup(name, self.exclude)[0]
   154         else:
   154         else:
   155             exc = ''
   155             exc = b''
   156         if (not self.include and exc) or (len(inc) <= len(exc)):
   156         if (not self.include and exc) or (len(inc) <= len(exc)):
   157             return None
   157             return None
   158         newpre, pre, suf = self.lookup(name, self.rename)
   158         newpre, pre, suf = self.lookup(name, self.rename)
   159         if newpre:
   159         if newpre:
   160             if newpre == '.':
   160             if newpre == b'.':
   161                 return suf
   161                 return suf
   162             if suf:
   162             if suf:
   163                 if newpre.endswith('/'):
   163                 if newpre.endswith(b'/'):
   164                     return newpre + suf
   164                     return newpre + suf
   165                 return newpre + '/' + suf
   165                 return newpre + b'/' + suf
   166             return newpre
   166             return newpre
   167         return name
   167         return name
   168 
   168 
   169     def active(self):
   169     def active(self):
   170         return bool(self.include or self.exclude or self.rename)
   170         return bool(self.include or self.exclude or self.rename)
   202         self.origparents = {}
   202         self.origparents = {}
   203         self.children = {}
   203         self.children = {}
   204         self.seenchildren = {}
   204         self.seenchildren = {}
   205         # experimental config: convert.ignoreancestorcheck
   205         # experimental config: convert.ignoreancestorcheck
   206         self.ignoreancestorcheck = self.ui.configbool(
   206         self.ignoreancestorcheck = self.ui.configbool(
   207             'convert', 'ignoreancestorcheck'
   207             b'convert', b'ignoreancestorcheck'
   208         )
   208         )
   209 
   209 
   210     def before(self):
   210     def before(self):
   211         self.base.before()
   211         self.base.before()
   212 
   212 
   254         for rev, wanted, arg in self.convertedorder:
   254         for rev, wanted, arg in self.convertedorder:
   255             if rev not in self.origparents:
   255             if rev not in self.origparents:
   256                 try:
   256                 try:
   257                     self.origparents[rev] = self.getcommit(rev).parents
   257                     self.origparents[rev] = self.getcommit(rev).parents
   258                 except error.RepoLookupError:
   258                 except error.RepoLookupError:
   259                     self.ui.debug("unknown revmap source: %s\n" % rev)
   259                     self.ui.debug(b"unknown revmap source: %s\n" % rev)
   260                     continue
   260                     continue
   261             if arg is not None:
   261             if arg is not None:
   262                 self.children[arg] = self.children.get(arg, 0) + 1
   262                 self.children[arg] = self.children.get(arg, 0) + 1
   263 
   263 
   264         for rev, wanted, arg in self.convertedorder:
   264         for rev, wanted, arg in self.convertedorder:
   314         # indicated by i.  If we're interested in any of these files,
   314         # indicated by i.  If we're interested in any of these files,
   315         # we're interested in rev.
   315         # we're interested in rev.
   316         try:
   316         try:
   317             files = self.base.getchangedfiles(rev, i)
   317             files = self.base.getchangedfiles(rev, i)
   318         except NotImplementedError:
   318         except NotImplementedError:
   319             raise error.Abort(_("source repository doesn't support --filemap"))
   319             raise error.Abort(_(b"source repository doesn't support --filemap"))
   320         for f in files:
   320         for f in files:
   321             if self.filemapper(f):
   321             if self.filemapper(f):
   322                 return True
   322                 return True
   323 
   323 
   324         # The include directive is documented to include nothing else (though
   324         # The include directive is documented to include nothing else (though
   329         # Allow empty commits in the source revision through.  The getchanges()
   329         # Allow empty commits in the source revision through.  The getchanges()
   330         # method doesn't even bother calling this if it determines that the
   330         # method doesn't even bother calling this if it determines that the
   331         # close marker is significant (i.e. all of the branch ancestors weren't
   331         # close marker is significant (i.e. all of the branch ancestors weren't
   332         # eliminated).  Therefore if there *is* a close marker, getchanges()
   332         # eliminated).  Therefore if there *is* a close marker, getchanges()
   333         # doesn't consider it significant, and this revision should be dropped.
   333         # doesn't consider it significant, and this revision should be dropped.
   334         return not files and 'close' not in self.commits[rev].extra
   334         return not files and b'close' not in self.commits[rev].extra
   335 
   335 
   336     def mark_not_wanted(self, rev, p):
   336     def mark_not_wanted(self, rev, p):
   337         # Mark rev as not interesting and update data structures.
   337         # Mark rev as not interesting and update data structures.
   338 
   338 
   339         if p is None:
   339         if p is None:
   361         wrev = set()
   361         wrev = set()
   362         for p in parents:
   362         for p in parents:
   363             if p in self.wantedancestors:
   363             if p in self.wantedancestors:
   364                 wrev.update(self.wantedancestors[p])
   364                 wrev.update(self.wantedancestors[p])
   365             else:
   365             else:
   366                 self.ui.warn(_('warning: %s parent %s is missing\n') % (rev, p))
   366                 self.ui.warn(
       
   367                     _(b'warning: %s parent %s is missing\n') % (rev, p)
       
   368                 )
   367         wrev.add(rev)
   369         wrev.add(rev)
   368         self.wantedancestors[rev] = wrev
   370         self.wantedancestors[rev] = wrev
   369 
   371 
   370     def getchanges(self, rev, full):
   372     def getchanges(self, rev, full):
   371         parents = self.commits[rev].parents
   373         parents = self.commits[rev].parents
   421             wp = 0
   423             wp = 0
   422 
   424 
   423         self.origparents[rev] = parents
   425         self.origparents[rev] = parents
   424 
   426 
   425         closed = False
   427         closed = False
   426         if 'close' in self.commits[rev].extra:
   428         if b'close' in self.commits[rev].extra:
   427             # A branch closing revision is only useful if one of its
   429             # A branch closing revision is only useful if one of its
   428             # parents belong to the branch being closed
   430             # parents belong to the branch being closed
   429             pbranches = [self._cachedcommit(p).branch for p in mparents]
   431             pbranches = [self._cachedcommit(p).branch for p in mparents]
   430             if branch in pbranches:
   432             if branch in pbranches:
   431                 closed = True
   433                 closed = True