hgext/convert/gnuarch.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43275 8c0fe77f47c5
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    29 
    29 
    30 class gnuarch_source(common.converter_source, common.commandline):
    30 class gnuarch_source(common.converter_source, common.commandline):
    31     class gnuarch_rev(object):
    31     class gnuarch_rev(object):
    32         def __init__(self, rev):
    32         def __init__(self, rev):
    33             self.rev = rev
    33             self.rev = rev
    34             self.summary = ''
    34             self.summary = b''
    35             self.date = None
    35             self.date = None
    36             self.author = ''
    36             self.author = b''
    37             self.continuationof = None
    37             self.continuationof = None
    38             self.add_files = []
    38             self.add_files = []
    39             self.mod_files = []
    39             self.mod_files = []
    40             self.del_files = []
    40             self.del_files = []
    41             self.ren_files = {}
    41             self.ren_files = {}
    42             self.ren_dirs = {}
    42             self.ren_dirs = {}
    43 
    43 
    44     def __init__(self, ui, repotype, path, revs=None):
    44     def __init__(self, ui, repotype, path, revs=None):
    45         super(gnuarch_source, self).__init__(ui, repotype, path, revs=revs)
    45         super(gnuarch_source, self).__init__(ui, repotype, path, revs=revs)
    46 
    46 
    47         if not os.path.exists(os.path.join(path, '{arch}')):
    47         if not os.path.exists(os.path.join(path, b'{arch}')):
    48             raise common.NoRepo(
    48             raise common.NoRepo(
    49                 _("%s does not look like a GNU Arch repository") % path
    49                 _(b"%s does not look like a GNU Arch repository") % path
    50             )
    50             )
    51 
    51 
    52         # Could use checktool, but we want to check for baz or tla.
    52         # Could use checktool, but we want to check for baz or tla.
    53         self.execmd = None
    53         self.execmd = None
    54         if procutil.findexe('baz'):
    54         if procutil.findexe(b'baz'):
    55             self.execmd = 'baz'
    55             self.execmd = b'baz'
    56         else:
    56         else:
    57             if procutil.findexe('tla'):
    57             if procutil.findexe(b'tla'):
    58                 self.execmd = 'tla'
    58                 self.execmd = b'tla'
    59             else:
    59             else:
    60                 raise error.Abort(_('cannot find a GNU Arch tool'))
    60                 raise error.Abort(_(b'cannot find a GNU Arch tool'))
    61 
    61 
    62         common.commandline.__init__(self, ui, self.execmd)
    62         common.commandline.__init__(self, ui, self.execmd)
    63 
    63 
    64         self.path = os.path.realpath(path)
    64         self.path = os.path.realpath(path)
    65         self.tmppath = None
    65         self.tmppath = None
    74         self.archives = []
    74         self.archives = []
    75 
    75 
    76     def before(self):
    76     def before(self):
    77         # Get registered archives
    77         # Get registered archives
    78         self.archives = [
    78         self.archives = [
    79             i.rstrip('\n') for i in self.runlines0('archives', '-n')
    79             i.rstrip(b'\n') for i in self.runlines0(b'archives', b'-n')
    80         ]
    80         ]
    81 
    81 
    82         if self.execmd == 'tla':
    82         if self.execmd == b'tla':
    83             output = self.run0('tree-version', self.path)
    83             output = self.run0(b'tree-version', self.path)
    84         else:
    84         else:
    85             output = self.run0('tree-version', '-d', self.path)
    85             output = self.run0(b'tree-version', b'-d', self.path)
    86         self.treeversion = output.strip()
    86         self.treeversion = output.strip()
    87 
    87 
    88         # Get name of temporary directory
    88         # Get name of temporary directory
    89         version = self.treeversion.split('/')
    89         version = self.treeversion.split(b'/')
    90         self.tmppath = os.path.join(
    90         self.tmppath = os.path.join(
    91             pycompat.fsencode(tempfile.gettempdir()), 'hg-%s' % version[1]
    91             pycompat.fsencode(tempfile.gettempdir()), b'hg-%s' % version[1]
    92         )
    92         )
    93 
    93 
    94         # Generate parents dictionary
    94         # Generate parents dictionary
    95         self.parents[None] = []
    95         self.parents[None] = []
    96         treeversion = self.treeversion
    96         treeversion = self.treeversion
    97         child = None
    97         child = None
    98         while treeversion:
    98         while treeversion:
    99             self.ui.status(_('analyzing tree version %s...\n') % treeversion)
    99             self.ui.status(_(b'analyzing tree version %s...\n') % treeversion)
   100 
   100 
   101             archive = treeversion.split('/')[0]
   101             archive = treeversion.split(b'/')[0]
   102             if archive not in self.archives:
   102             if archive not in self.archives:
   103                 self.ui.status(
   103                 self.ui.status(
   104                     _(
   104                     _(
   105                         'tree analysis stopped because it points to '
   105                         b'tree analysis stopped because it points to '
   106                         'an unregistered archive %s...\n'
   106                         b'an unregistered archive %s...\n'
   107                     )
   107                     )
   108                     % archive
   108                     % archive
   109                 )
   109                 )
   110                 break
   110                 break
   111 
   111 
   112             # Get the complete list of revisions for that tree version
   112             # Get the complete list of revisions for that tree version
   113             output, status = self.runlines('revisions', '-r', '-f', treeversion)
   113             output, status = self.runlines(
       
   114                 b'revisions', b'-r', b'-f', treeversion
       
   115             )
   114             self.checkexit(
   116             self.checkexit(
   115                 status, 'failed retrieving revisions for %s' % treeversion
   117                 status, b'failed retrieving revisions for %s' % treeversion
   116             )
   118             )
   117 
   119 
   118             # No new iteration unless a revision has a continuation-of header
   120             # No new iteration unless a revision has a continuation-of header
   119             treeversion = None
   121             treeversion = None
   120 
   122 
   122                 rev = l.strip()
   124                 rev = l.strip()
   123                 self.changes[rev] = self.gnuarch_rev(rev)
   125                 self.changes[rev] = self.gnuarch_rev(rev)
   124                 self.parents[rev] = []
   126                 self.parents[rev] = []
   125 
   127 
   126                 # Read author, date and summary
   128                 # Read author, date and summary
   127                 catlog, status = self.run('cat-log', '-d', self.path, rev)
   129                 catlog, status = self.run(b'cat-log', b'-d', self.path, rev)
   128                 if status:
   130                 if status:
   129                     catlog = self.run0('cat-archive-log', rev)
   131                     catlog = self.run0(b'cat-archive-log', rev)
   130                 self._parsecatlog(catlog, rev)
   132                 self._parsecatlog(catlog, rev)
   131 
   133 
   132                 # Populate the parents map
   134                 # Populate the parents map
   133                 self.parents[child].append(rev)
   135                 self.parents[child].append(rev)
   134 
   136 
   138 
   140 
   139                 # Check if we have to follow the usual incremental history
   141                 # Check if we have to follow the usual incremental history
   140                 # or if we have to 'jump' to a different treeversion given
   142                 # or if we have to 'jump' to a different treeversion given
   141                 # by the continuation-of header.
   143                 # by the continuation-of header.
   142                 if self.changes[rev].continuationof:
   144                 if self.changes[rev].continuationof:
   143                     treeversion = '--'.join(
   145                     treeversion = b'--'.join(
   144                         self.changes[rev].continuationof.split('--')[:-1]
   146                         self.changes[rev].continuationof.split(b'--')[:-1]
   145                     )
   147                     )
   146                     break
   148                     break
   147 
   149 
   148                 # If we reached a base-0 revision w/o any continuation-of
   150                 # If we reached a base-0 revision w/o any continuation-of
   149                 # header, it means the tree history ends here.
   151                 # header, it means the tree history ends here.
   150                 if rev[-6:] == 'base-0':
   152                 if rev[-6:] == b'base-0':
   151                     break
   153                     break
   152 
   154 
   153     def after(self):
   155     def after(self):
   154         self.ui.debug('cleaning up %s\n' % self.tmppath)
   156         self.ui.debug(b'cleaning up %s\n' % self.tmppath)
   155         shutil.rmtree(self.tmppath, ignore_errors=True)
   157         shutil.rmtree(self.tmppath, ignore_errors=True)
   156 
   158 
   157     def getheads(self):
   159     def getheads(self):
   158         return self.parents[None]
   160         return self.parents[None]
   159 
   161 
   160     def getfile(self, name, rev):
   162     def getfile(self, name, rev):
   161         if rev != self.lastrev:
   163         if rev != self.lastrev:
   162             raise error.Abort(_('internal calling inconsistency'))
   164             raise error.Abort(_(b'internal calling inconsistency'))
   163 
   165 
   164         if not os.path.lexists(os.path.join(self.tmppath, name)):
   166         if not os.path.lexists(os.path.join(self.tmppath, name)):
   165             return None, None
   167             return None, None
   166 
   168 
   167         return self._getfile(name, rev)
   169         return self._getfile(name, rev)
   168 
   170 
   169     def getchanges(self, rev, full):
   171     def getchanges(self, rev, full):
   170         if full:
   172         if full:
   171             raise error.Abort(_("convert from arch does not support --full"))
   173             raise error.Abort(_(b"convert from arch does not support --full"))
   172         self._update(rev)
   174         self._update(rev)
   173         changes = []
   175         changes = []
   174         copies = {}
   176         copies = {}
   175 
   177 
   176         for f in self.changes[rev].add_files:
   178         for f in self.changes[rev].add_files:
   212 
   214 
   213     def _execute(self, cmd, *args, **kwargs):
   215     def _execute(self, cmd, *args, **kwargs):
   214         cmdline = [self.execmd, cmd]
   216         cmdline = [self.execmd, cmd]
   215         cmdline += args
   217         cmdline += args
   216         cmdline = [procutil.shellquote(arg) for arg in cmdline]
   218         cmdline = [procutil.shellquote(arg) for arg in cmdline]
   217         cmdline += ['>', os.devnull, '2>', os.devnull]
   219         cmdline += [b'>', os.devnull, b'2>', os.devnull]
   218         cmdline = procutil.quotecommand(' '.join(cmdline))
   220         cmdline = procutil.quotecommand(b' '.join(cmdline))
   219         self.ui.debug(cmdline, '\n')
   221         self.ui.debug(cmdline, b'\n')
   220         return os.system(pycompat.rapply(procutil.tonativestr, cmdline))
   222         return os.system(pycompat.rapply(procutil.tonativestr, cmdline))
   221 
   223 
   222     def _update(self, rev):
   224     def _update(self, rev):
   223         self.ui.debug('applying revision %s...\n' % rev)
   225         self.ui.debug(b'applying revision %s...\n' % rev)
   224         changeset, status = self.runlines('replay', '-d', self.tmppath, rev)
   226         changeset, status = self.runlines(b'replay', b'-d', self.tmppath, rev)
   225         if status:
   227         if status:
   226             # Something went wrong while merging (baz or tla
   228             # Something went wrong while merging (baz or tla
   227             # issue?), get latest revision and try from there
   229             # issue?), get latest revision and try from there
   228             shutil.rmtree(self.tmppath, ignore_errors=True)
   230             shutil.rmtree(self.tmppath, ignore_errors=True)
   229             self._obtainrevision(rev)
   231             self._obtainrevision(rev)
   230         else:
   232         else:
   231             old_rev = self.parents[rev][0]
   233             old_rev = self.parents[rev][0]
   232             self.ui.debug(
   234             self.ui.debug(
   233                 'computing changeset between %s and %s...\n' % (old_rev, rev)
   235                 b'computing changeset between %s and %s...\n' % (old_rev, rev)
   234             )
   236             )
   235             self._parsechangeset(changeset, rev)
   237             self._parsechangeset(changeset, rev)
   236 
   238 
   237     def _getfile(self, name, rev):
   239     def _getfile(self, name, rev):
   238         mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
   240         mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
   239         if stat.S_ISLNK(mode):
   241         if stat.S_ISLNK(mode):
   240             data = util.readlink(os.path.join(self.tmppath, name))
   242             data = util.readlink(os.path.join(self.tmppath, name))
   241             if mode:
   243             if mode:
   242                 mode = 'l'
   244                 mode = b'l'
   243             else:
   245             else:
   244                 mode = ''
   246                 mode = b''
   245         else:
   247         else:
   246             data = util.readfile(os.path.join(self.tmppath, name))
   248             data = util.readfile(os.path.join(self.tmppath, name))
   247             mode = (mode & 0o111) and 'x' or ''
   249             mode = (mode & 0o111) and b'x' or b''
   248         return data, mode
   250         return data, mode
   249 
   251 
   250     def _exclude(self, name):
   252     def _exclude(self, name):
   251         exclude = ['{arch}', '.arch-ids', '.arch-inventory']
   253         exclude = [b'{arch}', b'.arch-ids', b'.arch-inventory']
   252         for exc in exclude:
   254         for exc in exclude:
   253             if name.find(exc) != -1:
   255             if name.find(exc) != -1:
   254                 return True
   256                 return True
   255         return False
   257         return False
   256 
   258 
   280             changes.append(d)
   282             changes.append(d)
   281             copies[d] = s
   283             copies[d] = s
   282         return changes, copies
   284         return changes, copies
   283 
   285 
   284     def _obtainrevision(self, rev):
   286     def _obtainrevision(self, rev):
   285         self.ui.debug('obtaining revision %s...\n' % rev)
   287         self.ui.debug(b'obtaining revision %s...\n' % rev)
   286         output = self._execute('get', rev, self.tmppath)
   288         output = self._execute(b'get', rev, self.tmppath)
   287         self.checkexit(output)
   289         self.checkexit(output)
   288         self.ui.debug('analyzing revision %s...\n' % rev)
   290         self.ui.debug(b'analyzing revision %s...\n' % rev)
   289         files = self._readcontents(self.tmppath)
   291         files = self._readcontents(self.tmppath)
   290         self.changes[rev].add_files += files
   292         self.changes[rev].add_files += files
   291 
   293 
   292     def _stripbasepath(self, path):
   294     def _stripbasepath(self, path):
   293         if path.startswith('./'):
   295         if path.startswith(b'./'):
   294             return path[2:]
   296             return path[2:]
   295         return path
   297         return path
   296 
   298 
   297     def _parsecatlog(self, data, rev):
   299     def _parsecatlog(self, data, rev):
   298         try:
   300         try:
   299             catlog = self.catlogparser.parsestr(data)
   301             catlog = self.catlogparser.parsestr(data)
   300 
   302 
   301             # Commit date
   303             # Commit date
   302             self.changes[rev].date = dateutil.datestr(
   304             self.changes[rev].date = dateutil.datestr(
   303                 dateutil.strdate(catlog['Standard-date'], '%Y-%m-%d %H:%M:%S')
   305                 dateutil.strdate(catlog[b'Standard-date'], b'%Y-%m-%d %H:%M:%S')
   304             )
   306             )
   305 
   307 
   306             # Commit author
   308             # Commit author
   307             self.changes[rev].author = self.recode(catlog['Creator'])
   309             self.changes[rev].author = self.recode(catlog[b'Creator'])
   308 
   310 
   309             # Commit description
   311             # Commit description
   310             self.changes[rev].summary = '\n\n'.join(
   312             self.changes[rev].summary = b'\n\n'.join(
   311                 (catlog['Summary'], catlog.get_payload())
   313                 (catlog[b'Summary'], catlog.get_payload())
   312             )
   314             )
   313             self.changes[rev].summary = self.recode(self.changes[rev].summary)
   315             self.changes[rev].summary = self.recode(self.changes[rev].summary)
   314 
   316 
   315             # Commit revision origin when dealing with a branch or tag
   317             # Commit revision origin when dealing with a branch or tag
   316             if 'Continuation-of' in catlog:
   318             if b'Continuation-of' in catlog:
   317                 self.changes[rev].continuationof = self.recode(
   319                 self.changes[rev].continuationof = self.recode(
   318                     catlog['Continuation-of']
   320                     catlog[b'Continuation-of']
   319                 )
   321                 )
   320         except Exception:
   322         except Exception:
   321             raise error.Abort(_('could not parse cat-log of %s') % rev)
   323             raise error.Abort(_(b'could not parse cat-log of %s') % rev)
   322 
   324 
   323     def _parsechangeset(self, data, rev):
   325     def _parsechangeset(self, data, rev):
   324         for l in data:
   326         for l in data:
   325             l = l.strip()
   327             l = l.strip()
   326             # Added file (ignore added directory)
   328             # Added file (ignore added directory)
   327             if l.startswith('A') and not l.startswith('A/'):
   329             if l.startswith(b'A') and not l.startswith(b'A/'):
   328                 file = self._stripbasepath(l[1:].strip())
   330                 file = self._stripbasepath(l[1:].strip())
   329                 if not self._exclude(file):
   331                 if not self._exclude(file):
   330                     self.changes[rev].add_files.append(file)
   332                     self.changes[rev].add_files.append(file)
   331             # Deleted file (ignore deleted directory)
   333             # Deleted file (ignore deleted directory)
   332             elif l.startswith('D') and not l.startswith('D/'):
   334             elif l.startswith(b'D') and not l.startswith(b'D/'):
   333                 file = self._stripbasepath(l[1:].strip())
   335                 file = self._stripbasepath(l[1:].strip())
   334                 if not self._exclude(file):
   336                 if not self._exclude(file):
   335                     self.changes[rev].del_files.append(file)
   337                     self.changes[rev].del_files.append(file)
   336             # Modified binary file
   338             # Modified binary file
   337             elif l.startswith('Mb'):
   339             elif l.startswith(b'Mb'):
   338                 file = self._stripbasepath(l[2:].strip())
   340                 file = self._stripbasepath(l[2:].strip())
   339                 if not self._exclude(file):
   341                 if not self._exclude(file):
   340                     self.changes[rev].mod_files.append(file)
   342                     self.changes[rev].mod_files.append(file)
   341             # Modified link
   343             # Modified link
   342             elif l.startswith('M->'):
   344             elif l.startswith(b'M->'):
   343                 file = self._stripbasepath(l[3:].strip())
   345                 file = self._stripbasepath(l[3:].strip())
   344                 if not self._exclude(file):
   346                 if not self._exclude(file):
   345                     self.changes[rev].mod_files.append(file)
   347                     self.changes[rev].mod_files.append(file)
   346             # Modified file
   348             # Modified file
   347             elif l.startswith('M'):
   349             elif l.startswith(b'M'):
   348                 file = self._stripbasepath(l[1:].strip())
   350                 file = self._stripbasepath(l[1:].strip())
   349                 if not self._exclude(file):
   351                 if not self._exclude(file):
   350                     self.changes[rev].mod_files.append(file)
   352                     self.changes[rev].mod_files.append(file)
   351             # Renamed file (or link)
   353             # Renamed file (or link)
   352             elif l.startswith('=>'):
   354             elif l.startswith(b'=>'):
   353                 files = l[2:].strip().split(' ')
   355                 files = l[2:].strip().split(b' ')
   354                 if len(files) == 1:
   356                 if len(files) == 1:
   355                     files = l[2:].strip().split('\t')
   357                     files = l[2:].strip().split(b'\t')
   356                 src = self._stripbasepath(files[0])
   358                 src = self._stripbasepath(files[0])
   357                 dst = self._stripbasepath(files[1])
   359                 dst = self._stripbasepath(files[1])
   358                 if not self._exclude(src) and not self._exclude(dst):
   360                 if not self._exclude(src) and not self._exclude(dst):
   359                     self.changes[rev].ren_files[src] = dst
   361                     self.changes[rev].ren_files[src] = dst
   360             # Conversion from file to link or from link to file (modified)
   362             # Conversion from file to link or from link to file (modified)
   361             elif l.startswith('ch'):
   363             elif l.startswith(b'ch'):
   362                 file = self._stripbasepath(l[2:].strip())
   364                 file = self._stripbasepath(l[2:].strip())
   363                 if not self._exclude(file):
   365                 if not self._exclude(file):
   364                     self.changes[rev].mod_files.append(file)
   366                     self.changes[rev].mod_files.append(file)
   365             # Renamed directory
   367             # Renamed directory
   366             elif l.startswith('/>'):
   368             elif l.startswith(b'/>'):
   367                 dirs = l[2:].strip().split(' ')
   369                 dirs = l[2:].strip().split(b' ')
   368                 if len(dirs) == 1:
   370                 if len(dirs) == 1:
   369                     dirs = l[2:].strip().split('\t')
   371                     dirs = l[2:].strip().split(b'\t')
   370                 src = self._stripbasepath(dirs[0])
   372                 src = self._stripbasepath(dirs[0])
   371                 dst = self._stripbasepath(dirs[1])
   373                 dst = self._stripbasepath(dirs[1])
   372                 if not self._exclude(src) and not self._exclude(dst):
   374                 if not self._exclude(src) and not self._exclude(dst):
   373                     self.changes[rev].ren_dirs[src] = dst
   375                     self.changes[rev].ren_dirs[src] = dst