##// END OF EJS Templates
convert: cvs.py - Allow user to use built-in CVS changeset code....
Frank Kingswood -
r6690:127e8c34 default
parent child Browse files
Show More
@@ -0,0 +1,60
1 #!/bin/sh
2
3 # This is http://www.selenic.com/mercurial/bts/issue1148
4
5 "$TESTDIR/hghave" cvs || exit 80
6
7 cvscall()
8 {
9 cvs -f "$@"
10 }
11
12 echo "[extensions]" >> $HGRCPATH
13 echo "convert = " >> $HGRCPATH
14 echo "graphlog = " >> $HGRCPATH
15 echo "[convert]" >> $HGRCPATH
16 echo "cvsps=builtin" >> $HGRCPATH
17
18 echo % create cvs repository
19 mkdir cvsrepo
20 cd cvsrepo
21 export CVSROOT=`pwd`
22 export CVS_OPTIONS=-f
23 cd ..
24
25 cvscall -q -d "$CVSROOT" init
26
27 echo % Create a new project
28
29 mkdir src
30 cd src
31 echo "1" > a > b
32 cvscall import -m "init" src v0 r0
33 cd ..
34 cvscall co src
35 cd src
36
37 echo % Branch the project
38
39 cvscall tag -b BRANCH
40 cvscall up -r BRANCH
41
42 echo % Modify file a, then b, then a
43
44 echo "2" > a
45 cvscall ci -m "mod a" | grep '<--' | sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g'
46
47 echo "2" > b
48 cvscall ci -m "mod b" | grep '<--' | sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g'
49
50 echo "3" > a
51 cvscall ci -m "mod a again" | grep '<--' | sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g'
52
53 echo % Convert
54
55 cd ..
56 hg convert src | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g'
57
58 echo % Check the result
59
60 hg -R src-hg glog --template '#rev# (#branches#) #desc# files: #files#\n'
@@ -0,0 +1,53
1 % create cvs repository
2 % Create a new project
3 N src/a
4 N src/b
5
6 No conflicts created by this import
7
8 cvs checkout: Updating src
9 U src/a
10 U src/b
11 % Branch the project
12 cvs tag: Tagging .
13 T a
14 T b
15 cvs update: Updating .
16 % Modify file a, then b, then a
17 cvs commit: Examining .
18 checking in src/a,v
19 cvs commit: Examining .
20 checking in src/b,v
21 cvs commit: Examining .
22 checking in src/a,v
23 % Convert
24 assuming destination src-hg
25 initializing destination src-hg repository
26 using builtin cvsps
27 collecting CVS rlog
28 7 log entries
29 creating changesets
30 5 changeset entries
31 connecting to cvsrepo
32 scanning source...
33 sorting...
34 converting...
35 4 Initial revision
36 3 init
37 2 mod a
38 1 mod b
39 0 mod a again
40 updating tags
41 % Check the result
42 o 5 () update tags files: .hgtags
43 |
44 | o 4 (BRANCH) mod a again files: a
45 | |
46 | o 3 (BRANCH) mod b files: b
47 | |
48 | o 2 (BRANCH) mod a files: a
49 | |
50 | o 1 (v0) init files:
51 |/
52 o 0 () Initial revision files: a b
53
@@ -0,0 +1,99
1 #!/bin/sh
2
3 "$TESTDIR/hghave" cvs || exit 80
4
5 cvscall()
6 {
7 cvs -f "$@"
8 }
9
10 echo "[extensions]" >> $HGRCPATH
11 echo "convert = " >> $HGRCPATH
12 echo "graphlog = " >> $HGRCPATH
13 echo "[convert]" >> $HGRCPATH
14 echo "cvsps=builtin" >> $HGRCPATH
15
16 echo % create cvs repository
17 mkdir cvsrepo
18 cd cvsrepo
19 export CVSROOT=`pwd`
20 export CVS_OPTIONS=-f
21 cd ..
22
23 cvscall -q -d "$CVSROOT" init
24
25 echo % create source directory
26 mkdir src-temp
27 cd src-temp
28 echo a > a
29 mkdir b
30 cd b
31 echo c > c
32 cd ..
33
34 echo % import source directory
35 cvscall -q import -m import src INITIAL start
36 cd ..
37
38 echo % checkout source directory
39 cvscall -q checkout src
40
41 echo % commit a new revision changing b/c
42 cd src
43 sleep 1
44 echo c >> b/c
45 cvscall -q commit -mci0 . | grep '<--' |\
46 sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g'
47 cd ..
48
49 echo % convert fresh repo
50 hg convert src src-hg | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g'
51 cat src-hg/a
52 cat src-hg/b/c
53
54 echo % convert fresh repo with --filemap
55 echo include b/c > filemap
56 hg convert --filemap filemap src src-filemap | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g'
57 cat src-hg/b/c
58 hg -R src-filemap log --template '#rev# #desc# files: #files#\n'
59
60 echo % commit new file revisions
61 cd src
62 echo a >> a
63 echo c >> b/c
64 cvscall -q commit -mci1 . | grep '<--' |\
65 sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g'
66 cd ..
67
68 echo % convert again
69 hg convert src src-hg | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g'
70 cat src-hg/a
71 cat src-hg/b/c
72
73 echo % convert again with --filemap
74 hg convert --filemap filemap src src-filemap | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g'
75 cat src-hg/b/c
76 hg -R src-filemap log --template '#rev# #desc# files: #files#\n'
77
78 echo % commit branch
79 cd src
80 cvs -q update -r1.1 b/c
81 cvs -q tag -b branch
82 cvs -q update -r branch
83 echo d >> b/c
84 cvs -q commit -mci2 . | grep '<--' |\
85 sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g'
86 cd ..
87
88 echo % convert again
89 hg convert src src-hg | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g'
90 cat src-hg/a
91 cat src-hg/b/c
92
93 echo % convert again with --filemap
94 hg convert --filemap filemap src src-filemap | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g'
95 cat src-hg/b/c
96 hg -R src-filemap log --template '#rev# #desc# files: #files#\n'
97
98 echo "graphlog = " >> $HGRCPATH
99 hg -R src-hg glog --template '#rev# (#branches#) #desc# files: #files#\n'
@@ -0,0 +1,139
1 % create cvs repository
2 % create source directory
3 % import source directory
4 N src/a
5 N src/b/c
6
7 No conflicts created by this import
8
9 % checkout source directory
10 U src/a
11 U src/b/c
12 % commit a new revision changing b/c
13 checking in src/b/c,v
14 % convert fresh repo
15 initializing destination src-hg repository
16 using builtin cvsps
17 collecting CVS rlog
18 5 log entries
19 creating changesets
20 3 changeset entries
21 connecting to cvsrepo
22 scanning source...
23 sorting...
24 converting...
25 2 Initial revision
26 1 import
27 0 ci0
28 updating tags
29 a
30 c
31 c
32 % convert fresh repo with --filemap
33 initializing destination src-filemap repository
34 using builtin cvsps
35 collecting CVS rlog
36 5 log entries
37 creating changesets
38 3 changeset entries
39 connecting to cvsrepo
40 scanning source...
41 sorting...
42 converting...
43 2 Initial revision
44 1 import
45 rolling back last transaction
46 0 ci0
47 updating tags
48 c
49 c
50 2 update tags files: .hgtags
51 1 ci0 files: b/c
52 0 Initial revision files: b/c
53 % commit new file revisions
54 checking in src/a,v
55 checking in src/b/c,v
56 % convert again
57 using builtin cvsps
58 collecting CVS rlog
59 7 log entries
60 creating changesets
61 4 changeset entries
62 connecting to cvsrepo
63 scanning source...
64 sorting...
65 converting...
66 0 ci1
67 a
68 a
69 c
70 c
71 c
72 % convert again with --filemap
73 using builtin cvsps
74 collecting CVS rlog
75 7 log entries
76 creating changesets
77 4 changeset entries
78 connecting to cvsrepo
79 scanning source...
80 sorting...
81 converting...
82 0 ci1
83 c
84 c
85 c
86 3 ci1 files: b/c
87 2 update tags files: .hgtags
88 1 ci0 files: b/c
89 0 Initial revision files: b/c
90 % commit branch
91 U b/c
92 T a
93 T b/c
94 checking in src/b/c,v
95 % convert again
96 using builtin cvsps
97 collecting CVS rlog
98 8 log entries
99 creating changesets
100 5 changeset entries
101 connecting to cvsrepo
102 scanning source...
103 sorting...
104 converting...
105 0 ci2
106 a
107 a
108 c
109 d
110 % convert again with --filemap
111 using builtin cvsps
112 collecting CVS rlog
113 8 log entries
114 creating changesets
115 5 changeset entries
116 connecting to cvsrepo
117 scanning source...
118 sorting...
119 converting...
120 0 ci2
121 c
122 d
123 4 ci2 files: b/c
124 3 ci1 files: b/c
125 2 update tags files: .hgtags
126 1 ci0 files: b/c
127 0 Initial revision files: b/c
128 o 5 (branch) ci2 files: b/c
129 |
130 | o 4 () ci1 files: a b/c
131 | |
132 | o 3 () update tags files: .hgtags
133 | |
134 | o 2 () ci0 files: b/c
135 |/
136 | o 1 (INITIAL) import files:
137 |/
138 o 0 () Initial revision files: a b/c
139
@@ -1,315 +1,355
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 cStringIO import StringIO
4 from cStringIO import StringIO
5 from mercurial import util
5 from mercurial import util
6 from mercurial.i18n import _
6
7
7 from common import NoRepo, commit, converter_source, checktool
8 from common import NoRepo, commit, converter_source, checktool
9 import cvsps
8
10
9 class convert_cvs(converter_source):
11 class convert_cvs(converter_source):
10 def __init__(self, ui, path, rev=None):
12 def __init__(self, ui, path, rev=None):
11 super(convert_cvs, self).__init__(ui, path, rev=rev)
13 super(convert_cvs, self).__init__(ui, path, rev=rev)
12
14
13 cvs = os.path.join(path, "CVS")
15 cvs = os.path.join(path, "CVS")
14 if not os.path.exists(cvs):
16 if not os.path.exists(cvs):
15 raise NoRepo("%s does not look like a CVS checkout" % path)
17 raise NoRepo("%s does not look like a CVS checkout" % path)
16
18
19 checktool('cvs')
17 self.cmd = ui.config('convert', 'cvsps', 'cvsps -A -u --cvs-direct -q')
20 self.cmd = ui.config('convert', 'cvsps', 'cvsps -A -u --cvs-direct -q')
18 cvspsexe = self.cmd.split(None, 1)[0]
21 cvspsexe = self.cmd.split(None, 1)[0]
19 for tool in (cvspsexe, 'cvs'):
22 self.builtin = cvspsexe == 'builtin'
20 checktool(tool)
23
24 if not self.builtin:
25 checktool(cvspsexe)
21
26
22 self.changeset = {}
27 self.changeset = {}
23 self.files = {}
28 self.files = {}
24 self.tags = {}
29 self.tags = {}
25 self.lastbranch = {}
30 self.lastbranch = {}
26 self.parent = {}
31 self.parent = {}
27 self.socket = None
32 self.socket = None
28 self.cvsroot = file(os.path.join(cvs, "Root")).read()[:-1]
33 self.cvsroot = file(os.path.join(cvs, "Root")).read()[:-1]
29 self.cvsrepo = file(os.path.join(cvs, "Repository")).read()[:-1]
34 self.cvsrepo = file(os.path.join(cvs, "Repository")).read()[:-1]
30 self.encoding = locale.getpreferredencoding()
35 self.encoding = locale.getpreferredencoding()
31 self._parse()
36
37 self._parse(ui)
32 self._connect()
38 self._connect()
33
39
34 def _parse(self):
40 def _parse(self, ui):
35 if self.changeset:
41 if self.changeset:
36 return
42 return
37
43
38 maxrev = 0
44 maxrev = 0
39 cmd = self.cmd
45 cmd = self.cmd
40 if self.rev:
46 if self.rev:
41 # TODO: handle tags
47 # TODO: handle tags
42 try:
48 try:
43 # patchset number?
49 # patchset number?
44 maxrev = int(self.rev)
50 maxrev = int(self.rev)
45 except ValueError:
51 except ValueError:
46 try:
52 try:
47 # date
53 # date
48 util.parsedate(self.rev, ['%Y/%m/%d %H:%M:%S'])
54 util.parsedate(self.rev, ['%Y/%m/%d %H:%M:%S'])
49 cmd = '%s -d "1970/01/01 00:00:01" -d "%s"' % (cmd, self.rev)
55 cmd = '%s -d "1970/01/01 00:00:01" -d "%s"' % (cmd, self.rev)
50 except util.Abort:
56 except util.Abort:
51 raise util.Abort('revision %s is not a patchset number or date' % self.rev)
57 raise util.Abort('revision %s is not a patchset number or date' % self.rev)
52
58
53 d = os.getcwd()
59 d = os.getcwd()
54 try:
60 try:
55 os.chdir(self.path)
61 os.chdir(self.path)
56 id = None
62 id = None
57 state = 0
63 state = 0
58 filerevids = {}
64 filerevids = {}
59 for l in util.popen(cmd):
65
60 if state == 0: # header
66 if self.builtin:
61 if l.startswith("PatchSet"):
67 # builtin cvsps code
62 id = l[9:-2]
68 ui.status(_('using builtin cvsps\n'))
63 if maxrev and int(id) > maxrev:
69
64 # ignore everything
70 db = cvsps.createlog(ui, cache='update')
65 state = 3
71 db = cvsps.createchangeset(ui, db,
66 elif l.startswith("Date"):
72 fuzz=int(ui.config('convert', 'cvsps.fuzz', 60)),
67 date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"])
73 mergeto=ui.config('convert', 'cvsps.mergeto', None),
68 date = util.datestr(date)
74 mergefrom=ui.config('convert', 'cvsps.mergefrom', None))
69 elif l.startswith("Branch"):
75
70 branch = l[8:-1]
76 for cs in db:
71 self.parent[id] = self.lastbranch.get(branch, 'bad')
77 if maxrev and cs.id>maxrev:
72 self.lastbranch[branch] = id
78 break
73 elif l.startswith("Ancestor branch"):
79 id = str(cs.id)
74 ancestor = l[17:-1]
80 cs.author = self.recode(cs.author)
75 # figure out the parent later
81 self.lastbranch[cs.branch] = id
76 self.parent[id] = self.lastbranch[ancestor]
82 cs.comment = self.recode(cs.comment)
77 elif l.startswith("Author"):
83 date = util.datestr(cs.date)
78 author = self.recode(l[8:-1])
84 self.tags.update(dict.fromkeys(cs.tags, id))
79 elif l.startswith("Tag:") or l.startswith("Tags:"):
85
80 t = l[l.index(':')+1:]
86 files = {}
81 t = [ut.strip() for ut in t.split(',')]
87 for f in cs.entries:
82 if (len(t) > 1) or (t[0] and (t[0] != "(none)")):
88 files[f.file] = "%s%s" % ('.'.join([str(x) for x in f.revision]),
83 self.tags.update(dict.fromkeys(t, id))
89 ['', '(DEAD)'][f.dead])
84 elif l.startswith("Log:"):
85 # switch to gathering log
86 state = 1
87 log = ""
88 elif state == 1: # log
89 if l == "Members: \n":
90 # switch to gathering members
91 files = {}
92 oldrevs = []
93 log = self.recode(log[:-1])
94 state = 2
95 else:
96 # gather log
97 log += l
98 elif state == 2: # members
99 if l == "\n": # start of next entry
100 state = 0
101 p = [self.parent[id]]
102 if id == "1":
103 p = []
104 if branch == "HEAD":
105 branch = ""
106 if branch:
107 latest = None
108 # the last changeset that contains a base
109 # file is our parent
110 for r in oldrevs:
111 latest = max(filerevids.get(r, None), latest)
112 if latest:
113 p = [latest]
114
90
115 # add current commit to set
91 # add current commit to set
116 c = commit(author=author, date=date, parents=p,
92 c = commit(author=cs.author, date=date,
117 desc=log, branch=branch)
93 parents=[str(p.id) for p in cs.parents],
118 self.changeset[id] = c
94 desc=cs.comment, branch=cs.branch or '')
119 self.files[id] = files
95 self.changeset[id] = c
120 else:
96 self.files[id] = files
121 colon = l.rfind(':')
97 else:
122 file = l[1:colon]
98 # external cvsps
123 rev = l[colon+1:-2]
99 for l in util.popen(cmd):
124 oldrev, rev = rev.split("->")
100 if state == 0: # header
125 files[file] = rev
101 if l.startswith("PatchSet"):
102 id = l[9:-2]
103 if maxrev and int(id) > maxrev:
104 # ignore everything
105 state = 3
106 elif l.startswith("Date"):
107 date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"])
108 date = util.datestr(date)
109 elif l.startswith("Branch"):
110 branch = l[8:-1]
111 self.parent[id] = self.lastbranch.get(branch, 'bad')
112 self.lastbranch[branch] = id
113 elif l.startswith("Ancestor branch"):
114 ancestor = l[17:-1]
115 # figure out the parent later
116 self.parent[id] = self.lastbranch[ancestor]
117 elif l.startswith("Author"):
118 author = self.recode(l[8:-1])
119 elif l.startswith("Tag:") or l.startswith("Tags:"):
120 t = l[l.index(':')+1:]
121 t = [ut.strip() for ut in t.split(',')]
122 if (len(t) > 1) or (t[0] and (t[0] != "(none)")):
123 self.tags.update(dict.fromkeys(t, id))
124 elif l.startswith("Log:"):
125 # switch to gathering log
126 state = 1
127 log = ""
128 elif state == 1: # log
129 if l == "Members: \n":
130 # switch to gathering members
131 files = {}
132 oldrevs = []
133 log = self.recode(log[:-1])
134 state = 2
135 else:
136 # gather log
137 log += l
138 elif state == 2: # members
139 if l == "\n": # start of next entry
140 state = 0
141 p = [self.parent[id]]
142 if id == "1":
143 p = []
144 if branch == "HEAD":
145 branch = ""
146 if branch:
147 latest = None
148 # the last changeset that contains a base
149 # file is our parent
150 for r in oldrevs:
151 latest = max(filerevids.get(r, None), latest)
152 if latest:
153 p = [latest]
126
154
127 # save some information for identifying branch points
155 # add current commit to set
128 oldrevs.append("%s:%s" % (oldrev, file))
156 c = commit(author=author, date=date, parents=p,
129 filerevids["%s:%s" % (rev, file)] = id
157 desc=log, branch=branch)
130 elif state == 3:
158 self.changeset[id] = c
131 # swallow all input
159 self.files[id] = files
132 continue
160 else:
161 colon = l.rfind(':')
162 file = l[1:colon]
163 rev = l[colon+1:-2]
164 oldrev, rev = rev.split("->")
165 files[file] = rev
166
167 # save some information for identifying branch points
168 oldrevs.append("%s:%s" % (oldrev, file))
169 filerevids["%s:%s" % (rev, file)] = id
170 elif state == 3:
171 # swallow all input
172 continue
133
173
134 self.heads = self.lastbranch.values()
174 self.heads = self.lastbranch.values()
135 finally:
175 finally:
136 os.chdir(d)
176 os.chdir(d)
137
177
138 def _connect(self):
178 def _connect(self):
139 root = self.cvsroot
179 root = self.cvsroot
140 conntype = None
180 conntype = None
141 user, host = None, None
181 user, host = None, None
142 cmd = ['cvs', 'server']
182 cmd = ['cvs', 'server']
143
183
144 self.ui.status("connecting to %s\n" % root)
184 self.ui.status("connecting to %s\n" % root)
145
185
146 if root.startswith(":pserver:"):
186 if root.startswith(":pserver:"):
147 root = root[9:]
187 root = root[9:]
148 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)',
188 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)',
149 root)
189 root)
150 if m:
190 if m:
151 conntype = "pserver"
191 conntype = "pserver"
152 user, passw, serv, port, root = m.groups()
192 user, passw, serv, port, root = m.groups()
153 if not user:
193 if not user:
154 user = "anonymous"
194 user = "anonymous"
155 if not port:
195 if not port:
156 port = 2401
196 port = 2401
157 else:
197 else:
158 port = int(port)
198 port = int(port)
159 format0 = ":pserver:%s@%s:%s" % (user, serv, root)
199 format0 = ":pserver:%s@%s:%s" % (user, serv, root)
160 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
200 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
161
201
162 if not passw:
202 if not passw:
163 passw = "A"
203 passw = "A"
164 pf = open(os.path.join(os.environ["HOME"], ".cvspass"))
204 pf = open(os.path.join(os.environ["HOME"], ".cvspass"))
165 for line in pf.read().splitlines():
205 for line in pf.read().splitlines():
166 part1, part2 = line.split(' ', 1)
206 part1, part2 = line.split(' ', 1)
167 if part1 == '/1':
207 if part1 == '/1':
168 # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
208 # /1 :pserver:user@example.com:2401/cvsroot/foo Ah<Z
169 part1, part2 = part2.split(' ', 1)
209 part1, part2 = part2.split(' ', 1)
170 format = format1
210 format = format1
171 else:
211 else:
172 # :pserver:user@example.com:/cvsroot/foo Ah<Z
212 # :pserver:user@example.com:/cvsroot/foo Ah<Z
173 format = format0
213 format = format0
174 if part1 == format:
214 if part1 == format:
175 passw = part2
215 passw = part2
176 break
216 break
177 pf.close()
217 pf.close()
178
218
179 sck = socket.socket()
219 sck = socket.socket()
180 sck.connect((serv, port))
220 sck.connect((serv, port))
181 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw,
221 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw,
182 "END AUTH REQUEST", ""]))
222 "END AUTH REQUEST", ""]))
183 if sck.recv(128) != "I LOVE YOU\n":
223 if sck.recv(128) != "I LOVE YOU\n":
184 raise util.Abort("CVS pserver authentication failed")
224 raise util.Abort("CVS pserver authentication failed")
185
225
186 self.writep = self.readp = sck.makefile('r+')
226 self.writep = self.readp = sck.makefile('r+')
187
227
188 if not conntype and root.startswith(":local:"):
228 if not conntype and root.startswith(":local:"):
189 conntype = "local"
229 conntype = "local"
190 root = root[7:]
230 root = root[7:]
191
231
192 if not conntype:
232 if not conntype:
193 # :ext:user@host/home/user/path/to/cvsroot
233 # :ext:user@host/home/user/path/to/cvsroot
194 if root.startswith(":ext:"):
234 if root.startswith(":ext:"):
195 root = root[5:]
235 root = root[5:]
196 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
236 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
197 # Do not take Windows path "c:\foo\bar" for a connection strings
237 # Do not take Windows path "c:\foo\bar" for a connection strings
198 if os.path.isdir(root) or not m:
238 if os.path.isdir(root) or not m:
199 conntype = "local"
239 conntype = "local"
200 else:
240 else:
201 conntype = "rsh"
241 conntype = "rsh"
202 user, host, root = m.group(1), m.group(2), m.group(3)
242 user, host, root = m.group(1), m.group(2), m.group(3)
203
243
204 if conntype != "pserver":
244 if conntype != "pserver":
205 if conntype == "rsh":
245 if conntype == "rsh":
206 rsh = os.environ.get("CVS_RSH") or "ssh"
246 rsh = os.environ.get("CVS_RSH") or "ssh"
207 if user:
247 if user:
208 cmd = [rsh, '-l', user, host] + cmd
248 cmd = [rsh, '-l', user, host] + cmd
209 else:
249 else:
210 cmd = [rsh, host] + cmd
250 cmd = [rsh, host] + cmd
211
251
212 # popen2 does not support argument lists under Windows
252 # popen2 does not support argument lists under Windows
213 cmd = [util.shellquote(arg) for arg in cmd]
253 cmd = [util.shellquote(arg) for arg in cmd]
214 cmd = util.quotecommand(' '.join(cmd))
254 cmd = util.quotecommand(' '.join(cmd))
215 self.writep, self.readp = os.popen2(cmd, 'b')
255 self.writep, self.readp = os.popen2(cmd, 'b')
216
256
217 self.realroot = root
257 self.realroot = root
218
258
219 self.writep.write("Root %s\n" % root)
259 self.writep.write("Root %s\n" % root)
220 self.writep.write("Valid-responses ok error Valid-requests Mode"
260 self.writep.write("Valid-responses ok error Valid-requests Mode"
221 " M Mbinary E Checked-in Created Updated"
261 " M Mbinary E Checked-in Created Updated"
222 " Merged Removed\n")
262 " Merged Removed\n")
223 self.writep.write("valid-requests\n")
263 self.writep.write("valid-requests\n")
224 self.writep.flush()
264 self.writep.flush()
225 r = self.readp.readline()
265 r = self.readp.readline()
226 if not r.startswith("Valid-requests"):
266 if not r.startswith("Valid-requests"):
227 raise util.Abort("server sucks")
267 raise util.Abort("server sucks")
228 if "UseUnchanged" in r:
268 if "UseUnchanged" in r:
229 self.writep.write("UseUnchanged\n")
269 self.writep.write("UseUnchanged\n")
230 self.writep.flush()
270 self.writep.flush()
231 r = self.readp.readline()
271 r = self.readp.readline()
232
272
233 def getheads(self):
273 def getheads(self):
234 return self.heads
274 return self.heads
235
275
236 def _getfile(self, name, rev):
276 def _getfile(self, name, rev):
237
277
238 def chunkedread(fp, count):
278 def chunkedread(fp, count):
239 # file-objects returned by socked.makefile() do not handle
279 # file-objects returned by socked.makefile() do not handle
240 # large read() requests very well.
280 # large read() requests very well.
241 chunksize = 65536
281 chunksize = 65536
242 output = StringIO()
282 output = StringIO()
243 while count > 0:
283 while count > 0:
244 data = fp.read(min(count, chunksize))
284 data = fp.read(min(count, chunksize))
245 if not data:
285 if not data:
246 raise util.Abort("%d bytes missing from remote file" % count)
286 raise util.Abort("%d bytes missing from remote file" % count)
247 count -= len(data)
287 count -= len(data)
248 output.write(data)
288 output.write(data)
249 return output.getvalue()
289 return output.getvalue()
250
290
251 if rev.endswith("(DEAD)"):
291 if rev.endswith("(DEAD)"):
252 raise IOError
292 raise IOError
253
293
254 args = ("-N -P -kk -r %s --" % rev).split()
294 args = ("-N -P -kk -r %s --" % rev).split()
255 args.append(self.cvsrepo + '/' + name)
295 args.append(self.cvsrepo + '/' + name)
256 for x in args:
296 for x in args:
257 self.writep.write("Argument %s\n" % x)
297 self.writep.write("Argument %s\n" % x)
258 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
298 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
259 self.writep.flush()
299 self.writep.flush()
260
300
261 data = ""
301 data = ""
262 while 1:
302 while 1:
263 line = self.readp.readline()
303 line = self.readp.readline()
264 if line.startswith("Created ") or line.startswith("Updated "):
304 if line.startswith("Created ") or line.startswith("Updated "):
265 self.readp.readline() # path
305 self.readp.readline() # path
266 self.readp.readline() # entries
306 self.readp.readline() # entries
267 mode = self.readp.readline()[:-1]
307 mode = self.readp.readline()[:-1]
268 count = int(self.readp.readline()[:-1])
308 count = int(self.readp.readline()[:-1])
269 data = chunkedread(self.readp, count)
309 data = chunkedread(self.readp, count)
270 elif line.startswith(" "):
310 elif line.startswith(" "):
271 data += line[1:]
311 data += line[1:]
272 elif line.startswith("M "):
312 elif line.startswith("M "):
273 pass
313 pass
274 elif line.startswith("Mbinary "):
314 elif line.startswith("Mbinary "):
275 count = int(self.readp.readline()[:-1])
315 count = int(self.readp.readline()[:-1])
276 data = chunkedread(self.readp, count)
316 data = chunkedread(self.readp, count)
277 else:
317 else:
278 if line == "ok\n":
318 if line == "ok\n":
279 return (data, "x" in mode and "x" or "")
319 return (data, "x" in mode and "x" or "")
280 elif line.startswith("E "):
320 elif line.startswith("E "):
281 self.ui.warn("cvs server: %s\n" % line[2:])
321 self.ui.warn("cvs server: %s\n" % line[2:])
282 elif line.startswith("Remove"):
322 elif line.startswith("Remove"):
283 l = self.readp.readline()
323 l = self.readp.readline()
284 l = self.readp.readline()
324 l = self.readp.readline()
285 if l != "ok\n":
325 if l != "ok\n":
286 raise util.Abort("unknown CVS response: %s" % l)
326 raise util.Abort("unknown CVS response: %s" % l)
287 else:
327 else:
288 raise util.Abort("unknown CVS response: %s" % line)
328 raise util.Abort("unknown CVS response: %s" % line)
289
329
290 def getfile(self, file, rev):
330 def getfile(self, file, rev):
291 data, mode = self._getfile(file, rev)
331 data, mode = self._getfile(file, rev)
292 self.modecache[(file, rev)] = mode
332 self.modecache[(file, rev)] = mode
293 return data
333 return data
294
334
295 def getmode(self, file, rev):
335 def getmode(self, file, rev):
296 return self.modecache[(file, rev)]
336 return self.modecache[(file, rev)]
297
337
298 def getchanges(self, rev):
338 def getchanges(self, rev):
299 self.modecache = {}
339 self.modecache = {}
300 files = self.files[rev]
340 files = self.files[rev]
301 cl = files.items()
341 cl = files.items()
302 cl.sort()
342 cl.sort()
303 return (cl, {})
343 return (cl, {})
304
344
305 def getcommit(self, rev):
345 def getcommit(self, rev):
306 return self.changeset[rev]
346 return self.changeset[rev]
307
347
308 def gettags(self):
348 def gettags(self):
309 return self.tags
349 return self.tags
310
350
311 def getchangedfiles(self, rev, i):
351 def getchangedfiles(self, rev, i):
312 files = self.files[rev].keys()
352 files = self.files[rev].keys()
313 files.sort()
353 files.sort()
314 return files
354 return files
315
355
General Comments 0
You need to be logged in to leave comments. Login now