##// END OF EJS Templates
cmdserver: add service that listens on unix domain socket and forks process...
Yuya Nishihara -
r22994:840be5ca default
parent child Browse files
Show More
@@ -1,268 +1,319 b''
1 # commandserver.py - communicate with Mercurial's API over a pipe
1 # commandserver.py - communicate with Mercurial's API over a pipe
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright Matt Mackall <mpm@selenic.com>
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
7
8 from i18n import _
8 from i18n import _
9 import struct
9 import struct
10 import sys, os
10 import sys, os, errno, traceback, SocketServer
11 import dispatch, encoding, util
11 import dispatch, encoding, util
12
12
13 logfile = None
13 logfile = None
14
14
15 def log(*args):
15 def log(*args):
16 if not logfile:
16 if not logfile:
17 return
17 return
18
18
19 for a in args:
19 for a in args:
20 logfile.write(str(a))
20 logfile.write(str(a))
21
21
22 logfile.flush()
22 logfile.flush()
23
23
24 class channeledoutput(object):
24 class channeledoutput(object):
25 """
25 """
26 Write data to out in the following format:
26 Write data to out in the following format:
27
27
28 data length (unsigned int),
28 data length (unsigned int),
29 data
29 data
30 """
30 """
31 def __init__(self, out, channel):
31 def __init__(self, out, channel):
32 self.out = out
32 self.out = out
33 self.channel = channel
33 self.channel = channel
34
34
35 def write(self, data):
35 def write(self, data):
36 if not data:
36 if not data:
37 return
37 return
38 self.out.write(struct.pack('>cI', self.channel, len(data)))
38 self.out.write(struct.pack('>cI', self.channel, len(data)))
39 self.out.write(data)
39 self.out.write(data)
40 self.out.flush()
40 self.out.flush()
41
41
42 def __getattr__(self, attr):
42 def __getattr__(self, attr):
43 if attr in ('isatty', 'fileno'):
43 if attr in ('isatty', 'fileno'):
44 raise AttributeError(attr)
44 raise AttributeError(attr)
45 return getattr(self.out, attr)
45 return getattr(self.out, attr)
46
46
47 class channeledinput(object):
47 class channeledinput(object):
48 """
48 """
49 Read data from in_.
49 Read data from in_.
50
50
51 Requests for input are written to out in the following format:
51 Requests for input are written to out in the following format:
52 channel identifier - 'I' for plain input, 'L' line based (1 byte)
52 channel identifier - 'I' for plain input, 'L' line based (1 byte)
53 how many bytes to send at most (unsigned int),
53 how many bytes to send at most (unsigned int),
54
54
55 The client replies with:
55 The client replies with:
56 data length (unsigned int), 0 meaning EOF
56 data length (unsigned int), 0 meaning EOF
57 data
57 data
58 """
58 """
59
59
60 maxchunksize = 4 * 1024
60 maxchunksize = 4 * 1024
61
61
62 def __init__(self, in_, out, channel):
62 def __init__(self, in_, out, channel):
63 self.in_ = in_
63 self.in_ = in_
64 self.out = out
64 self.out = out
65 self.channel = channel
65 self.channel = channel
66
66
67 def read(self, size=-1):
67 def read(self, size=-1):
68 if size < 0:
68 if size < 0:
69 # if we need to consume all the clients input, ask for 4k chunks
69 # if we need to consume all the clients input, ask for 4k chunks
70 # so the pipe doesn't fill up risking a deadlock
70 # so the pipe doesn't fill up risking a deadlock
71 size = self.maxchunksize
71 size = self.maxchunksize
72 s = self._read(size, self.channel)
72 s = self._read(size, self.channel)
73 buf = s
73 buf = s
74 while s:
74 while s:
75 s = self._read(size, self.channel)
75 s = self._read(size, self.channel)
76 buf += s
76 buf += s
77
77
78 return buf
78 return buf
79 else:
79 else:
80 return self._read(size, self.channel)
80 return self._read(size, self.channel)
81
81
82 def _read(self, size, channel):
82 def _read(self, size, channel):
83 if not size:
83 if not size:
84 return ''
84 return ''
85 assert size > 0
85 assert size > 0
86
86
87 # tell the client we need at most size bytes
87 # tell the client we need at most size bytes
88 self.out.write(struct.pack('>cI', channel, size))
88 self.out.write(struct.pack('>cI', channel, size))
89 self.out.flush()
89 self.out.flush()
90
90
91 length = self.in_.read(4)
91 length = self.in_.read(4)
92 length = struct.unpack('>I', length)[0]
92 length = struct.unpack('>I', length)[0]
93 if not length:
93 if not length:
94 return ''
94 return ''
95 else:
95 else:
96 return self.in_.read(length)
96 return self.in_.read(length)
97
97
98 def readline(self, size=-1):
98 def readline(self, size=-1):
99 if size < 0:
99 if size < 0:
100 size = self.maxchunksize
100 size = self.maxchunksize
101 s = self._read(size, 'L')
101 s = self._read(size, 'L')
102 buf = s
102 buf = s
103 # keep asking for more until there's either no more or
103 # keep asking for more until there's either no more or
104 # we got a full line
104 # we got a full line
105 while s and s[-1] != '\n':
105 while s and s[-1] != '\n':
106 s = self._read(size, 'L')
106 s = self._read(size, 'L')
107 buf += s
107 buf += s
108
108
109 return buf
109 return buf
110 else:
110 else:
111 return self._read(size, 'L')
111 return self._read(size, 'L')
112
112
113 def __iter__(self):
113 def __iter__(self):
114 return self
114 return self
115
115
116 def next(self):
116 def next(self):
117 l = self.readline()
117 l = self.readline()
118 if not l:
118 if not l:
119 raise StopIteration
119 raise StopIteration
120 return l
120 return l
121
121
122 def __getattr__(self, attr):
122 def __getattr__(self, attr):
123 if attr in ('isatty', 'fileno'):
123 if attr in ('isatty', 'fileno'):
124 raise AttributeError(attr)
124 raise AttributeError(attr)
125 return getattr(self.in_, attr)
125 return getattr(self.in_, attr)
126
126
127 class server(object):
127 class server(object):
128 """
128 """
129 Listens for commands on fin, runs them and writes the output on a channel
129 Listens for commands on fin, runs them and writes the output on a channel
130 based stream to fout.
130 based stream to fout.
131 """
131 """
132 def __init__(self, ui, repo, fin, fout):
132 def __init__(self, ui, repo, fin, fout):
133 self.cwd = os.getcwd()
133 self.cwd = os.getcwd()
134
134
135 logpath = ui.config("cmdserver", "log", None)
135 logpath = ui.config("cmdserver", "log", None)
136 if logpath:
136 if logpath:
137 global logfile
137 global logfile
138 if logpath == '-':
138 if logpath == '-':
139 # write log on a special 'd' (debug) channel
139 # write log on a special 'd' (debug) channel
140 logfile = channeledoutput(fout, 'd')
140 logfile = channeledoutput(fout, 'd')
141 else:
141 else:
142 logfile = open(logpath, 'a')
142 logfile = open(logpath, 'a')
143
143
144 if repo:
144 if repo:
145 # the ui here is really the repo ui so take its baseui so we don't
145 # the ui here is really the repo ui so take its baseui so we don't
146 # end up with its local configuration
146 # end up with its local configuration
147 self.ui = repo.baseui
147 self.ui = repo.baseui
148 self.repo = repo
148 self.repo = repo
149 self.repoui = repo.ui
149 self.repoui = repo.ui
150 else:
150 else:
151 self.ui = ui
151 self.ui = ui
152 self.repo = self.repoui = None
152 self.repo = self.repoui = None
153
153
154 self.cerr = channeledoutput(fout, 'e')
154 self.cerr = channeledoutput(fout, 'e')
155 self.cout = channeledoutput(fout, 'o')
155 self.cout = channeledoutput(fout, 'o')
156 self.cin = channeledinput(fin, fout, 'I')
156 self.cin = channeledinput(fin, fout, 'I')
157 self.cresult = channeledoutput(fout, 'r')
157 self.cresult = channeledoutput(fout, 'r')
158
158
159 self.client = fin
159 self.client = fin
160
160
161 def _read(self, size):
161 def _read(self, size):
162 if not size:
162 if not size:
163 return ''
163 return ''
164
164
165 data = self.client.read(size)
165 data = self.client.read(size)
166
166
167 # is the other end closed?
167 # is the other end closed?
168 if not data:
168 if not data:
169 raise EOFError
169 raise EOFError
170
170
171 return data
171 return data
172
172
173 def runcommand(self):
173 def runcommand(self):
174 """ reads a list of \0 terminated arguments, executes
174 """ reads a list of \0 terminated arguments, executes
175 and writes the return code to the result channel """
175 and writes the return code to the result channel """
176
176
177 length = struct.unpack('>I', self._read(4))[0]
177 length = struct.unpack('>I', self._read(4))[0]
178 if not length:
178 if not length:
179 args = []
179 args = []
180 else:
180 else:
181 args = self._read(length).split('\0')
181 args = self._read(length).split('\0')
182
182
183 # copy the uis so changes (e.g. --config or --verbose) don't
183 # copy the uis so changes (e.g. --config or --verbose) don't
184 # persist between requests
184 # persist between requests
185 copiedui = self.ui.copy()
185 copiedui = self.ui.copy()
186 uis = [copiedui]
186 uis = [copiedui]
187 if self.repo:
187 if self.repo:
188 self.repo.baseui = copiedui
188 self.repo.baseui = copiedui
189 # clone ui without using ui.copy because this is protected
189 # clone ui without using ui.copy because this is protected
190 repoui = self.repoui.__class__(self.repoui)
190 repoui = self.repoui.__class__(self.repoui)
191 repoui.copy = copiedui.copy # redo copy protection
191 repoui.copy = copiedui.copy # redo copy protection
192 uis.append(repoui)
192 uis.append(repoui)
193 self.repo.ui = self.repo.dirstate._ui = repoui
193 self.repo.ui = self.repo.dirstate._ui = repoui
194 self.repo.invalidateall()
194 self.repo.invalidateall()
195
195
196 for ui in uis:
196 for ui in uis:
197 # any kind of interaction must use server channels
197 # any kind of interaction must use server channels
198 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
198 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
199
199
200 req = dispatch.request(args[:], copiedui, self.repo, self.cin,
200 req = dispatch.request(args[:], copiedui, self.repo, self.cin,
201 self.cout, self.cerr)
201 self.cout, self.cerr)
202
202
203 ret = (dispatch.dispatch(req) or 0) & 255 # might return None
203 ret = (dispatch.dispatch(req) or 0) & 255 # might return None
204
204
205 # restore old cwd
205 # restore old cwd
206 if '--cwd' in args:
206 if '--cwd' in args:
207 os.chdir(self.cwd)
207 os.chdir(self.cwd)
208
208
209 self.cresult.write(struct.pack('>i', int(ret)))
209 self.cresult.write(struct.pack('>i', int(ret)))
210
210
211 def getencoding(self):
211 def getencoding(self):
212 """ writes the current encoding to the result channel """
212 """ writes the current encoding to the result channel """
213 self.cresult.write(encoding.encoding)
213 self.cresult.write(encoding.encoding)
214
214
215 def serveone(self):
215 def serveone(self):
216 cmd = self.client.readline()[:-1]
216 cmd = self.client.readline()[:-1]
217 if cmd:
217 if cmd:
218 handler = self.capabilities.get(cmd)
218 handler = self.capabilities.get(cmd)
219 if handler:
219 if handler:
220 handler(self)
220 handler(self)
221 else:
221 else:
222 # clients are expected to check what commands are supported by
222 # clients are expected to check what commands are supported by
223 # looking at the servers capabilities
223 # looking at the servers capabilities
224 raise util.Abort(_('unknown command %s') % cmd)
224 raise util.Abort(_('unknown command %s') % cmd)
225
225
226 return cmd != ''
226 return cmd != ''
227
227
228 capabilities = {'runcommand' : runcommand,
228 capabilities = {'runcommand' : runcommand,
229 'getencoding' : getencoding}
229 'getencoding' : getencoding}
230
230
231 def serve(self):
231 def serve(self):
232 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
232 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
233 hellomsg += '\n'
233 hellomsg += '\n'
234 hellomsg += 'encoding: ' + encoding.encoding
234 hellomsg += 'encoding: ' + encoding.encoding
235
235
236 # write the hello msg in -one- chunk
236 # write the hello msg in -one- chunk
237 self.cout.write(hellomsg)
237 self.cout.write(hellomsg)
238
238
239 try:
239 try:
240 while self.serveone():
240 while self.serveone():
241 pass
241 pass
242 except EOFError:
242 except EOFError:
243 # we'll get here if the client disconnected while we were reading
243 # we'll get here if the client disconnected while we were reading
244 # its request
244 # its request
245 return 1
245 return 1
246
246
247 return 0
247 return 0
248
248
249 class pipeservice(object):
249 class pipeservice(object):
250 def __init__(self, ui, repo, opts):
250 def __init__(self, ui, repo, opts):
251 self.server = server(ui, repo, sys.stdin, sys.stdout)
251 self.server = server(ui, repo, sys.stdin, sys.stdout)
252
252
253 def init(self):
253 def init(self):
254 pass
254 pass
255
255
256 def run(self):
256 def run(self):
257 return self.server.serve()
257 return self.server.serve()
258
258
259 class _requesthandler(SocketServer.StreamRequestHandler):
260 def handle(self):
261 ui = self.server.ui
262 repo = self.server.repo
263 sv = server(ui, repo, self.rfile, self.wfile)
264 try:
265 try:
266 sv.serve()
267 # handle exceptions that may be raised by command server. most of
268 # known exceptions are caught by dispatch.
269 except util.Abort, inst:
270 ui.warn(_('abort: %s\n') % inst)
271 except IOError, inst:
272 if inst.errno != errno.EPIPE:
273 raise
274 except KeyboardInterrupt:
275 pass
276 except: # re-raises
277 # also write traceback to error channel. otherwise client cannot
278 # see it because it is written to server's stderr by default.
279 traceback.print_exc(file=sv.cerr)
280 raise
281
282 class unixservice(object):
283 """
284 Listens on unix domain socket and forks server per connection
285 """
286 def __init__(self, ui, repo, opts):
287 self.ui = ui
288 self.repo = repo
289 self.address = opts['address']
290 if not util.safehasattr(SocketServer, 'UnixStreamServer'):
291 raise util.Abort(_('unsupported platform'))
292 if not self.address:
293 raise util.Abort(_('no socket path specified with --address'))
294
295 def init(self):
296 class cls(SocketServer.ForkingMixIn, SocketServer.UnixStreamServer):
297 ui = self.ui
298 repo = self.repo
299 self.server = cls(self.address, _requesthandler)
300 self.ui.status(_('listening at %s\n') % self.address)
301 self.ui.flush() # avoid buffering of status message
302
303 def run(self):
304 try:
305 self.server.serve_forever()
306 finally:
307 os.unlink(self.address)
308
259 _servicemap = {
309 _servicemap = {
260 'pipe': pipeservice,
310 'pipe': pipeservice,
311 'unix': unixservice,
261 }
312 }
262
313
263 def createservice(ui, repo, opts):
314 def createservice(ui, repo, opts):
264 mode = opts['cmdserver']
315 mode = opts['cmdserver']
265 try:
316 try:
266 return _servicemap[mode](ui, repo, opts)
317 return _servicemap[mode](ui, repo, opts)
267 except KeyError:
318 except KeyError:
268 raise util.Abort(_('unknown mode %s') % mode)
319 raise util.Abort(_('unknown mode %s') % mode)
@@ -1,359 +1,364 b''
1 import os, stat
1 import os, stat
2 import re
2 import re
3 import socket
3 import sys
4 import sys
4 import tempfile
5 import tempfile
5
6
6 tempprefix = 'hg-hghave-'
7 tempprefix = 'hg-hghave-'
7
8
8 checks = {
9 checks = {
9 "true": (lambda: True, "yak shaving"),
10 "true": (lambda: True, "yak shaving"),
10 "false": (lambda: False, "nail clipper"),
11 "false": (lambda: False, "nail clipper"),
11 }
12 }
12
13
13 def check(name, desc):
14 def check(name, desc):
14 def decorator(func):
15 def decorator(func):
15 checks[name] = (func, desc)
16 checks[name] = (func, desc)
16 return func
17 return func
17 return decorator
18 return decorator
18
19
19 def matchoutput(cmd, regexp, ignorestatus=False):
20 def matchoutput(cmd, regexp, ignorestatus=False):
20 """Return True if cmd executes successfully and its output
21 """Return True if cmd executes successfully and its output
21 is matched by the supplied regular expression.
22 is matched by the supplied regular expression.
22 """
23 """
23 r = re.compile(regexp)
24 r = re.compile(regexp)
24 fh = os.popen(cmd)
25 fh = os.popen(cmd)
25 s = fh.read()
26 s = fh.read()
26 try:
27 try:
27 ret = fh.close()
28 ret = fh.close()
28 except IOError:
29 except IOError:
29 # Happen in Windows test environment
30 # Happen in Windows test environment
30 ret = 1
31 ret = 1
31 return (ignorestatus or ret is None) and r.search(s)
32 return (ignorestatus or ret is None) and r.search(s)
32
33
33 @check("baz", "GNU Arch baz client")
34 @check("baz", "GNU Arch baz client")
34 def has_baz():
35 def has_baz():
35 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
36 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
36
37
37 @check("bzr", "Canonical's Bazaar client")
38 @check("bzr", "Canonical's Bazaar client")
38 def has_bzr():
39 def has_bzr():
39 try:
40 try:
40 import bzrlib
41 import bzrlib
41 return bzrlib.__doc__ is not None
42 return bzrlib.__doc__ is not None
42 except ImportError:
43 except ImportError:
43 return False
44 return False
44
45
45 @check("bzr114", "Canonical's Bazaar client >= 1.14")
46 @check("bzr114", "Canonical's Bazaar client >= 1.14")
46 def has_bzr114():
47 def has_bzr114():
47 try:
48 try:
48 import bzrlib
49 import bzrlib
49 return (bzrlib.__doc__ is not None
50 return (bzrlib.__doc__ is not None
50 and bzrlib.version_info[:2] >= (1, 14))
51 and bzrlib.version_info[:2] >= (1, 14))
51 except ImportError:
52 except ImportError:
52 return False
53 return False
53
54
54 @check("cvs", "cvs client/server")
55 @check("cvs", "cvs client/server")
55 def has_cvs():
56 def has_cvs():
56 re = r'Concurrent Versions System.*?server'
57 re = r'Concurrent Versions System.*?server'
57 return matchoutput('cvs --version 2>&1', re) and not has_msys()
58 return matchoutput('cvs --version 2>&1', re) and not has_msys()
58
59
59 @check("cvs112", "cvs client/server >= 1.12")
60 @check("cvs112", "cvs client/server >= 1.12")
60 def has_cvs112():
61 def has_cvs112():
61 re = r'Concurrent Versions System \(CVS\) 1.12.*?server'
62 re = r'Concurrent Versions System \(CVS\) 1.12.*?server'
62 return matchoutput('cvs --version 2>&1', re) and not has_msys()
63 return matchoutput('cvs --version 2>&1', re) and not has_msys()
63
64
64 @check("darcs", "darcs client")
65 @check("darcs", "darcs client")
65 def has_darcs():
66 def has_darcs():
66 return matchoutput('darcs --version', r'2\.[2-9]', True)
67 return matchoutput('darcs --version', r'2\.[2-9]', True)
67
68
68 @check("mtn", "monotone client (>= 1.0)")
69 @check("mtn", "monotone client (>= 1.0)")
69 def has_mtn():
70 def has_mtn():
70 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
71 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
71 'mtn --version', r'monotone 0\.', True)
72 'mtn --version', r'monotone 0\.', True)
72
73
73 @check("eol-in-paths", "end-of-lines in paths")
74 @check("eol-in-paths", "end-of-lines in paths")
74 def has_eol_in_paths():
75 def has_eol_in_paths():
75 try:
76 try:
76 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
77 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
77 os.close(fd)
78 os.close(fd)
78 os.remove(path)
79 os.remove(path)
79 return True
80 return True
80 except (IOError, OSError):
81 except (IOError, OSError):
81 return False
82 return False
82
83
83 @check("execbit", "executable bit")
84 @check("execbit", "executable bit")
84 def has_executablebit():
85 def has_executablebit():
85 try:
86 try:
86 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
87 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
87 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
88 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
88 try:
89 try:
89 os.close(fh)
90 os.close(fh)
90 m = os.stat(fn).st_mode & 0777
91 m = os.stat(fn).st_mode & 0777
91 new_file_has_exec = m & EXECFLAGS
92 new_file_has_exec = m & EXECFLAGS
92 os.chmod(fn, m ^ EXECFLAGS)
93 os.chmod(fn, m ^ EXECFLAGS)
93 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
94 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
94 finally:
95 finally:
95 os.unlink(fn)
96 os.unlink(fn)
96 except (IOError, OSError):
97 except (IOError, OSError):
97 # we don't care, the user probably won't be able to commit anyway
98 # we don't care, the user probably won't be able to commit anyway
98 return False
99 return False
99 return not (new_file_has_exec or exec_flags_cannot_flip)
100 return not (new_file_has_exec or exec_flags_cannot_flip)
100
101
101 @check("icasefs", "case insensitive file system")
102 @check("icasefs", "case insensitive file system")
102 def has_icasefs():
103 def has_icasefs():
103 # Stolen from mercurial.util
104 # Stolen from mercurial.util
104 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
105 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
105 os.close(fd)
106 os.close(fd)
106 try:
107 try:
107 s1 = os.stat(path)
108 s1 = os.stat(path)
108 d, b = os.path.split(path)
109 d, b = os.path.split(path)
109 p2 = os.path.join(d, b.upper())
110 p2 = os.path.join(d, b.upper())
110 if path == p2:
111 if path == p2:
111 p2 = os.path.join(d, b.lower())
112 p2 = os.path.join(d, b.lower())
112 try:
113 try:
113 s2 = os.stat(p2)
114 s2 = os.stat(p2)
114 return s2 == s1
115 return s2 == s1
115 except OSError:
116 except OSError:
116 return False
117 return False
117 finally:
118 finally:
118 os.remove(path)
119 os.remove(path)
119
120
120 @check("fifo", "named pipes")
121 @check("fifo", "named pipes")
121 def has_fifo():
122 def has_fifo():
122 if getattr(os, "mkfifo", None) is None:
123 if getattr(os, "mkfifo", None) is None:
123 return False
124 return False
124 name = tempfile.mktemp(dir='.', prefix=tempprefix)
125 name = tempfile.mktemp(dir='.', prefix=tempprefix)
125 try:
126 try:
126 os.mkfifo(name)
127 os.mkfifo(name)
127 os.unlink(name)
128 os.unlink(name)
128 return True
129 return True
129 except OSError:
130 except OSError:
130 return False
131 return False
131
132
132 @check("killdaemons", 'killdaemons.py support')
133 @check("killdaemons", 'killdaemons.py support')
133 def has_killdaemons():
134 def has_killdaemons():
134 return True
135 return True
135
136
136 @check("cacheable", "cacheable filesystem")
137 @check("cacheable", "cacheable filesystem")
137 def has_cacheable_fs():
138 def has_cacheable_fs():
138 from mercurial import util
139 from mercurial import util
139
140
140 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
141 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
141 os.close(fd)
142 os.close(fd)
142 try:
143 try:
143 return util.cachestat(path).cacheable()
144 return util.cachestat(path).cacheable()
144 finally:
145 finally:
145 os.remove(path)
146 os.remove(path)
146
147
147 @check("lsprof", "python lsprof module")
148 @check("lsprof", "python lsprof module")
148 def has_lsprof():
149 def has_lsprof():
149 try:
150 try:
150 import _lsprof
151 import _lsprof
151 _lsprof.Profiler # silence unused import warning
152 _lsprof.Profiler # silence unused import warning
152 return True
153 return True
153 except ImportError:
154 except ImportError:
154 return False
155 return False
155
156
156 @check("gettext", "GNU Gettext (msgfmt)")
157 @check("gettext", "GNU Gettext (msgfmt)")
157 def has_gettext():
158 def has_gettext():
158 return matchoutput('msgfmt --version', 'GNU gettext-tools')
159 return matchoutput('msgfmt --version', 'GNU gettext-tools')
159
160
160 @check("git", "git command line client")
161 @check("git", "git command line client")
161 def has_git():
162 def has_git():
162 return matchoutput('git --version 2>&1', r'^git version')
163 return matchoutput('git --version 2>&1', r'^git version')
163
164
164 @check("docutils", "Docutils text processing library")
165 @check("docutils", "Docutils text processing library")
165 def has_docutils():
166 def has_docutils():
166 try:
167 try:
167 from docutils.core import publish_cmdline
168 from docutils.core import publish_cmdline
168 publish_cmdline # silence unused import
169 publish_cmdline # silence unused import
169 return True
170 return True
170 except ImportError:
171 except ImportError:
171 return False
172 return False
172
173
173 def getsvnversion():
174 def getsvnversion():
174 m = matchoutput('svn --version --quiet 2>&1', r'^(\d+)\.(\d+)')
175 m = matchoutput('svn --version --quiet 2>&1', r'^(\d+)\.(\d+)')
175 if not m:
176 if not m:
176 return (0, 0)
177 return (0, 0)
177 return (int(m.group(1)), int(m.group(2)))
178 return (int(m.group(1)), int(m.group(2)))
178
179
179 @check("svn15", "subversion client and admin tools >= 1.5")
180 @check("svn15", "subversion client and admin tools >= 1.5")
180 def has_svn15():
181 def has_svn15():
181 return getsvnversion() >= (1, 5)
182 return getsvnversion() >= (1, 5)
182
183
183 @check("svn13", "subversion client and admin tools >= 1.3")
184 @check("svn13", "subversion client and admin tools >= 1.3")
184 def has_svn13():
185 def has_svn13():
185 return getsvnversion() >= (1, 3)
186 return getsvnversion() >= (1, 3)
186
187
187 @check("svn", "subversion client and admin tools")
188 @check("svn", "subversion client and admin tools")
188 def has_svn():
189 def has_svn():
189 return matchoutput('svn --version 2>&1', r'^svn, version') and \
190 return matchoutput('svn --version 2>&1', r'^svn, version') and \
190 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
191 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
191
192
192 @check("svn-bindings", "subversion python bindings")
193 @check("svn-bindings", "subversion python bindings")
193 def has_svn_bindings():
194 def has_svn_bindings():
194 try:
195 try:
195 import svn.core
196 import svn.core
196 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
197 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
197 if version < (1, 4):
198 if version < (1, 4):
198 return False
199 return False
199 return True
200 return True
200 except ImportError:
201 except ImportError:
201 return False
202 return False
202
203
203 @check("p4", "Perforce server and client")
204 @check("p4", "Perforce server and client")
204 def has_p4():
205 def has_p4():
205 return (matchoutput('p4 -V', r'Rev\. P4/') and
206 return (matchoutput('p4 -V', r'Rev\. P4/') and
206 matchoutput('p4d -V', r'Rev\. P4D/'))
207 matchoutput('p4d -V', r'Rev\. P4D/'))
207
208
208 @check("symlink", "symbolic links")
209 @check("symlink", "symbolic links")
209 def has_symlink():
210 def has_symlink():
210 if getattr(os, "symlink", None) is None:
211 if getattr(os, "symlink", None) is None:
211 return False
212 return False
212 name = tempfile.mktemp(dir='.', prefix=tempprefix)
213 name = tempfile.mktemp(dir='.', prefix=tempprefix)
213 try:
214 try:
214 os.symlink(".", name)
215 os.symlink(".", name)
215 os.unlink(name)
216 os.unlink(name)
216 return True
217 return True
217 except (OSError, AttributeError):
218 except (OSError, AttributeError):
218 return False
219 return False
219
220
220 @check("hardlink", "hardlinks")
221 @check("hardlink", "hardlinks")
221 def has_hardlink():
222 def has_hardlink():
222 from mercurial import util
223 from mercurial import util
223 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
224 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
224 os.close(fh)
225 os.close(fh)
225 name = tempfile.mktemp(dir='.', prefix=tempprefix)
226 name = tempfile.mktemp(dir='.', prefix=tempprefix)
226 try:
227 try:
227 try:
228 try:
228 util.oslink(fn, name)
229 util.oslink(fn, name)
229 os.unlink(name)
230 os.unlink(name)
230 return True
231 return True
231 except OSError:
232 except OSError:
232 return False
233 return False
233 finally:
234 finally:
234 os.unlink(fn)
235 os.unlink(fn)
235
236
236 @check("tla", "GNU Arch tla client")
237 @check("tla", "GNU Arch tla client")
237 def has_tla():
238 def has_tla():
238 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
239 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
239
240
240 @check("gpg", "gpg client")
241 @check("gpg", "gpg client")
241 def has_gpg():
242 def has_gpg():
242 return matchoutput('gpg --version 2>&1', r'GnuPG')
243 return matchoutput('gpg --version 2>&1', r'GnuPG')
243
244
244 @check("unix-permissions", "unix-style permissions")
245 @check("unix-permissions", "unix-style permissions")
245 def has_unix_permissions():
246 def has_unix_permissions():
246 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
247 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
247 try:
248 try:
248 fname = os.path.join(d, 'foo')
249 fname = os.path.join(d, 'foo')
249 for umask in (077, 007, 022):
250 for umask in (077, 007, 022):
250 os.umask(umask)
251 os.umask(umask)
251 f = open(fname, 'w')
252 f = open(fname, 'w')
252 f.close()
253 f.close()
253 mode = os.stat(fname).st_mode
254 mode = os.stat(fname).st_mode
254 os.unlink(fname)
255 os.unlink(fname)
255 if mode & 0777 != ~umask & 0666:
256 if mode & 0777 != ~umask & 0666:
256 return False
257 return False
257 return True
258 return True
258 finally:
259 finally:
259 os.rmdir(d)
260 os.rmdir(d)
260
261
262 @check("unix-socket", "AF_UNIX socket family")
263 def has_unix_socket():
264 return getattr(socket, 'AF_UNIX', None) is not None
265
261 @check("root", "root permissions")
266 @check("root", "root permissions")
262 def has_root():
267 def has_root():
263 return getattr(os, 'geteuid', None) and os.geteuid() == 0
268 return getattr(os, 'geteuid', None) and os.geteuid() == 0
264
269
265 @check("pyflakes", "Pyflakes python linter")
270 @check("pyflakes", "Pyflakes python linter")
266 def has_pyflakes():
271 def has_pyflakes():
267 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
272 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
268 r"<stdin>:1: 're' imported but unused",
273 r"<stdin>:1: 're' imported but unused",
269 True)
274 True)
270
275
271 @check("pygments", "Pygments source highlighting library")
276 @check("pygments", "Pygments source highlighting library")
272 def has_pygments():
277 def has_pygments():
273 try:
278 try:
274 import pygments
279 import pygments
275 pygments.highlight # silence unused import warning
280 pygments.highlight # silence unused import warning
276 return True
281 return True
277 except ImportError:
282 except ImportError:
278 return False
283 return False
279
284
280 @check("python243", "python >= 2.4.3")
285 @check("python243", "python >= 2.4.3")
281 def has_python243():
286 def has_python243():
282 return sys.version_info >= (2, 4, 3)
287 return sys.version_info >= (2, 4, 3)
283
288
284 @check("json", "some json module available")
289 @check("json", "some json module available")
285 def has_json():
290 def has_json():
286 try:
291 try:
287 if sys.version_info < (2, 7):
292 if sys.version_info < (2, 7):
288 import simplejson as json
293 import simplejson as json
289 else:
294 else:
290 import json
295 import json
291 json.dumps
296 json.dumps
292 return True
297 return True
293 except ImportError:
298 except ImportError:
294 return False
299 return False
295
300
296 @check("outer-repo", "outer repo")
301 @check("outer-repo", "outer repo")
297 def has_outer_repo():
302 def has_outer_repo():
298 # failing for other reasons than 'no repo' imply that there is a repo
303 # failing for other reasons than 'no repo' imply that there is a repo
299 return not matchoutput('hg root 2>&1',
304 return not matchoutput('hg root 2>&1',
300 r'abort: no repository found', True)
305 r'abort: no repository found', True)
301
306
302 @check("ssl", "python >= 2.6 ssl module and python OpenSSL")
307 @check("ssl", "python >= 2.6 ssl module and python OpenSSL")
303 def has_ssl():
308 def has_ssl():
304 try:
309 try:
305 import ssl
310 import ssl
306 ssl.wrap_socket # silence unused import warning
311 ssl.wrap_socket # silence unused import warning
307 import OpenSSL
312 import OpenSSL
308 OpenSSL.SSL.Context
313 OpenSSL.SSL.Context
309 return True
314 return True
310 except ImportError:
315 except ImportError:
311 return False
316 return False
312
317
313 @check("windows", "Windows")
318 @check("windows", "Windows")
314 def has_windows():
319 def has_windows():
315 return os.name == 'nt'
320 return os.name == 'nt'
316
321
317 @check("system-sh", "system() uses sh")
322 @check("system-sh", "system() uses sh")
318 def has_system_sh():
323 def has_system_sh():
319 return os.name != 'nt'
324 return os.name != 'nt'
320
325
321 @check("serve", "platform and python can manage 'hg serve -d'")
326 @check("serve", "platform and python can manage 'hg serve -d'")
322 def has_serve():
327 def has_serve():
323 return os.name != 'nt' # gross approximation
328 return os.name != 'nt' # gross approximation
324
329
325 @check("test-repo", "running tests from repository")
330 @check("test-repo", "running tests from repository")
326 def has_test_repo():
331 def has_test_repo():
327 t = os.environ["TESTDIR"]
332 t = os.environ["TESTDIR"]
328 return os.path.isdir(os.path.join(t, "..", ".hg"))
333 return os.path.isdir(os.path.join(t, "..", ".hg"))
329
334
330 @check("tic", "terminfo compiler and curses module")
335 @check("tic", "terminfo compiler and curses module")
331 def has_tic():
336 def has_tic():
332 try:
337 try:
333 import curses
338 import curses
334 curses.COLOR_BLUE
339 curses.COLOR_BLUE
335 return matchoutput('test -x "`which tic`"', '')
340 return matchoutput('test -x "`which tic`"', '')
336 except ImportError:
341 except ImportError:
337 return False
342 return False
338
343
339 @check("msys", "Windows with MSYS")
344 @check("msys", "Windows with MSYS")
340 def has_msys():
345 def has_msys():
341 return os.getenv('MSYSTEM')
346 return os.getenv('MSYSTEM')
342
347
343 @check("aix", "AIX")
348 @check("aix", "AIX")
344 def has_aix():
349 def has_aix():
345 return sys.platform.startswith("aix")
350 return sys.platform.startswith("aix")
346
351
347 @check("osx", "OS X")
352 @check("osx", "OS X")
348 def has_osx():
353 def has_osx():
349 return sys.platform == 'darwin'
354 return sys.platform == 'darwin'
350
355
351 @check("absimport", "absolute_import in __future__")
356 @check("absimport", "absolute_import in __future__")
352 def has_absimport():
357 def has_absimport():
353 import __future__
358 import __future__
354 from mercurial import util
359 from mercurial import util
355 return util.safehasattr(__future__, "absolute_import")
360 return util.safehasattr(__future__, "absolute_import")
356
361
357 @check("py3k", "running with Python 3.x")
362 @check("py3k", "running with Python 3.x")
358 def has_py3k():
363 def has_py3k():
359 return 3 == sys.version_info[0]
364 return 3 == sys.version_info[0]
@@ -1,547 +1,607 b''
1 #if windows
1 #if windows
2 $ PYTHONPATH="$TESTDIR/../contrib;$PYTHONPATH"
2 $ PYTHONPATH="$TESTDIR/../contrib;$PYTHONPATH"
3 #else
3 #else
4 $ PYTHONPATH="$TESTDIR/../contrib:$PYTHONPATH"
4 $ PYTHONPATH="$TESTDIR/../contrib:$PYTHONPATH"
5 #endif
5 #endif
6 $ export PYTHONPATH
6 $ export PYTHONPATH
7
7
8 $ hg init repo
8 $ hg init repo
9 $ cd repo
9 $ cd repo
10
10
11 >>> from hgclient import readchannel, runcommand, check
11 >>> from hgclient import readchannel, runcommand, check
12 >>> @check
12 >>> @check
13 ... def hellomessage(server):
13 ... def hellomessage(server):
14 ... ch, data = readchannel(server)
14 ... ch, data = readchannel(server)
15 ... print '%c, %r' % (ch, data)
15 ... print '%c, %r' % (ch, data)
16 ... # run an arbitrary command to make sure the next thing the server
16 ... # run an arbitrary command to make sure the next thing the server
17 ... # sends isn't part of the hello message
17 ... # sends isn't part of the hello message
18 ... runcommand(server, ['id'])
18 ... runcommand(server, ['id'])
19 o, 'capabilities: getencoding runcommand\nencoding: *' (glob)
19 o, 'capabilities: getencoding runcommand\nencoding: *' (glob)
20 *** runcommand id
20 *** runcommand id
21 000000000000 tip
21 000000000000 tip
22
22
23 >>> from hgclient import check
23 >>> from hgclient import check
24 >>> @check
24 >>> @check
25 ... def unknowncommand(server):
25 ... def unknowncommand(server):
26 ... server.stdin.write('unknowncommand\n')
26 ... server.stdin.write('unknowncommand\n')
27 abort: unknown command unknowncommand
27 abort: unknown command unknowncommand
28
28
29 >>> from hgclient import readchannel, runcommand, check
29 >>> from hgclient import readchannel, runcommand, check
30 >>> @check
30 >>> @check
31 ... def checkruncommand(server):
31 ... def checkruncommand(server):
32 ... # hello block
32 ... # hello block
33 ... readchannel(server)
33 ... readchannel(server)
34 ...
34 ...
35 ... # no args
35 ... # no args
36 ... runcommand(server, [])
36 ... runcommand(server, [])
37 ...
37 ...
38 ... # global options
38 ... # global options
39 ... runcommand(server, ['id', '--quiet'])
39 ... runcommand(server, ['id', '--quiet'])
40 ...
40 ...
41 ... # make sure global options don't stick through requests
41 ... # make sure global options don't stick through requests
42 ... runcommand(server, ['id'])
42 ... runcommand(server, ['id'])
43 ...
43 ...
44 ... # --config
44 ... # --config
45 ... runcommand(server, ['id', '--config', 'ui.quiet=True'])
45 ... runcommand(server, ['id', '--config', 'ui.quiet=True'])
46 ...
46 ...
47 ... # make sure --config doesn't stick
47 ... # make sure --config doesn't stick
48 ... runcommand(server, ['id'])
48 ... runcommand(server, ['id'])
49 ...
49 ...
50 ... # negative return code should be masked
50 ... # negative return code should be masked
51 ... runcommand(server, ['id', '-runknown'])
51 ... runcommand(server, ['id', '-runknown'])
52 *** runcommand
52 *** runcommand
53 Mercurial Distributed SCM
53 Mercurial Distributed SCM
54
54
55 basic commands:
55 basic commands:
56
56
57 add add the specified files on the next commit
57 add add the specified files on the next commit
58 annotate show changeset information by line for each file
58 annotate show changeset information by line for each file
59 clone make a copy of an existing repository
59 clone make a copy of an existing repository
60 commit commit the specified files or all outstanding changes
60 commit commit the specified files or all outstanding changes
61 diff diff repository (or selected files)
61 diff diff repository (or selected files)
62 export dump the header and diffs for one or more changesets
62 export dump the header and diffs for one or more changesets
63 forget forget the specified files on the next commit
63 forget forget the specified files on the next commit
64 init create a new repository in the given directory
64 init create a new repository in the given directory
65 log show revision history of entire repository or files
65 log show revision history of entire repository or files
66 merge merge working directory with another revision
66 merge merge working directory with another revision
67 pull pull changes from the specified source
67 pull pull changes from the specified source
68 push push changes to the specified destination
68 push push changes to the specified destination
69 remove remove the specified files on the next commit
69 remove remove the specified files on the next commit
70 serve start stand-alone webserver
70 serve start stand-alone webserver
71 status show changed files in the working directory
71 status show changed files in the working directory
72 summary summarize working directory state
72 summary summarize working directory state
73 update update working directory (or switch revisions)
73 update update working directory (or switch revisions)
74
74
75 (use "hg help" for the full list of commands or "hg -v" for details)
75 (use "hg help" for the full list of commands or "hg -v" for details)
76 *** runcommand id --quiet
76 *** runcommand id --quiet
77 000000000000
77 000000000000
78 *** runcommand id
78 *** runcommand id
79 000000000000 tip
79 000000000000 tip
80 *** runcommand id --config ui.quiet=True
80 *** runcommand id --config ui.quiet=True
81 000000000000
81 000000000000
82 *** runcommand id
82 *** runcommand id
83 000000000000 tip
83 000000000000 tip
84 *** runcommand id -runknown
84 *** runcommand id -runknown
85 abort: unknown revision 'unknown'!
85 abort: unknown revision 'unknown'!
86 [255]
86 [255]
87
87
88 >>> from hgclient import readchannel, check
88 >>> from hgclient import readchannel, check
89 >>> @check
89 >>> @check
90 ... def inputeof(server):
90 ... def inputeof(server):
91 ... readchannel(server)
91 ... readchannel(server)
92 ... server.stdin.write('runcommand\n')
92 ... server.stdin.write('runcommand\n')
93 ... # close stdin while server is waiting for input
93 ... # close stdin while server is waiting for input
94 ... server.stdin.close()
94 ... server.stdin.close()
95 ...
95 ...
96 ... # server exits with 1 if the pipe closed while reading the command
96 ... # server exits with 1 if the pipe closed while reading the command
97 ... print 'server exit code =', server.wait()
97 ... print 'server exit code =', server.wait()
98 server exit code = 1
98 server exit code = 1
99
99
100 >>> import cStringIO
100 >>> import cStringIO
101 >>> from hgclient import readchannel, runcommand, check
101 >>> from hgclient import readchannel, runcommand, check
102 >>> @check
102 >>> @check
103 ... def serverinput(server):
103 ... def serverinput(server):
104 ... readchannel(server)
104 ... readchannel(server)
105 ...
105 ...
106 ... patch = """
106 ... patch = """
107 ... # HG changeset patch
107 ... # HG changeset patch
108 ... # User test
108 ... # User test
109 ... # Date 0 0
109 ... # Date 0 0
110 ... # Node ID c103a3dec114d882c98382d684d8af798d09d857
110 ... # Node ID c103a3dec114d882c98382d684d8af798d09d857
111 ... # Parent 0000000000000000000000000000000000000000
111 ... # Parent 0000000000000000000000000000000000000000
112 ... 1
112 ... 1
113 ...
113 ...
114 ... diff -r 000000000000 -r c103a3dec114 a
114 ... diff -r 000000000000 -r c103a3dec114 a
115 ... --- /dev/null Thu Jan 01 00:00:00 1970 +0000
115 ... --- /dev/null Thu Jan 01 00:00:00 1970 +0000
116 ... +++ b/a Thu Jan 01 00:00:00 1970 +0000
116 ... +++ b/a Thu Jan 01 00:00:00 1970 +0000
117 ... @@ -0,0 +1,1 @@
117 ... @@ -0,0 +1,1 @@
118 ... +1
118 ... +1
119 ... """
119 ... """
120 ...
120 ...
121 ... runcommand(server, ['import', '-'], input=cStringIO.StringIO(patch))
121 ... runcommand(server, ['import', '-'], input=cStringIO.StringIO(patch))
122 ... runcommand(server, ['log'])
122 ... runcommand(server, ['log'])
123 *** runcommand import -
123 *** runcommand import -
124 applying patch from stdin
124 applying patch from stdin
125 *** runcommand log
125 *** runcommand log
126 changeset: 0:eff892de26ec
126 changeset: 0:eff892de26ec
127 tag: tip
127 tag: tip
128 user: test
128 user: test
129 date: Thu Jan 01 00:00:00 1970 +0000
129 date: Thu Jan 01 00:00:00 1970 +0000
130 summary: 1
130 summary: 1
131
131
132
132
133 check that --cwd doesn't persist between requests:
133 check that --cwd doesn't persist between requests:
134
134
135 $ mkdir foo
135 $ mkdir foo
136 $ touch foo/bar
136 $ touch foo/bar
137 >>> from hgclient import readchannel, runcommand, check
137 >>> from hgclient import readchannel, runcommand, check
138 >>> @check
138 >>> @check
139 ... def cwd(server):
139 ... def cwd(server):
140 ... readchannel(server)
140 ... readchannel(server)
141 ... runcommand(server, ['--cwd', 'foo', 'st', 'bar'])
141 ... runcommand(server, ['--cwd', 'foo', 'st', 'bar'])
142 ... runcommand(server, ['st', 'foo/bar'])
142 ... runcommand(server, ['st', 'foo/bar'])
143 *** runcommand --cwd foo st bar
143 *** runcommand --cwd foo st bar
144 ? bar
144 ? bar
145 *** runcommand st foo/bar
145 *** runcommand st foo/bar
146 ? foo/bar
146 ? foo/bar
147
147
148 $ rm foo/bar
148 $ rm foo/bar
149
149
150
150
151 check that local configs for the cached repo aren't inherited when -R is used:
151 check that local configs for the cached repo aren't inherited when -R is used:
152
152
153 $ cat <<EOF >> .hg/hgrc
153 $ cat <<EOF >> .hg/hgrc
154 > [ui]
154 > [ui]
155 > foo = bar
155 > foo = bar
156 > EOF
156 > EOF
157
157
158 >>> from hgclient import readchannel, sep, runcommand, check
158 >>> from hgclient import readchannel, sep, runcommand, check
159 >>> @check
159 >>> @check
160 ... def localhgrc(server):
160 ... def localhgrc(server):
161 ... readchannel(server)
161 ... readchannel(server)
162 ...
162 ...
163 ... # the cached repo local hgrc contains ui.foo=bar, so showconfig should
163 ... # the cached repo local hgrc contains ui.foo=bar, so showconfig should
164 ... # show it
164 ... # show it
165 ... runcommand(server, ['showconfig'], outfilter=sep)
165 ... runcommand(server, ['showconfig'], outfilter=sep)
166 ...
166 ...
167 ... # but not for this repo
167 ... # but not for this repo
168 ... runcommand(server, ['init', 'foo'])
168 ... runcommand(server, ['init', 'foo'])
169 ... runcommand(server, ['-R', 'foo', 'showconfig', 'ui', 'defaults'])
169 ... runcommand(server, ['-R', 'foo', 'showconfig', 'ui', 'defaults'])
170 *** runcommand showconfig
170 *** runcommand showconfig
171 bundle.mainreporoot=$TESTTMP/repo
171 bundle.mainreporoot=$TESTTMP/repo
172 defaults.backout=-d "0 0"
172 defaults.backout=-d "0 0"
173 defaults.commit=-d "0 0"
173 defaults.commit=-d "0 0"
174 defaults.shelve=--date "0 0"
174 defaults.shelve=--date "0 0"
175 defaults.tag=-d "0 0"
175 defaults.tag=-d "0 0"
176 ui.slash=True
176 ui.slash=True
177 ui.interactive=False
177 ui.interactive=False
178 ui.mergemarkers=detailed
178 ui.mergemarkers=detailed
179 ui.foo=bar
179 ui.foo=bar
180 ui.nontty=true
180 ui.nontty=true
181 *** runcommand init foo
181 *** runcommand init foo
182 *** runcommand -R foo showconfig ui defaults
182 *** runcommand -R foo showconfig ui defaults
183 defaults.backout=-d "0 0"
183 defaults.backout=-d "0 0"
184 defaults.commit=-d "0 0"
184 defaults.commit=-d "0 0"
185 defaults.shelve=--date "0 0"
185 defaults.shelve=--date "0 0"
186 defaults.tag=-d "0 0"
186 defaults.tag=-d "0 0"
187 ui.slash=True
187 ui.slash=True
188 ui.interactive=False
188 ui.interactive=False
189 ui.mergemarkers=detailed
189 ui.mergemarkers=detailed
190 ui.nontty=true
190 ui.nontty=true
191
191
192 $ rm -R foo
192 $ rm -R foo
193
193
194 #if windows
194 #if windows
195 $ PYTHONPATH="$TESTTMP/repo;$PYTHONPATH"
195 $ PYTHONPATH="$TESTTMP/repo;$PYTHONPATH"
196 #else
196 #else
197 $ PYTHONPATH="$TESTTMP/repo:$PYTHONPATH"
197 $ PYTHONPATH="$TESTTMP/repo:$PYTHONPATH"
198 #endif
198 #endif
199
199
200 $ cat <<EOF > hook.py
200 $ cat <<EOF > hook.py
201 > import sys
201 > import sys
202 > def hook(**args):
202 > def hook(**args):
203 > print 'hook talking'
203 > print 'hook talking'
204 > print 'now try to read something: %r' % sys.stdin.read()
204 > print 'now try to read something: %r' % sys.stdin.read()
205 > EOF
205 > EOF
206
206
207 >>> import cStringIO
207 >>> import cStringIO
208 >>> from hgclient import readchannel, runcommand, check
208 >>> from hgclient import readchannel, runcommand, check
209 >>> @check
209 >>> @check
210 ... def hookoutput(server):
210 ... def hookoutput(server):
211 ... readchannel(server)
211 ... readchannel(server)
212 ... runcommand(server, ['--config',
212 ... runcommand(server, ['--config',
213 ... 'hooks.pre-identify=python:hook.hook',
213 ... 'hooks.pre-identify=python:hook.hook',
214 ... 'id'],
214 ... 'id'],
215 ... input=cStringIO.StringIO('some input'))
215 ... input=cStringIO.StringIO('some input'))
216 *** runcommand --config hooks.pre-identify=python:hook.hook id
216 *** runcommand --config hooks.pre-identify=python:hook.hook id
217 hook talking
217 hook talking
218 now try to read something: 'some input'
218 now try to read something: 'some input'
219 eff892de26ec tip
219 eff892de26ec tip
220
220
221 $ rm hook.py*
221 $ rm hook.py*
222
222
223 $ echo a >> a
223 $ echo a >> a
224 >>> import os
224 >>> import os
225 >>> from hgclient import readchannel, runcommand, check
225 >>> from hgclient import readchannel, runcommand, check
226 >>> @check
226 >>> @check
227 ... def outsidechanges(server):
227 ... def outsidechanges(server):
228 ... readchannel(server)
228 ... readchannel(server)
229 ... runcommand(server, ['status'])
229 ... runcommand(server, ['status'])
230 ... os.system('hg ci -Am2')
230 ... os.system('hg ci -Am2')
231 ... runcommand(server, ['tip'])
231 ... runcommand(server, ['tip'])
232 ... runcommand(server, ['status'])
232 ... runcommand(server, ['status'])
233 *** runcommand status
233 *** runcommand status
234 M a
234 M a
235 *** runcommand tip
235 *** runcommand tip
236 changeset: 1:d3a0a68be6de
236 changeset: 1:d3a0a68be6de
237 tag: tip
237 tag: tip
238 user: test
238 user: test
239 date: Thu Jan 01 00:00:00 1970 +0000
239 date: Thu Jan 01 00:00:00 1970 +0000
240 summary: 2
240 summary: 2
241
241
242 *** runcommand status
242 *** runcommand status
243
243
244 >>> import os
244 >>> import os
245 >>> from hgclient import readchannel, runcommand, check
245 >>> from hgclient import readchannel, runcommand, check
246 >>> @check
246 >>> @check
247 ... def bookmarks(server):
247 ... def bookmarks(server):
248 ... readchannel(server)
248 ... readchannel(server)
249 ... runcommand(server, ['bookmarks'])
249 ... runcommand(server, ['bookmarks'])
250 ...
250 ...
251 ... # changes .hg/bookmarks
251 ... # changes .hg/bookmarks
252 ... os.system('hg bookmark -i bm1')
252 ... os.system('hg bookmark -i bm1')
253 ... os.system('hg bookmark -i bm2')
253 ... os.system('hg bookmark -i bm2')
254 ... runcommand(server, ['bookmarks'])
254 ... runcommand(server, ['bookmarks'])
255 ...
255 ...
256 ... # changes .hg/bookmarks.current
256 ... # changes .hg/bookmarks.current
257 ... os.system('hg upd bm1 -q')
257 ... os.system('hg upd bm1 -q')
258 ... runcommand(server, ['bookmarks'])
258 ... runcommand(server, ['bookmarks'])
259 ...
259 ...
260 ... runcommand(server, ['bookmarks', 'bm3'])
260 ... runcommand(server, ['bookmarks', 'bm3'])
261 ... f = open('a', 'ab')
261 ... f = open('a', 'ab')
262 ... f.write('a\n')
262 ... f.write('a\n')
263 ... f.close()
263 ... f.close()
264 ... runcommand(server, ['commit', '-Amm'])
264 ... runcommand(server, ['commit', '-Amm'])
265 ... runcommand(server, ['bookmarks'])
265 ... runcommand(server, ['bookmarks'])
266 *** runcommand bookmarks
266 *** runcommand bookmarks
267 no bookmarks set
267 no bookmarks set
268 *** runcommand bookmarks
268 *** runcommand bookmarks
269 bm1 1:d3a0a68be6de
269 bm1 1:d3a0a68be6de
270 bm2 1:d3a0a68be6de
270 bm2 1:d3a0a68be6de
271 *** runcommand bookmarks
271 *** runcommand bookmarks
272 * bm1 1:d3a0a68be6de
272 * bm1 1:d3a0a68be6de
273 bm2 1:d3a0a68be6de
273 bm2 1:d3a0a68be6de
274 *** runcommand bookmarks bm3
274 *** runcommand bookmarks bm3
275 *** runcommand commit -Amm
275 *** runcommand commit -Amm
276 *** runcommand bookmarks
276 *** runcommand bookmarks
277 bm1 1:d3a0a68be6de
277 bm1 1:d3a0a68be6de
278 bm2 1:d3a0a68be6de
278 bm2 1:d3a0a68be6de
279 * bm3 2:aef17e88f5f0
279 * bm3 2:aef17e88f5f0
280
280
281 >>> import os
281 >>> import os
282 >>> from hgclient import readchannel, runcommand, check
282 >>> from hgclient import readchannel, runcommand, check
283 >>> @check
283 >>> @check
284 ... def tagscache(server):
284 ... def tagscache(server):
285 ... readchannel(server)
285 ... readchannel(server)
286 ... runcommand(server, ['id', '-t', '-r', '0'])
286 ... runcommand(server, ['id', '-t', '-r', '0'])
287 ... os.system('hg tag -r 0 foo')
287 ... os.system('hg tag -r 0 foo')
288 ... runcommand(server, ['id', '-t', '-r', '0'])
288 ... runcommand(server, ['id', '-t', '-r', '0'])
289 *** runcommand id -t -r 0
289 *** runcommand id -t -r 0
290
290
291 *** runcommand id -t -r 0
291 *** runcommand id -t -r 0
292 foo
292 foo
293
293
294 >>> import os
294 >>> import os
295 >>> from hgclient import readchannel, runcommand, check
295 >>> from hgclient import readchannel, runcommand, check
296 >>> @check
296 >>> @check
297 ... def setphase(server):
297 ... def setphase(server):
298 ... readchannel(server)
298 ... readchannel(server)
299 ... runcommand(server, ['phase', '-r', '.'])
299 ... runcommand(server, ['phase', '-r', '.'])
300 ... os.system('hg phase -r . -p')
300 ... os.system('hg phase -r . -p')
301 ... runcommand(server, ['phase', '-r', '.'])
301 ... runcommand(server, ['phase', '-r', '.'])
302 *** runcommand phase -r .
302 *** runcommand phase -r .
303 3: draft
303 3: draft
304 *** runcommand phase -r .
304 *** runcommand phase -r .
305 3: public
305 3: public
306
306
307 $ echo a >> a
307 $ echo a >> a
308 >>> from hgclient import readchannel, runcommand, check
308 >>> from hgclient import readchannel, runcommand, check
309 >>> @check
309 >>> @check
310 ... def rollback(server):
310 ... def rollback(server):
311 ... readchannel(server)
311 ... readchannel(server)
312 ... runcommand(server, ['phase', '-r', '.', '-p'])
312 ... runcommand(server, ['phase', '-r', '.', '-p'])
313 ... runcommand(server, ['commit', '-Am.'])
313 ... runcommand(server, ['commit', '-Am.'])
314 ... runcommand(server, ['rollback'])
314 ... runcommand(server, ['rollback'])
315 ... runcommand(server, ['phase', '-r', '.'])
315 ... runcommand(server, ['phase', '-r', '.'])
316 *** runcommand phase -r . -p
316 *** runcommand phase -r . -p
317 no phases changed
317 no phases changed
318 [1]
318 [1]
319 *** runcommand commit -Am.
319 *** runcommand commit -Am.
320 *** runcommand rollback
320 *** runcommand rollback
321 repository tip rolled back to revision 3 (undo commit)
321 repository tip rolled back to revision 3 (undo commit)
322 working directory now based on revision 3
322 working directory now based on revision 3
323 *** runcommand phase -r .
323 *** runcommand phase -r .
324 3: public
324 3: public
325
325
326 >>> import os
326 >>> import os
327 >>> from hgclient import readchannel, runcommand, check
327 >>> from hgclient import readchannel, runcommand, check
328 >>> @check
328 >>> @check
329 ... def branch(server):
329 ... def branch(server):
330 ... readchannel(server)
330 ... readchannel(server)
331 ... runcommand(server, ['branch'])
331 ... runcommand(server, ['branch'])
332 ... os.system('hg branch foo')
332 ... os.system('hg branch foo')
333 ... runcommand(server, ['branch'])
333 ... runcommand(server, ['branch'])
334 ... os.system('hg branch default')
334 ... os.system('hg branch default')
335 *** runcommand branch
335 *** runcommand branch
336 default
336 default
337 marked working directory as branch foo
337 marked working directory as branch foo
338 (branches are permanent and global, did you want a bookmark?)
338 (branches are permanent and global, did you want a bookmark?)
339 *** runcommand branch
339 *** runcommand branch
340 foo
340 foo
341 marked working directory as branch default
341 marked working directory as branch default
342 (branches are permanent and global, did you want a bookmark?)
342 (branches are permanent and global, did you want a bookmark?)
343
343
344 $ touch .hgignore
344 $ touch .hgignore
345 >>> import os
345 >>> import os
346 >>> from hgclient import readchannel, runcommand, check
346 >>> from hgclient import readchannel, runcommand, check
347 >>> @check
347 >>> @check
348 ... def hgignore(server):
348 ... def hgignore(server):
349 ... readchannel(server)
349 ... readchannel(server)
350 ... runcommand(server, ['commit', '-Am.'])
350 ... runcommand(server, ['commit', '-Am.'])
351 ... f = open('ignored-file', 'ab')
351 ... f = open('ignored-file', 'ab')
352 ... f.write('')
352 ... f.write('')
353 ... f.close()
353 ... f.close()
354 ... f = open('.hgignore', 'ab')
354 ... f = open('.hgignore', 'ab')
355 ... f.write('ignored-file')
355 ... f.write('ignored-file')
356 ... f.close()
356 ... f.close()
357 ... runcommand(server, ['status', '-i', '-u'])
357 ... runcommand(server, ['status', '-i', '-u'])
358 *** runcommand commit -Am.
358 *** runcommand commit -Am.
359 adding .hgignore
359 adding .hgignore
360 *** runcommand status -i -u
360 *** runcommand status -i -u
361 I ignored-file
361 I ignored-file
362
362
363 >>> import os
363 >>> import os
364 >>> from hgclient import readchannel, sep, runcommand, check
364 >>> from hgclient import readchannel, sep, runcommand, check
365 >>> @check
365 >>> @check
366 ... def phasecacheafterstrip(server):
366 ... def phasecacheafterstrip(server):
367 ... readchannel(server)
367 ... readchannel(server)
368 ...
368 ...
369 ... # create new head, 5:731265503d86
369 ... # create new head, 5:731265503d86
370 ... runcommand(server, ['update', '-C', '0'])
370 ... runcommand(server, ['update', '-C', '0'])
371 ... f = open('a', 'ab')
371 ... f = open('a', 'ab')
372 ... f.write('a\n')
372 ... f.write('a\n')
373 ... f.close()
373 ... f.close()
374 ... runcommand(server, ['commit', '-Am.', 'a'])
374 ... runcommand(server, ['commit', '-Am.', 'a'])
375 ... runcommand(server, ['log', '-Gq'])
375 ... runcommand(server, ['log', '-Gq'])
376 ...
376 ...
377 ... # make it public; draft marker moves to 4:7966c8e3734d
377 ... # make it public; draft marker moves to 4:7966c8e3734d
378 ... runcommand(server, ['phase', '-p', '.'])
378 ... runcommand(server, ['phase', '-p', '.'])
379 ... # load _phasecache.phaseroots
379 ... # load _phasecache.phaseroots
380 ... runcommand(server, ['phase', '.'], outfilter=sep)
380 ... runcommand(server, ['phase', '.'], outfilter=sep)
381 ...
381 ...
382 ... # strip 1::4 outside server
382 ... # strip 1::4 outside server
383 ... os.system('hg -q --config extensions.mq= strip 1')
383 ... os.system('hg -q --config extensions.mq= strip 1')
384 ...
384 ...
385 ... # shouldn't raise "7966c8e3734d: no node!"
385 ... # shouldn't raise "7966c8e3734d: no node!"
386 ... runcommand(server, ['branches'])
386 ... runcommand(server, ['branches'])
387 *** runcommand update -C 0
387 *** runcommand update -C 0
388 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
388 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
389 (leaving bookmark bm3)
389 (leaving bookmark bm3)
390 *** runcommand commit -Am. a
390 *** runcommand commit -Am. a
391 created new head
391 created new head
392 *** runcommand log -Gq
392 *** runcommand log -Gq
393 @ 5:731265503d86
393 @ 5:731265503d86
394 |
394 |
395 | o 4:7966c8e3734d
395 | o 4:7966c8e3734d
396 | |
396 | |
397 | o 3:b9b85890c400
397 | o 3:b9b85890c400
398 | |
398 | |
399 | o 2:aef17e88f5f0
399 | o 2:aef17e88f5f0
400 | |
400 | |
401 | o 1:d3a0a68be6de
401 | o 1:d3a0a68be6de
402 |/
402 |/
403 o 0:eff892de26ec
403 o 0:eff892de26ec
404
404
405 *** runcommand phase -p .
405 *** runcommand phase -p .
406 *** runcommand phase .
406 *** runcommand phase .
407 5: public
407 5: public
408 *** runcommand branches
408 *** runcommand branches
409 default 1:731265503d86
409 default 1:731265503d86
410
410
411 $ cat >> .hg/hgrc << EOF
411 $ cat >> .hg/hgrc << EOF
412 > [experimental]
412 > [experimental]
413 > evolution=createmarkers
413 > evolution=createmarkers
414 > EOF
414 > EOF
415
415
416 >>> import os
416 >>> import os
417 >>> from hgclient import readchannel, runcommand, check
417 >>> from hgclient import readchannel, runcommand, check
418 >>> @check
418 >>> @check
419 ... def obsolete(server):
419 ... def obsolete(server):
420 ... readchannel(server)
420 ... readchannel(server)
421 ...
421 ...
422 ... runcommand(server, ['up', 'null'])
422 ... runcommand(server, ['up', 'null'])
423 ... runcommand(server, ['phase', '-df', 'tip'])
423 ... runcommand(server, ['phase', '-df', 'tip'])
424 ... cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
424 ... cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
425 ... if os.name == 'nt':
425 ... if os.name == 'nt':
426 ... cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
426 ... cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
427 ... os.system(cmd)
427 ... os.system(cmd)
428 ... runcommand(server, ['log', '--hidden'])
428 ... runcommand(server, ['log', '--hidden'])
429 ... runcommand(server, ['log'])
429 ... runcommand(server, ['log'])
430 *** runcommand up null
430 *** runcommand up null
431 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
431 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
432 *** runcommand phase -df tip
432 *** runcommand phase -df tip
433 *** runcommand log --hidden
433 *** runcommand log --hidden
434 changeset: 1:731265503d86
434 changeset: 1:731265503d86
435 tag: tip
435 tag: tip
436 user: test
436 user: test
437 date: Thu Jan 01 00:00:00 1970 +0000
437 date: Thu Jan 01 00:00:00 1970 +0000
438 summary: .
438 summary: .
439
439
440 changeset: 0:eff892de26ec
440 changeset: 0:eff892de26ec
441 bookmark: bm1
441 bookmark: bm1
442 bookmark: bm2
442 bookmark: bm2
443 bookmark: bm3
443 bookmark: bm3
444 user: test
444 user: test
445 date: Thu Jan 01 00:00:00 1970 +0000
445 date: Thu Jan 01 00:00:00 1970 +0000
446 summary: 1
446 summary: 1
447
447
448 *** runcommand log
448 *** runcommand log
449 changeset: 0:eff892de26ec
449 changeset: 0:eff892de26ec
450 bookmark: bm1
450 bookmark: bm1
451 bookmark: bm2
451 bookmark: bm2
452 bookmark: bm3
452 bookmark: bm3
453 tag: tip
453 tag: tip
454 user: test
454 user: test
455 date: Thu Jan 01 00:00:00 1970 +0000
455 date: Thu Jan 01 00:00:00 1970 +0000
456 summary: 1
456 summary: 1
457
457
458
458
459 $ cat <<EOF >> .hg/hgrc
459 $ cat <<EOF >> .hg/hgrc
460 > [extensions]
460 > [extensions]
461 > mq =
461 > mq =
462 > EOF
462 > EOF
463
463
464 >>> import os
464 >>> import os
465 >>> from hgclient import readchannel, runcommand, check
465 >>> from hgclient import readchannel, runcommand, check
466 >>> @check
466 >>> @check
467 ... def mqoutsidechanges(server):
467 ... def mqoutsidechanges(server):
468 ... readchannel(server)
468 ... readchannel(server)
469 ...
469 ...
470 ... # load repo.mq
470 ... # load repo.mq
471 ... runcommand(server, ['qapplied'])
471 ... runcommand(server, ['qapplied'])
472 ... os.system('hg qnew 0.diff')
472 ... os.system('hg qnew 0.diff')
473 ... # repo.mq should be invalidated
473 ... # repo.mq should be invalidated
474 ... runcommand(server, ['qapplied'])
474 ... runcommand(server, ['qapplied'])
475 ...
475 ...
476 ... runcommand(server, ['qpop', '--all'])
476 ... runcommand(server, ['qpop', '--all'])
477 ... os.system('hg qqueue --create foo')
477 ... os.system('hg qqueue --create foo')
478 ... # repo.mq should be recreated to point to new queue
478 ... # repo.mq should be recreated to point to new queue
479 ... runcommand(server, ['qqueue', '--active'])
479 ... runcommand(server, ['qqueue', '--active'])
480 *** runcommand qapplied
480 *** runcommand qapplied
481 *** runcommand qapplied
481 *** runcommand qapplied
482 0.diff
482 0.diff
483 *** runcommand qpop --all
483 *** runcommand qpop --all
484 popping 0.diff
484 popping 0.diff
485 patch queue now empty
485 patch queue now empty
486 *** runcommand qqueue --active
486 *** runcommand qqueue --active
487 foo
487 foo
488
488
489 $ cat <<EOF > dbgui.py
489 $ cat <<EOF > dbgui.py
490 > from mercurial import cmdutil, commands
490 > from mercurial import cmdutil, commands
491 > cmdtable = {}
491 > cmdtable = {}
492 > command = cmdutil.command(cmdtable)
492 > command = cmdutil.command(cmdtable)
493 > @command("debuggetpass", norepo=True)
493 > @command("debuggetpass", norepo=True)
494 > def debuggetpass(ui):
494 > def debuggetpass(ui):
495 > ui.write("%s\\n" % ui.getpass())
495 > ui.write("%s\\n" % ui.getpass())
496 > @command("debugprompt", norepo=True)
496 > @command("debugprompt", norepo=True)
497 > def debugprompt(ui):
497 > def debugprompt(ui):
498 > ui.write("%s\\n" % ui.prompt("prompt:"))
498 > ui.write("%s\\n" % ui.prompt("prompt:"))
499 > EOF
499 > EOF
500 $ cat <<EOF >> .hg/hgrc
500 $ cat <<EOF >> .hg/hgrc
501 > [extensions]
501 > [extensions]
502 > dbgui = dbgui.py
502 > dbgui = dbgui.py
503 > EOF
503 > EOF
504
504
505 >>> import cStringIO
505 >>> import cStringIO
506 >>> from hgclient import readchannel, runcommand, check
506 >>> from hgclient import readchannel, runcommand, check
507 >>> @check
507 >>> @check
508 ... def getpass(server):
508 ... def getpass(server):
509 ... readchannel(server)
509 ... readchannel(server)
510 ... runcommand(server, ['debuggetpass', '--config',
510 ... runcommand(server, ['debuggetpass', '--config',
511 ... 'ui.interactive=True'],
511 ... 'ui.interactive=True'],
512 ... input=cStringIO.StringIO('1234\n'))
512 ... input=cStringIO.StringIO('1234\n'))
513 ... runcommand(server, ['debugprompt', '--config',
513 ... runcommand(server, ['debugprompt', '--config',
514 ... 'ui.interactive=True'],
514 ... 'ui.interactive=True'],
515 ... input=cStringIO.StringIO('5678\n'))
515 ... input=cStringIO.StringIO('5678\n'))
516 *** runcommand debuggetpass --config ui.interactive=True
516 *** runcommand debuggetpass --config ui.interactive=True
517 password: 1234
517 password: 1234
518 *** runcommand debugprompt --config ui.interactive=True
518 *** runcommand debugprompt --config ui.interactive=True
519 prompt: 5678
519 prompt: 5678
520
520
521
521
522 start without repository:
522 start without repository:
523
523
524 $ cd ..
524 $ cd ..
525
525
526 >>> from hgclient import readchannel, runcommand, check
526 >>> from hgclient import readchannel, runcommand, check
527 >>> @check
527 >>> @check
528 ... def hellomessage(server):
528 ... def hellomessage(server):
529 ... ch, data = readchannel(server)
529 ... ch, data = readchannel(server)
530 ... print '%c, %r' % (ch, data)
530 ... print '%c, %r' % (ch, data)
531 ... # run an arbitrary command to make sure the next thing the server
531 ... # run an arbitrary command to make sure the next thing the server
532 ... # sends isn't part of the hello message
532 ... # sends isn't part of the hello message
533 ... runcommand(server, ['id'])
533 ... runcommand(server, ['id'])
534 o, 'capabilities: getencoding runcommand\nencoding: *' (glob)
534 o, 'capabilities: getencoding runcommand\nencoding: *' (glob)
535 *** runcommand id
535 *** runcommand id
536 abort: there is no Mercurial repository here (.hg not found)
536 abort: there is no Mercurial repository here (.hg not found)
537 [255]
537 [255]
538
538
539 >>> from hgclient import readchannel, runcommand, check
539 >>> from hgclient import readchannel, runcommand, check
540 >>> @check
540 >>> @check
541 ... def startwithoutrepo(server):
541 ... def startwithoutrepo(server):
542 ... readchannel(server)
542 ... readchannel(server)
543 ... runcommand(server, ['init', 'repo2'])
543 ... runcommand(server, ['init', 'repo2'])
544 ... runcommand(server, ['id', '-R', 'repo2'])
544 ... runcommand(server, ['id', '-R', 'repo2'])
545 *** runcommand init repo2
545 *** runcommand init repo2
546 *** runcommand id -R repo2
546 *** runcommand id -R repo2
547 000000000000 tip
547 000000000000 tip
548
549
550 unix domain socket:
551
552 $ cd repo
553 $ hg update -q
554
555 #if unix-socket
556
557 >>> import cStringIO
558 >>> from hgclient import unixserver, readchannel, runcommand, check
559 >>> server = unixserver('.hg/server.sock', '.hg/server.log')
560 >>> def hellomessage(conn):
561 ... ch, data = readchannel(conn)
562 ... print '%c, %r' % (ch, data)
563 ... runcommand(conn, ['id'])
564 >>> check(hellomessage, server.connect)
565 o, 'capabilities: getencoding runcommand\nencoding: *' (glob)
566 *** runcommand id
567 eff892de26ec tip bm1/bm2/bm3
568 >>> def unknowncommand(conn):
569 ... readchannel(conn)
570 ... conn.stdin.write('unknowncommand\n')
571 >>> check(unknowncommand, server.connect) # error sent to server.log
572 >>> def serverinput(conn):
573 ... readchannel(conn)
574 ... patch = """
575 ... # HG changeset patch
576 ... # User test
577 ... # Date 0 0
578 ... 2
579 ...
580 ... diff -r eff892de26ec -r 1ed24be7e7a0 a
581 ... --- a/a
582 ... +++ b/a
583 ... @@ -1,1 +1,2 @@
584 ... 1
585 ... +2
586 ... """
587 ... runcommand(conn, ['import', '-'], input=cStringIO.StringIO(patch))
588 ... runcommand(conn, ['log', '-rtip', '-q'])
589 >>> check(serverinput, server.connect)
590 *** runcommand import -
591 applying patch from stdin
592 *** runcommand log -rtip -q
593 2:1ed24be7e7a0
594 >>> server.shutdown()
595
596 $ cat .hg/server.log
597 listening at .hg/server.sock
598 abort: unknown command unknowncommand
599 killed!
600
601 #else
602
603 $ hg serve --cmdserver unix -a .hg/server.sock
604 abort: unsupported platform
605 [255]
606
607 #endif
General Comments 0
You need to be logged in to leave comments. Login now