37 |
37 |
38 class convert_cvs(converter_source): |
38 class convert_cvs(converter_source): |
39 def __init__(self, ui, repotype, path, revs=None): |
39 def __init__(self, ui, repotype, path, revs=None): |
40 super(convert_cvs, self).__init__(ui, repotype, path, revs=revs) |
40 super(convert_cvs, self).__init__(ui, repotype, path, revs=revs) |
41 |
41 |
42 cvs = os.path.join(path, "CVS") |
42 cvs = os.path.join(path, b"CVS") |
43 if not os.path.exists(cvs): |
43 if not os.path.exists(cvs): |
44 raise NoRepo(_("%s does not look like a CVS checkout") % path) |
44 raise NoRepo(_(b"%s does not look like a CVS checkout") % path) |
45 |
45 |
46 checktool('cvs') |
46 checktool(b'cvs') |
47 |
47 |
48 self.changeset = None |
48 self.changeset = None |
49 self.files = {} |
49 self.files = {} |
50 self.tags = {} |
50 self.tags = {} |
51 self.lastbranch = {} |
51 self.lastbranch = {} |
52 self.socket = None |
52 self.socket = None |
53 self.cvsroot = open(os.path.join(cvs, "Root"), 'rb').read()[:-1] |
53 self.cvsroot = open(os.path.join(cvs, b"Root"), b'rb').read()[:-1] |
54 self.cvsrepo = open(os.path.join(cvs, "Repository"), 'rb').read()[:-1] |
54 self.cvsrepo = open(os.path.join(cvs, b"Repository"), b'rb').read()[:-1] |
55 self.encoding = encoding.encoding |
55 self.encoding = encoding.encoding |
56 |
56 |
57 self._connect() |
57 self._connect() |
58 |
58 |
59 def _parse(self): |
59 def _parse(self): |
63 |
63 |
64 maxrev = 0 |
64 maxrev = 0 |
65 if self.revs: |
65 if self.revs: |
66 if len(self.revs) > 1: |
66 if len(self.revs) > 1: |
67 raise error.Abort( |
67 raise error.Abort( |
68 _('cvs source does not support specifying ' 'multiple revs') |
68 _( |
|
69 b'cvs source does not support specifying ' |
|
70 b'multiple revs' |
|
71 ) |
69 ) |
72 ) |
70 # TODO: handle tags |
73 # TODO: handle tags |
71 try: |
74 try: |
72 # patchset number? |
75 # patchset number? |
73 maxrev = int(self.revs[0]) |
76 maxrev = int(self.revs[0]) |
74 except ValueError: |
77 except ValueError: |
75 raise error.Abort( |
78 raise error.Abort( |
76 _('revision %s is not a patchset number') % self.revs[0] |
79 _(b'revision %s is not a patchset number') % self.revs[0] |
77 ) |
80 ) |
78 |
81 |
79 d = encoding.getcwd() |
82 d = encoding.getcwd() |
80 try: |
83 try: |
81 os.chdir(self.path) |
84 os.chdir(self.path) |
82 |
85 |
83 cache = 'update' |
86 cache = b'update' |
84 if not self.ui.configbool('convert', 'cvsps.cache'): |
87 if not self.ui.configbool(b'convert', b'cvsps.cache'): |
85 cache = None |
88 cache = None |
86 db = cvsps.createlog(self.ui, cache=cache) |
89 db = cvsps.createlog(self.ui, cache=cache) |
87 db = cvsps.createchangeset( |
90 db = cvsps.createchangeset( |
88 self.ui, |
91 self.ui, |
89 db, |
92 db, |
90 fuzz=int(self.ui.config('convert', 'cvsps.fuzz')), |
93 fuzz=int(self.ui.config(b'convert', b'cvsps.fuzz')), |
91 mergeto=self.ui.config('convert', 'cvsps.mergeto'), |
94 mergeto=self.ui.config(b'convert', b'cvsps.mergeto'), |
92 mergefrom=self.ui.config('convert', 'cvsps.mergefrom'), |
95 mergefrom=self.ui.config(b'convert', b'cvsps.mergefrom'), |
93 ) |
96 ) |
94 |
97 |
95 for cs in db: |
98 for cs in db: |
96 if maxrev and cs.id > maxrev: |
99 if maxrev and cs.id > maxrev: |
97 break |
100 break |
98 id = b"%d" % cs.id |
101 id = b"%d" % cs.id |
99 cs.author = self.recode(cs.author) |
102 cs.author = self.recode(cs.author) |
100 self.lastbranch[cs.branch] = id |
103 self.lastbranch[cs.branch] = id |
101 cs.comment = self.recode(cs.comment) |
104 cs.comment = self.recode(cs.comment) |
102 if self.ui.configbool('convert', 'localtimezone'): |
105 if self.ui.configbool(b'convert', b'localtimezone'): |
103 cs.date = makedatetimestamp(cs.date[0]) |
106 cs.date = makedatetimestamp(cs.date[0]) |
104 date = dateutil.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2') |
107 date = dateutil.datestr(cs.date, b'%Y-%m-%d %H:%M:%S %1%2') |
105 self.tags.update(dict.fromkeys(cs.tags, id)) |
108 self.tags.update(dict.fromkeys(cs.tags, id)) |
106 |
109 |
107 files = {} |
110 files = {} |
108 for f in cs.entries: |
111 for f in cs.entries: |
109 files[f.file] = "%s%s" % ( |
112 files[f.file] = b"%s%s" % ( |
110 '.'.join([(b"%d" % x) for x in f.revision]), |
113 b'.'.join([(b"%d" % x) for x in f.revision]), |
111 ['', '(DEAD)'][f.dead], |
114 [b'', b'(DEAD)'][f.dead], |
112 ) |
115 ) |
113 |
116 |
114 # add current commit to set |
117 # add current commit to set |
115 c = commit( |
118 c = commit( |
116 author=cs.author, |
119 author=cs.author, |
117 date=date, |
120 date=date, |
118 parents=[(b"%d" % p.id) for p in cs.parents], |
121 parents=[(b"%d" % p.id) for p in cs.parents], |
119 desc=cs.comment, |
122 desc=cs.comment, |
120 branch=cs.branch or '', |
123 branch=cs.branch or b'', |
121 ) |
124 ) |
122 self.changeset[id] = c |
125 self.changeset[id] = c |
123 self.files[id] = files |
126 self.files[id] = files |
124 |
127 |
125 self.heads = self.lastbranch.values() |
128 self.heads = self.lastbranch.values() |
128 |
131 |
129 def _connect(self): |
132 def _connect(self): |
130 root = self.cvsroot |
133 root = self.cvsroot |
131 conntype = None |
134 conntype = None |
132 user, host = None, None |
135 user, host = None, None |
133 cmd = ['cvs', 'server'] |
136 cmd = [b'cvs', b'server'] |
134 |
137 |
135 self.ui.status(_("connecting to %s\n") % root) |
138 self.ui.status(_(b"connecting to %s\n") % root) |
136 |
139 |
137 if root.startswith(":pserver:"): |
140 if root.startswith(b":pserver:"): |
138 root = root[9:] |
141 root = root[9:] |
139 m = re.match( |
142 m = re.match( |
140 r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)', root |
143 r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)', root |
141 ) |
144 ) |
142 if m: |
145 if m: |
143 conntype = "pserver" |
146 conntype = b"pserver" |
144 user, passw, serv, port, root = m.groups() |
147 user, passw, serv, port, root = m.groups() |
145 if not user: |
148 if not user: |
146 user = "anonymous" |
149 user = b"anonymous" |
147 if not port: |
150 if not port: |
148 port = 2401 |
151 port = 2401 |
149 else: |
152 else: |
150 port = int(port) |
153 port = int(port) |
151 format0 = ":pserver:%s@%s:%s" % (user, serv, root) |
154 format0 = b":pserver:%s@%s:%s" % (user, serv, root) |
152 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root) |
155 format1 = b":pserver:%s@%s:%d%s" % (user, serv, port, root) |
153 |
156 |
154 if not passw: |
157 if not passw: |
155 passw = "A" |
158 passw = b"A" |
156 cvspass = os.path.expanduser("~/.cvspass") |
159 cvspass = os.path.expanduser(b"~/.cvspass") |
157 try: |
160 try: |
158 pf = open(cvspass, 'rb') |
161 pf = open(cvspass, b'rb') |
159 for line in pf.read().splitlines(): |
162 for line in pf.read().splitlines(): |
160 part1, part2 = line.split(' ', 1) |
163 part1, part2 = line.split(b' ', 1) |
161 # /1 :pserver:user@example.com:2401/cvsroot/foo |
164 # /1 :pserver:user@example.com:2401/cvsroot/foo |
162 # Ah<Z |
165 # Ah<Z |
163 if part1 == '/1': |
166 if part1 == b'/1': |
164 part1, part2 = part2.split(' ', 1) |
167 part1, part2 = part2.split(b' ', 1) |
165 format = format1 |
168 format = format1 |
166 # :pserver:user@example.com:/cvsroot/foo Ah<Z |
169 # :pserver:user@example.com:/cvsroot/foo Ah<Z |
167 else: |
170 else: |
168 format = format0 |
171 format = format0 |
169 if part1 == format: |
172 if part1 == format: |
177 raise |
180 raise |
178 |
181 |
179 sck = socket.socket() |
182 sck = socket.socket() |
180 sck.connect((serv, port)) |
183 sck.connect((serv, port)) |
181 sck.send( |
184 sck.send( |
182 "\n".join( |
185 b"\n".join( |
183 [ |
186 [ |
184 "BEGIN AUTH REQUEST", |
187 b"BEGIN AUTH REQUEST", |
185 root, |
188 root, |
186 user, |
189 user, |
187 passw, |
190 passw, |
188 "END AUTH REQUEST", |
191 b"END AUTH REQUEST", |
189 "", |
192 b"", |
190 ] |
193 ] |
191 ) |
194 ) |
192 ) |
195 ) |
193 if sck.recv(128) != "I LOVE YOU\n": |
196 if sck.recv(128) != b"I LOVE YOU\n": |
194 raise error.Abort(_("CVS pserver authentication failed")) |
197 raise error.Abort(_(b"CVS pserver authentication failed")) |
195 |
198 |
196 self.writep = self.readp = sck.makefile('r+') |
199 self.writep = self.readp = sck.makefile(b'r+') |
197 |
200 |
198 if not conntype and root.startswith(":local:"): |
201 if not conntype and root.startswith(b":local:"): |
199 conntype = "local" |
202 conntype = b"local" |
200 root = root[7:] |
203 root = root[7:] |
201 |
204 |
202 if not conntype: |
205 if not conntype: |
203 # :ext:user@host/home/user/path/to/cvsroot |
206 # :ext:user@host/home/user/path/to/cvsroot |
204 if root.startswith(":ext:"): |
207 if root.startswith(b":ext:"): |
205 root = root[5:] |
208 root = root[5:] |
206 m = re.match(br'(?:([^@:/]+)@)?([^:/]+):?(.*)', root) |
209 m = re.match(br'(?:([^@:/]+)@)?([^:/]+):?(.*)', root) |
207 # Do not take Windows path "c:\foo\bar" for a connection strings |
210 # Do not take Windows path "c:\foo\bar" for a connection strings |
208 if os.path.isdir(root) or not m: |
211 if os.path.isdir(root) or not m: |
209 conntype = "local" |
212 conntype = b"local" |
210 else: |
213 else: |
211 conntype = "rsh" |
214 conntype = b"rsh" |
212 user, host, root = m.group(1), m.group(2), m.group(3) |
215 user, host, root = m.group(1), m.group(2), m.group(3) |
213 |
216 |
214 if conntype != "pserver": |
217 if conntype != b"pserver": |
215 if conntype == "rsh": |
218 if conntype == b"rsh": |
216 rsh = encoding.environ.get("CVS_RSH") or "ssh" |
219 rsh = encoding.environ.get(b"CVS_RSH") or b"ssh" |
217 if user: |
220 if user: |
218 cmd = [rsh, '-l', user, host] + cmd |
221 cmd = [rsh, b'-l', user, host] + cmd |
219 else: |
222 else: |
220 cmd = [rsh, host] + cmd |
223 cmd = [rsh, host] + cmd |
221 |
224 |
222 # popen2 does not support argument lists under Windows |
225 # popen2 does not support argument lists under Windows |
223 cmd = [procutil.shellquote(arg) for arg in cmd] |
226 cmd = [procutil.shellquote(arg) for arg in cmd] |
224 cmd = procutil.quotecommand(' '.join(cmd)) |
227 cmd = procutil.quotecommand(b' '.join(cmd)) |
225 self.writep, self.readp = procutil.popen2(cmd) |
228 self.writep, self.readp = procutil.popen2(cmd) |
226 |
229 |
227 self.realroot = root |
230 self.realroot = root |
228 |
231 |
229 self.writep.write("Root %s\n" % root) |
232 self.writep.write(b"Root %s\n" % root) |
230 self.writep.write( |
233 self.writep.write( |
231 "Valid-responses ok error Valid-requests Mode" |
234 b"Valid-responses ok error Valid-requests Mode" |
232 " M Mbinary E Checked-in Created Updated" |
235 b" M Mbinary E Checked-in Created Updated" |
233 " Merged Removed\n" |
236 b" Merged Removed\n" |
234 ) |
237 ) |
235 self.writep.write("valid-requests\n") |
238 self.writep.write(b"valid-requests\n") |
236 self.writep.flush() |
239 self.writep.flush() |
237 r = self.readp.readline() |
240 r = self.readp.readline() |
238 if not r.startswith("Valid-requests"): |
241 if not r.startswith(b"Valid-requests"): |
239 raise error.Abort( |
242 raise error.Abort( |
240 _( |
243 _( |
241 'unexpected response from CVS server ' |
244 b'unexpected response from CVS server ' |
242 '(expected "Valid-requests", but got %r)' |
245 b'(expected "Valid-requests", but got %r)' |
243 ) |
246 ) |
244 % r |
247 % r |
245 ) |
248 ) |
246 if "UseUnchanged" in r: |
249 if b"UseUnchanged" in r: |
247 self.writep.write("UseUnchanged\n") |
250 self.writep.write(b"UseUnchanged\n") |
248 self.writep.flush() |
251 self.writep.flush() |
249 self.readp.readline() |
252 self.readp.readline() |
250 |
253 |
251 def getheads(self): |
254 def getheads(self): |
252 self._parse() |
255 self._parse() |
260 output = stringio() |
263 output = stringio() |
261 while count > 0: |
264 while count > 0: |
262 data = fp.read(min(count, chunksize)) |
265 data = fp.read(min(count, chunksize)) |
263 if not data: |
266 if not data: |
264 raise error.Abort( |
267 raise error.Abort( |
265 _("%d bytes missing from remote file") % count |
268 _(b"%d bytes missing from remote file") % count |
266 ) |
269 ) |
267 count -= len(data) |
270 count -= len(data) |
268 output.write(data) |
271 output.write(data) |
269 return output.getvalue() |
272 return output.getvalue() |
270 |
273 |
271 self._parse() |
274 self._parse() |
272 if rev.endswith("(DEAD)"): |
275 if rev.endswith(b"(DEAD)"): |
273 return None, None |
276 return None, None |
274 |
277 |
275 args = ("-N -P -kk -r %s --" % rev).split() |
278 args = (b"-N -P -kk -r %s --" % rev).split() |
276 args.append(self.cvsrepo + '/' + name) |
279 args.append(self.cvsrepo + b'/' + name) |
277 for x in args: |
280 for x in args: |
278 self.writep.write("Argument %s\n" % x) |
281 self.writep.write(b"Argument %s\n" % x) |
279 self.writep.write("Directory .\n%s\nco\n" % self.realroot) |
282 self.writep.write(b"Directory .\n%s\nco\n" % self.realroot) |
280 self.writep.flush() |
283 self.writep.flush() |
281 |
284 |
282 data = "" |
285 data = b"" |
283 mode = None |
286 mode = None |
284 while True: |
287 while True: |
285 line = self.readp.readline() |
288 line = self.readp.readline() |
286 if line.startswith("Created ") or line.startswith("Updated "): |
289 if line.startswith(b"Created ") or line.startswith(b"Updated "): |
287 self.readp.readline() # path |
290 self.readp.readline() # path |
288 self.readp.readline() # entries |
291 self.readp.readline() # entries |
289 mode = self.readp.readline()[:-1] |
292 mode = self.readp.readline()[:-1] |
290 count = int(self.readp.readline()[:-1]) |
293 count = int(self.readp.readline()[:-1]) |
291 data = chunkedread(self.readp, count) |
294 data = chunkedread(self.readp, count) |
292 elif line.startswith(" "): |
295 elif line.startswith(b" "): |
293 data += line[1:] |
296 data += line[1:] |
294 elif line.startswith("M "): |
297 elif line.startswith(b"M "): |
295 pass |
298 pass |
296 elif line.startswith("Mbinary "): |
299 elif line.startswith(b"Mbinary "): |
297 count = int(self.readp.readline()[:-1]) |
300 count = int(self.readp.readline()[:-1]) |
298 data = chunkedread(self.readp, count) |
301 data = chunkedread(self.readp, count) |
299 else: |
302 else: |
300 if line == "ok\n": |
303 if line == b"ok\n": |
301 if mode is None: |
304 if mode is None: |
302 raise error.Abort(_('malformed response from CVS')) |
305 raise error.Abort(_(b'malformed response from CVS')) |
303 return (data, "x" in mode and "x" or "") |
306 return (data, b"x" in mode and b"x" or b"") |
304 elif line.startswith("E "): |
307 elif line.startswith(b"E "): |
305 self.ui.warn(_("cvs server: %s\n") % line[2:]) |
308 self.ui.warn(_(b"cvs server: %s\n") % line[2:]) |
306 elif line.startswith("Remove"): |
309 elif line.startswith(b"Remove"): |
307 self.readp.readline() |
310 self.readp.readline() |
308 else: |
311 else: |
309 raise error.Abort(_("unknown CVS response: %s") % line) |
312 raise error.Abort(_(b"unknown CVS response: %s") % line) |
310 |
313 |
311 def getchanges(self, rev, full): |
314 def getchanges(self, rev, full): |
312 if full: |
315 if full: |
313 raise error.Abort(_("convert from cvs does not support --full")) |
316 raise error.Abort(_(b"convert from cvs does not support --full")) |
314 self._parse() |
317 self._parse() |
315 return sorted(self.files[rev].iteritems()), {}, set() |
318 return sorted(self.files[rev].iteritems()), {}, set() |
316 |
319 |
317 def getcommit(self, rev): |
320 def getcommit(self, rev): |
318 self._parse() |
321 self._parse() |