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