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