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