eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/convert/p4.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # Perforce source for convert extension.
       
     2 #
       
     3 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
       
     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 mercurial import util
       
     9 from mercurial.i18n import _
       
    10 
       
    11 from common import commit, converter_source, checktool, NoRepo
       
    12 import marshal
       
    13 import re
       
    14 
       
    15 def loaditer(f):
       
    16     "Yield the dictionary objects generated by p4"
       
    17     try:
       
    18         while True:
       
    19             d = marshal.load(f)
       
    20             if not d:
       
    21                 break
       
    22             yield d
       
    23     except EOFError:
       
    24         pass
       
    25 
       
    26 class p4_source(converter_source):
       
    27     def __init__(self, ui, path, rev=None):
       
    28         super(p4_source, self).__init__(ui, path, rev=rev)
       
    29 
       
    30         if "/" in path and not path.startswith('//'):
       
    31             raise NoRepo(_('%s does not look like a P4 repository') % path)
       
    32 
       
    33         checktool('p4', abort=False)
       
    34 
       
    35         self.p4changes = {}
       
    36         self.heads = {}
       
    37         self.changeset = {}
       
    38         self.files = {}
       
    39         self.tags = {}
       
    40         self.lastbranch = {}
       
    41         self.parent = {}
       
    42         self.encoding = "latin_1"
       
    43         self.depotname = {}           # mapping from local name to depot name
       
    44         self.re_type = re.compile(
       
    45             "([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)"
       
    46             "(\+\w+)?$")
       
    47         self.re_keywords = re.compile(
       
    48             r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author)"
       
    49             r":[^$\n]*\$")
       
    50         self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
       
    51 
       
    52         self._parse(ui, path)
       
    53 
       
    54     def _parse_view(self, path):
       
    55         "Read changes affecting the path"
       
    56         cmd = 'p4 -G changes -s submitted %s' % util.shellquote(path)
       
    57         stdout = util.popen(cmd, mode='rb')
       
    58         for d in loaditer(stdout):
       
    59             c = d.get("change", None)
       
    60             if c:
       
    61                 self.p4changes[c] = True
       
    62 
       
    63     def _parse(self, ui, path):
       
    64         "Prepare list of P4 filenames and revisions to import"
       
    65         ui.status(_('reading p4 views\n'))
       
    66 
       
    67         # read client spec or view
       
    68         if "/" in path:
       
    69             self._parse_view(path)
       
    70             if path.startswith("//") and path.endswith("/..."):
       
    71                 views = {path[:-3]:""}
       
    72             else:
       
    73                 views = {"//": ""}
       
    74         else:
       
    75             cmd = 'p4 -G client -o %s' % util.shellquote(path)
       
    76             clientspec = marshal.load(util.popen(cmd, mode='rb'))
       
    77 
       
    78             views = {}
       
    79             for client in clientspec:
       
    80                 if client.startswith("View"):
       
    81                     sview, cview = clientspec[client].split()
       
    82                     self._parse_view(sview)
       
    83                     if sview.endswith("...") and cview.endswith("..."):
       
    84                         sview = sview[:-3]
       
    85                         cview = cview[:-3]
       
    86                     cview = cview[2:]
       
    87                     cview = cview[cview.find("/") + 1:]
       
    88                     views[sview] = cview
       
    89 
       
    90         # list of changes that affect our source files
       
    91         self.p4changes = self.p4changes.keys()
       
    92         self.p4changes.sort(key=int)
       
    93 
       
    94         # list with depot pathnames, longest first
       
    95         vieworder = views.keys()
       
    96         vieworder.sort(key=len, reverse=True)
       
    97 
       
    98         # handle revision limiting
       
    99         startrev = self.ui.config('convert', 'p4.startrev', default=0)
       
   100         self.p4changes = [x for x in self.p4changes
       
   101                           if ((not startrev or int(x) >= int(startrev)) and
       
   102                               (not self.rev or int(x) <= int(self.rev)))]
       
   103 
       
   104         # now read the full changelists to get the list of file revisions
       
   105         ui.status(_('collecting p4 changelists\n'))
       
   106         lastid = None
       
   107         for change in self.p4changes:
       
   108             cmd = "p4 -G describe -s %s" % change
       
   109             stdout = util.popen(cmd, mode='rb')
       
   110             d = marshal.load(stdout)
       
   111             desc = self.recode(d["desc"])
       
   112             shortdesc = desc.split("\n", 1)[0]
       
   113             t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
       
   114             ui.status(util.ellipsis(t, 80) + '\n')
       
   115 
       
   116             if lastid:
       
   117                 parents = [lastid]
       
   118             else:
       
   119                 parents = []
       
   120 
       
   121             date = (int(d["time"]), 0)     # timezone not set
       
   122             c = commit(author=self.recode(d["user"]), date=util.datestr(date),
       
   123                        parents=parents, desc=desc, branch='',
       
   124                        extra={"p4": change})
       
   125 
       
   126             files = []
       
   127             i = 0
       
   128             while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
       
   129                 oldname = d["depotFile%d" % i]
       
   130                 filename = None
       
   131                 for v in vieworder:
       
   132                     if oldname.startswith(v):
       
   133                         filename = views[v] + oldname[len(v):]
       
   134                         break
       
   135                 if filename:
       
   136                     files.append((filename, d["rev%d" % i]))
       
   137                     self.depotname[filename] = oldname
       
   138                 i += 1
       
   139             self.changeset[change] = c
       
   140             self.files[change] = files
       
   141             lastid = change
       
   142 
       
   143         if lastid:
       
   144             self.heads = [lastid]
       
   145 
       
   146     def getheads(self):
       
   147         return self.heads
       
   148 
       
   149     def getfile(self, name, rev):
       
   150         cmd = 'p4 -G print %s' \
       
   151             % util.shellquote("%s#%s" % (self.depotname[name], rev))
       
   152         stdout = util.popen(cmd, mode='rb')
       
   153 
       
   154         mode = None
       
   155         contents = ""
       
   156         keywords = None
       
   157 
       
   158         for d in loaditer(stdout):
       
   159             code = d["code"]
       
   160             data = d.get("data")
       
   161 
       
   162             if code == "error":
       
   163                 raise IOError(d["generic"], data)
       
   164 
       
   165             elif code == "stat":
       
   166                 p4type = self.re_type.match(d["type"])
       
   167                 if p4type:
       
   168                     mode = ""
       
   169                     flags = (p4type.group(1) or "") + (p4type.group(3) or "")
       
   170                     if "x" in flags:
       
   171                         mode = "x"
       
   172                     if p4type.group(2) == "symlink":
       
   173                         mode = "l"
       
   174                     if "ko" in flags:
       
   175                         keywords = self.re_keywords_old
       
   176                     elif "k" in flags:
       
   177                         keywords = self.re_keywords
       
   178 
       
   179             elif code == "text" or code == "binary":
       
   180                 contents += data
       
   181 
       
   182         if mode is None:
       
   183             raise IOError(0, "bad stat")
       
   184 
       
   185         if keywords:
       
   186             contents = keywords.sub("$\\1$", contents)
       
   187         if mode == "l" and contents.endswith("\n"):
       
   188             contents = contents[:-1]
       
   189 
       
   190         return contents, mode
       
   191 
       
   192     def getchanges(self, rev):
       
   193         return self.files[rev], {}
       
   194 
       
   195     def getcommit(self, rev):
       
   196         return self.changeset[rev]
       
   197 
       
   198     def gettags(self):
       
   199         return self.tags
       
   200 
       
   201     def getchangedfiles(self, rev, i):
       
   202         return sorted([x[0] for x in self.files[rev]])