##// END OF EJS Templates
convert: don't use long list comprehensions...
David Soria Parra -
r30597:fa2d2c8a default
parent child Browse files
Show More
@@ -1,296 +1,298 b''
1 1 # Perforce source for convert extension.
2 2 #
3 3 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 from __future__ import absolute_import
8 8
9 9 import marshal
10 10 import re
11 11
12 12 from mercurial.i18n import _
13 13 from mercurial import (
14 14 error,
15 15 util,
16 16 )
17 17
18 18 from . import common
19 19
20 20 def loaditer(f):
21 21 "Yield the dictionary objects generated by p4"
22 22 try:
23 23 while True:
24 24 d = marshal.load(f)
25 25 if not d:
26 26 break
27 27 yield d
28 28 except EOFError:
29 29 pass
30 30
31 31 def decodefilename(filename):
32 32 """Perforce escapes special characters @, #, *, or %
33 33 with %40, %23, %2A, or %25 respectively
34 34
35 35 >>> decodefilename('portable-net45%252Bnetcore45%252Bwp8%252BMonoAndroid')
36 36 'portable-net45%2Bnetcore45%2Bwp8%2BMonoAndroid'
37 37 >>> decodefilename('//Depot/Directory/%2525/%2523/%23%40.%2A')
38 38 '//Depot/Directory/%25/%23/#@.*'
39 39 """
40 40 replacements = [('%2A', '*'), ('%23', '#'), ('%40', '@'), ('%25', '%')]
41 41 for k, v in replacements:
42 42 filename = filename.replace(k, v)
43 43 return filename
44 44
45 45 class p4_source(common.converter_source):
46 46 def __init__(self, ui, path, revs=None):
47 47 # avoid import cycle
48 48 from . import convcmd
49 49
50 50 super(p4_source, self).__init__(ui, path, revs=revs)
51 51
52 52 if "/" in path and not path.startswith('//'):
53 53 raise common.NoRepo(_('%s does not look like a P4 repository') %
54 54 path)
55 55
56 56 common.checktool('p4', abort=False)
57 57
58 58 self.p4changes = {}
59 59 self.heads = {}
60 60 self.changeset = {}
61 61 self.files = {}
62 62 self.copies = {}
63 63 self.tags = {}
64 64 self.lastbranch = {}
65 65 self.parent = {}
66 66 self.encoding = self.ui.config('convert', 'p4.encoding',
67 67 default=convcmd.orig_encoding)
68 68 self.depotname = {} # mapping from local name to depot name
69 69 self.localname = {} # mapping from depot name to local name
70 70 self.re_type = re.compile(
71 71 "([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)"
72 72 "(\+\w+)?$")
73 73 self.re_keywords = re.compile(
74 74 r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author)"
75 75 r":[^$\n]*\$")
76 76 self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
77 77
78 78 if revs and len(revs) > 1:
79 79 raise error.Abort(_("p4 source does not support specifying "
80 80 "multiple revisions"))
81 81 self._parse(ui, path)
82 82
83 83 def _parse_view(self, path):
84 84 "Read changes affecting the path"
85 85 cmd = 'p4 -G changes -s submitted %s' % util.shellquote(path)
86 86 stdout = util.popen(cmd, mode='rb')
87 87 for d in loaditer(stdout):
88 88 c = d.get("change", None)
89 89 if c:
90 90 self.p4changes[c] = True
91 91
92 92 def _parse(self, ui, path):
93 93 "Prepare list of P4 filenames and revisions to import"
94 94 ui.status(_('reading p4 views\n'))
95 95
96 96 # read client spec or view
97 97 if "/" in path:
98 98 self._parse_view(path)
99 99 if path.startswith("//") and path.endswith("/..."):
100 100 views = {path[:-3]:""}
101 101 else:
102 102 views = {"//": ""}
103 103 else:
104 104 cmd = 'p4 -G client -o %s' % util.shellquote(path)
105 105 clientspec = marshal.load(util.popen(cmd, mode='rb'))
106 106
107 107 views = {}
108 108 for client in clientspec:
109 109 if client.startswith("View"):
110 110 sview, cview = clientspec[client].split()
111 111 self._parse_view(sview)
112 112 if sview.endswith("...") and cview.endswith("..."):
113 113 sview = sview[:-3]
114 114 cview = cview[:-3]
115 115 cview = cview[2:]
116 116 cview = cview[cview.find("/") + 1:]
117 117 views[sview] = cview
118 118
119 119 # list of changes that affect our source files
120 120 self.p4changes = self.p4changes.keys()
121 121 self.p4changes.sort(key=int)
122 122
123 123 # list with depot pathnames, longest first
124 124 vieworder = views.keys()
125 125 vieworder.sort(key=len, reverse=True)
126 126
127 127 # handle revision limiting
128 128 startrev = self.ui.config('convert', 'p4.startrev', default=0)
129 self.p4changes = [x for x in self.p4changes
130 if ((not startrev or int(x) >= int(startrev)) and
131 (not self.revs or int(x) <= int(self.revs[0])))]
132 129
133 130 # now read the full changelists to get the list of file revisions
134 131 ui.status(_('collecting p4 changelists\n'))
135 132 lastid = None
136 133 for change in self.p4changes:
134 if startrev and int(change) < int(startrev):
135 continue
136 if self.revs and int(change) > int(self.revs[0]):
137 continue
138
137 139 cmd = "p4 -G describe -s %s" % change
138 140 stdout = util.popen(cmd, mode='rb')
139 141 d = marshal.load(stdout)
140 142 desc = self.recode(d.get("desc", ""))
141 143 shortdesc = desc.split("\n", 1)[0]
142 144 t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
143 145 ui.status(util.ellipsis(t, 80) + '\n')
144 146
145 147 if lastid:
146 148 parents = [lastid]
147 149 else:
148 150 parents = []
149 151
150 152 date = (int(d["time"]), 0) # timezone not set
151 153 c = common.commit(author=self.recode(d["user"]),
152 154 date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'),
153 155 parents=parents, desc=desc, branch=None,
154 156 extra={"p4": change})
155 157
156 158 files = []
157 159 copies = {}
158 160 copiedfiles = []
159 161 i = 0
160 162 while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
161 163 oldname = d["depotFile%d" % i]
162 164 filename = None
163 165 for v in vieworder:
164 166 if oldname.lower().startswith(v.lower()):
165 167 filename = decodefilename(views[v] + oldname[len(v):])
166 168 break
167 169 if filename:
168 170 files.append((filename, d["rev%d" % i]))
169 171 self.depotname[filename] = oldname
170 172 if (d.get("action%d" % i) == "move/add"):
171 173 copiedfiles.append(filename)
172 174 self.localname[oldname] = filename
173 175 i += 1
174 176
175 177 # Collect information about copied files
176 178 for filename in copiedfiles:
177 179 oldname = self.depotname[filename]
178 180
179 181 flcmd = 'p4 -G filelog %s' \
180 182 % util.shellquote(oldname)
181 183 flstdout = util.popen(flcmd, mode='rb')
182 184
183 185 copiedfilename = None
184 186 for d in loaditer(flstdout):
185 187 copiedoldname = None
186 188
187 189 i = 0
188 190 while ("change%d" % i) in d:
189 191 if (d["change%d" % i] == change and
190 192 d["action%d" % i] == "move/add"):
191 193 j = 0
192 194 while ("file%d,%d" % (i, j)) in d:
193 195 if d["how%d,%d" % (i, j)] == "moved from":
194 196 copiedoldname = d["file%d,%d" % (i, j)]
195 197 break
196 198 j += 1
197 199 i += 1
198 200
199 201 if copiedoldname and copiedoldname in self.localname:
200 202 copiedfilename = self.localname[copiedoldname]
201 203 break
202 204
203 205 if copiedfilename:
204 206 copies[filename] = copiedfilename
205 207 else:
206 208 ui.warn(_("cannot find source for copied file: %s@%s\n")
207 209 % (filename, change))
208 210
209 211 self.changeset[change] = c
210 212 self.files[change] = files
211 213 self.copies[change] = copies
212 214 lastid = change
213 215
214 216 if lastid:
215 217 self.heads = [lastid]
216 218
217 219 def getheads(self):
218 220 return self.heads
219 221
220 222 def getfile(self, name, rev):
221 223 cmd = 'p4 -G print %s' \
222 224 % util.shellquote("%s#%s" % (self.depotname[name], rev))
223 225
224 226 lasterror = None
225 227 while True:
226 228 stdout = util.popen(cmd, mode='rb')
227 229
228 230 mode = None
229 231 contents = []
230 232 keywords = None
231 233
232 234 for d in loaditer(stdout):
233 235 code = d["code"]
234 236 data = d.get("data")
235 237
236 238 if code == "error":
237 239 # if this is the first time error happened
238 240 # re-attempt getting the file
239 241 if not lasterror:
240 242 lasterror = IOError(d["generic"], data)
241 243 # this will exit inner-most for-loop
242 244 break
243 245 else:
244 246 raise lasterror
245 247
246 248 elif code == "stat":
247 249 action = d.get("action")
248 250 if action in ["purge", "delete", "move/delete"]:
249 251 return None, None
250 252 p4type = self.re_type.match(d["type"])
251 253 if p4type:
252 254 mode = ""
253 255 flags = ((p4type.group(1) or "")
254 256 + (p4type.group(3) or ""))
255 257 if "x" in flags:
256 258 mode = "x"
257 259 if p4type.group(2) == "symlink":
258 260 mode = "l"
259 261 if "ko" in flags:
260 262 keywords = self.re_keywords_old
261 263 elif "k" in flags:
262 264 keywords = self.re_keywords
263 265
264 266 elif code == "text" or code == "binary":
265 267 contents.append(data)
266 268
267 269 lasterror = None
268 270
269 271 if not lasterror:
270 272 break
271 273
272 274 if mode is None:
273 275 return None, None
274 276
275 277 contents = ''.join(contents)
276 278
277 279 if keywords:
278 280 contents = keywords.sub("$\\1$", contents)
279 281 if mode == "l" and contents.endswith("\n"):
280 282 contents = contents[:-1]
281 283
282 284 return contents, mode
283 285
284 286 def getchanges(self, rev, full):
285 287 if full:
286 288 raise error.Abort(_("convert from p4 does not support --full"))
287 289 return self.files[rev], self.copies[rev], set()
288 290
289 291 def getcommit(self, rev):
290 292 return self.changeset[rev]
291 293
292 294 def gettags(self):
293 295 return self.tags
294 296
295 297 def getchangedfiles(self, rev, i):
296 298 return sorted([x[0] for x in self.files[rev]])
General Comments 0
You need to be logged in to leave comments. Login now