##// END OF EJS Templates
cmdserver: protect pipe server streams against corruption caused by direct io...
Yuya Nishihara -
r23324:69f86b93 default
parent child Browse files
Show More
@@ -1,324 +1,353 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 os, errno, traceback, SocketServer
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 hellomsg += '\n'
235 hellomsg += '\n'
236 hellomsg += 'pid: %d' % os.getpid()
236 hellomsg += 'pid: %d' % os.getpid()
237
237
238 # write the hello msg in -one- chunk
238 # write the hello msg in -one- chunk
239 self.cout.write(hellomsg)
239 self.cout.write(hellomsg)
240
240
241 try:
241 try:
242 while self.serveone():
242 while self.serveone():
243 pass
243 pass
244 except EOFError:
244 except EOFError:
245 # we'll get here if the client disconnected while we were reading
245 # we'll get here if the client disconnected while we were reading
246 # its request
246 # its request
247 return 1
247 return 1
248
248
249 return 0
249 return 0
250
250
251 def _protectio(ui):
252 """ duplicates streams and redirect original to null if ui uses stdio """
253 ui.flush()
254 newfiles = []
255 nullfd = os.open(os.devnull, os.O_RDWR)
256 for f, sysf, mode in [(ui.fin, sys.stdin, 'rb'),
257 (ui.fout, sys.stdout, 'wb')]:
258 if f is sysf:
259 newfd = os.dup(f.fileno())
260 os.dup2(nullfd, f.fileno())
261 f = os.fdopen(newfd, mode)
262 newfiles.append(f)
263 os.close(nullfd)
264 return tuple(newfiles)
265
266 def _restoreio(ui, fin, fout):
267 """ restores streams from duplicated ones """
268 ui.flush()
269 for f, uif in [(fin, ui.fin), (fout, ui.fout)]:
270 if f is not uif:
271 os.dup2(f.fileno(), uif.fileno())
272 f.close()
273
251 class pipeservice(object):
274 class pipeservice(object):
252 def __init__(self, ui, repo, opts):
275 def __init__(self, ui, repo, opts):
253 self.ui = ui
276 self.ui = ui
254 self.repo = repo
277 self.repo = repo
255
278
256 def init(self):
279 def init(self):
257 pass
280 pass
258
281
259 def run(self):
282 def run(self):
260 ui = self.ui
283 ui = self.ui
261 sv = server(ui, self.repo, ui.fin, ui.fout)
284 # redirect stdio to null device so that broken extensions or in-process
262 return sv.serve()
285 # hooks will never cause corruption of channel protocol.
286 fin, fout = _protectio(ui)
287 try:
288 sv = server(ui, self.repo, fin, fout)
289 return sv.serve()
290 finally:
291 _restoreio(ui, fin, fout)
263
292
264 class _requesthandler(SocketServer.StreamRequestHandler):
293 class _requesthandler(SocketServer.StreamRequestHandler):
265 def handle(self):
294 def handle(self):
266 ui = self.server.ui
295 ui = self.server.ui
267 repo = self.server.repo
296 repo = self.server.repo
268 sv = server(ui, repo, self.rfile, self.wfile)
297 sv = server(ui, repo, self.rfile, self.wfile)
269 try:
298 try:
270 try:
299 try:
271 sv.serve()
300 sv.serve()
272 # handle exceptions that may be raised by command server. most of
301 # handle exceptions that may be raised by command server. most of
273 # known exceptions are caught by dispatch.
302 # known exceptions are caught by dispatch.
274 except util.Abort, inst:
303 except util.Abort, inst:
275 ui.warn(_('abort: %s\n') % inst)
304 ui.warn(_('abort: %s\n') % inst)
276 except IOError, inst:
305 except IOError, inst:
277 if inst.errno != errno.EPIPE:
306 if inst.errno != errno.EPIPE:
278 raise
307 raise
279 except KeyboardInterrupt:
308 except KeyboardInterrupt:
280 pass
309 pass
281 except: # re-raises
310 except: # re-raises
282 # also write traceback to error channel. otherwise client cannot
311 # also write traceback to error channel. otherwise client cannot
283 # see it because it is written to server's stderr by default.
312 # see it because it is written to server's stderr by default.
284 traceback.print_exc(file=sv.cerr)
313 traceback.print_exc(file=sv.cerr)
285 raise
314 raise
286
315
287 class unixservice(object):
316 class unixservice(object):
288 """
317 """
289 Listens on unix domain socket and forks server per connection
318 Listens on unix domain socket and forks server per connection
290 """
319 """
291 def __init__(self, ui, repo, opts):
320 def __init__(self, ui, repo, opts):
292 self.ui = ui
321 self.ui = ui
293 self.repo = repo
322 self.repo = repo
294 self.address = opts['address']
323 self.address = opts['address']
295 if not util.safehasattr(SocketServer, 'UnixStreamServer'):
324 if not util.safehasattr(SocketServer, 'UnixStreamServer'):
296 raise util.Abort(_('unsupported platform'))
325 raise util.Abort(_('unsupported platform'))
297 if not self.address:
326 if not self.address:
298 raise util.Abort(_('no socket path specified with --address'))
327 raise util.Abort(_('no socket path specified with --address'))
299
328
300 def init(self):
329 def init(self):
301 class cls(SocketServer.ForkingMixIn, SocketServer.UnixStreamServer):
330 class cls(SocketServer.ForkingMixIn, SocketServer.UnixStreamServer):
302 ui = self.ui
331 ui = self.ui
303 repo = self.repo
332 repo = self.repo
304 self.server = cls(self.address, _requesthandler)
333 self.server = cls(self.address, _requesthandler)
305 self.ui.status(_('listening at %s\n') % self.address)
334 self.ui.status(_('listening at %s\n') % self.address)
306 self.ui.flush() # avoid buffering of status message
335 self.ui.flush() # avoid buffering of status message
307
336
308 def run(self):
337 def run(self):
309 try:
338 try:
310 self.server.serve_forever()
339 self.server.serve_forever()
311 finally:
340 finally:
312 os.unlink(self.address)
341 os.unlink(self.address)
313
342
314 _servicemap = {
343 _servicemap = {
315 'pipe': pipeservice,
344 'pipe': pipeservice,
316 'unix': unixservice,
345 'unix': unixservice,
317 }
346 }
318
347
319 def createservice(ui, repo, opts):
348 def createservice(ui, repo, opts):
320 mode = opts['cmdserver']
349 mode = opts['cmdserver']
321 try:
350 try:
322 return _servicemap[mode](ui, repo, opts)
351 return _servicemap[mode](ui, repo, opts)
323 except KeyError:
352 except KeyError:
324 raise util.Abort(_('unknown mode %s') % mode)
353 raise util.Abort(_('unknown mode %s') % mode)
@@ -1,633 +1,647 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 typical client does not want echo-back messages, so test without it:
8 typical client does not want echo-back messages, so test without it:
9
9
10 $ grep -v '^promptecho ' < $HGRCPATH >> $HGRCPATH.new
10 $ grep -v '^promptecho ' < $HGRCPATH >> $HGRCPATH.new
11 $ mv $HGRCPATH.new $HGRCPATH
11 $ mv $HGRCPATH.new $HGRCPATH
12
12
13 $ hg init repo
13 $ hg init repo
14 $ cd repo
14 $ cd repo
15
15
16 >>> from hgclient import readchannel, runcommand, check
16 >>> from hgclient import readchannel, runcommand, check
17 >>> @check
17 >>> @check
18 ... def hellomessage(server):
18 ... def hellomessage(server):
19 ... ch, data = readchannel(server)
19 ... ch, data = readchannel(server)
20 ... print '%c, %r' % (ch, data)
20 ... print '%c, %r' % (ch, data)
21 ... # run an arbitrary command to make sure the next thing the server
21 ... # run an arbitrary command to make sure the next thing the server
22 ... # sends isn't part of the hello message
22 ... # sends isn't part of the hello message
23 ... runcommand(server, ['id'])
23 ... runcommand(server, ['id'])
24 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
24 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
25 *** runcommand id
25 *** runcommand id
26 000000000000 tip
26 000000000000 tip
27
27
28 >>> from hgclient import check
28 >>> from hgclient import check
29 >>> @check
29 >>> @check
30 ... def unknowncommand(server):
30 ... def unknowncommand(server):
31 ... server.stdin.write('unknowncommand\n')
31 ... server.stdin.write('unknowncommand\n')
32 abort: unknown command unknowncommand
32 abort: unknown command unknowncommand
33
33
34 >>> from hgclient import readchannel, runcommand, check
34 >>> from hgclient import readchannel, runcommand, check
35 >>> @check
35 >>> @check
36 ... def checkruncommand(server):
36 ... def checkruncommand(server):
37 ... # hello block
37 ... # hello block
38 ... readchannel(server)
38 ... readchannel(server)
39 ...
39 ...
40 ... # no args
40 ... # no args
41 ... runcommand(server, [])
41 ... runcommand(server, [])
42 ...
42 ...
43 ... # global options
43 ... # global options
44 ... runcommand(server, ['id', '--quiet'])
44 ... runcommand(server, ['id', '--quiet'])
45 ...
45 ...
46 ... # make sure global options don't stick through requests
46 ... # make sure global options don't stick through requests
47 ... runcommand(server, ['id'])
47 ... runcommand(server, ['id'])
48 ...
48 ...
49 ... # --config
49 ... # --config
50 ... runcommand(server, ['id', '--config', 'ui.quiet=True'])
50 ... runcommand(server, ['id', '--config', 'ui.quiet=True'])
51 ...
51 ...
52 ... # make sure --config doesn't stick
52 ... # make sure --config doesn't stick
53 ... runcommand(server, ['id'])
53 ... runcommand(server, ['id'])
54 ...
54 ...
55 ... # negative return code should be masked
55 ... # negative return code should be masked
56 ... runcommand(server, ['id', '-runknown'])
56 ... runcommand(server, ['id', '-runknown'])
57 *** runcommand
57 *** runcommand
58 Mercurial Distributed SCM
58 Mercurial Distributed SCM
59
59
60 basic commands:
60 basic commands:
61
61
62 add add the specified files on the next commit
62 add add the specified files on the next commit
63 annotate show changeset information by line for each file
63 annotate show changeset information by line for each file
64 clone make a copy of an existing repository
64 clone make a copy of an existing repository
65 commit commit the specified files or all outstanding changes
65 commit commit the specified files or all outstanding changes
66 diff diff repository (or selected files)
66 diff diff repository (or selected files)
67 export dump the header and diffs for one or more changesets
67 export dump the header and diffs for one or more changesets
68 forget forget the specified files on the next commit
68 forget forget the specified files on the next commit
69 init create a new repository in the given directory
69 init create a new repository in the given directory
70 log show revision history of entire repository or files
70 log show revision history of entire repository or files
71 merge merge working directory with another revision
71 merge merge working directory with another revision
72 pull pull changes from the specified source
72 pull pull changes from the specified source
73 push push changes to the specified destination
73 push push changes to the specified destination
74 remove remove the specified files on the next commit
74 remove remove the specified files on the next commit
75 serve start stand-alone webserver
75 serve start stand-alone webserver
76 status show changed files in the working directory
76 status show changed files in the working directory
77 summary summarize working directory state
77 summary summarize working directory state
78 update update working directory (or switch revisions)
78 update update working directory (or switch revisions)
79
79
80 (use "hg help" for the full list of commands or "hg -v" for details)
80 (use "hg help" for the full list of commands or "hg -v" for details)
81 *** runcommand id --quiet
81 *** runcommand id --quiet
82 000000000000
82 000000000000
83 *** runcommand id
83 *** runcommand id
84 000000000000 tip
84 000000000000 tip
85 *** runcommand id --config ui.quiet=True
85 *** runcommand id --config ui.quiet=True
86 000000000000
86 000000000000
87 *** runcommand id
87 *** runcommand id
88 000000000000 tip
88 000000000000 tip
89 *** runcommand id -runknown
89 *** runcommand id -runknown
90 abort: unknown revision 'unknown'!
90 abort: unknown revision 'unknown'!
91 [255]
91 [255]
92
92
93 >>> from hgclient import readchannel, check
93 >>> from hgclient import readchannel, check
94 >>> @check
94 >>> @check
95 ... def inputeof(server):
95 ... def inputeof(server):
96 ... readchannel(server)
96 ... readchannel(server)
97 ... server.stdin.write('runcommand\n')
97 ... server.stdin.write('runcommand\n')
98 ... # close stdin while server is waiting for input
98 ... # close stdin while server is waiting for input
99 ... server.stdin.close()
99 ... server.stdin.close()
100 ...
100 ...
101 ... # server exits with 1 if the pipe closed while reading the command
101 ... # server exits with 1 if the pipe closed while reading the command
102 ... print 'server exit code =', server.wait()
102 ... print 'server exit code =', server.wait()
103 server exit code = 1
103 server exit code = 1
104
104
105 >>> import cStringIO
105 >>> import cStringIO
106 >>> from hgclient import readchannel, runcommand, check
106 >>> from hgclient import readchannel, runcommand, check
107 >>> @check
107 >>> @check
108 ... def serverinput(server):
108 ... def serverinput(server):
109 ... readchannel(server)
109 ... readchannel(server)
110 ...
110 ...
111 ... patch = """
111 ... patch = """
112 ... # HG changeset patch
112 ... # HG changeset patch
113 ... # User test
113 ... # User test
114 ... # Date 0 0
114 ... # Date 0 0
115 ... # Node ID c103a3dec114d882c98382d684d8af798d09d857
115 ... # Node ID c103a3dec114d882c98382d684d8af798d09d857
116 ... # Parent 0000000000000000000000000000000000000000
116 ... # Parent 0000000000000000000000000000000000000000
117 ... 1
117 ... 1
118 ...
118 ...
119 ... diff -r 000000000000 -r c103a3dec114 a
119 ... diff -r 000000000000 -r c103a3dec114 a
120 ... --- /dev/null Thu Jan 01 00:00:00 1970 +0000
120 ... --- /dev/null Thu Jan 01 00:00:00 1970 +0000
121 ... +++ b/a Thu Jan 01 00:00:00 1970 +0000
121 ... +++ b/a Thu Jan 01 00:00:00 1970 +0000
122 ... @@ -0,0 +1,1 @@
122 ... @@ -0,0 +1,1 @@
123 ... +1
123 ... +1
124 ... """
124 ... """
125 ...
125 ...
126 ... runcommand(server, ['import', '-'], input=cStringIO.StringIO(patch))
126 ... runcommand(server, ['import', '-'], input=cStringIO.StringIO(patch))
127 ... runcommand(server, ['log'])
127 ... runcommand(server, ['log'])
128 *** runcommand import -
128 *** runcommand import -
129 applying patch from stdin
129 applying patch from stdin
130 *** runcommand log
130 *** runcommand log
131 changeset: 0:eff892de26ec
131 changeset: 0:eff892de26ec
132 tag: tip
132 tag: tip
133 user: test
133 user: test
134 date: Thu Jan 01 00:00:00 1970 +0000
134 date: Thu Jan 01 00:00:00 1970 +0000
135 summary: 1
135 summary: 1
136
136
137
137
138 check that --cwd doesn't persist between requests:
138 check that --cwd doesn't persist between requests:
139
139
140 $ mkdir foo
140 $ mkdir foo
141 $ touch foo/bar
141 $ touch foo/bar
142 >>> from hgclient import readchannel, runcommand, check
142 >>> from hgclient import readchannel, runcommand, check
143 >>> @check
143 >>> @check
144 ... def cwd(server):
144 ... def cwd(server):
145 ... readchannel(server)
145 ... readchannel(server)
146 ... runcommand(server, ['--cwd', 'foo', 'st', 'bar'])
146 ... runcommand(server, ['--cwd', 'foo', 'st', 'bar'])
147 ... runcommand(server, ['st', 'foo/bar'])
147 ... runcommand(server, ['st', 'foo/bar'])
148 *** runcommand --cwd foo st bar
148 *** runcommand --cwd foo st bar
149 ? bar
149 ? bar
150 *** runcommand st foo/bar
150 *** runcommand st foo/bar
151 ? foo/bar
151 ? foo/bar
152
152
153 $ rm foo/bar
153 $ rm foo/bar
154
154
155
155
156 check that local configs for the cached repo aren't inherited when -R is used:
156 check that local configs for the cached repo aren't inherited when -R is used:
157
157
158 $ cat <<EOF >> .hg/hgrc
158 $ cat <<EOF >> .hg/hgrc
159 > [ui]
159 > [ui]
160 > foo = bar
160 > foo = bar
161 > EOF
161 > EOF
162
162
163 >>> from hgclient import readchannel, sep, runcommand, check
163 >>> from hgclient import readchannel, sep, runcommand, check
164 >>> @check
164 >>> @check
165 ... def localhgrc(server):
165 ... def localhgrc(server):
166 ... readchannel(server)
166 ... readchannel(server)
167 ...
167 ...
168 ... # the cached repo local hgrc contains ui.foo=bar, so showconfig should
168 ... # the cached repo local hgrc contains ui.foo=bar, so showconfig should
169 ... # show it
169 ... # show it
170 ... runcommand(server, ['showconfig'], outfilter=sep)
170 ... runcommand(server, ['showconfig'], outfilter=sep)
171 ...
171 ...
172 ... # but not for this repo
172 ... # but not for this repo
173 ... runcommand(server, ['init', 'foo'])
173 ... runcommand(server, ['init', 'foo'])
174 ... runcommand(server, ['-R', 'foo', 'showconfig', 'ui', 'defaults'])
174 ... runcommand(server, ['-R', 'foo', 'showconfig', 'ui', 'defaults'])
175 *** runcommand showconfig
175 *** runcommand showconfig
176 bundle.mainreporoot=$TESTTMP/repo
176 bundle.mainreporoot=$TESTTMP/repo
177 defaults.backout=-d "0 0"
177 defaults.backout=-d "0 0"
178 defaults.commit=-d "0 0"
178 defaults.commit=-d "0 0"
179 defaults.shelve=--date "0 0"
179 defaults.shelve=--date "0 0"
180 defaults.tag=-d "0 0"
180 defaults.tag=-d "0 0"
181 ui.slash=True
181 ui.slash=True
182 ui.interactive=False
182 ui.interactive=False
183 ui.mergemarkers=detailed
183 ui.mergemarkers=detailed
184 ui.foo=bar
184 ui.foo=bar
185 ui.nontty=true
185 ui.nontty=true
186 *** runcommand init foo
186 *** runcommand init foo
187 *** runcommand -R foo showconfig ui defaults
187 *** runcommand -R foo showconfig ui defaults
188 defaults.backout=-d "0 0"
188 defaults.backout=-d "0 0"
189 defaults.commit=-d "0 0"
189 defaults.commit=-d "0 0"
190 defaults.shelve=--date "0 0"
190 defaults.shelve=--date "0 0"
191 defaults.tag=-d "0 0"
191 defaults.tag=-d "0 0"
192 ui.slash=True
192 ui.slash=True
193 ui.interactive=False
193 ui.interactive=False
194 ui.mergemarkers=detailed
194 ui.mergemarkers=detailed
195 ui.nontty=true
195 ui.nontty=true
196
196
197 $ rm -R foo
197 $ rm -R foo
198
198
199 #if windows
199 #if windows
200 $ PYTHONPATH="$TESTTMP/repo;$PYTHONPATH"
200 $ PYTHONPATH="$TESTTMP/repo;$PYTHONPATH"
201 #else
201 #else
202 $ PYTHONPATH="$TESTTMP/repo:$PYTHONPATH"
202 $ PYTHONPATH="$TESTTMP/repo:$PYTHONPATH"
203 #endif
203 #endif
204
204
205 $ cat <<EOF > hook.py
205 $ cat <<EOF > hook.py
206 > import sys
206 > import sys
207 > def hook(**args):
207 > def hook(**args):
208 > print 'hook talking'
208 > print 'hook talking'
209 > print 'now try to read something: %r' % sys.stdin.read()
209 > print 'now try to read something: %r' % sys.stdin.read()
210 > EOF
210 > EOF
211
211
212 >>> import cStringIO
212 >>> import cStringIO
213 >>> from hgclient import readchannel, runcommand, check
213 >>> from hgclient import readchannel, runcommand, check
214 >>> @check
214 >>> @check
215 ... def hookoutput(server):
215 ... def hookoutput(server):
216 ... readchannel(server)
216 ... readchannel(server)
217 ... runcommand(server, ['--config',
217 ... runcommand(server, ['--config',
218 ... 'hooks.pre-identify=python:hook.hook',
218 ... 'hooks.pre-identify=python:hook.hook',
219 ... 'id'],
219 ... 'id'],
220 ... input=cStringIO.StringIO('some input'))
220 ... input=cStringIO.StringIO('some input'))
221 *** runcommand --config hooks.pre-identify=python:hook.hook id
221 *** runcommand --config hooks.pre-identify=python:hook.hook id
222 hook talking
222 hook talking
223 now try to read something: 'some input'
223 now try to read something: 'some input'
224 eff892de26ec tip
224 eff892de26ec tip
225
225
226 $ rm hook.py*
226 $ rm hook.py*
227
227
228 $ echo a >> a
228 $ echo a >> a
229 >>> import os
229 >>> import os
230 >>> from hgclient import readchannel, runcommand, check
230 >>> from hgclient import readchannel, runcommand, check
231 >>> @check
231 >>> @check
232 ... def outsidechanges(server):
232 ... def outsidechanges(server):
233 ... readchannel(server)
233 ... readchannel(server)
234 ... runcommand(server, ['status'])
234 ... runcommand(server, ['status'])
235 ... os.system('hg ci -Am2')
235 ... os.system('hg ci -Am2')
236 ... runcommand(server, ['tip'])
236 ... runcommand(server, ['tip'])
237 ... runcommand(server, ['status'])
237 ... runcommand(server, ['status'])
238 *** runcommand status
238 *** runcommand status
239 M a
239 M a
240 *** runcommand tip
240 *** runcommand tip
241 changeset: 1:d3a0a68be6de
241 changeset: 1:d3a0a68be6de
242 tag: tip
242 tag: tip
243 user: test
243 user: test
244 date: Thu Jan 01 00:00:00 1970 +0000
244 date: Thu Jan 01 00:00:00 1970 +0000
245 summary: 2
245 summary: 2
246
246
247 *** runcommand status
247 *** runcommand status
248
248
249 >>> import os
249 >>> import os
250 >>> from hgclient import readchannel, runcommand, check
250 >>> from hgclient import readchannel, runcommand, check
251 >>> @check
251 >>> @check
252 ... def bookmarks(server):
252 ... def bookmarks(server):
253 ... readchannel(server)
253 ... readchannel(server)
254 ... runcommand(server, ['bookmarks'])
254 ... runcommand(server, ['bookmarks'])
255 ...
255 ...
256 ... # changes .hg/bookmarks
256 ... # changes .hg/bookmarks
257 ... os.system('hg bookmark -i bm1')
257 ... os.system('hg bookmark -i bm1')
258 ... os.system('hg bookmark -i bm2')
258 ... os.system('hg bookmark -i bm2')
259 ... runcommand(server, ['bookmarks'])
259 ... runcommand(server, ['bookmarks'])
260 ...
260 ...
261 ... # changes .hg/bookmarks.current
261 ... # changes .hg/bookmarks.current
262 ... os.system('hg upd bm1 -q')
262 ... os.system('hg upd bm1 -q')
263 ... runcommand(server, ['bookmarks'])
263 ... runcommand(server, ['bookmarks'])
264 ...
264 ...
265 ... runcommand(server, ['bookmarks', 'bm3'])
265 ... runcommand(server, ['bookmarks', 'bm3'])
266 ... f = open('a', 'ab')
266 ... f = open('a', 'ab')
267 ... f.write('a\n')
267 ... f.write('a\n')
268 ... f.close()
268 ... f.close()
269 ... runcommand(server, ['commit', '-Amm'])
269 ... runcommand(server, ['commit', '-Amm'])
270 ... runcommand(server, ['bookmarks'])
270 ... runcommand(server, ['bookmarks'])
271 *** runcommand bookmarks
271 *** runcommand bookmarks
272 no bookmarks set
272 no bookmarks set
273 *** runcommand bookmarks
273 *** runcommand bookmarks
274 bm1 1:d3a0a68be6de
274 bm1 1:d3a0a68be6de
275 bm2 1:d3a0a68be6de
275 bm2 1:d3a0a68be6de
276 *** runcommand bookmarks
276 *** runcommand bookmarks
277 * bm1 1:d3a0a68be6de
277 * bm1 1:d3a0a68be6de
278 bm2 1:d3a0a68be6de
278 bm2 1:d3a0a68be6de
279 *** runcommand bookmarks bm3
279 *** runcommand bookmarks bm3
280 *** runcommand commit -Amm
280 *** runcommand commit -Amm
281 *** runcommand bookmarks
281 *** runcommand bookmarks
282 bm1 1:d3a0a68be6de
282 bm1 1:d3a0a68be6de
283 bm2 1:d3a0a68be6de
283 bm2 1:d3a0a68be6de
284 * bm3 2:aef17e88f5f0
284 * bm3 2:aef17e88f5f0
285
285
286 >>> import os
286 >>> import os
287 >>> from hgclient import readchannel, runcommand, check
287 >>> from hgclient import readchannel, runcommand, check
288 >>> @check
288 >>> @check
289 ... def tagscache(server):
289 ... def tagscache(server):
290 ... readchannel(server)
290 ... readchannel(server)
291 ... runcommand(server, ['id', '-t', '-r', '0'])
291 ... runcommand(server, ['id', '-t', '-r', '0'])
292 ... os.system('hg tag -r 0 foo')
292 ... os.system('hg tag -r 0 foo')
293 ... runcommand(server, ['id', '-t', '-r', '0'])
293 ... runcommand(server, ['id', '-t', '-r', '0'])
294 *** runcommand id -t -r 0
294 *** runcommand id -t -r 0
295
295
296 *** runcommand id -t -r 0
296 *** runcommand id -t -r 0
297 foo
297 foo
298
298
299 >>> import os
299 >>> import os
300 >>> from hgclient import readchannel, runcommand, check
300 >>> from hgclient import readchannel, runcommand, check
301 >>> @check
301 >>> @check
302 ... def setphase(server):
302 ... def setphase(server):
303 ... readchannel(server)
303 ... readchannel(server)
304 ... runcommand(server, ['phase', '-r', '.'])
304 ... runcommand(server, ['phase', '-r', '.'])
305 ... os.system('hg phase -r . -p')
305 ... os.system('hg phase -r . -p')
306 ... runcommand(server, ['phase', '-r', '.'])
306 ... runcommand(server, ['phase', '-r', '.'])
307 *** runcommand phase -r .
307 *** runcommand phase -r .
308 3: draft
308 3: draft
309 *** runcommand phase -r .
309 *** runcommand phase -r .
310 3: public
310 3: public
311
311
312 $ echo a >> a
312 $ echo a >> a
313 >>> from hgclient import readchannel, runcommand, check
313 >>> from hgclient import readchannel, runcommand, check
314 >>> @check
314 >>> @check
315 ... def rollback(server):
315 ... def rollback(server):
316 ... readchannel(server)
316 ... readchannel(server)
317 ... runcommand(server, ['phase', '-r', '.', '-p'])
317 ... runcommand(server, ['phase', '-r', '.', '-p'])
318 ... runcommand(server, ['commit', '-Am.'])
318 ... runcommand(server, ['commit', '-Am.'])
319 ... runcommand(server, ['rollback'])
319 ... runcommand(server, ['rollback'])
320 ... runcommand(server, ['phase', '-r', '.'])
320 ... runcommand(server, ['phase', '-r', '.'])
321 *** runcommand phase -r . -p
321 *** runcommand phase -r . -p
322 no phases changed
322 no phases changed
323 [1]
323 [1]
324 *** runcommand commit -Am.
324 *** runcommand commit -Am.
325 *** runcommand rollback
325 *** runcommand rollback
326 repository tip rolled back to revision 3 (undo commit)
326 repository tip rolled back to revision 3 (undo commit)
327 working directory now based on revision 3
327 working directory now based on revision 3
328 *** runcommand phase -r .
328 *** runcommand phase -r .
329 3: public
329 3: public
330
330
331 >>> import os
331 >>> import os
332 >>> from hgclient import readchannel, runcommand, check
332 >>> from hgclient import readchannel, runcommand, check
333 >>> @check
333 >>> @check
334 ... def branch(server):
334 ... def branch(server):
335 ... readchannel(server)
335 ... readchannel(server)
336 ... runcommand(server, ['branch'])
336 ... runcommand(server, ['branch'])
337 ... os.system('hg branch foo')
337 ... os.system('hg branch foo')
338 ... runcommand(server, ['branch'])
338 ... runcommand(server, ['branch'])
339 ... os.system('hg branch default')
339 ... os.system('hg branch default')
340 *** runcommand branch
340 *** runcommand branch
341 default
341 default
342 marked working directory as branch foo
342 marked working directory as branch foo
343 (branches are permanent and global, did you want a bookmark?)
343 (branches are permanent and global, did you want a bookmark?)
344 *** runcommand branch
344 *** runcommand branch
345 foo
345 foo
346 marked working directory as branch default
346 marked working directory as branch default
347 (branches are permanent and global, did you want a bookmark?)
347 (branches are permanent and global, did you want a bookmark?)
348
348
349 $ touch .hgignore
349 $ touch .hgignore
350 >>> import os
350 >>> import os
351 >>> from hgclient import readchannel, runcommand, check
351 >>> from hgclient import readchannel, runcommand, check
352 >>> @check
352 >>> @check
353 ... def hgignore(server):
353 ... def hgignore(server):
354 ... readchannel(server)
354 ... readchannel(server)
355 ... runcommand(server, ['commit', '-Am.'])
355 ... runcommand(server, ['commit', '-Am.'])
356 ... f = open('ignored-file', 'ab')
356 ... f = open('ignored-file', 'ab')
357 ... f.write('')
357 ... f.write('')
358 ... f.close()
358 ... f.close()
359 ... f = open('.hgignore', 'ab')
359 ... f = open('.hgignore', 'ab')
360 ... f.write('ignored-file')
360 ... f.write('ignored-file')
361 ... f.close()
361 ... f.close()
362 ... runcommand(server, ['status', '-i', '-u'])
362 ... runcommand(server, ['status', '-i', '-u'])
363 *** runcommand commit -Am.
363 *** runcommand commit -Am.
364 adding .hgignore
364 adding .hgignore
365 *** runcommand status -i -u
365 *** runcommand status -i -u
366 I ignored-file
366 I ignored-file
367
367
368 >>> import os
368 >>> import os
369 >>> from hgclient import readchannel, sep, runcommand, check
369 >>> from hgclient import readchannel, sep, runcommand, check
370 >>> @check
370 >>> @check
371 ... def phasecacheafterstrip(server):
371 ... def phasecacheafterstrip(server):
372 ... readchannel(server)
372 ... readchannel(server)
373 ...
373 ...
374 ... # create new head, 5:731265503d86
374 ... # create new head, 5:731265503d86
375 ... runcommand(server, ['update', '-C', '0'])
375 ... runcommand(server, ['update', '-C', '0'])
376 ... f = open('a', 'ab')
376 ... f = open('a', 'ab')
377 ... f.write('a\n')
377 ... f.write('a\n')
378 ... f.close()
378 ... f.close()
379 ... runcommand(server, ['commit', '-Am.', 'a'])
379 ... runcommand(server, ['commit', '-Am.', 'a'])
380 ... runcommand(server, ['log', '-Gq'])
380 ... runcommand(server, ['log', '-Gq'])
381 ...
381 ...
382 ... # make it public; draft marker moves to 4:7966c8e3734d
382 ... # make it public; draft marker moves to 4:7966c8e3734d
383 ... runcommand(server, ['phase', '-p', '.'])
383 ... runcommand(server, ['phase', '-p', '.'])
384 ... # load _phasecache.phaseroots
384 ... # load _phasecache.phaseroots
385 ... runcommand(server, ['phase', '.'], outfilter=sep)
385 ... runcommand(server, ['phase', '.'], outfilter=sep)
386 ...
386 ...
387 ... # strip 1::4 outside server
387 ... # strip 1::4 outside server
388 ... os.system('hg -q --config extensions.mq= strip 1')
388 ... os.system('hg -q --config extensions.mq= strip 1')
389 ...
389 ...
390 ... # shouldn't raise "7966c8e3734d: no node!"
390 ... # shouldn't raise "7966c8e3734d: no node!"
391 ... runcommand(server, ['branches'])
391 ... runcommand(server, ['branches'])
392 *** runcommand update -C 0
392 *** runcommand update -C 0
393 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
393 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
394 (leaving bookmark bm3)
394 (leaving bookmark bm3)
395 *** runcommand commit -Am. a
395 *** runcommand commit -Am. a
396 created new head
396 created new head
397 *** runcommand log -Gq
397 *** runcommand log -Gq
398 @ 5:731265503d86
398 @ 5:731265503d86
399 |
399 |
400 | o 4:7966c8e3734d
400 | o 4:7966c8e3734d
401 | |
401 | |
402 | o 3:b9b85890c400
402 | o 3:b9b85890c400
403 | |
403 | |
404 | o 2:aef17e88f5f0
404 | o 2:aef17e88f5f0
405 | |
405 | |
406 | o 1:d3a0a68be6de
406 | o 1:d3a0a68be6de
407 |/
407 |/
408 o 0:eff892de26ec
408 o 0:eff892de26ec
409
409
410 *** runcommand phase -p .
410 *** runcommand phase -p .
411 *** runcommand phase .
411 *** runcommand phase .
412 5: public
412 5: public
413 *** runcommand branches
413 *** runcommand branches
414 default 1:731265503d86
414 default 1:731265503d86
415
415
416 $ cat >> .hg/hgrc << EOF
416 $ cat >> .hg/hgrc << EOF
417 > [experimental]
417 > [experimental]
418 > evolution=createmarkers
418 > evolution=createmarkers
419 > EOF
419 > EOF
420
420
421 >>> import os
421 >>> import os
422 >>> from hgclient import readchannel, runcommand, check
422 >>> from hgclient import readchannel, runcommand, check
423 >>> @check
423 >>> @check
424 ... def obsolete(server):
424 ... def obsolete(server):
425 ... readchannel(server)
425 ... readchannel(server)
426 ...
426 ...
427 ... runcommand(server, ['up', 'null'])
427 ... runcommand(server, ['up', 'null'])
428 ... runcommand(server, ['phase', '-df', 'tip'])
428 ... runcommand(server, ['phase', '-df', 'tip'])
429 ... cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
429 ... cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
430 ... if os.name == 'nt':
430 ... if os.name == 'nt':
431 ... cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
431 ... cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
432 ... os.system(cmd)
432 ... os.system(cmd)
433 ... runcommand(server, ['log', '--hidden'])
433 ... runcommand(server, ['log', '--hidden'])
434 ... runcommand(server, ['log'])
434 ... runcommand(server, ['log'])
435 *** runcommand up null
435 *** runcommand up null
436 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
436 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
437 *** runcommand phase -df tip
437 *** runcommand phase -df tip
438 *** runcommand log --hidden
438 *** runcommand log --hidden
439 changeset: 1:731265503d86
439 changeset: 1:731265503d86
440 tag: tip
440 tag: tip
441 user: test
441 user: test
442 date: Thu Jan 01 00:00:00 1970 +0000
442 date: Thu Jan 01 00:00:00 1970 +0000
443 summary: .
443 summary: .
444
444
445 changeset: 0:eff892de26ec
445 changeset: 0:eff892de26ec
446 bookmark: bm1
446 bookmark: bm1
447 bookmark: bm2
447 bookmark: bm2
448 bookmark: bm3
448 bookmark: bm3
449 user: test
449 user: test
450 date: Thu Jan 01 00:00:00 1970 +0000
450 date: Thu Jan 01 00:00:00 1970 +0000
451 summary: 1
451 summary: 1
452
452
453 *** runcommand log
453 *** runcommand log
454 changeset: 0:eff892de26ec
454 changeset: 0:eff892de26ec
455 bookmark: bm1
455 bookmark: bm1
456 bookmark: bm2
456 bookmark: bm2
457 bookmark: bm3
457 bookmark: bm3
458 tag: tip
458 tag: tip
459 user: test
459 user: test
460 date: Thu Jan 01 00:00:00 1970 +0000
460 date: Thu Jan 01 00:00:00 1970 +0000
461 summary: 1
461 summary: 1
462
462
463
463
464 $ cat <<EOF >> .hg/hgrc
464 $ cat <<EOF >> .hg/hgrc
465 > [extensions]
465 > [extensions]
466 > mq =
466 > mq =
467 > EOF
467 > EOF
468
468
469 >>> import os
469 >>> import os
470 >>> from hgclient import readchannel, runcommand, check
470 >>> from hgclient import readchannel, runcommand, check
471 >>> @check
471 >>> @check
472 ... def mqoutsidechanges(server):
472 ... def mqoutsidechanges(server):
473 ... readchannel(server)
473 ... readchannel(server)
474 ...
474 ...
475 ... # load repo.mq
475 ... # load repo.mq
476 ... runcommand(server, ['qapplied'])
476 ... runcommand(server, ['qapplied'])
477 ... os.system('hg qnew 0.diff')
477 ... os.system('hg qnew 0.diff')
478 ... # repo.mq should be invalidated
478 ... # repo.mq should be invalidated
479 ... runcommand(server, ['qapplied'])
479 ... runcommand(server, ['qapplied'])
480 ...
480 ...
481 ... runcommand(server, ['qpop', '--all'])
481 ... runcommand(server, ['qpop', '--all'])
482 ... os.system('hg qqueue --create foo')
482 ... os.system('hg qqueue --create foo')
483 ... # repo.mq should be recreated to point to new queue
483 ... # repo.mq should be recreated to point to new queue
484 ... runcommand(server, ['qqueue', '--active'])
484 ... runcommand(server, ['qqueue', '--active'])
485 *** runcommand qapplied
485 *** runcommand qapplied
486 *** runcommand qapplied
486 *** runcommand qapplied
487 0.diff
487 0.diff
488 *** runcommand qpop --all
488 *** runcommand qpop --all
489 popping 0.diff
489 popping 0.diff
490 patch queue now empty
490 patch queue now empty
491 *** runcommand qqueue --active
491 *** runcommand qqueue --active
492 foo
492 foo
493
493
494 $ cat <<EOF > dbgui.py
494 $ cat <<EOF > dbgui.py
495 > import os, sys
495 > from mercurial import cmdutil, commands
496 > from mercurial import cmdutil, commands
496 > cmdtable = {}
497 > cmdtable = {}
497 > command = cmdutil.command(cmdtable)
498 > command = cmdutil.command(cmdtable)
498 > @command("debuggetpass", norepo=True)
499 > @command("debuggetpass", norepo=True)
499 > def debuggetpass(ui):
500 > def debuggetpass(ui):
500 > ui.write("%s\\n" % ui.getpass())
501 > ui.write("%s\\n" % ui.getpass())
501 > @command("debugprompt", norepo=True)
502 > @command("debugprompt", norepo=True)
502 > def debugprompt(ui):
503 > def debugprompt(ui):
503 > ui.write("%s\\n" % ui.prompt("prompt:"))
504 > ui.write("%s\\n" % ui.prompt("prompt:"))
505 > @command("debugreadstdin", norepo=True)
506 > def debugreadstdin(ui):
507 > ui.write("read: %r\n" % sys.stdin.read(1))
508 > @command("debugwritestdout", norepo=True)
509 > def debugwritestdout(ui):
510 > os.write(1, "low-level stdout fd and\n")
511 > sys.stdout.write("stdout should be redirected to /dev/null\n")
512 > sys.stdout.flush()
504 > EOF
513 > EOF
505 $ cat <<EOF >> .hg/hgrc
514 $ cat <<EOF >> .hg/hgrc
506 > [extensions]
515 > [extensions]
507 > dbgui = dbgui.py
516 > dbgui = dbgui.py
508 > EOF
517 > EOF
509
518
510 >>> import cStringIO
519 >>> import cStringIO
511 >>> from hgclient import readchannel, runcommand, check
520 >>> from hgclient import readchannel, runcommand, check
512 >>> @check
521 >>> @check
513 ... def getpass(server):
522 ... def getpass(server):
514 ... readchannel(server)
523 ... readchannel(server)
515 ... runcommand(server, ['debuggetpass', '--config',
524 ... runcommand(server, ['debuggetpass', '--config',
516 ... 'ui.interactive=True'],
525 ... 'ui.interactive=True'],
517 ... input=cStringIO.StringIO('1234\n'))
526 ... input=cStringIO.StringIO('1234\n'))
518 ... runcommand(server, ['debugprompt', '--config',
527 ... runcommand(server, ['debugprompt', '--config',
519 ... 'ui.interactive=True'],
528 ... 'ui.interactive=True'],
520 ... input=cStringIO.StringIO('5678\n'))
529 ... input=cStringIO.StringIO('5678\n'))
530 ... runcommand(server, ['debugreadstdin'])
531 ... runcommand(server, ['debugwritestdout'])
521 *** runcommand debuggetpass --config ui.interactive=True
532 *** runcommand debuggetpass --config ui.interactive=True
522 password: 1234
533 password: 1234
523 *** runcommand debugprompt --config ui.interactive=True
534 *** runcommand debugprompt --config ui.interactive=True
524 prompt: 5678
535 prompt: 5678
536 *** runcommand debugreadstdin
537 read: ''
538 *** runcommand debugwritestdout
525
539
526
540
527 run commandserver in commandserver, which is silly but should work:
541 run commandserver in commandserver, which is silly but should work:
528
542
529 >>> import cStringIO
543 >>> import cStringIO
530 >>> from hgclient import readchannel, runcommand, check
544 >>> from hgclient import readchannel, runcommand, check
531 >>> @check
545 >>> @check
532 ... def nested(server):
546 ... def nested(server):
533 ... print '%c, %r' % readchannel(server)
547 ... print '%c, %r' % readchannel(server)
534 ... class nestedserver(object):
548 ... class nestedserver(object):
535 ... stdin = cStringIO.StringIO('getencoding\n')
549 ... stdin = cStringIO.StringIO('getencoding\n')
536 ... stdout = cStringIO.StringIO()
550 ... stdout = cStringIO.StringIO()
537 ... runcommand(server, ['serve', '--cmdserver', 'pipe'],
551 ... runcommand(server, ['serve', '--cmdserver', 'pipe'],
538 ... output=nestedserver.stdout, input=nestedserver.stdin)
552 ... output=nestedserver.stdout, input=nestedserver.stdin)
539 ... nestedserver.stdout.seek(0)
553 ... nestedserver.stdout.seek(0)
540 ... print '%c, %r' % readchannel(nestedserver) # hello
554 ... print '%c, %r' % readchannel(nestedserver) # hello
541 ... print '%c, %r' % readchannel(nestedserver) # getencoding
555 ... print '%c, %r' % readchannel(nestedserver) # getencoding
542 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
556 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
543 *** runcommand serve --cmdserver pipe
557 *** runcommand serve --cmdserver pipe
544 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
558 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
545 r, '*' (glob)
559 r, '*' (glob)
546
560
547
561
548 start without repository:
562 start without repository:
549
563
550 $ cd ..
564 $ cd ..
551
565
552 >>> from hgclient import readchannel, runcommand, check
566 >>> from hgclient import readchannel, runcommand, check
553 >>> @check
567 >>> @check
554 ... def hellomessage(server):
568 ... def hellomessage(server):
555 ... ch, data = readchannel(server)
569 ... ch, data = readchannel(server)
556 ... print '%c, %r' % (ch, data)
570 ... print '%c, %r' % (ch, data)
557 ... # run an arbitrary command to make sure the next thing the server
571 ... # run an arbitrary command to make sure the next thing the server
558 ... # sends isn't part of the hello message
572 ... # sends isn't part of the hello message
559 ... runcommand(server, ['id'])
573 ... runcommand(server, ['id'])
560 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
574 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
561 *** runcommand id
575 *** runcommand id
562 abort: there is no Mercurial repository here (.hg not found)
576 abort: there is no Mercurial repository here (.hg not found)
563 [255]
577 [255]
564
578
565 >>> from hgclient import readchannel, runcommand, check
579 >>> from hgclient import readchannel, runcommand, check
566 >>> @check
580 >>> @check
567 ... def startwithoutrepo(server):
581 ... def startwithoutrepo(server):
568 ... readchannel(server)
582 ... readchannel(server)
569 ... runcommand(server, ['init', 'repo2'])
583 ... runcommand(server, ['init', 'repo2'])
570 ... runcommand(server, ['id', '-R', 'repo2'])
584 ... runcommand(server, ['id', '-R', 'repo2'])
571 *** runcommand init repo2
585 *** runcommand init repo2
572 *** runcommand id -R repo2
586 *** runcommand id -R repo2
573 000000000000 tip
587 000000000000 tip
574
588
575
589
576 unix domain socket:
590 unix domain socket:
577
591
578 $ cd repo
592 $ cd repo
579 $ hg update -q
593 $ hg update -q
580
594
581 #if unix-socket unix-permissions
595 #if unix-socket unix-permissions
582
596
583 >>> import cStringIO
597 >>> import cStringIO
584 >>> from hgclient import unixserver, readchannel, runcommand, check
598 >>> from hgclient import unixserver, readchannel, runcommand, check
585 >>> server = unixserver('.hg/server.sock', '.hg/server.log')
599 >>> server = unixserver('.hg/server.sock', '.hg/server.log')
586 >>> def hellomessage(conn):
600 >>> def hellomessage(conn):
587 ... ch, data = readchannel(conn)
601 ... ch, data = readchannel(conn)
588 ... print '%c, %r' % (ch, data)
602 ... print '%c, %r' % (ch, data)
589 ... runcommand(conn, ['id'])
603 ... runcommand(conn, ['id'])
590 >>> check(hellomessage, server.connect)
604 >>> check(hellomessage, server.connect)
591 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
605 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
592 *** runcommand id
606 *** runcommand id
593 eff892de26ec tip bm1/bm2/bm3
607 eff892de26ec tip bm1/bm2/bm3
594 >>> def unknowncommand(conn):
608 >>> def unknowncommand(conn):
595 ... readchannel(conn)
609 ... readchannel(conn)
596 ... conn.stdin.write('unknowncommand\n')
610 ... conn.stdin.write('unknowncommand\n')
597 >>> check(unknowncommand, server.connect) # error sent to server.log
611 >>> check(unknowncommand, server.connect) # error sent to server.log
598 >>> def serverinput(conn):
612 >>> def serverinput(conn):
599 ... readchannel(conn)
613 ... readchannel(conn)
600 ... patch = """
614 ... patch = """
601 ... # HG changeset patch
615 ... # HG changeset patch
602 ... # User test
616 ... # User test
603 ... # Date 0 0
617 ... # Date 0 0
604 ... 2
618 ... 2
605 ...
619 ...
606 ... diff -r eff892de26ec -r 1ed24be7e7a0 a
620 ... diff -r eff892de26ec -r 1ed24be7e7a0 a
607 ... --- a/a
621 ... --- a/a
608 ... +++ b/a
622 ... +++ b/a
609 ... @@ -1,1 +1,2 @@
623 ... @@ -1,1 +1,2 @@
610 ... 1
624 ... 1
611 ... +2
625 ... +2
612 ... """
626 ... """
613 ... runcommand(conn, ['import', '-'], input=cStringIO.StringIO(patch))
627 ... runcommand(conn, ['import', '-'], input=cStringIO.StringIO(patch))
614 ... runcommand(conn, ['log', '-rtip', '-q'])
628 ... runcommand(conn, ['log', '-rtip', '-q'])
615 >>> check(serverinput, server.connect)
629 >>> check(serverinput, server.connect)
616 *** runcommand import -
630 *** runcommand import -
617 applying patch from stdin
631 applying patch from stdin
618 *** runcommand log -rtip -q
632 *** runcommand log -rtip -q
619 2:1ed24be7e7a0
633 2:1ed24be7e7a0
620 >>> server.shutdown()
634 >>> server.shutdown()
621
635
622 $ cat .hg/server.log
636 $ cat .hg/server.log
623 listening at .hg/server.sock
637 listening at .hg/server.sock
624 abort: unknown command unknowncommand
638 abort: unknown command unknowncommand
625 killed!
639 killed!
626 #endif
640 #endif
627 #if no-unix-socket
641 #if no-unix-socket
628
642
629 $ hg serve --cmdserver unix -a .hg/server.sock
643 $ hg serve --cmdserver unix -a .hg/server.sock
630 abort: unsupported platform
644 abort: unsupported platform
631 [255]
645 [255]
632
646
633 #endif
647 #endif
General Comments 0
You need to be logged in to leave comments. Login now