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