comparison hgext/convert/cvs.py @ 4534:cc9b79216a76

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