##// END OF EJS Templates
convert: check existence of ~/.cvspass before reading it
Edouard Gomez -
r7442:a14ce129 default
parent child Browse files
Show More
@@ -1,349 +1,351 b''
1 1 # CVS conversion code inspired by hg-cvs-import and git-cvsimport
2 2
3 3 import os, locale, re, socket
4 4 from cStringIO import StringIO
5 5 from mercurial import util
6 6 from mercurial.i18n import _
7 7
8 8 from common import NoRepo, commit, converter_source, checktool
9 9 import cvsps
10 10
11 11 class convert_cvs(converter_source):
12 12 def __init__(self, ui, path, rev=None):
13 13 super(convert_cvs, self).__init__(ui, path, rev=rev)
14 14
15 15 cvs = os.path.join(path, "CVS")
16 16 if not os.path.exists(cvs):
17 17 raise NoRepo("%s does not look like a CVS checkout" % path)
18 18
19 19 checktool('cvs')
20 20 self.cmd = ui.config('convert', 'cvsps', 'builtin')
21 21 cvspsexe = self.cmd.split(None, 1)[0]
22 22 self.builtin = cvspsexe == 'builtin'
23 23
24 24 if not self.builtin:
25 25 checktool(cvspsexe)
26 26
27 27 self.changeset = {}
28 28 self.files = {}
29 29 self.tags = {}
30 30 self.lastbranch = {}
31 31 self.parent = {}
32 32 self.socket = None
33 33 self.cvsroot = file(os.path.join(cvs, "Root")).read()[:-1]
34 34 self.cvsrepo = file(os.path.join(cvs, "Repository")).read()[:-1]
35 35 self.encoding = locale.getpreferredencoding()
36 36
37 37 self._parse(ui)
38 38 self._connect()
39 39
40 40 def _parse(self, ui):
41 41 if self.changeset:
42 42 return
43 43
44 44 maxrev = 0
45 45 cmd = self.cmd
46 46 if self.rev:
47 47 # TODO: handle tags
48 48 try:
49 49 # patchset number?
50 50 maxrev = int(self.rev)
51 51 except ValueError:
52 52 try:
53 53 # date
54 54 util.parsedate(self.rev, ['%Y/%m/%d %H:%M:%S'])
55 55 cmd = '%s -d "1970/01/01 00:00:01" -d "%s"' % (cmd, self.rev)
56 56 except util.Abort:
57 57 raise util.Abort(_('revision %s is not a patchset number or date') % self.rev)
58 58
59 59 d = os.getcwd()
60 60 try:
61 61 os.chdir(self.path)
62 62 id = None
63 63 state = 0
64 64 filerevids = {}
65 65
66 66 if self.builtin:
67 67 # builtin cvsps code
68 68 ui.status(_('using builtin cvsps\n'))
69 69
70 70 db = cvsps.createlog(ui, cache='update')
71 71 db = cvsps.createchangeset(ui, db,
72 72 fuzz=int(ui.config('convert', 'cvsps.fuzz', 60)),
73 73 mergeto=ui.config('convert', 'cvsps.mergeto', None),
74 74 mergefrom=ui.config('convert', 'cvsps.mergefrom', None))
75 75
76 76 for cs in db:
77 77 if maxrev and cs.id>maxrev:
78 78 break
79 79 id = str(cs.id)
80 80 cs.author = self.recode(cs.author)
81 81 self.lastbranch[cs.branch] = id
82 82 cs.comment = self.recode(cs.comment)
83 83 date = util.datestr(cs.date)
84 84 self.tags.update(dict.fromkeys(cs.tags, id))
85 85
86 86 files = {}
87 87 for f in cs.entries:
88 88 files[f.file] = "%s%s" % ('.'.join([str(x) for x in f.revision]),
89 89 ['', '(DEAD)'][f.dead])
90 90
91 91 # add current commit to set
92 92 c = commit(author=cs.author, date=date,
93 93 parents=[str(p.id) for p in cs.parents],
94 94 desc=cs.comment, branch=cs.branch or '')
95 95 self.changeset[id] = c
96 96 self.files[id] = files
97 97 else:
98 98 # external cvsps
99 99 for l in util.popen(cmd):
100 100 if state == 0: # header
101 101 if l.startswith("PatchSet"):
102 102 id = l[9:-2]
103 103 if maxrev and int(id) > maxrev:
104 104 # ignore everything
105 105 state = 3
106 106 elif l.startswith("Date:"):
107 107 date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"])
108 108 date = util.datestr(date)
109 109 elif l.startswith("Branch:"):
110 110 branch = l[8:-1]
111 111 self.parent[id] = self.lastbranch.get(branch, 'bad')
112 112 self.lastbranch[branch] = id
113 113 elif l.startswith("Ancestor branch:"):
114 114 ancestor = l[17:-1]
115 115 # figure out the parent later
116 116 self.parent[id] = self.lastbranch[ancestor]
117 117 elif l.startswith("Author:"):
118 118 author = self.recode(l[8:-1])
119 119 elif l.startswith("Tag:") or l.startswith("Tags:"):
120 120 t = l[l.index(':')+1:]
121 121 t = [ut.strip() for ut in t.split(',')]
122 122 if (len(t) > 1) or (t[0] and (t[0] != "(none)")):
123 123 self.tags.update(dict.fromkeys(t, id))
124 124 elif l.startswith("Log:"):
125 125 # switch to gathering log
126 126 state = 1
127 127 log = ""
128 128 elif state == 1: # log
129 129 if l == "Members: \n":
130 130 # switch to gathering members
131 131 files = {}
132 132 oldrevs = []
133 133 log = self.recode(log[:-1])
134 134 state = 2
135 135 else:
136 136 # gather log
137 137 log += l
138 138 elif state == 2: # members
139 139 if l == "\n": # start of next entry
140 140 state = 0
141 141 p = [self.parent[id]]
142 142 if id == "1":
143 143 p = []
144 144 if branch == "HEAD":
145 145 branch = ""
146 146 if branch:
147 147 latest = None
148 148 # the last changeset that contains a base
149 149 # file is our parent
150 150 for r in oldrevs:
151 151 latest = max(filerevids.get(r, None), latest)
152 152 if latest:
153 153 p = [latest]
154 154
155 155 # add current commit to set
156 156 c = commit(author=author, date=date, parents=p,
157 157 desc=log, branch=branch)
158 158 self.changeset[id] = c
159 159 self.files[id] = files
160 160 else:
161 161 colon = l.rfind(':')
162 162 file = l[1:colon]
163 163 rev = l[colon+1:-2]
164 164 oldrev, rev = rev.split("->")
165 165 files[file] = rev
166 166
167 167 # save some information for identifying branch points
168 168 oldrevs.append("%s:%s" % (oldrev, file))
169 169 filerevids["%s:%s" % (rev, file)] = id
170 170 elif state == 3:
171 171 # swallow all input
172 172 continue
173 173
174 174 self.heads = self.lastbranch.values()
175 175 finally:
176 176 os.chdir(d)
177 177
178 178 def _connect(self):
179 179 root = self.cvsroot
180 180 conntype = None
181 181 user, host = None, None
182 182 cmd = ['cvs', 'server']
183 183
184 184 self.ui.status(_("connecting to %s\n") % root)
185 185
186 186 if root.startswith(":pserver:"):
187 187 root = root[9:]
188 188 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)',
189 189 root)
190 190 if m:
191 191 conntype = "pserver"
192 192 user, passw, serv, port, root = m.groups()
193 193 if not user:
194 194 user = "anonymous"
195 195 if not port:
196 196 port = 2401
197 197 else:
198 198 port = int(port)
199 199 format0 = ":pserver:%s@%s:%s" % (user, serv, root)
200 200 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
201 201
202 202 if not passw:
203 203 passw = "A"
204 pf = open(os.path.expanduser("~/.cvspass"))
205 for line in pf.read().splitlines():
206 part1, part2 = line.split(' ', 1)
207 if part1 == '/1':
208 # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
209 part1, part2 = part2.split(' ', 1)
210 format = format1
211 else:
212 # :pserver:user@example.com:/cvsroot/foo Ah<Z
213 format = format0
214 if part1 == format:
215 passw = part2
216 break
217 pf.close()
204 cvspass = os.path.expanduser("~/.cvspass")
205 if os.path.exists(cvspass):
206 pf = open(cvspass)
207 for line in pf.read().splitlines():
208 part1, part2 = line.split(' ', 1)
209 if part1 == '/1':
210 # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
211 part1, part2 = part2.split(' ', 1)
212 format = format1
213 else:
214 # :pserver:user@example.com:/cvsroot/foo Ah<Z
215 format = format0
216 if part1 == format:
217 passw = part2
218 break
219 pf.close()
218 220
219 221 sck = socket.socket()
220 222 sck.connect((serv, port))
221 223 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw,
222 224 "END AUTH REQUEST", ""]))
223 225 if sck.recv(128) != "I LOVE YOU\n":
224 226 raise util.Abort(_("CVS pserver authentication failed"))
225 227
226 228 self.writep = self.readp = sck.makefile('r+')
227 229
228 230 if not conntype and root.startswith(":local:"):
229 231 conntype = "local"
230 232 root = root[7:]
231 233
232 234 if not conntype:
233 235 # :ext:user@host/home/user/path/to/cvsroot
234 236 if root.startswith(":ext:"):
235 237 root = root[5:]
236 238 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
237 239 # Do not take Windows path "c:\foo\bar" for a connection strings
238 240 if os.path.isdir(root) or not m:
239 241 conntype = "local"
240 242 else:
241 243 conntype = "rsh"
242 244 user, host, root = m.group(1), m.group(2), m.group(3)
243 245
244 246 if conntype != "pserver":
245 247 if conntype == "rsh":
246 248 rsh = os.environ.get("CVS_RSH") or "ssh"
247 249 if user:
248 250 cmd = [rsh, '-l', user, host] + cmd
249 251 else:
250 252 cmd = [rsh, host] + cmd
251 253
252 254 # popen2 does not support argument lists under Windows
253 255 cmd = [util.shellquote(arg) for arg in cmd]
254 256 cmd = util.quotecommand(' '.join(cmd))
255 257 self.writep, self.readp = util.popen2(cmd, 'b')
256 258
257 259 self.realroot = root
258 260
259 261 self.writep.write("Root %s\n" % root)
260 262 self.writep.write("Valid-responses ok error Valid-requests Mode"
261 263 " M Mbinary E Checked-in Created Updated"
262 264 " Merged Removed\n")
263 265 self.writep.write("valid-requests\n")
264 266 self.writep.flush()
265 267 r = self.readp.readline()
266 268 if not r.startswith("Valid-requests"):
267 269 raise util.Abort(_("server sucks"))
268 270 if "UseUnchanged" in r:
269 271 self.writep.write("UseUnchanged\n")
270 272 self.writep.flush()
271 273 r = self.readp.readline()
272 274
273 275 def getheads(self):
274 276 return self.heads
275 277
276 278 def _getfile(self, name, rev):
277 279
278 280 def chunkedread(fp, count):
279 281 # file-objects returned by socked.makefile() do not handle
280 282 # large read() requests very well.
281 283 chunksize = 65536
282 284 output = StringIO()
283 285 while count > 0:
284 286 data = fp.read(min(count, chunksize))
285 287 if not data:
286 288 raise util.Abort(_("%d bytes missing from remote file") % count)
287 289 count -= len(data)
288 290 output.write(data)
289 291 return output.getvalue()
290 292
291 293 if rev.endswith("(DEAD)"):
292 294 raise IOError
293 295
294 296 args = ("-N -P -kk -r %s --" % rev).split()
295 297 args.append(self.cvsrepo + '/' + name)
296 298 for x in args:
297 299 self.writep.write("Argument %s\n" % x)
298 300 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
299 301 self.writep.flush()
300 302
301 303 data = ""
302 304 while 1:
303 305 line = self.readp.readline()
304 306 if line.startswith("Created ") or line.startswith("Updated "):
305 307 self.readp.readline() # path
306 308 self.readp.readline() # entries
307 309 mode = self.readp.readline()[:-1]
308 310 count = int(self.readp.readline()[:-1])
309 311 data = chunkedread(self.readp, count)
310 312 elif line.startswith(" "):
311 313 data += line[1:]
312 314 elif line.startswith("M "):
313 315 pass
314 316 elif line.startswith("Mbinary "):
315 317 count = int(self.readp.readline()[:-1])
316 318 data = chunkedread(self.readp, count)
317 319 else:
318 320 if line == "ok\n":
319 321 return (data, "x" in mode and "x" or "")
320 322 elif line.startswith("E "):
321 323 self.ui.warn(_("cvs server: %s\n") % line[2:])
322 324 elif line.startswith("Remove"):
323 325 l = self.readp.readline()
324 326 l = self.readp.readline()
325 327 if l != "ok\n":
326 328 raise util.Abort(_("unknown CVS response: %s") % l)
327 329 else:
328 330 raise util.Abort(_("unknown CVS response: %s") % line)
329 331
330 332 def getfile(self, file, rev):
331 333 data, mode = self._getfile(file, rev)
332 334 self.modecache[(file, rev)] = mode
333 335 return data
334 336
335 337 def getmode(self, file, rev):
336 338 return self.modecache[(file, rev)]
337 339
338 340 def getchanges(self, rev):
339 341 self.modecache = {}
340 342 return util.sort(self.files[rev].items()), {}
341 343
342 344 def getcommit(self, rev):
343 345 return self.changeset[rev]
344 346
345 347 def gettags(self):
346 348 return self.tags
347 349
348 350 def getchangedfiles(self, rev, i):
349 351 return util.sort(self.files[rev].keys())
General Comments 0
You need to be logged in to leave comments. Login now