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