Mercurial > hg
diff hgext/convert/p4.py @ 7823:11efa41037e2 1.2
convert: Perforce source for conversion to Mercurial
author | Frank Kingswood <frank@kingswood-consulting.co.uk> |
---|---|
date | Tue, 03 Mar 2009 21:32:23 +0000 |
parents | |
children | bc027d72c289 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/convert/p4.py Tue Mar 03 21:32:23 2009 +0000 @@ -0,0 +1,176 @@ +# +# Perforce source for convert extension. +# +# Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. +# + +from mercurial import util +from mercurial.i18n import _ + +from common import commit, converter_source, checktool +import marshal + +def loaditer(f): + "Yield the dictionary objects generated by p4" + try: + while True: + d = marshal.load(f) + if not d: + break + yield d + except EOFError: + pass + +class p4_source(converter_source): + def __init__(self, ui, path, rev=None): + super(p4_source, self).__init__(ui, path, rev=rev) + + checktool('p4') + + self.p4changes = {} + self.heads = {} + self.changeset = {} + self.files = {} + self.tags = {} + self.lastbranch = {} + self.parent = {} + self.encoding = "latin_1" + self.depotname = {} # mapping from local name to depot name + self.modecache = {} + + self._parse(ui, path) + + def _parse_view(self, path): + "Read changes affecting the path" + cmd = "p4 -G changes -s submitted '%s'" % path + stdout = util.popen(cmd) + for d in loaditer(stdout): + c = d.get("change", None) + if c: + self.p4changes[c] = True + + def _parse(self, ui, path): + "Prepare list of P4 filenames and revisions to import" + ui.status(_('reading p4 views\n')) + + # read client spec or view + if "/" in path: + self._parse_view(path) + if path.startswith("//") and path.endswith("/..."): + views = {path[:-3]:""} + else: + views = {"//": ""} + else: + cmd = "p4 -G client -o '%s'" % path + clientspec = marshal.load(util.popen(cmd)) + + views = {} + for client in clientspec: + if client.startswith("View"): + sview, cview = clientspec[client].split() + self._parse_view(sview) + if sview.endswith("...") and cview.endswith("..."): + sview = sview[:-3] + cview = cview[:-3] + cview = cview[2:] + cview = cview[cview.find("/") + 1:] + views[sview] = cview + + # list of changes that affect our source files + self.p4changes = self.p4changes.keys() + self.p4changes.sort(key=int) + + # list with depot pathnames, longest first + vieworder = views.keys() + vieworder.sort(key=lambda x: -len(x)) + + # handle revision limiting + startrev = self.ui.config('convert', 'p4.startrev', default=0) + self.p4changes = [x for x in self.p4changes + if ((not startrev or int(x) >= int(startrev)) and + (not self.rev or int(x) <= int(self.rev)))] + + # now read the full changelists to get the list of file revisions + ui.status(_('collecting p4 changelists\n')) + lastid = None + for change in self.p4changes: + cmd = "p4 -G describe %s" % change + stdout = util.popen(cmd) + d = marshal.load(stdout) + + desc = self.recode(d["desc"]) + shortdesc = desc.split("\n", 1)[0] + t = '%s %s' % (d["change"], repr(shortdesc)[1:-1]) + ui.status(util.ellipsis(t, 80) + '\n') + + if lastid: + parents = [lastid] + else: + parents = [] + + date = (int(d["time"]), 0) # timezone not set + c = commit(author=self.recode(d["user"]), date=util.datestr(date), + parents=parents, desc=desc, branch='', extra={"p4": change}) + + files = [] + i = 0 + while ("depotFile%d" % i) in d and ("rev%d" % i) in d: + oldname = d["depotFile%d" % i] + filename = None + for v in vieworder: + if oldname.startswith(v): + filename = views[v] + oldname[len(v):] + break + if filename: + files.append((filename, d["rev%d" % i])) + self.depotname[filename] = oldname + i += 1 + self.changeset[change] = c + self.files[change] = files + lastid = change + + if lastid: + self.heads = [lastid] + + def getheads(self): + return self.heads + + def getfile(self, name, rev): + cmd = "p4 -G print '%s#%s'" % (self.depotname[name], rev) + stdout = util.popen(cmd) + + mode = None + data = "" + + for d in loaditer(stdout): + if d["code"] == "stat": + if "+x" in d["type"]: + mode = "x" + else: + mode = "" + elif d["code"] == "text": + data += d["data"] + + if mode is None: + raise IOError() + + self.modecache[(name, rev)] = mode + return data + + def getmode(self, name, rev): + return self.modecache[(name, rev)] + + def getchanges(self, rev): + return self.files[rev], {} + + def getcommit(self, rev): + return self.changeset[rev] + + def gettags(self): + return self.tags + + def getchangedfiles(self, rev, i): + return util.sort([x[0] for x in self.files[rev]])