hgext/convert/p4.py
changeset 7823 11efa41037e2
child 7869 bc027d72c289
equal deleted inserted replaced
7822:1079e666e938 7823:11efa41037e2
       
     1 #
       
     2 # Perforce source for convert extension.
       
     3 #
       
     4 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
       
     5 #
       
     6 # This software may be used and distributed according to the terms
       
     7 # of the GNU General Public License, incorporated herein by reference.
       
     8 #
       
     9 
       
    10 from mercurial import util
       
    11 from mercurial.i18n import _
       
    12 
       
    13 from common import commit, converter_source, checktool
       
    14 import marshal
       
    15 
       
    16 def loaditer(f):
       
    17     "Yield the dictionary objects generated by p4"
       
    18     try:
       
    19         while True:
       
    20             d = marshal.load(f)
       
    21             if not d:
       
    22                 break
       
    23             yield d
       
    24     except EOFError:
       
    25         pass
       
    26 
       
    27 class p4_source(converter_source):
       
    28     def __init__(self, ui, path, rev=None):
       
    29         super(p4_source, self).__init__(ui, path, rev=rev)
       
    30 
       
    31         checktool('p4')
       
    32 
       
    33         self.p4changes = {}
       
    34         self.heads = {}
       
    35         self.changeset = {}
       
    36         self.files = {}
       
    37         self.tags = {}
       
    38         self.lastbranch = {}
       
    39         self.parent = {}
       
    40         self.encoding = "latin_1"
       
    41         self.depotname = {}           # mapping from local name to depot name
       
    42         self.modecache = {}
       
    43 
       
    44         self._parse(ui, path)
       
    45 
       
    46     def _parse_view(self, path):
       
    47         "Read changes affecting the path"
       
    48         cmd = "p4 -G changes -s submitted '%s'" % path
       
    49         stdout = util.popen(cmd)
       
    50         for d in loaditer(stdout):
       
    51             c = d.get("change", None)
       
    52             if c:
       
    53                 self.p4changes[c] = True
       
    54 
       
    55     def _parse(self, ui, path):
       
    56         "Prepare list of P4 filenames and revisions to import"
       
    57         ui.status(_('reading p4 views\n'))
       
    58 
       
    59         # read client spec or view
       
    60         if "/" in path:
       
    61             self._parse_view(path)
       
    62             if path.startswith("//") and path.endswith("/..."):
       
    63                 views = {path[:-3]:""}
       
    64             else:
       
    65                 views = {"//": ""}
       
    66         else:
       
    67             cmd = "p4 -G client -o '%s'" % path
       
    68             clientspec = marshal.load(util.popen(cmd))
       
    69             
       
    70             views = {}
       
    71             for client in clientspec:
       
    72                 if client.startswith("View"):
       
    73                     sview, cview = clientspec[client].split()
       
    74                     self._parse_view(sview)
       
    75                     if sview.endswith("...") and cview.endswith("..."):
       
    76                         sview = sview[:-3]
       
    77                         cview = cview[:-3]
       
    78                     cview = cview[2:]
       
    79                     cview = cview[cview.find("/") + 1:]
       
    80                     views[sview] = cview
       
    81 
       
    82         # list of changes that affect our source files
       
    83         self.p4changes = self.p4changes.keys()
       
    84         self.p4changes.sort(key=int)
       
    85 
       
    86         # list with depot pathnames, longest first
       
    87         vieworder = views.keys()
       
    88         vieworder.sort(key=lambda x: -len(x))
       
    89 
       
    90         # handle revision limiting
       
    91         startrev = self.ui.config('convert', 'p4.startrev', default=0)
       
    92         self.p4changes = [x for x in self.p4changes 
       
    93                           if ((not startrev or int(x) >= int(startrev)) and 
       
    94                               (not self.rev or int(x) <= int(self.rev)))]
       
    95 
       
    96         # now read the full changelists to get the list of file revisions
       
    97         ui.status(_('collecting p4 changelists\n'))
       
    98         lastid = None
       
    99         for change in self.p4changes:
       
   100             cmd = "p4 -G describe %s" % change
       
   101             stdout = util.popen(cmd)
       
   102             d = marshal.load(stdout)
       
   103 
       
   104             desc = self.recode(d["desc"])
       
   105             shortdesc = desc.split("\n", 1)[0]
       
   106             t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
       
   107             ui.status(util.ellipsis(t, 80) + '\n')
       
   108 
       
   109             if lastid:
       
   110                 parents = [lastid]
       
   111             else:
       
   112                 parents = []
       
   113             
       
   114             date = (int(d["time"]), 0)     # timezone not set
       
   115             c = commit(author=self.recode(d["user"]), date=util.datestr(date),
       
   116                         parents=parents, desc=desc, branch='', extra={"p4": change})
       
   117 
       
   118             files = []
       
   119             i = 0
       
   120             while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
       
   121                 oldname = d["depotFile%d" % i]
       
   122                 filename = None
       
   123                 for v in vieworder:
       
   124                     if oldname.startswith(v):
       
   125                         filename = views[v] + oldname[len(v):]
       
   126                         break
       
   127                 if filename:
       
   128                     files.append((filename, d["rev%d" % i]))
       
   129                     self.depotname[filename] = oldname
       
   130                 i += 1
       
   131             self.changeset[change] = c
       
   132             self.files[change] = files
       
   133             lastid = change
       
   134         
       
   135         if lastid:
       
   136             self.heads = [lastid]
       
   137 
       
   138     def getheads(self):
       
   139         return self.heads
       
   140 
       
   141     def getfile(self, name, rev):
       
   142         cmd = "p4 -G print '%s#%s'" % (self.depotname[name], rev)
       
   143         stdout = util.popen(cmd)
       
   144 
       
   145         mode = None
       
   146         data = ""
       
   147 
       
   148         for d in loaditer(stdout):
       
   149             if d["code"] == "stat":
       
   150                 if "+x" in d["type"]:
       
   151                     mode = "x"
       
   152                 else:
       
   153                     mode = ""
       
   154             elif d["code"] == "text":
       
   155                 data += d["data"]
       
   156 
       
   157         if mode is None:
       
   158             raise IOError()
       
   159 
       
   160         self.modecache[(name, rev)] = mode
       
   161         return data
       
   162 
       
   163     def getmode(self, name, rev):
       
   164         return self.modecache[(name, rev)]
       
   165 
       
   166     def getchanges(self, rev):
       
   167         return self.files[rev], {}
       
   168 
       
   169     def getcommit(self, rev):
       
   170         return self.changeset[rev]
       
   171 
       
   172     def gettags(self):
       
   173         return self.tags
       
   174 
       
   175     def getchangedfiles(self, rev, i):
       
   176         return util.sort([x[0] for x in self.files[rev]])