##// END OF EJS Templates
py3: make sure regexes are bytes...
Pulkit Goyal -
r36411:54f4328a default
parent child Browse files
Show More
@@ -1,297 +1,297 b''
1 # cvs.py: CVS conversion code inspired by hg-cvs-import and git-cvsimport
1 # cvs.py: CVS conversion code inspired by hg-cvs-import and git-cvsimport
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import errno
9 import errno
10 import os
10 import os
11 import re
11 import re
12 import socket
12 import socket
13
13
14 from mercurial.i18n import _
14 from mercurial.i18n import _
15 from mercurial import (
15 from mercurial import (
16 encoding,
16 encoding,
17 error,
17 error,
18 pycompat,
18 pycompat,
19 util,
19 util,
20 )
20 )
21
21
22 from . import (
22 from . import (
23 common,
23 common,
24 cvsps,
24 cvsps,
25 )
25 )
26
26
27 stringio = util.stringio
27 stringio = util.stringio
28 checktool = common.checktool
28 checktool = common.checktool
29 commit = common.commit
29 commit = common.commit
30 converter_source = common.converter_source
30 converter_source = common.converter_source
31 makedatetimestamp = common.makedatetimestamp
31 makedatetimestamp = common.makedatetimestamp
32 NoRepo = common.NoRepo
32 NoRepo = common.NoRepo
33
33
34 class convert_cvs(converter_source):
34 class convert_cvs(converter_source):
35 def __init__(self, ui, repotype, path, revs=None):
35 def __init__(self, ui, repotype, path, revs=None):
36 super(convert_cvs, self).__init__(ui, repotype, path, revs=revs)
36 super(convert_cvs, self).__init__(ui, repotype, path, revs=revs)
37
37
38 cvs = os.path.join(path, "CVS")
38 cvs = os.path.join(path, "CVS")
39 if not os.path.exists(cvs):
39 if not os.path.exists(cvs):
40 raise NoRepo(_("%s does not look like a CVS checkout") % path)
40 raise NoRepo(_("%s does not look like a CVS checkout") % path)
41
41
42 checktool('cvs')
42 checktool('cvs')
43
43
44 self.changeset = None
44 self.changeset = None
45 self.files = {}
45 self.files = {}
46 self.tags = {}
46 self.tags = {}
47 self.lastbranch = {}
47 self.lastbranch = {}
48 self.socket = None
48 self.socket = None
49 self.cvsroot = open(os.path.join(cvs, "Root"), 'rb').read()[:-1]
49 self.cvsroot = open(os.path.join(cvs, "Root"), 'rb').read()[:-1]
50 self.cvsrepo = open(os.path.join(cvs, "Repository"), 'rb').read()[:-1]
50 self.cvsrepo = open(os.path.join(cvs, "Repository"), 'rb').read()[:-1]
51 self.encoding = encoding.encoding
51 self.encoding = encoding.encoding
52
52
53 self._connect()
53 self._connect()
54
54
55 def _parse(self):
55 def _parse(self):
56 if self.changeset is not None:
56 if self.changeset is not None:
57 return
57 return
58 self.changeset = {}
58 self.changeset = {}
59
59
60 maxrev = 0
60 maxrev = 0
61 if self.revs:
61 if self.revs:
62 if len(self.revs) > 1:
62 if len(self.revs) > 1:
63 raise error.Abort(_('cvs source does not support specifying '
63 raise error.Abort(_('cvs source does not support specifying '
64 'multiple revs'))
64 'multiple revs'))
65 # TODO: handle tags
65 # TODO: handle tags
66 try:
66 try:
67 # patchset number?
67 # patchset number?
68 maxrev = int(self.revs[0])
68 maxrev = int(self.revs[0])
69 except ValueError:
69 except ValueError:
70 raise error.Abort(_('revision %s is not a patchset number')
70 raise error.Abort(_('revision %s is not a patchset number')
71 % self.revs[0])
71 % self.revs[0])
72
72
73 d = pycompat.getcwd()
73 d = pycompat.getcwd()
74 try:
74 try:
75 os.chdir(self.path)
75 os.chdir(self.path)
76 id = None
76 id = None
77
77
78 cache = 'update'
78 cache = 'update'
79 if not self.ui.configbool('convert', 'cvsps.cache'):
79 if not self.ui.configbool('convert', 'cvsps.cache'):
80 cache = None
80 cache = None
81 db = cvsps.createlog(self.ui, cache=cache)
81 db = cvsps.createlog(self.ui, cache=cache)
82 db = cvsps.createchangeset(self.ui, db,
82 db = cvsps.createchangeset(self.ui, db,
83 fuzz=int(self.ui.config('convert', 'cvsps.fuzz')),
83 fuzz=int(self.ui.config('convert', 'cvsps.fuzz')),
84 mergeto=self.ui.config('convert', 'cvsps.mergeto'),
84 mergeto=self.ui.config('convert', 'cvsps.mergeto'),
85 mergefrom=self.ui.config('convert', 'cvsps.mergefrom'))
85 mergefrom=self.ui.config('convert', 'cvsps.mergefrom'))
86
86
87 for cs in db:
87 for cs in db:
88 if maxrev and cs.id > maxrev:
88 if maxrev and cs.id > maxrev:
89 break
89 break
90 id = str(cs.id)
90 id = str(cs.id)
91 cs.author = self.recode(cs.author)
91 cs.author = self.recode(cs.author)
92 self.lastbranch[cs.branch] = id
92 self.lastbranch[cs.branch] = id
93 cs.comment = self.recode(cs.comment)
93 cs.comment = self.recode(cs.comment)
94 if self.ui.configbool('convert', 'localtimezone'):
94 if self.ui.configbool('convert', 'localtimezone'):
95 cs.date = makedatetimestamp(cs.date[0])
95 cs.date = makedatetimestamp(cs.date[0])
96 date = util.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2')
96 date = util.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2')
97 self.tags.update(dict.fromkeys(cs.tags, id))
97 self.tags.update(dict.fromkeys(cs.tags, id))
98
98
99 files = {}
99 files = {}
100 for f in cs.entries:
100 for f in cs.entries:
101 files[f.file] = "%s%s" % ('.'.join([str(x)
101 files[f.file] = "%s%s" % ('.'.join([str(x)
102 for x in f.revision]),
102 for x in f.revision]),
103 ['', '(DEAD)'][f.dead])
103 ['', '(DEAD)'][f.dead])
104
104
105 # add current commit to set
105 # add current commit to set
106 c = commit(author=cs.author, date=date,
106 c = commit(author=cs.author, date=date,
107 parents=[str(p.id) for p in cs.parents],
107 parents=[str(p.id) for p in cs.parents],
108 desc=cs.comment, branch=cs.branch or '')
108 desc=cs.comment, branch=cs.branch or '')
109 self.changeset[id] = c
109 self.changeset[id] = c
110 self.files[id] = files
110 self.files[id] = files
111
111
112 self.heads = self.lastbranch.values()
112 self.heads = self.lastbranch.values()
113 finally:
113 finally:
114 os.chdir(d)
114 os.chdir(d)
115
115
116 def _connect(self):
116 def _connect(self):
117 root = self.cvsroot
117 root = self.cvsroot
118 conntype = None
118 conntype = None
119 user, host = None, None
119 user, host = None, None
120 cmd = ['cvs', 'server']
120 cmd = ['cvs', 'server']
121
121
122 self.ui.status(_("connecting to %s\n") % root)
122 self.ui.status(_("connecting to %s\n") % root)
123
123
124 if root.startswith(":pserver:"):
124 if root.startswith(":pserver:"):
125 root = root[9:]
125 root = root[9:]
126 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)',
126 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)',
127 root)
127 root)
128 if m:
128 if m:
129 conntype = "pserver"
129 conntype = "pserver"
130 user, passw, serv, port, root = m.groups()
130 user, passw, serv, port, root = m.groups()
131 if not user:
131 if not user:
132 user = "anonymous"
132 user = "anonymous"
133 if not port:
133 if not port:
134 port = 2401
134 port = 2401
135 else:
135 else:
136 port = int(port)
136 port = int(port)
137 format0 = ":pserver:%s@%s:%s" % (user, serv, root)
137 format0 = ":pserver:%s@%s:%s" % (user, serv, root)
138 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
138 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
139
139
140 if not passw:
140 if not passw:
141 passw = "A"
141 passw = "A"
142 cvspass = os.path.expanduser("~/.cvspass")
142 cvspass = os.path.expanduser("~/.cvspass")
143 try:
143 try:
144 pf = open(cvspass, 'rb')
144 pf = open(cvspass, 'rb')
145 for line in pf.read().splitlines():
145 for line in pf.read().splitlines():
146 part1, part2 = line.split(' ', 1)
146 part1, part2 = line.split(' ', 1)
147 # /1 :pserver:user@example.com:2401/cvsroot/foo
147 # /1 :pserver:user@example.com:2401/cvsroot/foo
148 # Ah<Z
148 # Ah<Z
149 if part1 == '/1':
149 if part1 == '/1':
150 part1, part2 = part2.split(' ', 1)
150 part1, part2 = part2.split(' ', 1)
151 format = format1
151 format = format1
152 # :pserver:user@example.com:/cvsroot/foo Ah<Z
152 # :pserver:user@example.com:/cvsroot/foo Ah<Z
153 else:
153 else:
154 format = format0
154 format = format0
155 if part1 == format:
155 if part1 == format:
156 passw = part2
156 passw = part2
157 break
157 break
158 pf.close()
158 pf.close()
159 except IOError as inst:
159 except IOError as inst:
160 if inst.errno != errno.ENOENT:
160 if inst.errno != errno.ENOENT:
161 if not getattr(inst, 'filename', None):
161 if not getattr(inst, 'filename', None):
162 inst.filename = cvspass
162 inst.filename = cvspass
163 raise
163 raise
164
164
165 sck = socket.socket()
165 sck = socket.socket()
166 sck.connect((serv, port))
166 sck.connect((serv, port))
167 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw,
167 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw,
168 "END AUTH REQUEST", ""]))
168 "END AUTH REQUEST", ""]))
169 if sck.recv(128) != "I LOVE YOU\n":
169 if sck.recv(128) != "I LOVE YOU\n":
170 raise error.Abort(_("CVS pserver authentication failed"))
170 raise error.Abort(_("CVS pserver authentication failed"))
171
171
172 self.writep = self.readp = sck.makefile('r+')
172 self.writep = self.readp = sck.makefile('r+')
173
173
174 if not conntype and root.startswith(":local:"):
174 if not conntype and root.startswith(":local:"):
175 conntype = "local"
175 conntype = "local"
176 root = root[7:]
176 root = root[7:]
177
177
178 if not conntype:
178 if not conntype:
179 # :ext:user@host/home/user/path/to/cvsroot
179 # :ext:user@host/home/user/path/to/cvsroot
180 if root.startswith(":ext:"):
180 if root.startswith(":ext:"):
181 root = root[5:]
181 root = root[5:]
182 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
182 m = re.match(br'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
183 # Do not take Windows path "c:\foo\bar" for a connection strings
183 # Do not take Windows path "c:\foo\bar" for a connection strings
184 if os.path.isdir(root) or not m:
184 if os.path.isdir(root) or not m:
185 conntype = "local"
185 conntype = "local"
186 else:
186 else:
187 conntype = "rsh"
187 conntype = "rsh"
188 user, host, root = m.group(1), m.group(2), m.group(3)
188 user, host, root = m.group(1), m.group(2), m.group(3)
189
189
190 if conntype != "pserver":
190 if conntype != "pserver":
191 if conntype == "rsh":
191 if conntype == "rsh":
192 rsh = encoding.environ.get("CVS_RSH") or "ssh"
192 rsh = encoding.environ.get("CVS_RSH") or "ssh"
193 if user:
193 if user:
194 cmd = [rsh, '-l', user, host] + cmd
194 cmd = [rsh, '-l', user, host] + cmd
195 else:
195 else:
196 cmd = [rsh, host] + cmd
196 cmd = [rsh, host] + cmd
197
197
198 # popen2 does not support argument lists under Windows
198 # popen2 does not support argument lists under Windows
199 cmd = [util.shellquote(arg) for arg in cmd]
199 cmd = [util.shellquote(arg) for arg in cmd]
200 cmd = util.quotecommand(' '.join(cmd))
200 cmd = util.quotecommand(' '.join(cmd))
201 self.writep, self.readp = util.popen2(cmd)
201 self.writep, self.readp = util.popen2(cmd)
202
202
203 self.realroot = root
203 self.realroot = root
204
204
205 self.writep.write("Root %s\n" % root)
205 self.writep.write("Root %s\n" % root)
206 self.writep.write("Valid-responses ok error Valid-requests Mode"
206 self.writep.write("Valid-responses ok error Valid-requests Mode"
207 " M Mbinary E Checked-in Created Updated"
207 " M Mbinary E Checked-in Created Updated"
208 " Merged Removed\n")
208 " Merged Removed\n")
209 self.writep.write("valid-requests\n")
209 self.writep.write("valid-requests\n")
210 self.writep.flush()
210 self.writep.flush()
211 r = self.readp.readline()
211 r = self.readp.readline()
212 if not r.startswith("Valid-requests"):
212 if not r.startswith("Valid-requests"):
213 raise error.Abort(_('unexpected response from CVS server '
213 raise error.Abort(_('unexpected response from CVS server '
214 '(expected "Valid-requests", but got %r)')
214 '(expected "Valid-requests", but got %r)')
215 % r)
215 % r)
216 if "UseUnchanged" in r:
216 if "UseUnchanged" in r:
217 self.writep.write("UseUnchanged\n")
217 self.writep.write("UseUnchanged\n")
218 self.writep.flush()
218 self.writep.flush()
219 r = self.readp.readline()
219 r = self.readp.readline()
220
220
221 def getheads(self):
221 def getheads(self):
222 self._parse()
222 self._parse()
223 return self.heads
223 return self.heads
224
224
225 def getfile(self, name, rev):
225 def getfile(self, name, rev):
226
226
227 def chunkedread(fp, count):
227 def chunkedread(fp, count):
228 # file-objects returned by socket.makefile() do not handle
228 # file-objects returned by socket.makefile() do not handle
229 # large read() requests very well.
229 # large read() requests very well.
230 chunksize = 65536
230 chunksize = 65536
231 output = stringio()
231 output = stringio()
232 while count > 0:
232 while count > 0:
233 data = fp.read(min(count, chunksize))
233 data = fp.read(min(count, chunksize))
234 if not data:
234 if not data:
235 raise error.Abort(_("%d bytes missing from remote file")
235 raise error.Abort(_("%d bytes missing from remote file")
236 % count)
236 % count)
237 count -= len(data)
237 count -= len(data)
238 output.write(data)
238 output.write(data)
239 return output.getvalue()
239 return output.getvalue()
240
240
241 self._parse()
241 self._parse()
242 if rev.endswith("(DEAD)"):
242 if rev.endswith("(DEAD)"):
243 return None, None
243 return None, None
244
244
245 args = ("-N -P -kk -r %s --" % rev).split()
245 args = ("-N -P -kk -r %s --" % rev).split()
246 args.append(self.cvsrepo + '/' + name)
246 args.append(self.cvsrepo + '/' + name)
247 for x in args:
247 for x in args:
248 self.writep.write("Argument %s\n" % x)
248 self.writep.write("Argument %s\n" % x)
249 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
249 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
250 self.writep.flush()
250 self.writep.flush()
251
251
252 data = ""
252 data = ""
253 mode = None
253 mode = None
254 while True:
254 while True:
255 line = self.readp.readline()
255 line = self.readp.readline()
256 if line.startswith("Created ") or line.startswith("Updated "):
256 if line.startswith("Created ") or line.startswith("Updated "):
257 self.readp.readline() # path
257 self.readp.readline() # path
258 self.readp.readline() # entries
258 self.readp.readline() # entries
259 mode = self.readp.readline()[:-1]
259 mode = self.readp.readline()[:-1]
260 count = int(self.readp.readline()[:-1])
260 count = int(self.readp.readline()[:-1])
261 data = chunkedread(self.readp, count)
261 data = chunkedread(self.readp, count)
262 elif line.startswith(" "):
262 elif line.startswith(" "):
263 data += line[1:]
263 data += line[1:]
264 elif line.startswith("M "):
264 elif line.startswith("M "):
265 pass
265 pass
266 elif line.startswith("Mbinary "):
266 elif line.startswith("Mbinary "):
267 count = int(self.readp.readline()[:-1])
267 count = int(self.readp.readline()[:-1])
268 data = chunkedread(self.readp, count)
268 data = chunkedread(self.readp, count)
269 else:
269 else:
270 if line == "ok\n":
270 if line == "ok\n":
271 if mode is None:
271 if mode is None:
272 raise error.Abort(_('malformed response from CVS'))
272 raise error.Abort(_('malformed response from CVS'))
273 return (data, "x" in mode and "x" or "")
273 return (data, "x" in mode and "x" or "")
274 elif line.startswith("E "):
274 elif line.startswith("E "):
275 self.ui.warn(_("cvs server: %s\n") % line[2:])
275 self.ui.warn(_("cvs server: %s\n") % line[2:])
276 elif line.startswith("Remove"):
276 elif line.startswith("Remove"):
277 self.readp.readline()
277 self.readp.readline()
278 else:
278 else:
279 raise error.Abort(_("unknown CVS response: %s") % line)
279 raise error.Abort(_("unknown CVS response: %s") % line)
280
280
281 def getchanges(self, rev, full):
281 def getchanges(self, rev, full):
282 if full:
282 if full:
283 raise error.Abort(_("convert from cvs does not support --full"))
283 raise error.Abort(_("convert from cvs does not support --full"))
284 self._parse()
284 self._parse()
285 return sorted(self.files[rev].iteritems()), {}, set()
285 return sorted(self.files[rev].iteritems()), {}, set()
286
286
287 def getcommit(self, rev):
287 def getcommit(self, rev):
288 self._parse()
288 self._parse()
289 return self.changeset[rev]
289 return self.changeset[rev]
290
290
291 def gettags(self):
291 def gettags(self):
292 self._parse()
292 self._parse()
293 return self.tags
293 return self.tags
294
294
295 def getchangedfiles(self, rev, i):
295 def getchangedfiles(self, rev, i):
296 self._parse()
296 self._parse()
297 return sorted(self.files[rev])
297 return sorted(self.files[rev])
@@ -1,373 +1,373 b''
1 # monotone.py - monotone support for the convert extension
1 # monotone.py - monotone support for the convert extension
2 #
2 #
3 # Copyright 2008, 2009 Mikkel Fahnoe Jorgensen <mikkel@dvide.com> and
3 # Copyright 2008, 2009 Mikkel Fahnoe Jorgensen <mikkel@dvide.com> and
4 # others
4 # others
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11 import re
11 import re
12
12
13 from mercurial.i18n import _
13 from mercurial.i18n import _
14 from mercurial import (
14 from mercurial import (
15 error,
15 error,
16 pycompat,
16 pycompat,
17 util,
17 util,
18 )
18 )
19
19
20 from . import common
20 from . import common
21
21
22 class monotone_source(common.converter_source, common.commandline):
22 class monotone_source(common.converter_source, common.commandline):
23 def __init__(self, ui, repotype, path=None, revs=None):
23 def __init__(self, ui, repotype, path=None, revs=None):
24 common.converter_source.__init__(self, ui, repotype, path, revs)
24 common.converter_source.__init__(self, ui, repotype, path, revs)
25 if revs and len(revs) > 1:
25 if revs and len(revs) > 1:
26 raise error.Abort(_('monotone source does not support specifying '
26 raise error.Abort(_('monotone source does not support specifying '
27 'multiple revs'))
27 'multiple revs'))
28 common.commandline.__init__(self, ui, 'mtn')
28 common.commandline.__init__(self, ui, 'mtn')
29
29
30 self.ui = ui
30 self.ui = ui
31 self.path = path
31 self.path = path
32 self.automatestdio = False
32 self.automatestdio = False
33 self.revs = revs
33 self.revs = revs
34
34
35 norepo = common.NoRepo(_("%s does not look like a monotone repository")
35 norepo = common.NoRepo(_("%s does not look like a monotone repository")
36 % path)
36 % path)
37 if not os.path.exists(os.path.join(path, '_MTN')):
37 if not os.path.exists(os.path.join(path, '_MTN')):
38 # Could be a monotone repository (SQLite db file)
38 # Could be a monotone repository (SQLite db file)
39 try:
39 try:
40 f = file(path, 'rb')
40 f = file(path, 'rb')
41 header = f.read(16)
41 header = f.read(16)
42 f.close()
42 f.close()
43 except IOError:
43 except IOError:
44 header = ''
44 header = ''
45 if header != 'SQLite format 3\x00':
45 if header != 'SQLite format 3\x00':
46 raise norepo
46 raise norepo
47
47
48 # regular expressions for parsing monotone output
48 # regular expressions for parsing monotone output
49 space = r'\s*'
49 space = br'\s*'
50 name = r'\s+"((?:\\"|[^"])*)"\s*'
50 name = br'\s+"((?:\\"|[^"])*)"\s*'
51 value = name
51 value = name
52 revision = r'\s+\[(\w+)\]\s*'
52 revision = br'\s+\[(\w+)\]\s*'
53 lines = r'(?:.|\n)+'
53 lines = br'(?:.|\n)+'
54
54
55 self.dir_re = re.compile(space + "dir" + name)
55 self.dir_re = re.compile(space + "dir" + name)
56 self.file_re = re.compile(space + "file" + name +
56 self.file_re = re.compile(space + "file" + name +
57 "content" + revision)
57 "content" + revision)
58 self.add_file_re = re.compile(space + "add_file" + name +
58 self.add_file_re = re.compile(space + "add_file" + name +
59 "content" + revision)
59 "content" + revision)
60 self.patch_re = re.compile(space + "patch" + name +
60 self.patch_re = re.compile(space + "patch" + name +
61 "from" + revision + "to" + revision)
61 "from" + revision + "to" + revision)
62 self.rename_re = re.compile(space + "rename" + name + "to" + name)
62 self.rename_re = re.compile(space + "rename" + name + "to" + name)
63 self.delete_re = re.compile(space + "delete" + name)
63 self.delete_re = re.compile(space + "delete" + name)
64 self.tag_re = re.compile(space + "tag" + name + "revision" +
64 self.tag_re = re.compile(space + "tag" + name + "revision" +
65 revision)
65 revision)
66 self.cert_re = re.compile(lines + space + "name" + name +
66 self.cert_re = re.compile(lines + space + "name" + name +
67 "value" + value)
67 "value" + value)
68
68
69 attr = space + "file" + lines + space + "attr" + space
69 attr = space + "file" + lines + space + "attr" + space
70 self.attr_execute_re = re.compile(attr + '"mtn:execute"' +
70 self.attr_execute_re = re.compile(attr + '"mtn:execute"' +
71 space + '"true"')
71 space + '"true"')
72
72
73 # cached data
73 # cached data
74 self.manifest_rev = None
74 self.manifest_rev = None
75 self.manifest = None
75 self.manifest = None
76 self.files = None
76 self.files = None
77 self.dirs = None
77 self.dirs = None
78
78
79 common.checktool('mtn', abort=False)
79 common.checktool('mtn', abort=False)
80
80
81 def mtnrun(self, *args, **kwargs):
81 def mtnrun(self, *args, **kwargs):
82 if self.automatestdio:
82 if self.automatestdio:
83 return self.mtnrunstdio(*args, **kwargs)
83 return self.mtnrunstdio(*args, **kwargs)
84 else:
84 else:
85 return self.mtnrunsingle(*args, **kwargs)
85 return self.mtnrunsingle(*args, **kwargs)
86
86
87 def mtnrunsingle(self, *args, **kwargs):
87 def mtnrunsingle(self, *args, **kwargs):
88 kwargs[r'd'] = self.path
88 kwargs[r'd'] = self.path
89 return self.run0('automate', *args, **kwargs)
89 return self.run0('automate', *args, **kwargs)
90
90
91 def mtnrunstdio(self, *args, **kwargs):
91 def mtnrunstdio(self, *args, **kwargs):
92 # Prepare the command in automate stdio format
92 # Prepare the command in automate stdio format
93 kwargs = pycompat.byteskwargs(kwargs)
93 kwargs = pycompat.byteskwargs(kwargs)
94 command = []
94 command = []
95 for k, v in kwargs.iteritems():
95 for k, v in kwargs.iteritems():
96 command.append("%s:%s" % (len(k), k))
96 command.append("%s:%s" % (len(k), k))
97 if v:
97 if v:
98 command.append("%s:%s" % (len(v), v))
98 command.append("%s:%s" % (len(v), v))
99 if command:
99 if command:
100 command.insert(0, 'o')
100 command.insert(0, 'o')
101 command.append('e')
101 command.append('e')
102
102
103 command.append('l')
103 command.append('l')
104 for arg in args:
104 for arg in args:
105 command += "%s:%s" % (len(arg), arg)
105 command += "%s:%s" % (len(arg), arg)
106 command.append('e')
106 command.append('e')
107 command = ''.join(command)
107 command = ''.join(command)
108
108
109 self.ui.debug("mtn: sending '%s'\n" % command)
109 self.ui.debug("mtn: sending '%s'\n" % command)
110 self.mtnwritefp.write(command)
110 self.mtnwritefp.write(command)
111 self.mtnwritefp.flush()
111 self.mtnwritefp.flush()
112
112
113 return self.mtnstdioreadcommandoutput(command)
113 return self.mtnstdioreadcommandoutput(command)
114
114
115 def mtnstdioreadpacket(self):
115 def mtnstdioreadpacket(self):
116 read = None
116 read = None
117 commandnbr = ''
117 commandnbr = ''
118 while read != ':':
118 while read != ':':
119 read = self.mtnreadfp.read(1)
119 read = self.mtnreadfp.read(1)
120 if not read:
120 if not read:
121 raise error.Abort(_('bad mtn packet - no end of commandnbr'))
121 raise error.Abort(_('bad mtn packet - no end of commandnbr'))
122 commandnbr += read
122 commandnbr += read
123 commandnbr = commandnbr[:-1]
123 commandnbr = commandnbr[:-1]
124
124
125 stream = self.mtnreadfp.read(1)
125 stream = self.mtnreadfp.read(1)
126 if stream not in 'mewptl':
126 if stream not in 'mewptl':
127 raise error.Abort(_('bad mtn packet - bad stream type %s') % stream)
127 raise error.Abort(_('bad mtn packet - bad stream type %s') % stream)
128
128
129 read = self.mtnreadfp.read(1)
129 read = self.mtnreadfp.read(1)
130 if read != ':':
130 if read != ':':
131 raise error.Abort(_('bad mtn packet - no divider before size'))
131 raise error.Abort(_('bad mtn packet - no divider before size'))
132
132
133 read = None
133 read = None
134 lengthstr = ''
134 lengthstr = ''
135 while read != ':':
135 while read != ':':
136 read = self.mtnreadfp.read(1)
136 read = self.mtnreadfp.read(1)
137 if not read:
137 if not read:
138 raise error.Abort(_('bad mtn packet - no end of packet size'))
138 raise error.Abort(_('bad mtn packet - no end of packet size'))
139 lengthstr += read
139 lengthstr += read
140 try:
140 try:
141 length = long(lengthstr[:-1])
141 length = long(lengthstr[:-1])
142 except TypeError:
142 except TypeError:
143 raise error.Abort(_('bad mtn packet - bad packet size %s')
143 raise error.Abort(_('bad mtn packet - bad packet size %s')
144 % lengthstr)
144 % lengthstr)
145
145
146 read = self.mtnreadfp.read(length)
146 read = self.mtnreadfp.read(length)
147 if len(read) != length:
147 if len(read) != length:
148 raise error.Abort(_("bad mtn packet - unable to read full packet "
148 raise error.Abort(_("bad mtn packet - unable to read full packet "
149 "read %s of %s") % (len(read), length))
149 "read %s of %s") % (len(read), length))
150
150
151 return (commandnbr, stream, length, read)
151 return (commandnbr, stream, length, read)
152
152
153 def mtnstdioreadcommandoutput(self, command):
153 def mtnstdioreadcommandoutput(self, command):
154 retval = []
154 retval = []
155 while True:
155 while True:
156 commandnbr, stream, length, output = self.mtnstdioreadpacket()
156 commandnbr, stream, length, output = self.mtnstdioreadpacket()
157 self.ui.debug('mtn: read packet %s:%s:%s\n' %
157 self.ui.debug('mtn: read packet %s:%s:%s\n' %
158 (commandnbr, stream, length))
158 (commandnbr, stream, length))
159
159
160 if stream == 'l':
160 if stream == 'l':
161 # End of command
161 # End of command
162 if output != '0':
162 if output != '0':
163 raise error.Abort(_("mtn command '%s' returned %s") %
163 raise error.Abort(_("mtn command '%s' returned %s") %
164 (command, output))
164 (command, output))
165 break
165 break
166 elif stream in 'ew':
166 elif stream in 'ew':
167 # Error, warning output
167 # Error, warning output
168 self.ui.warn(_('%s error:\n') % self.command)
168 self.ui.warn(_('%s error:\n') % self.command)
169 self.ui.warn(output)
169 self.ui.warn(output)
170 elif stream == 'p':
170 elif stream == 'p':
171 # Progress messages
171 # Progress messages
172 self.ui.debug('mtn: ' + output)
172 self.ui.debug('mtn: ' + output)
173 elif stream == 'm':
173 elif stream == 'm':
174 # Main stream - command output
174 # Main stream - command output
175 retval.append(output)
175 retval.append(output)
176
176
177 return ''.join(retval)
177 return ''.join(retval)
178
178
179 def mtnloadmanifest(self, rev):
179 def mtnloadmanifest(self, rev):
180 if self.manifest_rev == rev:
180 if self.manifest_rev == rev:
181 return
181 return
182 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
182 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
183 self.manifest_rev = rev
183 self.manifest_rev = rev
184 self.files = {}
184 self.files = {}
185 self.dirs = {}
185 self.dirs = {}
186
186
187 for e in self.manifest:
187 for e in self.manifest:
188 m = self.file_re.match(e)
188 m = self.file_re.match(e)
189 if m:
189 if m:
190 attr = ""
190 attr = ""
191 name = m.group(1)
191 name = m.group(1)
192 node = m.group(2)
192 node = m.group(2)
193 if self.attr_execute_re.match(e):
193 if self.attr_execute_re.match(e):
194 attr += "x"
194 attr += "x"
195 self.files[name] = (node, attr)
195 self.files[name] = (node, attr)
196 m = self.dir_re.match(e)
196 m = self.dir_re.match(e)
197 if m:
197 if m:
198 self.dirs[m.group(1)] = True
198 self.dirs[m.group(1)] = True
199
199
200 def mtnisfile(self, name, rev):
200 def mtnisfile(self, name, rev):
201 # a non-file could be a directory or a deleted or renamed file
201 # a non-file could be a directory or a deleted or renamed file
202 self.mtnloadmanifest(rev)
202 self.mtnloadmanifest(rev)
203 return name in self.files
203 return name in self.files
204
204
205 def mtnisdir(self, name, rev):
205 def mtnisdir(self, name, rev):
206 self.mtnloadmanifest(rev)
206 self.mtnloadmanifest(rev)
207 return name in self.dirs
207 return name in self.dirs
208
208
209 def mtngetcerts(self, rev):
209 def mtngetcerts(self, rev):
210 certs = {"author":"<missing>", "date":"<missing>",
210 certs = {"author":"<missing>", "date":"<missing>",
211 "changelog":"<missing>", "branch":"<missing>"}
211 "changelog":"<missing>", "branch":"<missing>"}
212 certlist = self.mtnrun("certs", rev)
212 certlist = self.mtnrun("certs", rev)
213 # mtn < 0.45:
213 # mtn < 0.45:
214 # key "test@selenic.com"
214 # key "test@selenic.com"
215 # mtn >= 0.45:
215 # mtn >= 0.45:
216 # key [ff58a7ffb771907c4ff68995eada1c4da068d328]
216 # key [ff58a7ffb771907c4ff68995eada1c4da068d328]
217 certlist = re.split('\n\n key ["\[]', certlist)
217 certlist = re.split('\n\n key ["\[]', certlist)
218 for e in certlist:
218 for e in certlist:
219 m = self.cert_re.match(e)
219 m = self.cert_re.match(e)
220 if m:
220 if m:
221 name, value = m.groups()
221 name, value = m.groups()
222 value = value.replace(r'\"', '"')
222 value = value.replace(r'\"', '"')
223 value = value.replace(r'\\', '\\')
223 value = value.replace(r'\\', '\\')
224 certs[name] = value
224 certs[name] = value
225 # Monotone may have subsecond dates: 2005-02-05T09:39:12.364306
225 # Monotone may have subsecond dates: 2005-02-05T09:39:12.364306
226 # and all times are stored in UTC
226 # and all times are stored in UTC
227 certs["date"] = certs["date"].split('.')[0] + " UTC"
227 certs["date"] = certs["date"].split('.')[0] + " UTC"
228 return certs
228 return certs
229
229
230 # implement the converter_source interface:
230 # implement the converter_source interface:
231
231
232 def getheads(self):
232 def getheads(self):
233 if not self.revs:
233 if not self.revs:
234 return self.mtnrun("leaves").splitlines()
234 return self.mtnrun("leaves").splitlines()
235 else:
235 else:
236 return self.revs
236 return self.revs
237
237
238 def getchanges(self, rev, full):
238 def getchanges(self, rev, full):
239 if full:
239 if full:
240 raise error.Abort(_("convert from monotone does not support "
240 raise error.Abort(_("convert from monotone does not support "
241 "--full"))
241 "--full"))
242 revision = self.mtnrun("get_revision", rev).split("\n\n")
242 revision = self.mtnrun("get_revision", rev).split("\n\n")
243 files = {}
243 files = {}
244 ignoremove = {}
244 ignoremove = {}
245 renameddirs = []
245 renameddirs = []
246 copies = {}
246 copies = {}
247 for e in revision:
247 for e in revision:
248 m = self.add_file_re.match(e)
248 m = self.add_file_re.match(e)
249 if m:
249 if m:
250 files[m.group(1)] = rev
250 files[m.group(1)] = rev
251 ignoremove[m.group(1)] = rev
251 ignoremove[m.group(1)] = rev
252 m = self.patch_re.match(e)
252 m = self.patch_re.match(e)
253 if m:
253 if m:
254 files[m.group(1)] = rev
254 files[m.group(1)] = rev
255 # Delete/rename is handled later when the convert engine
255 # Delete/rename is handled later when the convert engine
256 # discovers an IOError exception from getfile,
256 # discovers an IOError exception from getfile,
257 # but only if we add the "from" file to the list of changes.
257 # but only if we add the "from" file to the list of changes.
258 m = self.delete_re.match(e)
258 m = self.delete_re.match(e)
259 if m:
259 if m:
260 files[m.group(1)] = rev
260 files[m.group(1)] = rev
261 m = self.rename_re.match(e)
261 m = self.rename_re.match(e)
262 if m:
262 if m:
263 toname = m.group(2)
263 toname = m.group(2)
264 fromname = m.group(1)
264 fromname = m.group(1)
265 if self.mtnisfile(toname, rev):
265 if self.mtnisfile(toname, rev):
266 ignoremove[toname] = 1
266 ignoremove[toname] = 1
267 copies[toname] = fromname
267 copies[toname] = fromname
268 files[toname] = rev
268 files[toname] = rev
269 files[fromname] = rev
269 files[fromname] = rev
270 elif self.mtnisdir(toname, rev):
270 elif self.mtnisdir(toname, rev):
271 renameddirs.append((fromname, toname))
271 renameddirs.append((fromname, toname))
272
272
273 # Directory renames can be handled only once we have recorded
273 # Directory renames can be handled only once we have recorded
274 # all new files
274 # all new files
275 for fromdir, todir in renameddirs:
275 for fromdir, todir in renameddirs:
276 renamed = {}
276 renamed = {}
277 for tofile in self.files:
277 for tofile in self.files:
278 if tofile in ignoremove:
278 if tofile in ignoremove:
279 continue
279 continue
280 if tofile.startswith(todir + '/'):
280 if tofile.startswith(todir + '/'):
281 renamed[tofile] = fromdir + tofile[len(todir):]
281 renamed[tofile] = fromdir + tofile[len(todir):]
282 # Avoid chained moves like:
282 # Avoid chained moves like:
283 # d1(/a) => d3/d1(/a)
283 # d1(/a) => d3/d1(/a)
284 # d2 => d3
284 # d2 => d3
285 ignoremove[tofile] = 1
285 ignoremove[tofile] = 1
286 for tofile, fromfile in renamed.items():
286 for tofile, fromfile in renamed.items():
287 self.ui.debug (_("copying file in renamed directory "
287 self.ui.debug (_("copying file in renamed directory "
288 "from '%s' to '%s'")
288 "from '%s' to '%s'")
289 % (fromfile, tofile), '\n')
289 % (fromfile, tofile), '\n')
290 files[tofile] = rev
290 files[tofile] = rev
291 copies[tofile] = fromfile
291 copies[tofile] = fromfile
292 for fromfile in renamed.values():
292 for fromfile in renamed.values():
293 files[fromfile] = rev
293 files[fromfile] = rev
294
294
295 return (files.items(), copies, set())
295 return (files.items(), copies, set())
296
296
297 def getfile(self, name, rev):
297 def getfile(self, name, rev):
298 if not self.mtnisfile(name, rev):
298 if not self.mtnisfile(name, rev):
299 return None, None
299 return None, None
300 try:
300 try:
301 data = self.mtnrun("get_file_of", name, r=rev)
301 data = self.mtnrun("get_file_of", name, r=rev)
302 except Exception:
302 except Exception:
303 return None, None
303 return None, None
304 self.mtnloadmanifest(rev)
304 self.mtnloadmanifest(rev)
305 node, attr = self.files.get(name, (None, ""))
305 node, attr = self.files.get(name, (None, ""))
306 return data, attr
306 return data, attr
307
307
308 def getcommit(self, rev):
308 def getcommit(self, rev):
309 extra = {}
309 extra = {}
310 certs = self.mtngetcerts(rev)
310 certs = self.mtngetcerts(rev)
311 if certs.get('suspend') == certs["branch"]:
311 if certs.get('suspend') == certs["branch"]:
312 extra['close'] = 1
312 extra['close'] = 1
313 return common.commit(
313 return common.commit(
314 author=certs["author"],
314 author=certs["author"],
315 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
315 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
316 desc=certs["changelog"],
316 desc=certs["changelog"],
317 rev=rev,
317 rev=rev,
318 parents=self.mtnrun("parents", rev).splitlines(),
318 parents=self.mtnrun("parents", rev).splitlines(),
319 branch=certs["branch"],
319 branch=certs["branch"],
320 extra=extra)
320 extra=extra)
321
321
322 def gettags(self):
322 def gettags(self):
323 tags = {}
323 tags = {}
324 for e in self.mtnrun("tags").split("\n\n"):
324 for e in self.mtnrun("tags").split("\n\n"):
325 m = self.tag_re.match(e)
325 m = self.tag_re.match(e)
326 if m:
326 if m:
327 tags[m.group(1)] = m.group(2)
327 tags[m.group(1)] = m.group(2)
328 return tags
328 return tags
329
329
330 def getchangedfiles(self, rev, i):
330 def getchangedfiles(self, rev, i):
331 # This function is only needed to support --filemap
331 # This function is only needed to support --filemap
332 # ... and we don't support that
332 # ... and we don't support that
333 raise NotImplementedError
333 raise NotImplementedError
334
334
335 def before(self):
335 def before(self):
336 # Check if we have a new enough version to use automate stdio
336 # Check if we have a new enough version to use automate stdio
337 version = 0.0
337 version = 0.0
338 try:
338 try:
339 versionstr = self.mtnrunsingle("interface_version")
339 versionstr = self.mtnrunsingle("interface_version")
340 version = float(versionstr)
340 version = float(versionstr)
341 except Exception:
341 except Exception:
342 raise error.Abort(_("unable to determine mtn automate interface "
342 raise error.Abort(_("unable to determine mtn automate interface "
343 "version"))
343 "version"))
344
344
345 if version >= 12.0:
345 if version >= 12.0:
346 self.automatestdio = True
346 self.automatestdio = True
347 self.ui.debug("mtn automate version %s - using automate stdio\n" %
347 self.ui.debug("mtn automate version %s - using automate stdio\n" %
348 version)
348 version)
349
349
350 # launch the long-running automate stdio process
350 # launch the long-running automate stdio process
351 self.mtnwritefp, self.mtnreadfp = self._run2('automate', 'stdio',
351 self.mtnwritefp, self.mtnreadfp = self._run2('automate', 'stdio',
352 '-d', self.path)
352 '-d', self.path)
353 # read the headers
353 # read the headers
354 read = self.mtnreadfp.readline()
354 read = self.mtnreadfp.readline()
355 if read != 'format-version: 2\n':
355 if read != 'format-version: 2\n':
356 raise error.Abort(_('mtn automate stdio header unexpected: %s')
356 raise error.Abort(_('mtn automate stdio header unexpected: %s')
357 % read)
357 % read)
358 while read != '\n':
358 while read != '\n':
359 read = self.mtnreadfp.readline()
359 read = self.mtnreadfp.readline()
360 if not read:
360 if not read:
361 raise error.Abort(_("failed to reach end of mtn automate "
361 raise error.Abort(_("failed to reach end of mtn automate "
362 "stdio headers"))
362 "stdio headers"))
363 else:
363 else:
364 self.ui.debug("mtn automate version %s - not using automate stdio "
364 self.ui.debug("mtn automate version %s - not using automate stdio "
365 "(automate >= 12.0 - mtn >= 0.46 is needed)\n" % version)
365 "(automate >= 12.0 - mtn >= 0.46 is needed)\n" % version)
366
366
367 def after(self):
367 def after(self):
368 if self.automatestdio:
368 if self.automatestdio:
369 self.mtnwritefp.close()
369 self.mtnwritefp.close()
370 self.mtnwritefp = None
370 self.mtnwritefp = None
371 self.mtnreadfp.close()
371 self.mtnreadfp.close()
372 self.mtnreadfp = None
372 self.mtnreadfp = None
373
373
General Comments 0
You need to be logged in to leave comments. Login now