##// END OF EJS Templates
convert: fix marshalling in P4 convert extension to use a binary stream...
Peter Ingebretson -
r9474:6ea65327 default
parent child Browse files
Show More
@@ -1,205 +1,205 b''
1 #
1 #
2 # Perforce source for convert extension.
2 # Perforce source for convert extension.
3 #
3 #
4 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
4 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8 #
8 #
9
9
10 from mercurial import util
10 from mercurial import util
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12
12
13 from common import commit, converter_source, checktool, NoRepo
13 from common import commit, converter_source, checktool, NoRepo
14 import marshal
14 import marshal
15 import re
15 import re
16
16
17 def loaditer(f):
17 def loaditer(f):
18 "Yield the dictionary objects generated by p4"
18 "Yield the dictionary objects generated by p4"
19 try:
19 try:
20 while True:
20 while True:
21 d = marshal.load(f)
21 d = marshal.load(f)
22 if not d:
22 if not d:
23 break
23 break
24 yield d
24 yield d
25 except EOFError:
25 except EOFError:
26 pass
26 pass
27
27
28 class p4_source(converter_source):
28 class p4_source(converter_source):
29 def __init__(self, ui, path, rev=None):
29 def __init__(self, ui, path, rev=None):
30 super(p4_source, self).__init__(ui, path, rev=rev)
30 super(p4_source, self).__init__(ui, path, rev=rev)
31
31
32 if "/" in path and not path.startswith('//'):
32 if "/" in path and not path.startswith('//'):
33 raise NoRepo('%s does not look like a P4 repo' % path)
33 raise NoRepo('%s does not look like a P4 repo' % path)
34
34
35 checktool('p4', abort=False)
35 checktool('p4', abort=False)
36
36
37 self.p4changes = {}
37 self.p4changes = {}
38 self.heads = {}
38 self.heads = {}
39 self.changeset = {}
39 self.changeset = {}
40 self.files = {}
40 self.files = {}
41 self.tags = {}
41 self.tags = {}
42 self.lastbranch = {}
42 self.lastbranch = {}
43 self.parent = {}
43 self.parent = {}
44 self.encoding = "latin_1"
44 self.encoding = "latin_1"
45 self.depotname = {} # mapping from local name to depot name
45 self.depotname = {} # mapping from local name to depot name
46 self.modecache = {}
46 self.modecache = {}
47 self.re_type = re.compile("([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)(\+\w+)?$")
47 self.re_type = re.compile("([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)(\+\w+)?$")
48 self.re_keywords = re.compile(r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author):[^$\n]*\$")
48 self.re_keywords = re.compile(r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author):[^$\n]*\$")
49 self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
49 self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
50
50
51 self._parse(ui, path)
51 self._parse(ui, path)
52
52
53 def _parse_view(self, path):
53 def _parse_view(self, path):
54 "Read changes affecting the path"
54 "Read changes affecting the path"
55 cmd = 'p4 -G changes -s submitted "%s"' % path
55 cmd = 'p4 -G changes -s submitted "%s"' % path
56 stdout = util.popen(cmd)
56 stdout = util.popen(cmd, mode='rb')
57 for d in loaditer(stdout):
57 for d in loaditer(stdout):
58 c = d.get("change", None)
58 c = d.get("change", None)
59 if c:
59 if c:
60 self.p4changes[c] = True
60 self.p4changes[c] = True
61
61
62 def _parse(self, ui, path):
62 def _parse(self, ui, path):
63 "Prepare list of P4 filenames and revisions to import"
63 "Prepare list of P4 filenames and revisions to import"
64 ui.status(_('reading p4 views\n'))
64 ui.status(_('reading p4 views\n'))
65
65
66 # read client spec or view
66 # read client spec or view
67 if "/" in path:
67 if "/" in path:
68 self._parse_view(path)
68 self._parse_view(path)
69 if path.startswith("//") and path.endswith("/..."):
69 if path.startswith("//") and path.endswith("/..."):
70 views = {path[:-3]:""}
70 views = {path[:-3]:""}
71 else:
71 else:
72 views = {"//": ""}
72 views = {"//": ""}
73 else:
73 else:
74 cmd = 'p4 -G client -o "%s"' % path
74 cmd = 'p4 -G client -o "%s"' % path
75 clientspec = marshal.load(util.popen(cmd))
75 clientspec = marshal.load(util.popen(cmd, mode='rb'))
76
76
77 views = {}
77 views = {}
78 for client in clientspec:
78 for client in clientspec:
79 if client.startswith("View"):
79 if client.startswith("View"):
80 sview, cview = clientspec[client].split()
80 sview, cview = clientspec[client].split()
81 self._parse_view(sview)
81 self._parse_view(sview)
82 if sview.endswith("...") and cview.endswith("..."):
82 if sview.endswith("...") and cview.endswith("..."):
83 sview = sview[:-3]
83 sview = sview[:-3]
84 cview = cview[:-3]
84 cview = cview[:-3]
85 cview = cview[2:]
85 cview = cview[2:]
86 cview = cview[cview.find("/") + 1:]
86 cview = cview[cview.find("/") + 1:]
87 views[sview] = cview
87 views[sview] = cview
88
88
89 # list of changes that affect our source files
89 # list of changes that affect our source files
90 self.p4changes = self.p4changes.keys()
90 self.p4changes = self.p4changes.keys()
91 self.p4changes.sort(key=int)
91 self.p4changes.sort(key=int)
92
92
93 # list with depot pathnames, longest first
93 # list with depot pathnames, longest first
94 vieworder = views.keys()
94 vieworder = views.keys()
95 vieworder.sort(key=lambda x: -len(x))
95 vieworder.sort(key=lambda x: -len(x))
96
96
97 # handle revision limiting
97 # handle revision limiting
98 startrev = self.ui.config('convert', 'p4.startrev', default=0)
98 startrev = self.ui.config('convert', 'p4.startrev', default=0)
99 self.p4changes = [x for x in self.p4changes
99 self.p4changes = [x for x in self.p4changes
100 if ((not startrev or int(x) >= int(startrev)) and
100 if ((not startrev or int(x) >= int(startrev)) and
101 (not self.rev or int(x) <= int(self.rev)))]
101 (not self.rev or int(x) <= int(self.rev)))]
102
102
103 # now read the full changelists to get the list of file revisions
103 # now read the full changelists to get the list of file revisions
104 ui.status(_('collecting p4 changelists\n'))
104 ui.status(_('collecting p4 changelists\n'))
105 lastid = None
105 lastid = None
106 for change in self.p4changes:
106 for change in self.p4changes:
107 cmd = "p4 -G describe %s" % change
107 cmd = "p4 -G describe %s" % change
108 stdout = util.popen(cmd)
108 stdout = util.popen(cmd, mode='rb')
109 d = marshal.load(stdout)
109 d = marshal.load(stdout)
110
110
111 desc = self.recode(d["desc"])
111 desc = self.recode(d["desc"])
112 shortdesc = desc.split("\n", 1)[0]
112 shortdesc = desc.split("\n", 1)[0]
113 t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
113 t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
114 ui.status(util.ellipsis(t, 80) + '\n')
114 ui.status(util.ellipsis(t, 80) + '\n')
115
115
116 if lastid:
116 if lastid:
117 parents = [lastid]
117 parents = [lastid]
118 else:
118 else:
119 parents = []
119 parents = []
120
120
121 date = (int(d["time"]), 0) # timezone not set
121 date = (int(d["time"]), 0) # timezone not set
122 c = commit(author=self.recode(d["user"]), date=util.datestr(date),
122 c = commit(author=self.recode(d["user"]), date=util.datestr(date),
123 parents=parents, desc=desc, branch='', extra={"p4": change})
123 parents=parents, desc=desc, branch='', extra={"p4": change})
124
124
125 files = []
125 files = []
126 i = 0
126 i = 0
127 while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
127 while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
128 oldname = d["depotFile%d" % i]
128 oldname = d["depotFile%d" % i]
129 filename = None
129 filename = None
130 for v in vieworder:
130 for v in vieworder:
131 if oldname.startswith(v):
131 if oldname.startswith(v):
132 filename = views[v] + oldname[len(v):]
132 filename = views[v] + oldname[len(v):]
133 break
133 break
134 if filename:
134 if filename:
135 files.append((filename, d["rev%d" % i]))
135 files.append((filename, d["rev%d" % i]))
136 self.depotname[filename] = oldname
136 self.depotname[filename] = oldname
137 i += 1
137 i += 1
138 self.changeset[change] = c
138 self.changeset[change] = c
139 self.files[change] = files
139 self.files[change] = files
140 lastid = change
140 lastid = change
141
141
142 if lastid:
142 if lastid:
143 self.heads = [lastid]
143 self.heads = [lastid]
144
144
145 def getheads(self):
145 def getheads(self):
146 return self.heads
146 return self.heads
147
147
148 def getfile(self, name, rev):
148 def getfile(self, name, rev):
149 cmd = 'p4 -G print "%s#%s"' % (self.depotname[name], rev)
149 cmd = 'p4 -G print "%s#%s"' % (self.depotname[name], rev)
150 stdout = util.popen(cmd)
150 stdout = util.popen(cmd, mode='rb')
151
151
152 mode = None
152 mode = None
153 contents = ""
153 contents = ""
154 keywords = None
154 keywords = None
155
155
156 for d in loaditer(stdout):
156 for d in loaditer(stdout):
157 code = d["code"]
157 code = d["code"]
158 data = d.get("data")
158 data = d.get("data")
159
159
160 if code == "error":
160 if code == "error":
161 raise IOError(d["generic"], data)
161 raise IOError(d["generic"], data)
162
162
163 elif code == "stat":
163 elif code == "stat":
164 p4type = self.re_type.match(d["type"])
164 p4type = self.re_type.match(d["type"])
165 if p4type:
165 if p4type:
166 mode = ""
166 mode = ""
167 flags = (p4type.group(1) or "") + (p4type.group(3) or "")
167 flags = (p4type.group(1) or "") + (p4type.group(3) or "")
168 if "x" in flags:
168 if "x" in flags:
169 mode = "x"
169 mode = "x"
170 if p4type.group(2) == "symlink":
170 if p4type.group(2) == "symlink":
171 mode = "l"
171 mode = "l"
172 if "ko" in flags:
172 if "ko" in flags:
173 keywords = self.re_keywords_old
173 keywords = self.re_keywords_old
174 elif "k" in flags:
174 elif "k" in flags:
175 keywords = self.re_keywords
175 keywords = self.re_keywords
176
176
177 elif code == "text" or code == "binary":
177 elif code == "text" or code == "binary":
178 contents += data
178 contents += data
179
179
180 if mode is None:
180 if mode is None:
181 raise IOError(0, "bad stat")
181 raise IOError(0, "bad stat")
182
182
183 self.modecache[(name, rev)] = mode
183 self.modecache[(name, rev)] = mode
184
184
185 if keywords:
185 if keywords:
186 contents = keywords.sub("$\\1$", contents)
186 contents = keywords.sub("$\\1$", contents)
187 if mode == "l" and contents.endswith("\n"):
187 if mode == "l" and contents.endswith("\n"):
188 contents = contents[:-1]
188 contents = contents[:-1]
189
189
190 return contents
190 return contents
191
191
192 def getmode(self, name, rev):
192 def getmode(self, name, rev):
193 return self.modecache[(name, rev)]
193 return self.modecache[(name, rev)]
194
194
195 def getchanges(self, rev):
195 def getchanges(self, rev):
196 return self.files[rev], {}
196 return self.files[rev], {}
197
197
198 def getcommit(self, rev):
198 def getcommit(self, rev):
199 return self.changeset[rev]
199 return self.changeset[rev]
200
200
201 def gettags(self):
201 def gettags(self):
202 return self.tags
202 return self.tags
203
203
204 def getchangedfiles(self, rev, i):
204 def getchangedfiles(self, rev, i):
205 return sorted([x[0] for x in self.files[rev]])
205 return sorted([x[0] for x in self.files[rev]])
General Comments 0
You need to be logged in to leave comments. Login now