eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/convert/darcs.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # darcs.py - darcs support for the convert extension
       
     2 #
       
     3 #  Copyright 2007-2009 Matt Mackall <mpm@selenic.com> and others
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 from common import NoRepo, checktool, commandline, commit, converter_source
       
     9 from mercurial.i18n import _
       
    10 from mercurial import encoding, util
       
    11 import os, shutil, tempfile, re
       
    12 
       
    13 # The naming drift of ElementTree is fun!
       
    14 
       
    15 try:
       
    16     from xml.etree.cElementTree import ElementTree, XMLParser
       
    17 except ImportError:
       
    18     try:
       
    19         from xml.etree.ElementTree import ElementTree, XMLParser
       
    20     except ImportError:
       
    21         try:
       
    22             from elementtree.cElementTree import ElementTree, XMLParser
       
    23         except ImportError:
       
    24             try:
       
    25                 from elementtree.ElementTree import ElementTree, XMLParser
       
    26             except ImportError:
       
    27                 ElementTree = None
       
    28 
       
    29 class darcs_source(converter_source, commandline):
       
    30     def __init__(self, ui, path, rev=None):
       
    31         converter_source.__init__(self, ui, path, rev=rev)
       
    32         commandline.__init__(self, ui, 'darcs')
       
    33 
       
    34         # check for _darcs, ElementTree so that we can easily skip
       
    35         # test-convert-darcs if ElementTree is not around
       
    36         if not os.path.exists(os.path.join(path, '_darcs')):
       
    37             raise NoRepo(_("%s does not look like a darcs repository") % path)
       
    38 
       
    39         checktool('darcs')
       
    40         version = self.run0('--version').splitlines()[0].strip()
       
    41         if version < '2.1':
       
    42             raise util.Abort(_('darcs version 2.1 or newer needed (found %r)') %
       
    43                              version)
       
    44 
       
    45         if ElementTree is None:
       
    46             raise util.Abort(_("Python ElementTree module is not available"))
       
    47 
       
    48         self.path = os.path.realpath(path)
       
    49 
       
    50         self.lastrev = None
       
    51         self.changes = {}
       
    52         self.parents = {}
       
    53         self.tags = {}
       
    54 
       
    55         # Check darcs repository format
       
    56         format = self.format()
       
    57         if format:
       
    58             if format in ('darcs-1.0', 'hashed'):
       
    59                 raise NoRepo(_("%s repository format is unsupported, "
       
    60                                "please upgrade") % format)
       
    61         else:
       
    62             self.ui.warn(_('failed to detect repository format!'))
       
    63 
       
    64     def before(self):
       
    65         self.tmppath = tempfile.mkdtemp(
       
    66             prefix='convert-' + os.path.basename(self.path) + '-')
       
    67         output, status = self.run('init', repodir=self.tmppath)
       
    68         self.checkexit(status)
       
    69 
       
    70         tree = self.xml('changes', xml_output=True, summary=True,
       
    71                         repodir=self.path)
       
    72         tagname = None
       
    73         child = None
       
    74         for elt in tree.findall('patch'):
       
    75             node = elt.get('hash')
       
    76             name = elt.findtext('name', '')
       
    77             if name.startswith('TAG '):
       
    78                 tagname = name[4:].strip()
       
    79             elif tagname is not None:
       
    80                 self.tags[tagname] = node
       
    81                 tagname = None
       
    82             self.changes[node] = elt
       
    83             self.parents[child] = [node]
       
    84             child = node
       
    85         self.parents[child] = []
       
    86 
       
    87     def after(self):
       
    88         self.ui.debug('cleaning up %s\n' % self.tmppath)
       
    89         shutil.rmtree(self.tmppath, ignore_errors=True)
       
    90 
       
    91     def recode(self, s, encoding=None):
       
    92         if isinstance(s, unicode):
       
    93             # XMLParser returns unicode objects for anything it can't
       
    94             # encode into ASCII. We convert them back to str to get
       
    95             # recode's normal conversion behavior.
       
    96             s = s.encode('latin-1')
       
    97         return super(darcs_source, self).recode(s, encoding)
       
    98 
       
    99     def xml(self, cmd, **kwargs):
       
   100         # NOTE: darcs is currently encoding agnostic and will print
       
   101         # patch metadata byte-for-byte, even in the XML changelog.
       
   102         etree = ElementTree()
       
   103         # While we are decoding the XML as latin-1 to be as liberal as
       
   104         # possible, etree will still raise an exception if any
       
   105         # non-printable characters are in the XML changelog.
       
   106         parser = XMLParser(encoding='latin-1')
       
   107         fp = self._run(cmd, **kwargs)
       
   108         etree.parse(fp, parser=parser)
       
   109         self.checkexit(fp.close())
       
   110         return etree.getroot()
       
   111 
       
   112     def format(self):
       
   113         output, status = self.run('show', 'repo', no_files=True,
       
   114                                   repodir=self.path)
       
   115         self.checkexit(status)
       
   116         m = re.search(r'^\s*Format:\s*(.*)$', output, re.MULTILINE)
       
   117         if not m:
       
   118             return None
       
   119         return ','.join(sorted(f.strip() for f in m.group(1).split(',')))
       
   120 
       
   121     def manifest(self):
       
   122         man = []
       
   123         output, status = self.run('show', 'files', no_directories=True,
       
   124                                   repodir=self.tmppath)
       
   125         self.checkexit(status)
       
   126         for line in output.split('\n'):
       
   127             path = line[2:]
       
   128             if path:
       
   129                 man.append(path)
       
   130         return man
       
   131 
       
   132     def getheads(self):
       
   133         return self.parents[None]
       
   134 
       
   135     def getcommit(self, rev):
       
   136         elt = self.changes[rev]
       
   137         date = util.strdate(elt.get('local_date'), '%a %b %d %H:%M:%S %Z %Y')
       
   138         desc = elt.findtext('name') + '\n' + elt.findtext('comment', '')
       
   139         # etree can return unicode objects for name, comment, and author,
       
   140         # so recode() is used to ensure str objects are emitted.
       
   141         return commit(author=self.recode(elt.get('author')),
       
   142                       date=util.datestr(date),
       
   143                       desc=self.recode(desc).strip(),
       
   144                       parents=self.parents[rev])
       
   145 
       
   146     def pull(self, rev):
       
   147         output, status = self.run('pull', self.path, all=True,
       
   148                                   match='hash %s' % rev,
       
   149                                   no_test=True, no_posthook=True,
       
   150                                   external_merge='/bin/false',
       
   151                                   repodir=self.tmppath)
       
   152         if status:
       
   153             if output.find('We have conflicts in') == -1:
       
   154                 self.checkexit(status, output)
       
   155             output, status = self.run('revert', all=True, repodir=self.tmppath)
       
   156             self.checkexit(status, output)
       
   157 
       
   158     def getchanges(self, rev):
       
   159         copies = {}
       
   160         changes = []
       
   161         man = None
       
   162         for elt in self.changes[rev].find('summary').getchildren():
       
   163             if elt.tag in ('add_directory', 'remove_directory'):
       
   164                 continue
       
   165             if elt.tag == 'move':
       
   166                 if man is None:
       
   167                     man = self.manifest()
       
   168                 source, dest = elt.get('from'), elt.get('to')
       
   169                 if source in man:
       
   170                     # File move
       
   171                     changes.append((source, rev))
       
   172                     changes.append((dest, rev))
       
   173                     copies[dest] = source
       
   174                 else:
       
   175                     # Directory move, deduce file moves from manifest
       
   176                     source = source + '/'
       
   177                     for f in man:
       
   178                         if not f.startswith(source):
       
   179                             continue
       
   180                         fdest = dest + '/' + f[len(source):]
       
   181                         changes.append((f, rev))
       
   182                         changes.append((fdest, rev))
       
   183                         copies[fdest] = f
       
   184             else:
       
   185                 changes.append((elt.text.strip(), rev))
       
   186         self.pull(rev)
       
   187         self.lastrev = rev
       
   188         return sorted(changes), copies
       
   189 
       
   190     def getfile(self, name, rev):
       
   191         if rev != self.lastrev:
       
   192             raise util.Abort(_('internal calling inconsistency'))
       
   193         path = os.path.join(self.tmppath, name)
       
   194         data = open(path, 'rb').read()
       
   195         mode = os.lstat(path).st_mode
       
   196         mode = (mode & 0111) and 'x' or ''
       
   197         return data, mode
       
   198 
       
   199     def gettags(self):
       
   200         return self.tags