hgext/convert/gnuarch.py
changeset 6035 df659eb23360
child 6037 dd3267698d84
equal deleted inserted replaced
6020:20b05618b3e2 6035:df659eb23360
       
     1 # GNU Arch support for the convert extension
       
     2 
       
     3 from common import NoRepo, checktool, commandline, commit, converter_source
       
     4 from mercurial.i18n import _
       
     5 from mercurial import util
       
     6 import os, shutil, tempfile, stat
       
     7 
       
     8 class gnuarch_source(converter_source, commandline):
       
     9 
       
    10     class gnuarch_rev:
       
    11         def __init__(self, rev):
       
    12             self.rev = rev
       
    13             self.summary = ''
       
    14             self.date = None
       
    15             self.author = ''
       
    16             self.add_files = []
       
    17             self.mod_files = []
       
    18             self.del_files = []
       
    19             self.ren_files = {}
       
    20             self.ren_dirs = {}
       
    21 
       
    22     def __init__(self, ui, path, rev=None):
       
    23         super(gnuarch_source, self).__init__(ui, path, rev=rev)
       
    24 
       
    25         if not os.path.exists(os.path.join(path, '{arch}')):
       
    26             raise NoRepo(_("couldn't open GNU Arch repo %s" % path))
       
    27 
       
    28         # Could use checktool, but we want to check for baz or tla.
       
    29         self.execmd = None
       
    30         if util.find_exe('tla'):
       
    31             self.execmd = 'tla'
       
    32         else:
       
    33             if util.find_exe('baz'):
       
    34                 self.execmd = 'baz'
       
    35             else:
       
    36                 raise util.Abort(_('cannot find a GNU Arch tool'))
       
    37 
       
    38         commandline.__init__(self, ui, self.execmd)
       
    39 
       
    40         self.path = os.path.realpath(path)
       
    41         self.tmppath = None
       
    42 
       
    43         self.treeversion = None
       
    44         self.lastrev = None
       
    45         self.changes = {}
       
    46         self.parents = {}
       
    47         self.tags = {}
       
    48         self.modecache = {}
       
    49 
       
    50     def before(self):
       
    51         if self.execmd == 'tla':
       
    52             output = self.run0('tree-version', self.path)
       
    53         else:
       
    54             output = self.run0('tree-version', '-d', self.path)
       
    55         self.treeversion = output.strip()
       
    56 
       
    57         self.ui.status(_('analyzing tree version %s...\n' % self.treeversion))
       
    58 
       
    59         # Get name of temporary directory
       
    60         version = self.treeversion.split('/')
       
    61         self.tmppath = os.path.join(tempfile.gettempdir(),
       
    62                                     'hg-%s' % version[1])
       
    63 
       
    64         # Generate parents dictionary
       
    65         child = []
       
    66         output, status = self.runlines('revisions', self.treeversion)
       
    67         self.checkexit(status, 'archive registered?')
       
    68         for l in output:
       
    69             rev = l.strip()
       
    70             self.changes[rev] = self.gnuarch_rev(rev)
       
    71 
       
    72             # Read author, date and summary
       
    73             catlog = self.runlines0('cat-log', '-d', self.path, rev)
       
    74             self._parsecatlog(catlog, rev)
       
    75 
       
    76             self.parents[rev] = child
       
    77             child = [rev]
       
    78             if rev == self.rev:
       
    79                 break
       
    80         self.parents[None] = child
       
    81 
       
    82     def after(self):
       
    83         self.ui.debug(_('cleaning up %s\n' % self.tmppath))
       
    84         shutil.rmtree(self.tmppath, ignore_errors=True)
       
    85 
       
    86     def getheads(self):
       
    87         return self.parents[None]
       
    88 
       
    89     def getfile(self, name, rev):
       
    90         if rev != self.lastrev:
       
    91             raise util.Abort(_('internal calling inconsistency'))
       
    92 
       
    93         # Raise IOError if necessary (i.e. deleted files).
       
    94         if not os.path.exists(os.path.join(self.tmppath, name)):
       
    95             raise IOError
       
    96 
       
    97         data, mode = self._getfile(name, rev)
       
    98         self.modecache[(name, rev)] = mode
       
    99 
       
   100         return data
       
   101 
       
   102     def getmode(self, name, rev):
       
   103         return self.modecache[(name, rev)]
       
   104 
       
   105     def getchanges(self, rev):
       
   106         self.modecache = {}
       
   107         self._update(rev)
       
   108         changes = []
       
   109         copies = {}
       
   110 
       
   111         for f in self.changes[rev].add_files:
       
   112             changes.append((f, rev))
       
   113 
       
   114         for f in self.changes[rev].mod_files:
       
   115             changes.append((f, rev))
       
   116 
       
   117         for f in self.changes[rev].del_files:
       
   118             changes.append((f, rev))
       
   119 
       
   120         for src in self.changes[rev].ren_files:
       
   121             to = self.changes[rev].ren_files[src]
       
   122             changes.append((src, rev))
       
   123             changes.append((to, rev))
       
   124             copies[src] = to
       
   125 
       
   126         for src in self.changes[rev].ren_dirs:
       
   127             to = self.changes[rev].ren_dirs[src]
       
   128             chgs, cps = self._rendirchanges(src, to);
       
   129             changes += [(f, rev) for f in chgs]
       
   130             for c in cps:
       
   131                 copies[c] = cps[c]
       
   132 
       
   133         changes.sort()
       
   134         self.lastrev = rev
       
   135 
       
   136         return changes, copies
       
   137 
       
   138     def getcommit(self, rev):
       
   139         changes = self.changes[rev]
       
   140         return commit(author = changes.author, date = changes.date,
       
   141                       desc = changes.summary, parents = self.parents[rev])
       
   142 
       
   143     def gettags(self):
       
   144         return self.tags
       
   145 
       
   146     def _execute(self, cmd, *args, **kwargs):
       
   147         cmdline = [self.execmd, cmd]
       
   148         cmdline += args
       
   149         cmdline = [util.shellquote(arg) for arg in cmdline]
       
   150         cmdline += ['>', util.nulldev, '2>', util.nulldev]
       
   151         cmdline = util.quotecommand(' '.join(cmdline))
       
   152         self.ui.debug(cmdline, '\n')
       
   153         return os.system(cmdline)
       
   154 
       
   155     def _update(self, rev):
       
   156         if rev == 'base-0':
       
   157             # Initialise 'base-0' revision
       
   158             self.ui.debug(_('obtaining revision %s...\n' % rev))
       
   159             revision = '%s--%s' % (self.treeversion, rev)
       
   160             output = self._execute('get', revision, self.tmppath)
       
   161             self.ui.debug(_('analysing revision %s...\n' % rev))
       
   162             files = self._readcontents(self.tmppath)
       
   163             self.changes[rev].add_files += files
       
   164         else:
       
   165             self.ui.debug(_('applying revision %s...\n' % rev))
       
   166             revision = '%s--%s' % (self.treeversion, rev)
       
   167             output = self._execute('replay', '-d', self.tmppath, revision)
       
   168 
       
   169             old_rev = self.parents[rev][0]
       
   170             self.ui.debug(_('computing changeset between %s and %s...\n' \
       
   171                                % (old_rev, rev)))
       
   172             rev_a = '%s--%s' % (self.treeversion, old_rev)
       
   173             rev_b = '%s--%s' % (self.treeversion, rev)
       
   174             delta = self.runlines0('delta', '-n', rev_a, rev_b)
       
   175             self._parsedelta(delta, rev)
       
   176 
       
   177     def _getfile(self, name, rev):
       
   178         mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
       
   179         if stat.S_ISLNK(mode):
       
   180             data = os.readlink(os.path.join(self.tmppath, name))
       
   181             mode = mode and 'l' or ''
       
   182         else:
       
   183             data = open(os.path.join(self.tmppath, name), 'rb').read()
       
   184             mode = (mode & 0111) and 'x' or ''
       
   185         return data, mode
       
   186 
       
   187     def _exclude(self, name):
       
   188         exclude = [ '{arch}', '.arch-ids', '.arch-inventory' ]
       
   189         for exc in exclude:
       
   190             if name.find(exc) != -1:
       
   191                 return True
       
   192         return False
       
   193 
       
   194     def _readcontents(self, path):
       
   195         files = []
       
   196         contents = os.listdir(path)
       
   197         while len(contents) > 0:
       
   198             c = contents.pop()
       
   199             p = os.path.join(path, c)
       
   200             if not self._exclude(p):
       
   201                 if os.path.isdir(p):
       
   202                     contents += [os.path.join(c, f) for f in os.listdir(p)]
       
   203                 else:
       
   204                     files.append(c)
       
   205         return files
       
   206 
       
   207     def _rendirchanges(self, src, dest):
       
   208         changes = []
       
   209         copies = {}
       
   210         files = self._readcontents(os.path.join(self.tmppath, dest))
       
   211         for f in files:
       
   212             s = os.path.join(src, f)
       
   213             d = os.path.join(dest, f)
       
   214             changes.append(s)
       
   215             changes.append(d)
       
   216             copies[s] = d
       
   217         return changes, copies
       
   218 
       
   219     def _parsecatlog(self, data, rev):
       
   220         for l in data:
       
   221             l = l.strip()
       
   222             if l.startswith('Summary:'):
       
   223                 self.changes[rev].summary = l[len('Summary: '):]
       
   224 
       
   225             if l.startswith('Standard-date:'):
       
   226                 date = l[len('Standard-date: '):]
       
   227                 strdate = util.strdate(date, '%Y-%m-%d %H:%M:%S')
       
   228                 self.changes[rev].date = util.datestr(strdate)
       
   229 
       
   230             if l.startswith('Creator:'):
       
   231                 self.changes[rev].author = l[len('Creator: '):]
       
   232 
       
   233     def _parsedelta(self, data, rev):
       
   234         for l in data:
       
   235             l = l.strip()
       
   236             if l.startswith('A') and not l.startswith('A/'):
       
   237                 file = l[1:].strip()
       
   238                 if not self._exclude(file):
       
   239                     self.changes[rev].add_files.append(file)
       
   240             elif l.startswith('/>'):
       
   241                 dirs = l[2:].strip().split(' ')
       
   242                 if len(dirs) == 1:
       
   243                     dirs = l[2:].strip().split('\t')
       
   244                 if not self._exclude(dirs[0]) and not self._exclude(dirs[1]):
       
   245                     self.changes[rev].ren_dirs[dirs[0]] = dirs[1]
       
   246             elif l.startswith('M'):
       
   247                 file = l[1:].strip()
       
   248                 if not self._exclude(file):
       
   249                     self.changes[rev].mod_files.append(file)
       
   250             elif l.startswith('->'):
       
   251                 file = l[2:].strip()
       
   252                 if not self._exclude(file):
       
   253                     self.changes[rev].mod_files.append(file)
       
   254             elif l.startswith('D') and not l.startswith('D/'):
       
   255                 file = l[1:].strip()
       
   256                 if not self._exclude(file):
       
   257                     self.changes[rev].del_files.append(file)
       
   258             elif l.startswith('=>'):
       
   259                 files = l[2:].strip().split(' ')
       
   260                 if len(files) == 1:
       
   261                     files = l[2:].strip().split('\t')
       
   262                 if not self._exclude(files[0]) and not self._exclude(files[1]):
       
   263                     self.changes[rev].ren_files[files[0]] = files[1]