##// END OF EJS Templates
convert: avoid interpreting Windows path as CVS connection strings....
Patrick Mezard -
r5304:b85f7cc1 default
parent child Browse files
Show More
@@ -1,252 +1,253 b''
1 # CVS conversion code inspired by hg-cvs-import and git-cvsimport
1 # CVS conversion code inspired by hg-cvs-import and git-cvsimport
2
2
3 import os, locale, re, socket
3 import os, locale, re, socket
4 from mercurial import util
4 from mercurial import util
5
5
6 from common import NoRepo, commit, converter_source
6 from common import NoRepo, commit, converter_source
7
7
8 class convert_cvs(converter_source):
8 class convert_cvs(converter_source):
9 def __init__(self, ui, path):
9 def __init__(self, ui, path):
10 self.path = path
10 self.path = path
11 self.ui = ui
11 self.ui = ui
12 cvs = os.path.join(path, "CVS")
12 cvs = os.path.join(path, "CVS")
13 if not os.path.exists(cvs):
13 if not os.path.exists(cvs):
14 raise NoRepo("couldn't open CVS repo %s" % path)
14 raise NoRepo("couldn't open CVS repo %s" % path)
15
15
16 self.changeset = {}
16 self.changeset = {}
17 self.files = {}
17 self.files = {}
18 self.tags = {}
18 self.tags = {}
19 self.lastbranch = {}
19 self.lastbranch = {}
20 self.parent = {}
20 self.parent = {}
21 self.socket = None
21 self.socket = None
22 self.cvsroot = file(os.path.join(cvs, "Root")).read()[:-1]
22 self.cvsroot = file(os.path.join(cvs, "Root")).read()[:-1]
23 self.cvsrepo = file(os.path.join(cvs, "Repository")).read()[:-1]
23 self.cvsrepo = file(os.path.join(cvs, "Repository")).read()[:-1]
24 self.encoding = locale.getpreferredencoding()
24 self.encoding = locale.getpreferredencoding()
25 self._parse()
25 self._parse()
26 self._connect()
26 self._connect()
27
27
28 def _parse(self):
28 def _parse(self):
29 if self.changeset:
29 if self.changeset:
30 return
30 return
31
31
32 d = os.getcwd()
32 d = os.getcwd()
33 try:
33 try:
34 os.chdir(self.path)
34 os.chdir(self.path)
35 id = None
35 id = None
36 state = 0
36 state = 0
37 for l in os.popen("cvsps -A -u --cvs-direct -q"):
37 for l in os.popen("cvsps -A -u --cvs-direct -q"):
38 if state == 0: # header
38 if state == 0: # header
39 if l.startswith("PatchSet"):
39 if l.startswith("PatchSet"):
40 id = l[9:-2]
40 id = l[9:-2]
41 elif l.startswith("Date"):
41 elif l.startswith("Date"):
42 date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"])
42 date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"])
43 date = util.datestr(date)
43 date = util.datestr(date)
44 elif l.startswith("Branch"):
44 elif l.startswith("Branch"):
45 branch = l[8:-1]
45 branch = l[8:-1]
46 self.parent[id] = self.lastbranch.get(branch, 'bad')
46 self.parent[id] = self.lastbranch.get(branch, 'bad')
47 self.lastbranch[branch] = id
47 self.lastbranch[branch] = id
48 elif l.startswith("Ancestor branch"):
48 elif l.startswith("Ancestor branch"):
49 ancestor = l[17:-1]
49 ancestor = l[17:-1]
50 self.parent[id] = self.lastbranch[ancestor]
50 self.parent[id] = self.lastbranch[ancestor]
51 elif l.startswith("Author"):
51 elif l.startswith("Author"):
52 author = self.recode(l[8:-1])
52 author = self.recode(l[8:-1])
53 elif l.startswith("Tag:") or l.startswith("Tags:"):
53 elif l.startswith("Tag:") or l.startswith("Tags:"):
54 t = l[l.index(':')+1:]
54 t = l[l.index(':')+1:]
55 t = [ut.strip() for ut in t.split(',')]
55 t = [ut.strip() for ut in t.split(',')]
56 if (len(t) > 1) or (t[0] and (t[0] != "(none)")):
56 if (len(t) > 1) or (t[0] and (t[0] != "(none)")):
57 self.tags.update(dict.fromkeys(t, id))
57 self.tags.update(dict.fromkeys(t, id))
58 elif l.startswith("Log:"):
58 elif l.startswith("Log:"):
59 state = 1
59 state = 1
60 log = ""
60 log = ""
61 elif state == 1: # log
61 elif state == 1: # log
62 if l == "Members: \n":
62 if l == "Members: \n":
63 files = {}
63 files = {}
64 log = self.recode(log[:-1])
64 log = self.recode(log[:-1])
65 if log.isspace():
65 if log.isspace():
66 log = "*** empty log message ***\n"
66 log = "*** empty log message ***\n"
67 state = 2
67 state = 2
68 else:
68 else:
69 log += l
69 log += l
70 elif state == 2:
70 elif state == 2:
71 if l == "\n": #
71 if l == "\n": #
72 state = 0
72 state = 0
73 p = [self.parent[id]]
73 p = [self.parent[id]]
74 if id == "1":
74 if id == "1":
75 p = []
75 p = []
76 if branch == "HEAD":
76 if branch == "HEAD":
77 branch = ""
77 branch = ""
78 c = commit(author=author, date=date, parents=p,
78 c = commit(author=author, date=date, parents=p,
79 desc=log, branch=branch)
79 desc=log, branch=branch)
80 self.changeset[id] = c
80 self.changeset[id] = c
81 self.files[id] = files
81 self.files[id] = files
82 else:
82 else:
83 colon = l.rfind(':')
83 colon = l.rfind(':')
84 file = l[1:colon]
84 file = l[1:colon]
85 rev = l[colon+1:-2]
85 rev = l[colon+1:-2]
86 rev = rev.split("->")[1]
86 rev = rev.split("->")[1]
87 files[file] = rev
87 files[file] = rev
88
88
89 self.heads = self.lastbranch.values()
89 self.heads = self.lastbranch.values()
90 finally:
90 finally:
91 os.chdir(d)
91 os.chdir(d)
92
92
93 def _connect(self):
93 def _connect(self):
94 root = self.cvsroot
94 root = self.cvsroot
95 conntype = None
95 conntype = None
96 user, host = None, None
96 user, host = None, None
97 cmd = ['cvs', 'server']
97 cmd = ['cvs', 'server']
98
98
99 self.ui.status("connecting to %s\n" % root)
99 self.ui.status("connecting to %s\n" % root)
100
100
101 if root.startswith(":pserver:"):
101 if root.startswith(":pserver:"):
102 root = root[9:]
102 root = root[9:]
103 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)',
103 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)',
104 root)
104 root)
105 if m:
105 if m:
106 conntype = "pserver"
106 conntype = "pserver"
107 user, passw, serv, port, root = m.groups()
107 user, passw, serv, port, root = m.groups()
108 if not user:
108 if not user:
109 user = "anonymous"
109 user = "anonymous"
110 if not port:
110 if not port:
111 port = 2401
111 port = 2401
112 else:
112 else:
113 port = int(port)
113 port = int(port)
114 format0 = ":pserver:%s@%s:%s" % (user, serv, root)
114 format0 = ":pserver:%s@%s:%s" % (user, serv, root)
115 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
115 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
116
116
117 if not passw:
117 if not passw:
118 passw = "A"
118 passw = "A"
119 pf = open(os.path.join(os.environ["HOME"], ".cvspass"))
119 pf = open(os.path.join(os.environ["HOME"], ".cvspass"))
120 for line in pf.read().splitlines():
120 for line in pf.read().splitlines():
121 part1, part2 = line.split(' ', 1)
121 part1, part2 = line.split(' ', 1)
122 if part1 == '/1':
122 if part1 == '/1':
123 # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
123 # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
124 part1, part2 = part2.split(' ', 1)
124 part1, part2 = part2.split(' ', 1)
125 format = format1
125 format = format1
126 else:
126 else:
127 # :pserver:user@example.com:/cvsroot/foo Ah<Z
127 # :pserver:user@example.com:/cvsroot/foo Ah<Z
128 format = format0
128 format = format0
129 if part1 == format:
129 if part1 == format:
130 passw = part2
130 passw = part2
131 break
131 break
132 pf.close()
132 pf.close()
133
133
134 sck = socket.socket()
134 sck = socket.socket()
135 sck.connect((serv, port))
135 sck.connect((serv, port))
136 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw,
136 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw,
137 "END AUTH REQUEST", ""]))
137 "END AUTH REQUEST", ""]))
138 if sck.recv(128) != "I LOVE YOU\n":
138 if sck.recv(128) != "I LOVE YOU\n":
139 raise NoRepo("CVS pserver authentication failed")
139 raise NoRepo("CVS pserver authentication failed")
140
140
141 self.writep = self.readp = sck.makefile('r+')
141 self.writep = self.readp = sck.makefile('r+')
142
142
143 if not conntype and root.startswith(":local:"):
143 if not conntype and root.startswith(":local:"):
144 conntype = "local"
144 conntype = "local"
145 root = root[7:]
145 root = root[7:]
146
146
147 if not conntype:
147 if not conntype:
148 # :ext:user@host/home/user/path/to/cvsroot
148 # :ext:user@host/home/user/path/to/cvsroot
149 if root.startswith(":ext:"):
149 if root.startswith(":ext:"):
150 root = root[5:]
150 root = root[5:]
151 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
151 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
152 if not m:
152 # Do not take Windows path "c:\foo\bar" for a connection strings
153 if os.path.isdir(root) or not m:
153 conntype = "local"
154 conntype = "local"
154 else:
155 else:
155 conntype = "rsh"
156 conntype = "rsh"
156 user, host, root = m.group(1), m.group(2), m.group(3)
157 user, host, root = m.group(1), m.group(2), m.group(3)
157
158
158 if conntype != "pserver":
159 if conntype != "pserver":
159 if conntype == "rsh":
160 if conntype == "rsh":
160 rsh = os.environ.get("CVS_RSH" or "rsh")
161 rsh = os.environ.get("CVS_RSH" or "rsh")
161 if user:
162 if user:
162 cmd = [rsh, '-l', user, host] + cmd
163 cmd = [rsh, '-l', user, host] + cmd
163 else:
164 else:
164 cmd = [rsh, host] + cmd
165 cmd = [rsh, host] + cmd
165
166
166 # popen2 does not support argument lists under Windows
167 # popen2 does not support argument lists under Windows
167 cmd = [util.shellquote(arg) for arg in cmd]
168 cmd = [util.shellquote(arg) for arg in cmd]
168 cmd = util.quotecommand(' '.join(cmd))
169 cmd = util.quotecommand(' '.join(cmd))
169 self.writep, self.readp = os.popen2(cmd, 'b')
170 self.writep, self.readp = os.popen2(cmd, 'b')
170
171
171 self.realroot = root
172 self.realroot = root
172
173
173 self.writep.write("Root %s\n" % root)
174 self.writep.write("Root %s\n" % root)
174 self.writep.write("Valid-responses ok error Valid-requests Mode"
175 self.writep.write("Valid-responses ok error Valid-requests Mode"
175 " M Mbinary E Checked-in Created Updated"
176 " M Mbinary E Checked-in Created Updated"
176 " Merged Removed\n")
177 " Merged Removed\n")
177 self.writep.write("valid-requests\n")
178 self.writep.write("valid-requests\n")
178 self.writep.flush()
179 self.writep.flush()
179 r = self.readp.readline()
180 r = self.readp.readline()
180 if not r.startswith("Valid-requests"):
181 if not r.startswith("Valid-requests"):
181 raise util.Abort("server sucks")
182 raise util.Abort("server sucks")
182 if "UseUnchanged" in r:
183 if "UseUnchanged" in r:
183 self.writep.write("UseUnchanged\n")
184 self.writep.write("UseUnchanged\n")
184 self.writep.flush()
185 self.writep.flush()
185 r = self.readp.readline()
186 r = self.readp.readline()
186
187
187 def getheads(self):
188 def getheads(self):
188 return self.heads
189 return self.heads
189
190
190 def _getfile(self, name, rev):
191 def _getfile(self, name, rev):
191 if rev.endswith("(DEAD)"):
192 if rev.endswith("(DEAD)"):
192 raise IOError
193 raise IOError
193
194
194 args = ("-N -P -kk -r %s --" % rev).split()
195 args = ("-N -P -kk -r %s --" % rev).split()
195 args.append(os.path.join(self.cvsrepo, name))
196 args.append(os.path.join(self.cvsrepo, name))
196 for x in args:
197 for x in args:
197 self.writep.write("Argument %s\n" % x)
198 self.writep.write("Argument %s\n" % x)
198 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
199 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
199 self.writep.flush()
200 self.writep.flush()
200
201
201 data = ""
202 data = ""
202 while 1:
203 while 1:
203 line = self.readp.readline()
204 line = self.readp.readline()
204 if line.startswith("Created ") or line.startswith("Updated "):
205 if line.startswith("Created ") or line.startswith("Updated "):
205 self.readp.readline() # path
206 self.readp.readline() # path
206 self.readp.readline() # entries
207 self.readp.readline() # entries
207 mode = self.readp.readline()[:-1]
208 mode = self.readp.readline()[:-1]
208 count = int(self.readp.readline()[:-1])
209 count = int(self.readp.readline()[:-1])
209 data = self.readp.read(count)
210 data = self.readp.read(count)
210 elif line.startswith(" "):
211 elif line.startswith(" "):
211 data += line[1:]
212 data += line[1:]
212 elif line.startswith("M "):
213 elif line.startswith("M "):
213 pass
214 pass
214 elif line.startswith("Mbinary "):
215 elif line.startswith("Mbinary "):
215 count = int(self.readp.readline()[:-1])
216 count = int(self.readp.readline()[:-1])
216 data = self.readp.read(count)
217 data = self.readp.read(count)
217 else:
218 else:
218 if line == "ok\n":
219 if line == "ok\n":
219 return (data, "x" in mode and "x" or "")
220 return (data, "x" in mode and "x" or "")
220 elif line.startswith("E "):
221 elif line.startswith("E "):
221 self.ui.warn("cvs server: %s\n" % line[2:])
222 self.ui.warn("cvs server: %s\n" % line[2:])
222 elif line.startswith("Remove"):
223 elif line.startswith("Remove"):
223 l = self.readp.readline()
224 l = self.readp.readline()
224 l = self.readp.readline()
225 l = self.readp.readline()
225 if l != "ok\n":
226 if l != "ok\n":
226 raise util.Abort("unknown CVS response: %s" % l)
227 raise util.Abort("unknown CVS response: %s" % l)
227 else:
228 else:
228 raise util.Abort("unknown CVS response: %s" % line)
229 raise util.Abort("unknown CVS response: %s" % line)
229
230
230 def getfile(self, file, rev):
231 def getfile(self, file, rev):
231 data, mode = self._getfile(file, rev)
232 data, mode = self._getfile(file, rev)
232 self.modecache[(file, rev)] = mode
233 self.modecache[(file, rev)] = mode
233 return data
234 return data
234
235
235 def getmode(self, file, rev):
236 def getmode(self, file, rev):
236 return self.modecache[(file, rev)]
237 return self.modecache[(file, rev)]
237
238
238 def getchanges(self, rev):
239 def getchanges(self, rev):
239 self.modecache = {}
240 self.modecache = {}
240 files = self.files[rev]
241 files = self.files[rev]
241 cl = files.items()
242 cl = files.items()
242 cl.sort()
243 cl.sort()
243 return cl
244 return cl
244
245
245 def recode(self, text):
246 def recode(self, text):
246 return text.decode(self.encoding, "replace").encode("utf-8")
247 return text.decode(self.encoding, "replace").encode("utf-8")
247
248
248 def getcommit(self, rev):
249 def getcommit(self, rev):
249 return self.changeset[rev]
250 return self.changeset[rev]
250
251
251 def gettags(self):
252 def gettags(self):
252 return self.tags
253 return self.tags
General Comments 0
You need to be logged in to leave comments. Login now