comparison 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
comparison
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]])