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