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