##// END OF EJS Templates
cmdserver: forcibly use L channel to read password input (issue3161)...
Yuya Nishihara -
r21195:9336bc7d stable
parent child Browse files
Show More
@@ -1,245 +1,251 b''
1 # commandserver.py - communicate with Mercurial's API over a pipe
1 # commandserver.py - communicate with Mercurial's API over a pipe
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import struct
9 import struct
10 import sys, os
10 import sys, os
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 from in_ to out in the following format:
26 Write data from in_ 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, in_, out, channel):
31 def __init__(self, in_, out, channel):
32 self.in_ = in_
32 self.in_ = in_
33 self.out = out
33 self.out = out
34 self.channel = channel
34 self.channel = channel
35
35
36 def write(self, data):
36 def write(self, data):
37 if not data:
37 if not data:
38 return
38 return
39 self.out.write(struct.pack('>cI', self.channel, len(data)))
39 self.out.write(struct.pack('>cI', self.channel, len(data)))
40 self.out.write(data)
40 self.out.write(data)
41 self.out.flush()
41 self.out.flush()
42
42
43 def __getattr__(self, attr):
43 def __getattr__(self, attr):
44 if attr in ('isatty', 'fileno'):
44 if attr in ('isatty', 'fileno'):
45 raise AttributeError(attr)
45 raise AttributeError(attr)
46 return getattr(self.in_, attr)
46 return getattr(self.in_, attr)
47
47
48 class channeledinput(object):
48 class channeledinput(object):
49 """
49 """
50 Read data from in_.
50 Read data from in_.
51
51
52 Requests for input are written to out in the following format:
52 Requests for input are written to out in the following format:
53 channel identifier - 'I' for plain input, 'L' line based (1 byte)
53 channel identifier - 'I' for plain input, 'L' line based (1 byte)
54 how many bytes to send at most (unsigned int),
54 how many bytes to send at most (unsigned int),
55
55
56 The client replies with:
56 The client replies with:
57 data length (unsigned int), 0 meaning EOF
57 data length (unsigned int), 0 meaning EOF
58 data
58 data
59 """
59 """
60
60
61 maxchunksize = 4 * 1024
61 maxchunksize = 4 * 1024
62
62
63 def __init__(self, in_, out, channel):
63 def __init__(self, in_, out, channel):
64 self.in_ = in_
64 self.in_ = in_
65 self.out = out
65 self.out = out
66 self.channel = channel
66 self.channel = channel
67
67
68 def read(self, size=-1):
68 def read(self, size=-1):
69 if size < 0:
69 if size < 0:
70 # if we need to consume all the clients input, ask for 4k chunks
70 # if we need to consume all the clients input, ask for 4k chunks
71 # so the pipe doesn't fill up risking a deadlock
71 # so the pipe doesn't fill up risking a deadlock
72 size = self.maxchunksize
72 size = self.maxchunksize
73 s = self._read(size, self.channel)
73 s = self._read(size, self.channel)
74 buf = s
74 buf = s
75 while s:
75 while s:
76 s = self._read(size, self.channel)
76 s = self._read(size, self.channel)
77 buf += s
77 buf += s
78
78
79 return buf
79 return buf
80 else:
80 else:
81 return self._read(size, self.channel)
81 return self._read(size, self.channel)
82
82
83 def _read(self, size, channel):
83 def _read(self, size, channel):
84 if not size:
84 if not size:
85 return ''
85 return ''
86 assert size > 0
86 assert size > 0
87
87
88 # tell the client we need at most size bytes
88 # tell the client we need at most size bytes
89 self.out.write(struct.pack('>cI', channel, size))
89 self.out.write(struct.pack('>cI', channel, size))
90 self.out.flush()
90 self.out.flush()
91
91
92 length = self.in_.read(4)
92 length = self.in_.read(4)
93 length = struct.unpack('>I', length)[0]
93 length = struct.unpack('>I', length)[0]
94 if not length:
94 if not length:
95 return ''
95 return ''
96 else:
96 else:
97 return self.in_.read(length)
97 return self.in_.read(length)
98
98
99 def readline(self, size=-1):
99 def readline(self, size=-1):
100 if size < 0:
100 if size < 0:
101 size = self.maxchunksize
101 size = self.maxchunksize
102 s = self._read(size, 'L')
102 s = self._read(size, 'L')
103 buf = s
103 buf = s
104 # keep asking for more until there's either no more or
104 # keep asking for more until there's either no more or
105 # we got a full line
105 # we got a full line
106 while s and s[-1] != '\n':
106 while s and s[-1] != '\n':
107 s = self._read(size, 'L')
107 s = self._read(size, 'L')
108 buf += s
108 buf += s
109
109
110 return buf
110 return buf
111 else:
111 else:
112 return self._read(size, 'L')
112 return self._read(size, 'L')
113
113
114 def __iter__(self):
114 def __iter__(self):
115 return self
115 return self
116
116
117 def next(self):
117 def next(self):
118 l = self.readline()
118 l = self.readline()
119 if not l:
119 if not l:
120 raise StopIteration
120 raise StopIteration
121 return l
121 return l
122
122
123 def __getattr__(self, attr):
123 def __getattr__(self, attr):
124 if attr in ('isatty', 'fileno'):
124 if attr in ('isatty', 'fileno'):
125 raise AttributeError(attr)
125 raise AttributeError(attr)
126 return getattr(self.in_, attr)
126 return getattr(self.in_, attr)
127
127
128 class server(object):
128 class server(object):
129 """
129 """
130 Listens for commands on stdin, runs them and writes the output on a channel
130 Listens for commands on stdin, runs them and writes the output on a channel
131 based stream to stdout.
131 based stream to stdout.
132 """
132 """
133 def __init__(self, ui, repo, mode):
133 def __init__(self, ui, repo, mode):
134 self.cwd = os.getcwd()
134 self.cwd = os.getcwd()
135
135
136 logpath = ui.config("cmdserver", "log", None)
136 logpath = ui.config("cmdserver", "log", None)
137 if logpath:
137 if logpath:
138 global logfile
138 global logfile
139 if logpath == '-':
139 if logpath == '-':
140 # write log on a special 'd' (debug) channel
140 # write log on a special 'd' (debug) channel
141 logfile = channeledoutput(sys.stdout, sys.stdout, 'd')
141 logfile = channeledoutput(sys.stdout, sys.stdout, 'd')
142 else:
142 else:
143 logfile = open(logpath, 'a')
143 logfile = open(logpath, 'a')
144
144
145 if repo:
145 if repo:
146 # the ui here is really the repo ui so take its baseui so we don't
146 # the ui here is really the repo ui so take its baseui so we don't
147 # end up with its local configuration
147 # end up with its local configuration
148 self.ui = repo.baseui
148 self.ui = repo.baseui
149 self.repo = repo
149 self.repo = repo
150 self.repoui = repo.ui
150 self.repoui = repo.ui
151 else:
151 else:
152 self.ui = ui
152 self.ui = ui
153 self.repo = self.repoui = None
153 self.repo = self.repoui = None
154
154
155 if mode == 'pipe':
155 if mode == 'pipe':
156 self.cerr = channeledoutput(sys.stderr, sys.stdout, 'e')
156 self.cerr = channeledoutput(sys.stderr, sys.stdout, 'e')
157 self.cout = channeledoutput(sys.stdout, sys.stdout, 'o')
157 self.cout = channeledoutput(sys.stdout, sys.stdout, 'o')
158 self.cin = channeledinput(sys.stdin, sys.stdout, 'I')
158 self.cin = channeledinput(sys.stdin, sys.stdout, 'I')
159 self.cresult = channeledoutput(sys.stdout, sys.stdout, 'r')
159 self.cresult = channeledoutput(sys.stdout, sys.stdout, 'r')
160
160
161 self.client = sys.stdin
161 self.client = sys.stdin
162 else:
162 else:
163 raise util.Abort(_('unknown mode %s') % mode)
163 raise util.Abort(_('unknown mode %s') % mode)
164
164
165 def _read(self, size):
165 def _read(self, size):
166 if not size:
166 if not size:
167 return ''
167 return ''
168
168
169 data = self.client.read(size)
169 data = self.client.read(size)
170
170
171 # is the other end closed?
171 # is the other end closed?
172 if not data:
172 if not data:
173 raise EOFError
173 raise EOFError
174
174
175 return data
175 return data
176
176
177 def runcommand(self):
177 def runcommand(self):
178 """ reads a list of \0 terminated arguments, executes
178 """ reads a list of \0 terminated arguments, executes
179 and writes the return code to the result channel """
179 and writes the return code to the result channel """
180
180
181 length = struct.unpack('>I', self._read(4))[0]
181 length = struct.unpack('>I', self._read(4))[0]
182 if not length:
182 if not length:
183 args = []
183 args = []
184 else:
184 else:
185 args = self._read(length).split('\0')
185 args = self._read(length).split('\0')
186
186
187 # copy the uis so changes (e.g. --config or --verbose) don't
187 # copy the uis so changes (e.g. --config or --verbose) don't
188 # persist between requests
188 # persist between requests
189 copiedui = self.ui.copy()
189 copiedui = self.ui.copy()
190 uis = [copiedui]
190 if self.repo:
191 if self.repo:
191 self.repo.baseui = copiedui
192 self.repo.baseui = copiedui
192 # clone ui without using ui.copy because this is protected
193 # clone ui without using ui.copy because this is protected
193 repoui = self.repoui.__class__(self.repoui)
194 repoui = self.repoui.__class__(self.repoui)
194 repoui.copy = copiedui.copy # redo copy protection
195 repoui.copy = copiedui.copy # redo copy protection
196 uis.append(repoui)
195 self.repo.ui = self.repo.dirstate._ui = repoui
197 self.repo.ui = self.repo.dirstate._ui = repoui
196 self.repo.invalidateall()
198 self.repo.invalidateall()
197
199
200 for ui in uis:
201 # any kind of interaction must use server channels
202 ui.setconfig('ui', 'nontty', 'true', 'commandserver')
203
198 req = dispatch.request(args[:], copiedui, self.repo, self.cin,
204 req = dispatch.request(args[:], copiedui, self.repo, self.cin,
199 self.cout, self.cerr)
205 self.cout, self.cerr)
200
206
201 ret = (dispatch.dispatch(req) or 0) & 255 # might return None
207 ret = (dispatch.dispatch(req) or 0) & 255 # might return None
202
208
203 # restore old cwd
209 # restore old cwd
204 if '--cwd' in args:
210 if '--cwd' in args:
205 os.chdir(self.cwd)
211 os.chdir(self.cwd)
206
212
207 self.cresult.write(struct.pack('>i', int(ret)))
213 self.cresult.write(struct.pack('>i', int(ret)))
208
214
209 def getencoding(self):
215 def getencoding(self):
210 """ writes the current encoding to the result channel """
216 """ writes the current encoding to the result channel """
211 self.cresult.write(encoding.encoding)
217 self.cresult.write(encoding.encoding)
212
218
213 def serveone(self):
219 def serveone(self):
214 cmd = self.client.readline()[:-1]
220 cmd = self.client.readline()[:-1]
215 if cmd:
221 if cmd:
216 handler = self.capabilities.get(cmd)
222 handler = self.capabilities.get(cmd)
217 if handler:
223 if handler:
218 handler(self)
224 handler(self)
219 else:
225 else:
220 # clients are expected to check what commands are supported by
226 # clients are expected to check what commands are supported by
221 # looking at the servers capabilities
227 # looking at the servers capabilities
222 raise util.Abort(_('unknown command %s') % cmd)
228 raise util.Abort(_('unknown command %s') % cmd)
223
229
224 return cmd != ''
230 return cmd != ''
225
231
226 capabilities = {'runcommand' : runcommand,
232 capabilities = {'runcommand' : runcommand,
227 'getencoding' : getencoding}
233 'getencoding' : getencoding}
228
234
229 def serve(self):
235 def serve(self):
230 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
236 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities))
231 hellomsg += '\n'
237 hellomsg += '\n'
232 hellomsg += 'encoding: ' + encoding.encoding
238 hellomsg += 'encoding: ' + encoding.encoding
233
239
234 # write the hello msg in -one- chunk
240 # write the hello msg in -one- chunk
235 self.cout.write(hellomsg)
241 self.cout.write(hellomsg)
236
242
237 try:
243 try:
238 while self.serveone():
244 while self.serveone():
239 pass
245 pass
240 except EOFError:
246 except EOFError:
241 # we'll get here if the client disconnected while we were reading
247 # we'll get here if the client disconnected while we were reading
242 # its request
248 # its request
243 return 1
249 return 1
244
250
245 return 0
251 return 0
@@ -1,846 +1,851 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 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 errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, scmutil, util, error, formatter
10 import config, scmutil, util, error, formatter
11 from node import hex
11 from node import hex
12
12
13 class ui(object):
13 class ui(object):
14 def __init__(self, src=None):
14 def __init__(self, src=None):
15 # _buffers: used for temporary capture of output
15 # _buffers: used for temporary capture of output
16 self._buffers = []
16 self._buffers = []
17 # _bufferstates: Should the temporary capture includes stderr
17 # _bufferstates: Should the temporary capture includes stderr
18 self._bufferstates = []
18 self._bufferstates = []
19 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
19 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
20 self._reportuntrusted = True
20 self._reportuntrusted = True
21 self._ocfg = config.config() # overlay
21 self._ocfg = config.config() # overlay
22 self._tcfg = config.config() # trusted
22 self._tcfg = config.config() # trusted
23 self._ucfg = config.config() # untrusted
23 self._ucfg = config.config() # untrusted
24 self._trustusers = set()
24 self._trustusers = set()
25 self._trustgroups = set()
25 self._trustgroups = set()
26 self.callhooks = True
26 self.callhooks = True
27
27
28 if src:
28 if src:
29 self.fout = src.fout
29 self.fout = src.fout
30 self.ferr = src.ferr
30 self.ferr = src.ferr
31 self.fin = src.fin
31 self.fin = src.fin
32
32
33 self._tcfg = src._tcfg.copy()
33 self._tcfg = src._tcfg.copy()
34 self._ucfg = src._ucfg.copy()
34 self._ucfg = src._ucfg.copy()
35 self._ocfg = src._ocfg.copy()
35 self._ocfg = src._ocfg.copy()
36 self._trustusers = src._trustusers.copy()
36 self._trustusers = src._trustusers.copy()
37 self._trustgroups = src._trustgroups.copy()
37 self._trustgroups = src._trustgroups.copy()
38 self.environ = src.environ
38 self.environ = src.environ
39 self.callhooks = src.callhooks
39 self.callhooks = src.callhooks
40 self.fixconfig()
40 self.fixconfig()
41 else:
41 else:
42 self.fout = sys.stdout
42 self.fout = sys.stdout
43 self.ferr = sys.stderr
43 self.ferr = sys.stderr
44 self.fin = sys.stdin
44 self.fin = sys.stdin
45
45
46 # shared read-only environment
46 # shared read-only environment
47 self.environ = os.environ
47 self.environ = os.environ
48 # we always trust global config files
48 # we always trust global config files
49 for f in scmutil.rcpath():
49 for f in scmutil.rcpath():
50 self.readconfig(f, trust=True)
50 self.readconfig(f, trust=True)
51
51
52 def copy(self):
52 def copy(self):
53 return self.__class__(self)
53 return self.__class__(self)
54
54
55 def formatter(self, topic, opts):
55 def formatter(self, topic, opts):
56 return formatter.formatter(self, topic, opts)
56 return formatter.formatter(self, topic, opts)
57
57
58 def _trusted(self, fp, f):
58 def _trusted(self, fp, f):
59 st = util.fstat(fp)
59 st = util.fstat(fp)
60 if util.isowner(st):
60 if util.isowner(st):
61 return True
61 return True
62
62
63 tusers, tgroups = self._trustusers, self._trustgroups
63 tusers, tgroups = self._trustusers, self._trustgroups
64 if '*' in tusers or '*' in tgroups:
64 if '*' in tusers or '*' in tgroups:
65 return True
65 return True
66
66
67 user = util.username(st.st_uid)
67 user = util.username(st.st_uid)
68 group = util.groupname(st.st_gid)
68 group = util.groupname(st.st_gid)
69 if user in tusers or group in tgroups or user == util.username():
69 if user in tusers or group in tgroups or user == util.username():
70 return True
70 return True
71
71
72 if self._reportuntrusted:
72 if self._reportuntrusted:
73 self.warn(_('not trusting file %s from untrusted '
73 self.warn(_('not trusting file %s from untrusted '
74 'user %s, group %s\n') % (f, user, group))
74 'user %s, group %s\n') % (f, user, group))
75 return False
75 return False
76
76
77 def readconfig(self, filename, root=None, trust=False,
77 def readconfig(self, filename, root=None, trust=False,
78 sections=None, remap=None):
78 sections=None, remap=None):
79 try:
79 try:
80 fp = open(filename)
80 fp = open(filename)
81 except IOError:
81 except IOError:
82 if not sections: # ignore unless we were looking for something
82 if not sections: # ignore unless we were looking for something
83 return
83 return
84 raise
84 raise
85
85
86 cfg = config.config()
86 cfg = config.config()
87 trusted = sections or trust or self._trusted(fp, filename)
87 trusted = sections or trust or self._trusted(fp, filename)
88
88
89 try:
89 try:
90 cfg.read(filename, fp, sections=sections, remap=remap)
90 cfg.read(filename, fp, sections=sections, remap=remap)
91 fp.close()
91 fp.close()
92 except error.ConfigError, inst:
92 except error.ConfigError, inst:
93 if trusted:
93 if trusted:
94 raise
94 raise
95 self.warn(_("ignored: %s\n") % str(inst))
95 self.warn(_("ignored: %s\n") % str(inst))
96
96
97 if self.plain():
97 if self.plain():
98 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
98 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
99 'logtemplate', 'style',
99 'logtemplate', 'style',
100 'traceback', 'verbose'):
100 'traceback', 'verbose'):
101 if k in cfg['ui']:
101 if k in cfg['ui']:
102 del cfg['ui'][k]
102 del cfg['ui'][k]
103 for k, v in cfg.items('defaults'):
103 for k, v in cfg.items('defaults'):
104 del cfg['defaults'][k]
104 del cfg['defaults'][k]
105 # Don't remove aliases from the configuration if in the exceptionlist
105 # Don't remove aliases from the configuration if in the exceptionlist
106 if self.plain('alias'):
106 if self.plain('alias'):
107 for k, v in cfg.items('alias'):
107 for k, v in cfg.items('alias'):
108 del cfg['alias'][k]
108 del cfg['alias'][k]
109
109
110 if trusted:
110 if trusted:
111 self._tcfg.update(cfg)
111 self._tcfg.update(cfg)
112 self._tcfg.update(self._ocfg)
112 self._tcfg.update(self._ocfg)
113 self._ucfg.update(cfg)
113 self._ucfg.update(cfg)
114 self._ucfg.update(self._ocfg)
114 self._ucfg.update(self._ocfg)
115
115
116 if root is None:
116 if root is None:
117 root = os.path.expanduser('~')
117 root = os.path.expanduser('~')
118 self.fixconfig(root=root)
118 self.fixconfig(root=root)
119
119
120 def fixconfig(self, root=None, section=None):
120 def fixconfig(self, root=None, section=None):
121 if section in (None, 'paths'):
121 if section in (None, 'paths'):
122 # expand vars and ~
122 # expand vars and ~
123 # translate paths relative to root (or home) into absolute paths
123 # translate paths relative to root (or home) into absolute paths
124 root = root or os.getcwd()
124 root = root or os.getcwd()
125 for c in self._tcfg, self._ucfg, self._ocfg:
125 for c in self._tcfg, self._ucfg, self._ocfg:
126 for n, p in c.items('paths'):
126 for n, p in c.items('paths'):
127 if not p:
127 if not p:
128 continue
128 continue
129 if '%%' in p:
129 if '%%' in p:
130 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
130 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
131 % (n, p, self.configsource('paths', n)))
131 % (n, p, self.configsource('paths', n)))
132 p = p.replace('%%', '%')
132 p = p.replace('%%', '%')
133 p = util.expandpath(p)
133 p = util.expandpath(p)
134 if not util.hasscheme(p) and not os.path.isabs(p):
134 if not util.hasscheme(p) and not os.path.isabs(p):
135 p = os.path.normpath(os.path.join(root, p))
135 p = os.path.normpath(os.path.join(root, p))
136 c.set("paths", n, p)
136 c.set("paths", n, p)
137
137
138 if section in (None, 'ui'):
138 if section in (None, 'ui'):
139 # update ui options
139 # update ui options
140 self.debugflag = self.configbool('ui', 'debug')
140 self.debugflag = self.configbool('ui', 'debug')
141 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
141 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
142 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
142 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
143 if self.verbose and self.quiet:
143 if self.verbose and self.quiet:
144 self.quiet = self.verbose = False
144 self.quiet = self.verbose = False
145 self._reportuntrusted = self.debugflag or self.configbool("ui",
145 self._reportuntrusted = self.debugflag or self.configbool("ui",
146 "report_untrusted", True)
146 "report_untrusted", True)
147 self.tracebackflag = self.configbool('ui', 'traceback', False)
147 self.tracebackflag = self.configbool('ui', 'traceback', False)
148
148
149 if section in (None, 'trusted'):
149 if section in (None, 'trusted'):
150 # update trust information
150 # update trust information
151 self._trustusers.update(self.configlist('trusted', 'users'))
151 self._trustusers.update(self.configlist('trusted', 'users'))
152 self._trustgroups.update(self.configlist('trusted', 'groups'))
152 self._trustgroups.update(self.configlist('trusted', 'groups'))
153
153
154 def backupconfig(self, section, item):
154 def backupconfig(self, section, item):
155 return (self._ocfg.backup(section, item),
155 return (self._ocfg.backup(section, item),
156 self._tcfg.backup(section, item),
156 self._tcfg.backup(section, item),
157 self._ucfg.backup(section, item),)
157 self._ucfg.backup(section, item),)
158 def restoreconfig(self, data):
158 def restoreconfig(self, data):
159 self._ocfg.restore(data[0])
159 self._ocfg.restore(data[0])
160 self._tcfg.restore(data[1])
160 self._tcfg.restore(data[1])
161 self._ucfg.restore(data[2])
161 self._ucfg.restore(data[2])
162
162
163 def setconfig(self, section, name, value, source=''):
163 def setconfig(self, section, name, value, source=''):
164 for cfg in (self._ocfg, self._tcfg, self._ucfg):
164 for cfg in (self._ocfg, self._tcfg, self._ucfg):
165 cfg.set(section, name, value, source)
165 cfg.set(section, name, value, source)
166 self.fixconfig(section=section)
166 self.fixconfig(section=section)
167
167
168 def _data(self, untrusted):
168 def _data(self, untrusted):
169 return untrusted and self._ucfg or self._tcfg
169 return untrusted and self._ucfg or self._tcfg
170
170
171 def configsource(self, section, name, untrusted=False):
171 def configsource(self, section, name, untrusted=False):
172 return self._data(untrusted).source(section, name) or 'none'
172 return self._data(untrusted).source(section, name) or 'none'
173
173
174 def config(self, section, name, default=None, untrusted=False):
174 def config(self, section, name, default=None, untrusted=False):
175 if isinstance(name, list):
175 if isinstance(name, list):
176 alternates = name
176 alternates = name
177 else:
177 else:
178 alternates = [name]
178 alternates = [name]
179
179
180 for n in alternates:
180 for n in alternates:
181 value = self._data(untrusted).get(section, n, None)
181 value = self._data(untrusted).get(section, n, None)
182 if value is not None:
182 if value is not None:
183 name = n
183 name = n
184 break
184 break
185 else:
185 else:
186 value = default
186 value = default
187
187
188 if self.debugflag and not untrusted and self._reportuntrusted:
188 if self.debugflag and not untrusted and self._reportuntrusted:
189 for n in alternates:
189 for n in alternates:
190 uvalue = self._ucfg.get(section, n)
190 uvalue = self._ucfg.get(section, n)
191 if uvalue is not None and uvalue != value:
191 if uvalue is not None and uvalue != value:
192 self.debug("ignoring untrusted configuration option "
192 self.debug("ignoring untrusted configuration option "
193 "%s.%s = %s\n" % (section, n, uvalue))
193 "%s.%s = %s\n" % (section, n, uvalue))
194 return value
194 return value
195
195
196 def configpath(self, section, name, default=None, untrusted=False):
196 def configpath(self, section, name, default=None, untrusted=False):
197 'get a path config item, expanded relative to repo root or config file'
197 'get a path config item, expanded relative to repo root or config file'
198 v = self.config(section, name, default, untrusted)
198 v = self.config(section, name, default, untrusted)
199 if v is None:
199 if v is None:
200 return None
200 return None
201 if not os.path.isabs(v) or "://" not in v:
201 if not os.path.isabs(v) or "://" not in v:
202 src = self.configsource(section, name, untrusted)
202 src = self.configsource(section, name, untrusted)
203 if ':' in src:
203 if ':' in src:
204 base = os.path.dirname(src.rsplit(':')[0])
204 base = os.path.dirname(src.rsplit(':')[0])
205 v = os.path.join(base, os.path.expanduser(v))
205 v = os.path.join(base, os.path.expanduser(v))
206 return v
206 return v
207
207
208 def configbool(self, section, name, default=False, untrusted=False):
208 def configbool(self, section, name, default=False, untrusted=False):
209 """parse a configuration element as a boolean
209 """parse a configuration element as a boolean
210
210
211 >>> u = ui(); s = 'foo'
211 >>> u = ui(); s = 'foo'
212 >>> u.setconfig(s, 'true', 'yes')
212 >>> u.setconfig(s, 'true', 'yes')
213 >>> u.configbool(s, 'true')
213 >>> u.configbool(s, 'true')
214 True
214 True
215 >>> u.setconfig(s, 'false', 'no')
215 >>> u.setconfig(s, 'false', 'no')
216 >>> u.configbool(s, 'false')
216 >>> u.configbool(s, 'false')
217 False
217 False
218 >>> u.configbool(s, 'unknown')
218 >>> u.configbool(s, 'unknown')
219 False
219 False
220 >>> u.configbool(s, 'unknown', True)
220 >>> u.configbool(s, 'unknown', True)
221 True
221 True
222 >>> u.setconfig(s, 'invalid', 'somevalue')
222 >>> u.setconfig(s, 'invalid', 'somevalue')
223 >>> u.configbool(s, 'invalid')
223 >>> u.configbool(s, 'invalid')
224 Traceback (most recent call last):
224 Traceback (most recent call last):
225 ...
225 ...
226 ConfigError: foo.invalid is not a boolean ('somevalue')
226 ConfigError: foo.invalid is not a boolean ('somevalue')
227 """
227 """
228
228
229 v = self.config(section, name, None, untrusted)
229 v = self.config(section, name, None, untrusted)
230 if v is None:
230 if v is None:
231 return default
231 return default
232 if isinstance(v, bool):
232 if isinstance(v, bool):
233 return v
233 return v
234 b = util.parsebool(v)
234 b = util.parsebool(v)
235 if b is None:
235 if b is None:
236 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
236 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
237 % (section, name, v))
237 % (section, name, v))
238 return b
238 return b
239
239
240 def configint(self, section, name, default=None, untrusted=False):
240 def configint(self, section, name, default=None, untrusted=False):
241 """parse a configuration element as an integer
241 """parse a configuration element as an integer
242
242
243 >>> u = ui(); s = 'foo'
243 >>> u = ui(); s = 'foo'
244 >>> u.setconfig(s, 'int1', '42')
244 >>> u.setconfig(s, 'int1', '42')
245 >>> u.configint(s, 'int1')
245 >>> u.configint(s, 'int1')
246 42
246 42
247 >>> u.setconfig(s, 'int2', '-42')
247 >>> u.setconfig(s, 'int2', '-42')
248 >>> u.configint(s, 'int2')
248 >>> u.configint(s, 'int2')
249 -42
249 -42
250 >>> u.configint(s, 'unknown', 7)
250 >>> u.configint(s, 'unknown', 7)
251 7
251 7
252 >>> u.setconfig(s, 'invalid', 'somevalue')
252 >>> u.setconfig(s, 'invalid', 'somevalue')
253 >>> u.configint(s, 'invalid')
253 >>> u.configint(s, 'invalid')
254 Traceback (most recent call last):
254 Traceback (most recent call last):
255 ...
255 ...
256 ConfigError: foo.invalid is not an integer ('somevalue')
256 ConfigError: foo.invalid is not an integer ('somevalue')
257 """
257 """
258
258
259 v = self.config(section, name, None, untrusted)
259 v = self.config(section, name, None, untrusted)
260 if v is None:
260 if v is None:
261 return default
261 return default
262 try:
262 try:
263 return int(v)
263 return int(v)
264 except ValueError:
264 except ValueError:
265 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
265 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
266 % (section, name, v))
266 % (section, name, v))
267
267
268 def configbytes(self, section, name, default=0, untrusted=False):
268 def configbytes(self, section, name, default=0, untrusted=False):
269 """parse a configuration element as a quantity in bytes
269 """parse a configuration element as a quantity in bytes
270
270
271 Units can be specified as b (bytes), k or kb (kilobytes), m or
271 Units can be specified as b (bytes), k or kb (kilobytes), m or
272 mb (megabytes), g or gb (gigabytes).
272 mb (megabytes), g or gb (gigabytes).
273
273
274 >>> u = ui(); s = 'foo'
274 >>> u = ui(); s = 'foo'
275 >>> u.setconfig(s, 'val1', '42')
275 >>> u.setconfig(s, 'val1', '42')
276 >>> u.configbytes(s, 'val1')
276 >>> u.configbytes(s, 'val1')
277 42
277 42
278 >>> u.setconfig(s, 'val2', '42.5 kb')
278 >>> u.setconfig(s, 'val2', '42.5 kb')
279 >>> u.configbytes(s, 'val2')
279 >>> u.configbytes(s, 'val2')
280 43520
280 43520
281 >>> u.configbytes(s, 'unknown', '7 MB')
281 >>> u.configbytes(s, 'unknown', '7 MB')
282 7340032
282 7340032
283 >>> u.setconfig(s, 'invalid', 'somevalue')
283 >>> u.setconfig(s, 'invalid', 'somevalue')
284 >>> u.configbytes(s, 'invalid')
284 >>> u.configbytes(s, 'invalid')
285 Traceback (most recent call last):
285 Traceback (most recent call last):
286 ...
286 ...
287 ConfigError: foo.invalid is not a byte quantity ('somevalue')
287 ConfigError: foo.invalid is not a byte quantity ('somevalue')
288 """
288 """
289
289
290 value = self.config(section, name)
290 value = self.config(section, name)
291 if value is None:
291 if value is None:
292 if not isinstance(default, str):
292 if not isinstance(default, str):
293 return default
293 return default
294 value = default
294 value = default
295 try:
295 try:
296 return util.sizetoint(value)
296 return util.sizetoint(value)
297 except error.ParseError:
297 except error.ParseError:
298 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
298 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
299 % (section, name, value))
299 % (section, name, value))
300
300
301 def configlist(self, section, name, default=None, untrusted=False):
301 def configlist(self, section, name, default=None, untrusted=False):
302 """parse a configuration element as a list of comma/space separated
302 """parse a configuration element as a list of comma/space separated
303 strings
303 strings
304
304
305 >>> u = ui(); s = 'foo'
305 >>> u = ui(); s = 'foo'
306 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
306 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
307 >>> u.configlist(s, 'list1')
307 >>> u.configlist(s, 'list1')
308 ['this', 'is', 'a small', 'test']
308 ['this', 'is', 'a small', 'test']
309 """
309 """
310
310
311 def _parse_plain(parts, s, offset):
311 def _parse_plain(parts, s, offset):
312 whitespace = False
312 whitespace = False
313 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
313 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
314 whitespace = True
314 whitespace = True
315 offset += 1
315 offset += 1
316 if offset >= len(s):
316 if offset >= len(s):
317 return None, parts, offset
317 return None, parts, offset
318 if whitespace:
318 if whitespace:
319 parts.append('')
319 parts.append('')
320 if s[offset] == '"' and not parts[-1]:
320 if s[offset] == '"' and not parts[-1]:
321 return _parse_quote, parts, offset + 1
321 return _parse_quote, parts, offset + 1
322 elif s[offset] == '"' and parts[-1][-1] == '\\':
322 elif s[offset] == '"' and parts[-1][-1] == '\\':
323 parts[-1] = parts[-1][:-1] + s[offset]
323 parts[-1] = parts[-1][:-1] + s[offset]
324 return _parse_plain, parts, offset + 1
324 return _parse_plain, parts, offset + 1
325 parts[-1] += s[offset]
325 parts[-1] += s[offset]
326 return _parse_plain, parts, offset + 1
326 return _parse_plain, parts, offset + 1
327
327
328 def _parse_quote(parts, s, offset):
328 def _parse_quote(parts, s, offset):
329 if offset < len(s) and s[offset] == '"': # ""
329 if offset < len(s) and s[offset] == '"': # ""
330 parts.append('')
330 parts.append('')
331 offset += 1
331 offset += 1
332 while offset < len(s) and (s[offset].isspace() or
332 while offset < len(s) and (s[offset].isspace() or
333 s[offset] == ','):
333 s[offset] == ','):
334 offset += 1
334 offset += 1
335 return _parse_plain, parts, offset
335 return _parse_plain, parts, offset
336
336
337 while offset < len(s) and s[offset] != '"':
337 while offset < len(s) and s[offset] != '"':
338 if (s[offset] == '\\' and offset + 1 < len(s)
338 if (s[offset] == '\\' and offset + 1 < len(s)
339 and s[offset + 1] == '"'):
339 and s[offset + 1] == '"'):
340 offset += 1
340 offset += 1
341 parts[-1] += '"'
341 parts[-1] += '"'
342 else:
342 else:
343 parts[-1] += s[offset]
343 parts[-1] += s[offset]
344 offset += 1
344 offset += 1
345
345
346 if offset >= len(s):
346 if offset >= len(s):
347 real_parts = _configlist(parts[-1])
347 real_parts = _configlist(parts[-1])
348 if not real_parts:
348 if not real_parts:
349 parts[-1] = '"'
349 parts[-1] = '"'
350 else:
350 else:
351 real_parts[0] = '"' + real_parts[0]
351 real_parts[0] = '"' + real_parts[0]
352 parts = parts[:-1]
352 parts = parts[:-1]
353 parts.extend(real_parts)
353 parts.extend(real_parts)
354 return None, parts, offset
354 return None, parts, offset
355
355
356 offset += 1
356 offset += 1
357 while offset < len(s) and s[offset] in [' ', ',']:
357 while offset < len(s) and s[offset] in [' ', ',']:
358 offset += 1
358 offset += 1
359
359
360 if offset < len(s):
360 if offset < len(s):
361 if offset + 1 == len(s) and s[offset] == '"':
361 if offset + 1 == len(s) and s[offset] == '"':
362 parts[-1] += '"'
362 parts[-1] += '"'
363 offset += 1
363 offset += 1
364 else:
364 else:
365 parts.append('')
365 parts.append('')
366 else:
366 else:
367 return None, parts, offset
367 return None, parts, offset
368
368
369 return _parse_plain, parts, offset
369 return _parse_plain, parts, offset
370
370
371 def _configlist(s):
371 def _configlist(s):
372 s = s.rstrip(' ,')
372 s = s.rstrip(' ,')
373 if not s:
373 if not s:
374 return []
374 return []
375 parser, parts, offset = _parse_plain, [''], 0
375 parser, parts, offset = _parse_plain, [''], 0
376 while parser:
376 while parser:
377 parser, parts, offset = parser(parts, s, offset)
377 parser, parts, offset = parser(parts, s, offset)
378 return parts
378 return parts
379
379
380 result = self.config(section, name, untrusted=untrusted)
380 result = self.config(section, name, untrusted=untrusted)
381 if result is None:
381 if result is None:
382 result = default or []
382 result = default or []
383 if isinstance(result, basestring):
383 if isinstance(result, basestring):
384 result = _configlist(result.lstrip(' ,\n'))
384 result = _configlist(result.lstrip(' ,\n'))
385 if result is None:
385 if result is None:
386 result = default or []
386 result = default or []
387 return result
387 return result
388
388
389 def has_section(self, section, untrusted=False):
389 def has_section(self, section, untrusted=False):
390 '''tell whether section exists in config.'''
390 '''tell whether section exists in config.'''
391 return section in self._data(untrusted)
391 return section in self._data(untrusted)
392
392
393 def configitems(self, section, untrusted=False):
393 def configitems(self, section, untrusted=False):
394 items = self._data(untrusted).items(section)
394 items = self._data(untrusted).items(section)
395 if self.debugflag and not untrusted and self._reportuntrusted:
395 if self.debugflag and not untrusted and self._reportuntrusted:
396 for k, v in self._ucfg.items(section):
396 for k, v in self._ucfg.items(section):
397 if self._tcfg.get(section, k) != v:
397 if self._tcfg.get(section, k) != v:
398 self.debug("ignoring untrusted configuration option "
398 self.debug("ignoring untrusted configuration option "
399 "%s.%s = %s\n" % (section, k, v))
399 "%s.%s = %s\n" % (section, k, v))
400 return items
400 return items
401
401
402 def walkconfig(self, untrusted=False):
402 def walkconfig(self, untrusted=False):
403 cfg = self._data(untrusted)
403 cfg = self._data(untrusted)
404 for section in cfg.sections():
404 for section in cfg.sections():
405 for name, value in self.configitems(section, untrusted):
405 for name, value in self.configitems(section, untrusted):
406 yield section, name, value
406 yield section, name, value
407
407
408 def plain(self, feature=None):
408 def plain(self, feature=None):
409 '''is plain mode active?
409 '''is plain mode active?
410
410
411 Plain mode means that all configuration variables which affect
411 Plain mode means that all configuration variables which affect
412 the behavior and output of Mercurial should be
412 the behavior and output of Mercurial should be
413 ignored. Additionally, the output should be stable,
413 ignored. Additionally, the output should be stable,
414 reproducible and suitable for use in scripts or applications.
414 reproducible and suitable for use in scripts or applications.
415
415
416 The only way to trigger plain mode is by setting either the
416 The only way to trigger plain mode is by setting either the
417 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
417 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
418
418
419 The return value can either be
419 The return value can either be
420 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
420 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
421 - True otherwise
421 - True otherwise
422 '''
422 '''
423 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
423 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
424 return False
424 return False
425 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
425 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
426 if feature and exceptions:
426 if feature and exceptions:
427 return feature not in exceptions
427 return feature not in exceptions
428 return True
428 return True
429
429
430 def username(self):
430 def username(self):
431 """Return default username to be used in commits.
431 """Return default username to be used in commits.
432
432
433 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
433 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
434 and stop searching if one of these is set.
434 and stop searching if one of these is set.
435 If not found and ui.askusername is True, ask the user, else use
435 If not found and ui.askusername is True, ask the user, else use
436 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
436 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
437 """
437 """
438 user = os.environ.get("HGUSER")
438 user = os.environ.get("HGUSER")
439 if user is None:
439 if user is None:
440 user = self.config("ui", "username")
440 user = self.config("ui", "username")
441 if user is not None:
441 if user is not None:
442 user = os.path.expandvars(user)
442 user = os.path.expandvars(user)
443 if user is None:
443 if user is None:
444 user = os.environ.get("EMAIL")
444 user = os.environ.get("EMAIL")
445 if user is None and self.configbool("ui", "askusername"):
445 if user is None and self.configbool("ui", "askusername"):
446 user = self.prompt(_("enter a commit username:"), default=None)
446 user = self.prompt(_("enter a commit username:"), default=None)
447 if user is None and not self.interactive():
447 if user is None and not self.interactive():
448 try:
448 try:
449 user = '%s@%s' % (util.getuser(), socket.getfqdn())
449 user = '%s@%s' % (util.getuser(), socket.getfqdn())
450 self.warn(_("no username found, using '%s' instead\n") % user)
450 self.warn(_("no username found, using '%s' instead\n") % user)
451 except KeyError:
451 except KeyError:
452 pass
452 pass
453 if not user:
453 if not user:
454 raise util.Abort(_('no username supplied'),
454 raise util.Abort(_('no username supplied'),
455 hint=_('use "hg config --edit" '
455 hint=_('use "hg config --edit" '
456 'to set your username'))
456 'to set your username'))
457 if "\n" in user:
457 if "\n" in user:
458 raise util.Abort(_("username %s contains a newline\n") % repr(user))
458 raise util.Abort(_("username %s contains a newline\n") % repr(user))
459 return user
459 return user
460
460
461 def shortuser(self, user):
461 def shortuser(self, user):
462 """Return a short representation of a user name or email address."""
462 """Return a short representation of a user name or email address."""
463 if not self.verbose:
463 if not self.verbose:
464 user = util.shortuser(user)
464 user = util.shortuser(user)
465 return user
465 return user
466
466
467 def expandpath(self, loc, default=None):
467 def expandpath(self, loc, default=None):
468 """Return repository location relative to cwd or from [paths]"""
468 """Return repository location relative to cwd or from [paths]"""
469 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
469 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
470 return loc
470 return loc
471
471
472 path = self.config('paths', loc)
472 path = self.config('paths', loc)
473 if not path and default is not None:
473 if not path and default is not None:
474 path = self.config('paths', default)
474 path = self.config('paths', default)
475 return path or loc
475 return path or loc
476
476
477 def pushbuffer(self, error=False):
477 def pushbuffer(self, error=False):
478 """install a buffer to capture standar output of the ui object
478 """install a buffer to capture standar output of the ui object
479
479
480 If error is True, the error output will be captured too."""
480 If error is True, the error output will be captured too."""
481 self._buffers.append([])
481 self._buffers.append([])
482 self._bufferstates.append(error)
482 self._bufferstates.append(error)
483
483
484 def popbuffer(self, labeled=False):
484 def popbuffer(self, labeled=False):
485 '''pop the last buffer and return the buffered output
485 '''pop the last buffer and return the buffered output
486
486
487 If labeled is True, any labels associated with buffered
487 If labeled is True, any labels associated with buffered
488 output will be handled. By default, this has no effect
488 output will be handled. By default, this has no effect
489 on the output returned, but extensions and GUI tools may
489 on the output returned, but extensions and GUI tools may
490 handle this argument and returned styled output. If output
490 handle this argument and returned styled output. If output
491 is being buffered so it can be captured and parsed or
491 is being buffered so it can be captured and parsed or
492 processed, labeled should not be set to True.
492 processed, labeled should not be set to True.
493 '''
493 '''
494 self._bufferstates.pop()
494 self._bufferstates.pop()
495 return "".join(self._buffers.pop())
495 return "".join(self._buffers.pop())
496
496
497 def write(self, *args, **opts):
497 def write(self, *args, **opts):
498 '''write args to output
498 '''write args to output
499
499
500 By default, this method simply writes to the buffer or stdout,
500 By default, this method simply writes to the buffer or stdout,
501 but extensions or GUI tools may override this method,
501 but extensions or GUI tools may override this method,
502 write_err(), popbuffer(), and label() to style output from
502 write_err(), popbuffer(), and label() to style output from
503 various parts of hg.
503 various parts of hg.
504
504
505 An optional keyword argument, "label", can be passed in.
505 An optional keyword argument, "label", can be passed in.
506 This should be a string containing label names separated by
506 This should be a string containing label names separated by
507 space. Label names take the form of "topic.type". For example,
507 space. Label names take the form of "topic.type". For example,
508 ui.debug() issues a label of "ui.debug".
508 ui.debug() issues a label of "ui.debug".
509
509
510 When labeling output for a specific command, a label of
510 When labeling output for a specific command, a label of
511 "cmdname.type" is recommended. For example, status issues
511 "cmdname.type" is recommended. For example, status issues
512 a label of "status.modified" for modified files.
512 a label of "status.modified" for modified files.
513 '''
513 '''
514 if self._buffers:
514 if self._buffers:
515 self._buffers[-1].extend([str(a) for a in args])
515 self._buffers[-1].extend([str(a) for a in args])
516 else:
516 else:
517 for a in args:
517 for a in args:
518 self.fout.write(str(a))
518 self.fout.write(str(a))
519
519
520 def write_err(self, *args, **opts):
520 def write_err(self, *args, **opts):
521 try:
521 try:
522 if self._bufferstates and self._bufferstates[-1]:
522 if self._bufferstates and self._bufferstates[-1]:
523 return self.write(*args, **opts)
523 return self.write(*args, **opts)
524 if not getattr(self.fout, 'closed', False):
524 if not getattr(self.fout, 'closed', False):
525 self.fout.flush()
525 self.fout.flush()
526 for a in args:
526 for a in args:
527 self.ferr.write(str(a))
527 self.ferr.write(str(a))
528 # stderr may be buffered under win32 when redirected to files,
528 # stderr may be buffered under win32 when redirected to files,
529 # including stdout.
529 # including stdout.
530 if not getattr(self.ferr, 'closed', False):
530 if not getattr(self.ferr, 'closed', False):
531 self.ferr.flush()
531 self.ferr.flush()
532 except IOError, inst:
532 except IOError, inst:
533 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
533 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
534 raise
534 raise
535
535
536 def flush(self):
536 def flush(self):
537 try: self.fout.flush()
537 try: self.fout.flush()
538 except (IOError, ValueError): pass
538 except (IOError, ValueError): pass
539 try: self.ferr.flush()
539 try: self.ferr.flush()
540 except (IOError, ValueError): pass
540 except (IOError, ValueError): pass
541
541
542 def _isatty(self, fh):
542 def _isatty(self, fh):
543 if self.configbool('ui', 'nontty', False):
543 if self.configbool('ui', 'nontty', False):
544 return False
544 return False
545 return util.isatty(fh)
545 return util.isatty(fh)
546
546
547 def interactive(self):
547 def interactive(self):
548 '''is interactive input allowed?
548 '''is interactive input allowed?
549
549
550 An interactive session is a session where input can be reasonably read
550 An interactive session is a session where input can be reasonably read
551 from `sys.stdin'. If this function returns false, any attempt to read
551 from `sys.stdin'. If this function returns false, any attempt to read
552 from stdin should fail with an error, unless a sensible default has been
552 from stdin should fail with an error, unless a sensible default has been
553 specified.
553 specified.
554
554
555 Interactiveness is triggered by the value of the `ui.interactive'
555 Interactiveness is triggered by the value of the `ui.interactive'
556 configuration variable or - if it is unset - when `sys.stdin' points
556 configuration variable or - if it is unset - when `sys.stdin' points
557 to a terminal device.
557 to a terminal device.
558
558
559 This function refers to input only; for output, see `ui.formatted()'.
559 This function refers to input only; for output, see `ui.formatted()'.
560 '''
560 '''
561 i = self.configbool("ui", "interactive", None)
561 i = self.configbool("ui", "interactive", None)
562 if i is None:
562 if i is None:
563 # some environments replace stdin without implementing isatty
563 # some environments replace stdin without implementing isatty
564 # usually those are non-interactive
564 # usually those are non-interactive
565 return self._isatty(self.fin)
565 return self._isatty(self.fin)
566
566
567 return i
567 return i
568
568
569 def termwidth(self):
569 def termwidth(self):
570 '''how wide is the terminal in columns?
570 '''how wide is the terminal in columns?
571 '''
571 '''
572 if 'COLUMNS' in os.environ:
572 if 'COLUMNS' in os.environ:
573 try:
573 try:
574 return int(os.environ['COLUMNS'])
574 return int(os.environ['COLUMNS'])
575 except ValueError:
575 except ValueError:
576 pass
576 pass
577 return util.termwidth()
577 return util.termwidth()
578
578
579 def formatted(self):
579 def formatted(self):
580 '''should formatted output be used?
580 '''should formatted output be used?
581
581
582 It is often desirable to format the output to suite the output medium.
582 It is often desirable to format the output to suite the output medium.
583 Examples of this are truncating long lines or colorizing messages.
583 Examples of this are truncating long lines or colorizing messages.
584 However, this is not often not desirable when piping output into other
584 However, this is not often not desirable when piping output into other
585 utilities, e.g. `grep'.
585 utilities, e.g. `grep'.
586
586
587 Formatted output is triggered by the value of the `ui.formatted'
587 Formatted output is triggered by the value of the `ui.formatted'
588 configuration variable or - if it is unset - when `sys.stdout' points
588 configuration variable or - if it is unset - when `sys.stdout' points
589 to a terminal device. Please note that `ui.formatted' should be
589 to a terminal device. Please note that `ui.formatted' should be
590 considered an implementation detail; it is not intended for use outside
590 considered an implementation detail; it is not intended for use outside
591 Mercurial or its extensions.
591 Mercurial or its extensions.
592
592
593 This function refers to output only; for input, see `ui.interactive()'.
593 This function refers to output only; for input, see `ui.interactive()'.
594 This function always returns false when in plain mode, see `ui.plain()'.
594 This function always returns false when in plain mode, see `ui.plain()'.
595 '''
595 '''
596 if self.plain():
596 if self.plain():
597 return False
597 return False
598
598
599 i = self.configbool("ui", "formatted", None)
599 i = self.configbool("ui", "formatted", None)
600 if i is None:
600 if i is None:
601 # some environments replace stdout without implementing isatty
601 # some environments replace stdout without implementing isatty
602 # usually those are non-interactive
602 # usually those are non-interactive
603 return self._isatty(self.fout)
603 return self._isatty(self.fout)
604
604
605 return i
605 return i
606
606
607 def _readline(self, prompt=''):
607 def _readline(self, prompt=''):
608 if self._isatty(self.fin):
608 if self._isatty(self.fin):
609 try:
609 try:
610 # magically add command line editing support, where
610 # magically add command line editing support, where
611 # available
611 # available
612 import readline
612 import readline
613 # force demandimport to really load the module
613 # force demandimport to really load the module
614 readline.read_history_file
614 readline.read_history_file
615 # windows sometimes raises something other than ImportError
615 # windows sometimes raises something other than ImportError
616 except Exception:
616 except Exception:
617 pass
617 pass
618
618
619 # call write() so output goes through subclassed implementation
619 # call write() so output goes through subclassed implementation
620 # e.g. color extension on Windows
620 # e.g. color extension on Windows
621 self.write(prompt)
621 self.write(prompt)
622
622
623 # instead of trying to emulate raw_input, swap (self.fin,
623 # instead of trying to emulate raw_input, swap (self.fin,
624 # self.fout) with (sys.stdin, sys.stdout)
624 # self.fout) with (sys.stdin, sys.stdout)
625 oldin = sys.stdin
625 oldin = sys.stdin
626 oldout = sys.stdout
626 oldout = sys.stdout
627 sys.stdin = self.fin
627 sys.stdin = self.fin
628 sys.stdout = self.fout
628 sys.stdout = self.fout
629 line = raw_input(' ')
629 line = raw_input(' ')
630 sys.stdin = oldin
630 sys.stdin = oldin
631 sys.stdout = oldout
631 sys.stdout = oldout
632
632
633 # When stdin is in binary mode on Windows, it can cause
633 # When stdin is in binary mode on Windows, it can cause
634 # raw_input() to emit an extra trailing carriage return
634 # raw_input() to emit an extra trailing carriage return
635 if os.linesep == '\r\n' and line and line[-1] == '\r':
635 if os.linesep == '\r\n' and line and line[-1] == '\r':
636 line = line[:-1]
636 line = line[:-1]
637 return line
637 return line
638
638
639 def prompt(self, msg, default="y"):
639 def prompt(self, msg, default="y"):
640 """Prompt user with msg, read response.
640 """Prompt user with msg, read response.
641 If ui is not interactive, the default is returned.
641 If ui is not interactive, the default is returned.
642 """
642 """
643 if not self.interactive():
643 if not self.interactive():
644 self.write(msg, ' ', default, "\n")
644 self.write(msg, ' ', default, "\n")
645 return default
645 return default
646 try:
646 try:
647 r = self._readline(self.label(msg, 'ui.prompt'))
647 r = self._readline(self.label(msg, 'ui.prompt'))
648 if not r:
648 if not r:
649 return default
649 return default
650 return r
650 return r
651 except EOFError:
651 except EOFError:
652 raise util.Abort(_('response expected'))
652 raise util.Abort(_('response expected'))
653
653
654 @staticmethod
654 @staticmethod
655 def extractchoices(prompt):
655 def extractchoices(prompt):
656 """Extract prompt message and list of choices from specified prompt.
656 """Extract prompt message and list of choices from specified prompt.
657
657
658 This returns tuple "(message, choices)", and "choices" is the
658 This returns tuple "(message, choices)", and "choices" is the
659 list of tuple "(response character, text without &)".
659 list of tuple "(response character, text without &)".
660 """
660 """
661 parts = prompt.split('$$')
661 parts = prompt.split('$$')
662 msg = parts[0].rstrip(' ')
662 msg = parts[0].rstrip(' ')
663 choices = [p.strip(' ') for p in parts[1:]]
663 choices = [p.strip(' ') for p in parts[1:]]
664 return (msg,
664 return (msg,
665 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
665 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
666 for s in choices])
666 for s in choices])
667
667
668 def promptchoice(self, prompt, default=0):
668 def promptchoice(self, prompt, default=0):
669 """Prompt user with a message, read response, and ensure it matches
669 """Prompt user with a message, read response, and ensure it matches
670 one of the provided choices. The prompt is formatted as follows:
670 one of the provided choices. The prompt is formatted as follows:
671
671
672 "would you like fries with that (Yn)? $$ &Yes $$ &No"
672 "would you like fries with that (Yn)? $$ &Yes $$ &No"
673
673
674 The index of the choice is returned. Responses are case
674 The index of the choice is returned. Responses are case
675 insensitive. If ui is not interactive, the default is
675 insensitive. If ui is not interactive, the default is
676 returned.
676 returned.
677 """
677 """
678
678
679 msg, choices = self.extractchoices(prompt)
679 msg, choices = self.extractchoices(prompt)
680 resps = [r for r, t in choices]
680 resps = [r for r, t in choices]
681 while True:
681 while True:
682 r = self.prompt(msg, resps[default])
682 r = self.prompt(msg, resps[default])
683 if r.lower() in resps:
683 if r.lower() in resps:
684 return resps.index(r.lower())
684 return resps.index(r.lower())
685 self.write(_("unrecognized response\n"))
685 self.write(_("unrecognized response\n"))
686
686
687 def getpass(self, prompt=None, default=None):
687 def getpass(self, prompt=None, default=None):
688 if not self.interactive():
688 if not self.interactive():
689 return default
689 return default
690 try:
690 try:
691 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
691 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
692 return getpass.getpass('')
692 # disable getpass() only if explicitly specified. it's still valid
693 # to interact with tty even if fin is not a tty.
694 if self.configbool('ui', 'nontty'):
695 return self.fin.readline().rstrip('\n')
696 else:
697 return getpass.getpass('')
693 except EOFError:
698 except EOFError:
694 raise util.Abort(_('response expected'))
699 raise util.Abort(_('response expected'))
695 def status(self, *msg, **opts):
700 def status(self, *msg, **opts):
696 '''write status message to output (if ui.quiet is False)
701 '''write status message to output (if ui.quiet is False)
697
702
698 This adds an output label of "ui.status".
703 This adds an output label of "ui.status".
699 '''
704 '''
700 if not self.quiet:
705 if not self.quiet:
701 opts['label'] = opts.get('label', '') + ' ui.status'
706 opts['label'] = opts.get('label', '') + ' ui.status'
702 self.write(*msg, **opts)
707 self.write(*msg, **opts)
703 def warn(self, *msg, **opts):
708 def warn(self, *msg, **opts):
704 '''write warning message to output (stderr)
709 '''write warning message to output (stderr)
705
710
706 This adds an output label of "ui.warning".
711 This adds an output label of "ui.warning".
707 '''
712 '''
708 opts['label'] = opts.get('label', '') + ' ui.warning'
713 opts['label'] = opts.get('label', '') + ' ui.warning'
709 self.write_err(*msg, **opts)
714 self.write_err(*msg, **opts)
710 def note(self, *msg, **opts):
715 def note(self, *msg, **opts):
711 '''write note to output (if ui.verbose is True)
716 '''write note to output (if ui.verbose is True)
712
717
713 This adds an output label of "ui.note".
718 This adds an output label of "ui.note".
714 '''
719 '''
715 if self.verbose:
720 if self.verbose:
716 opts['label'] = opts.get('label', '') + ' ui.note'
721 opts['label'] = opts.get('label', '') + ' ui.note'
717 self.write(*msg, **opts)
722 self.write(*msg, **opts)
718 def debug(self, *msg, **opts):
723 def debug(self, *msg, **opts):
719 '''write debug message to output (if ui.debugflag is True)
724 '''write debug message to output (if ui.debugflag is True)
720
725
721 This adds an output label of "ui.debug".
726 This adds an output label of "ui.debug".
722 '''
727 '''
723 if self.debugflag:
728 if self.debugflag:
724 opts['label'] = opts.get('label', '') + ' ui.debug'
729 opts['label'] = opts.get('label', '') + ' ui.debug'
725 self.write(*msg, **opts)
730 self.write(*msg, **opts)
726 def edit(self, text, user, extra={}):
731 def edit(self, text, user, extra={}):
727 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
732 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
728 text=True)
733 text=True)
729 try:
734 try:
730 f = os.fdopen(fd, "w")
735 f = os.fdopen(fd, "w")
731 f.write(text)
736 f.write(text)
732 f.close()
737 f.close()
733
738
734 environ = {'HGUSER': user}
739 environ = {'HGUSER': user}
735 if 'transplant_source' in extra:
740 if 'transplant_source' in extra:
736 environ.update({'HGREVISION': hex(extra['transplant_source'])})
741 environ.update({'HGREVISION': hex(extra['transplant_source'])})
737 for label in ('source', 'rebase_source'):
742 for label in ('source', 'rebase_source'):
738 if label in extra:
743 if label in extra:
739 environ.update({'HGREVISION': extra[label]})
744 environ.update({'HGREVISION': extra[label]})
740 break
745 break
741
746
742 editor = self.geteditor()
747 editor = self.geteditor()
743
748
744 util.system("%s \"%s\"" % (editor, name),
749 util.system("%s \"%s\"" % (editor, name),
745 environ=environ,
750 environ=environ,
746 onerr=util.Abort, errprefix=_("edit failed"),
751 onerr=util.Abort, errprefix=_("edit failed"),
747 out=self.fout)
752 out=self.fout)
748
753
749 f = open(name)
754 f = open(name)
750 t = f.read()
755 t = f.read()
751 f.close()
756 f.close()
752 finally:
757 finally:
753 os.unlink(name)
758 os.unlink(name)
754
759
755 return t
760 return t
756
761
757 def traceback(self, exc=None, force=False):
762 def traceback(self, exc=None, force=False):
758 '''print exception traceback if traceback printing enabled or forced.
763 '''print exception traceback if traceback printing enabled or forced.
759 only to call in exception handler. returns true if traceback
764 only to call in exception handler. returns true if traceback
760 printed.'''
765 printed.'''
761 if self.tracebackflag or force:
766 if self.tracebackflag or force:
762 if exc is None:
767 if exc is None:
763 exc = sys.exc_info()
768 exc = sys.exc_info()
764 cause = getattr(exc[1], 'cause', None)
769 cause = getattr(exc[1], 'cause', None)
765
770
766 if cause is not None:
771 if cause is not None:
767 causetb = traceback.format_tb(cause[2])
772 causetb = traceback.format_tb(cause[2])
768 exctb = traceback.format_tb(exc[2])
773 exctb = traceback.format_tb(exc[2])
769 exconly = traceback.format_exception_only(cause[0], cause[1])
774 exconly = traceback.format_exception_only(cause[0], cause[1])
770
775
771 # exclude frame where 'exc' was chained and rethrown from exctb
776 # exclude frame where 'exc' was chained and rethrown from exctb
772 self.write_err('Traceback (most recent call last):\n',
777 self.write_err('Traceback (most recent call last):\n',
773 ''.join(exctb[:-1]),
778 ''.join(exctb[:-1]),
774 ''.join(causetb),
779 ''.join(causetb),
775 ''.join(exconly))
780 ''.join(exconly))
776 else:
781 else:
777 traceback.print_exception(exc[0], exc[1], exc[2],
782 traceback.print_exception(exc[0], exc[1], exc[2],
778 file=self.ferr)
783 file=self.ferr)
779 return self.tracebackflag or force
784 return self.tracebackflag or force
780
785
781 def geteditor(self):
786 def geteditor(self):
782 '''return editor to use'''
787 '''return editor to use'''
783 if sys.platform == 'plan9':
788 if sys.platform == 'plan9':
784 # vi is the MIPS instruction simulator on Plan 9. We
789 # vi is the MIPS instruction simulator on Plan 9. We
785 # instead default to E to plumb commit messages to
790 # instead default to E to plumb commit messages to
786 # avoid confusion.
791 # avoid confusion.
787 editor = 'E'
792 editor = 'E'
788 else:
793 else:
789 editor = 'vi'
794 editor = 'vi'
790 return (os.environ.get("HGEDITOR") or
795 return (os.environ.get("HGEDITOR") or
791 self.config("ui", "editor") or
796 self.config("ui", "editor") or
792 os.environ.get("VISUAL") or
797 os.environ.get("VISUAL") or
793 os.environ.get("EDITOR", editor))
798 os.environ.get("EDITOR", editor))
794
799
795 def progress(self, topic, pos, item="", unit="", total=None):
800 def progress(self, topic, pos, item="", unit="", total=None):
796 '''show a progress message
801 '''show a progress message
797
802
798 With stock hg, this is simply a debug message that is hidden
803 With stock hg, this is simply a debug message that is hidden
799 by default, but with extensions or GUI tools it may be
804 by default, but with extensions or GUI tools it may be
800 visible. 'topic' is the current operation, 'item' is a
805 visible. 'topic' is the current operation, 'item' is a
801 non-numeric marker of the current position (i.e. the currently
806 non-numeric marker of the current position (i.e. the currently
802 in-process file), 'pos' is the current numeric position (i.e.
807 in-process file), 'pos' is the current numeric position (i.e.
803 revision, bytes, etc.), unit is a corresponding unit label,
808 revision, bytes, etc.), unit is a corresponding unit label,
804 and total is the highest expected pos.
809 and total is the highest expected pos.
805
810
806 Multiple nested topics may be active at a time.
811 Multiple nested topics may be active at a time.
807
812
808 All topics should be marked closed by setting pos to None at
813 All topics should be marked closed by setting pos to None at
809 termination.
814 termination.
810 '''
815 '''
811
816
812 if pos is None or not self.debugflag:
817 if pos is None or not self.debugflag:
813 return
818 return
814
819
815 if unit:
820 if unit:
816 unit = ' ' + unit
821 unit = ' ' + unit
817 if item:
822 if item:
818 item = ' ' + item
823 item = ' ' + item
819
824
820 if total:
825 if total:
821 pct = 100.0 * pos / total
826 pct = 100.0 * pos / total
822 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
827 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
823 % (topic, item, pos, total, unit, pct))
828 % (topic, item, pos, total, unit, pct))
824 else:
829 else:
825 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
830 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
826
831
827 def log(self, service, *msg, **opts):
832 def log(self, service, *msg, **opts):
828 '''hook for logging facility extensions
833 '''hook for logging facility extensions
829
834
830 service should be a readily-identifiable subsystem, which will
835 service should be a readily-identifiable subsystem, which will
831 allow filtering.
836 allow filtering.
832 message should be a newline-terminated string to log.
837 message should be a newline-terminated string to log.
833 '''
838 '''
834 pass
839 pass
835
840
836 def label(self, msg, label):
841 def label(self, msg, label):
837 '''style msg based on supplied label
842 '''style msg based on supplied label
838
843
839 Like ui.write(), this just returns msg unchanged, but extensions
844 Like ui.write(), this just returns msg unchanged, but extensions
840 and GUI tools can override it to allow styling output without
845 and GUI tools can override it to allow styling output without
841 writing it.
846 writing it.
842
847
843 ui.write(s, 'label') is equivalent to
848 ui.write(s, 'label') is equivalent to
844 ui.write(ui.label(s, 'label')).
849 ui.write(ui.label(s, 'label')).
845 '''
850 '''
846 return msg
851 return msg
@@ -1,340 +1,358 b''
1 import sys, os, struct, subprocess, cStringIO, re, shutil
1 import sys, os, struct, subprocess, cStringIO, re, shutil
2
2
3 def connect(path=None):
3 def connect(path=None):
4 cmdline = ['hg', 'serve', '--cmdserver', 'pipe']
4 cmdline = ['hg', 'serve', '--cmdserver', 'pipe']
5 if path:
5 if path:
6 cmdline += ['-R', path]
6 cmdline += ['-R', path]
7
7
8 server = subprocess.Popen(cmdline, stdin=subprocess.PIPE,
8 server = subprocess.Popen(cmdline, stdin=subprocess.PIPE,
9 stdout=subprocess.PIPE)
9 stdout=subprocess.PIPE)
10
10
11 return server
11 return server
12
12
13 def writeblock(server, data):
13 def writeblock(server, data):
14 server.stdin.write(struct.pack('>I', len(data)))
14 server.stdin.write(struct.pack('>I', len(data)))
15 server.stdin.write(data)
15 server.stdin.write(data)
16 server.stdin.flush()
16 server.stdin.flush()
17
17
18 def readchannel(server):
18 def readchannel(server):
19 data = server.stdout.read(5)
19 data = server.stdout.read(5)
20 if not data:
20 if not data:
21 raise EOFError
21 raise EOFError
22 channel, length = struct.unpack('>cI', data)
22 channel, length = struct.unpack('>cI', data)
23 if channel in 'IL':
23 if channel in 'IL':
24 return channel, length
24 return channel, length
25 else:
25 else:
26 return channel, server.stdout.read(length)
26 return channel, server.stdout.read(length)
27
27
28 def sep(text):
28 def sep(text):
29 return text.replace('\\', '/')
29 return text.replace('\\', '/')
30
30
31 def runcommand(server, args, output=sys.stdout, error=sys.stderr, input=None,
31 def runcommand(server, args, output=sys.stdout, error=sys.stderr, input=None,
32 outfilter=lambda x: x):
32 outfilter=lambda x: x):
33 print ' runcommand', ' '.join(args)
33 print ' runcommand', ' '.join(args)
34 sys.stdout.flush()
34 sys.stdout.flush()
35 server.stdin.write('runcommand\n')
35 server.stdin.write('runcommand\n')
36 writeblock(server, '\0'.join(args))
36 writeblock(server, '\0'.join(args))
37
37
38 if not input:
38 if not input:
39 input = cStringIO.StringIO()
39 input = cStringIO.StringIO()
40
40
41 while True:
41 while True:
42 ch, data = readchannel(server)
42 ch, data = readchannel(server)
43 if ch == 'o':
43 if ch == 'o':
44 output.write(outfilter(data))
44 output.write(outfilter(data))
45 output.flush()
45 output.flush()
46 elif ch == 'e':
46 elif ch == 'e':
47 error.write(data)
47 error.write(data)
48 error.flush()
48 error.flush()
49 elif ch == 'I':
49 elif ch == 'I':
50 writeblock(server, input.read(data))
50 writeblock(server, input.read(data))
51 elif ch == 'L':
51 elif ch == 'L':
52 writeblock(server, input.readline(data))
52 writeblock(server, input.readline(data))
53 elif ch == 'r':
53 elif ch == 'r':
54 ret, = struct.unpack('>i', data)
54 ret, = struct.unpack('>i', data)
55 if ret != 0:
55 if ret != 0:
56 print ' [%d]' % ret
56 print ' [%d]' % ret
57 return ret
57 return ret
58 else:
58 else:
59 print "unexpected channel %c: %r" % (ch, data)
59 print "unexpected channel %c: %r" % (ch, data)
60 if ch.isupper():
60 if ch.isupper():
61 return
61 return
62
62
63 def check(func, repopath=None):
63 def check(func, repopath=None):
64 print
64 print
65 print 'testing %s:' % func.__name__
65 print 'testing %s:' % func.__name__
66 print
66 print
67 sys.stdout.flush()
67 sys.stdout.flush()
68 server = connect(repopath)
68 server = connect(repopath)
69 try:
69 try:
70 return func(server)
70 return func(server)
71 finally:
71 finally:
72 server.stdin.close()
72 server.stdin.close()
73 server.wait()
73 server.wait()
74
74
75 def unknowncommand(server):
75 def unknowncommand(server):
76 server.stdin.write('unknowncommand\n')
76 server.stdin.write('unknowncommand\n')
77
77
78 def hellomessage(server):
78 def hellomessage(server):
79 ch, data = readchannel(server)
79 ch, data = readchannel(server)
80 # escaping python tests output not supported
80 # escaping python tests output not supported
81 print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***',
81 print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***',
82 data))
82 data))
83
83
84 # run an arbitrary command to make sure the next thing the server sends
84 # run an arbitrary command to make sure the next thing the server sends
85 # isn't part of the hello message
85 # isn't part of the hello message
86 runcommand(server, ['id'])
86 runcommand(server, ['id'])
87
87
88 def checkruncommand(server):
88 def checkruncommand(server):
89 # hello block
89 # hello block
90 readchannel(server)
90 readchannel(server)
91
91
92 # no args
92 # no args
93 runcommand(server, [])
93 runcommand(server, [])
94
94
95 # global options
95 # global options
96 runcommand(server, ['id', '--quiet'])
96 runcommand(server, ['id', '--quiet'])
97
97
98 # make sure global options don't stick through requests
98 # make sure global options don't stick through requests
99 runcommand(server, ['id'])
99 runcommand(server, ['id'])
100
100
101 # --config
101 # --config
102 runcommand(server, ['id', '--config', 'ui.quiet=True'])
102 runcommand(server, ['id', '--config', 'ui.quiet=True'])
103
103
104 # make sure --config doesn't stick
104 # make sure --config doesn't stick
105 runcommand(server, ['id'])
105 runcommand(server, ['id'])
106
106
107 # negative return code should be masked
107 # negative return code should be masked
108 runcommand(server, ['id', '-runknown'])
108 runcommand(server, ['id', '-runknown'])
109
109
110 def inputeof(server):
110 def inputeof(server):
111 readchannel(server)
111 readchannel(server)
112 server.stdin.write('runcommand\n')
112 server.stdin.write('runcommand\n')
113 # close stdin while server is waiting for input
113 # close stdin while server is waiting for input
114 server.stdin.close()
114 server.stdin.close()
115
115
116 # server exits with 1 if the pipe closed while reading the command
116 # server exits with 1 if the pipe closed while reading the command
117 print 'server exit code =', server.wait()
117 print 'server exit code =', server.wait()
118
118
119 def serverinput(server):
119 def serverinput(server):
120 readchannel(server)
120 readchannel(server)
121
121
122 patch = """
122 patch = """
123 # HG changeset patch
123 # HG changeset patch
124 # User test
124 # User test
125 # Date 0 0
125 # Date 0 0
126 # Node ID c103a3dec114d882c98382d684d8af798d09d857
126 # Node ID c103a3dec114d882c98382d684d8af798d09d857
127 # Parent 0000000000000000000000000000000000000000
127 # Parent 0000000000000000000000000000000000000000
128 1
128 1
129
129
130 diff -r 000000000000 -r c103a3dec114 a
130 diff -r 000000000000 -r c103a3dec114 a
131 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
131 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
132 +++ b/a Thu Jan 01 00:00:00 1970 +0000
132 +++ b/a Thu Jan 01 00:00:00 1970 +0000
133 @@ -0,0 +1,1 @@
133 @@ -0,0 +1,1 @@
134 +1
134 +1
135 """
135 """
136
136
137 runcommand(server, ['import', '-'], input=cStringIO.StringIO(patch))
137 runcommand(server, ['import', '-'], input=cStringIO.StringIO(patch))
138 runcommand(server, ['log'])
138 runcommand(server, ['log'])
139
139
140 def cwd(server):
140 def cwd(server):
141 """ check that --cwd doesn't persist between requests """
141 """ check that --cwd doesn't persist between requests """
142 readchannel(server)
142 readchannel(server)
143 os.mkdir('foo')
143 os.mkdir('foo')
144 f = open('foo/bar', 'wb')
144 f = open('foo/bar', 'wb')
145 f.write('a')
145 f.write('a')
146 f.close()
146 f.close()
147 runcommand(server, ['--cwd', 'foo', 'st', 'bar'])
147 runcommand(server, ['--cwd', 'foo', 'st', 'bar'])
148 runcommand(server, ['st', 'foo/bar'])
148 runcommand(server, ['st', 'foo/bar'])
149 os.remove('foo/bar')
149 os.remove('foo/bar')
150
150
151 def localhgrc(server):
151 def localhgrc(server):
152 """ check that local configs for the cached repo aren't inherited when -R
152 """ check that local configs for the cached repo aren't inherited when -R
153 is used """
153 is used """
154 readchannel(server)
154 readchannel(server)
155
155
156 # the cached repo local hgrc contains ui.foo=bar, so showconfig should
156 # the cached repo local hgrc contains ui.foo=bar, so showconfig should
157 # show it
157 # show it
158 runcommand(server, ['showconfig'])
158 runcommand(server, ['showconfig'])
159
159
160 # but not for this repo
160 # but not for this repo
161 runcommand(server, ['init', 'foo'])
161 runcommand(server, ['init', 'foo'])
162 runcommand(server, ['-R', 'foo', 'showconfig', 'ui', 'defaults'])
162 runcommand(server, ['-R', 'foo', 'showconfig', 'ui', 'defaults'])
163 shutil.rmtree('foo')
163 shutil.rmtree('foo')
164
164
165 def hook(**args):
165 def hook(**args):
166 print 'hook talking'
166 print 'hook talking'
167 print 'now try to read something: %r' % sys.stdin.read()
167 print 'now try to read something: %r' % sys.stdin.read()
168
168
169 def hookoutput(server):
169 def hookoutput(server):
170 readchannel(server)
170 readchannel(server)
171 runcommand(server, ['--config',
171 runcommand(server, ['--config',
172 'hooks.pre-identify=python:test-commandserver.hook',
172 'hooks.pre-identify=python:test-commandserver.hook',
173 'id'],
173 'id'],
174 input=cStringIO.StringIO('some input'))
174 input=cStringIO.StringIO('some input'))
175
175
176 def outsidechanges(server):
176 def outsidechanges(server):
177 readchannel(server)
177 readchannel(server)
178 f = open('a', 'ab')
178 f = open('a', 'ab')
179 f.write('a\n')
179 f.write('a\n')
180 f.close()
180 f.close()
181 runcommand(server, ['status'])
181 runcommand(server, ['status'])
182 os.system('hg ci -Am2')
182 os.system('hg ci -Am2')
183 runcommand(server, ['tip'])
183 runcommand(server, ['tip'])
184 runcommand(server, ['status'])
184 runcommand(server, ['status'])
185
185
186 def bookmarks(server):
186 def bookmarks(server):
187 readchannel(server)
187 readchannel(server)
188 runcommand(server, ['bookmarks'])
188 runcommand(server, ['bookmarks'])
189
189
190 # changes .hg/bookmarks
190 # changes .hg/bookmarks
191 os.system('hg bookmark -i bm1')
191 os.system('hg bookmark -i bm1')
192 os.system('hg bookmark -i bm2')
192 os.system('hg bookmark -i bm2')
193 runcommand(server, ['bookmarks'])
193 runcommand(server, ['bookmarks'])
194
194
195 # changes .hg/bookmarks.current
195 # changes .hg/bookmarks.current
196 os.system('hg upd bm1 -q')
196 os.system('hg upd bm1 -q')
197 runcommand(server, ['bookmarks'])
197 runcommand(server, ['bookmarks'])
198
198
199 runcommand(server, ['bookmarks', 'bm3'])
199 runcommand(server, ['bookmarks', 'bm3'])
200 f = open('a', 'ab')
200 f = open('a', 'ab')
201 f.write('a\n')
201 f.write('a\n')
202 f.close()
202 f.close()
203 runcommand(server, ['commit', '-Amm'])
203 runcommand(server, ['commit', '-Amm'])
204 runcommand(server, ['bookmarks'])
204 runcommand(server, ['bookmarks'])
205
205
206 def tagscache(server):
206 def tagscache(server):
207 readchannel(server)
207 readchannel(server)
208 runcommand(server, ['id', '-t', '-r', '0'])
208 runcommand(server, ['id', '-t', '-r', '0'])
209 os.system('hg tag -r 0 foo')
209 os.system('hg tag -r 0 foo')
210 runcommand(server, ['id', '-t', '-r', '0'])
210 runcommand(server, ['id', '-t', '-r', '0'])
211
211
212 def setphase(server):
212 def setphase(server):
213 readchannel(server)
213 readchannel(server)
214 runcommand(server, ['phase', '-r', '.'])
214 runcommand(server, ['phase', '-r', '.'])
215 os.system('hg phase -r . -p')
215 os.system('hg phase -r . -p')
216 runcommand(server, ['phase', '-r', '.'])
216 runcommand(server, ['phase', '-r', '.'])
217
217
218 def rollback(server):
218 def rollback(server):
219 readchannel(server)
219 readchannel(server)
220 runcommand(server, ['phase', '-r', '.', '-p'])
220 runcommand(server, ['phase', '-r', '.', '-p'])
221 f = open('a', 'ab')
221 f = open('a', 'ab')
222 f.write('a\n')
222 f.write('a\n')
223 f.close()
223 f.close()
224 runcommand(server, ['commit', '-Am.'])
224 runcommand(server, ['commit', '-Am.'])
225 runcommand(server, ['rollback'])
225 runcommand(server, ['rollback'])
226 runcommand(server, ['phase', '-r', '.'])
226 runcommand(server, ['phase', '-r', '.'])
227
227
228 def branch(server):
228 def branch(server):
229 readchannel(server)
229 readchannel(server)
230 runcommand(server, ['branch'])
230 runcommand(server, ['branch'])
231 os.system('hg branch foo')
231 os.system('hg branch foo')
232 runcommand(server, ['branch'])
232 runcommand(server, ['branch'])
233 os.system('hg branch default')
233 os.system('hg branch default')
234
234
235 def hgignore(server):
235 def hgignore(server):
236 readchannel(server)
236 readchannel(server)
237 f = open('.hgignore', 'ab')
237 f = open('.hgignore', 'ab')
238 f.write('')
238 f.write('')
239 f.close()
239 f.close()
240 runcommand(server, ['commit', '-Am.'])
240 runcommand(server, ['commit', '-Am.'])
241 f = open('ignored-file', 'ab')
241 f = open('ignored-file', 'ab')
242 f.write('')
242 f.write('')
243 f.close()
243 f.close()
244 f = open('.hgignore', 'ab')
244 f = open('.hgignore', 'ab')
245 f.write('ignored-file')
245 f.write('ignored-file')
246 f.close()
246 f.close()
247 runcommand(server, ['status', '-i', '-u'])
247 runcommand(server, ['status', '-i', '-u'])
248
248
249 def phasecacheafterstrip(server):
249 def phasecacheafterstrip(server):
250 readchannel(server)
250 readchannel(server)
251
251
252 # create new head, 5:731265503d86
252 # create new head, 5:731265503d86
253 runcommand(server, ['update', '-C', '0'])
253 runcommand(server, ['update', '-C', '0'])
254 f = open('a', 'ab')
254 f = open('a', 'ab')
255 f.write('a\n')
255 f.write('a\n')
256 f.close()
256 f.close()
257 runcommand(server, ['commit', '-Am.', 'a'])
257 runcommand(server, ['commit', '-Am.', 'a'])
258 runcommand(server, ['log', '-Gq'])
258 runcommand(server, ['log', '-Gq'])
259
259
260 # make it public; draft marker moves to 4:7966c8e3734d
260 # make it public; draft marker moves to 4:7966c8e3734d
261 runcommand(server, ['phase', '-p', '.'])
261 runcommand(server, ['phase', '-p', '.'])
262 # load _phasecache.phaseroots
262 # load _phasecache.phaseroots
263 runcommand(server, ['phase', '.'], outfilter=sep)
263 runcommand(server, ['phase', '.'], outfilter=sep)
264
264
265 # strip 1::4 outside server
265 # strip 1::4 outside server
266 os.system('hg -q --config extensions.mq= strip 1')
266 os.system('hg -q --config extensions.mq= strip 1')
267
267
268 # shouldn't raise "7966c8e3734d: no node!"
268 # shouldn't raise "7966c8e3734d: no node!"
269 runcommand(server, ['branches'])
269 runcommand(server, ['branches'])
270
270
271 def obsolete(server):
271 def obsolete(server):
272 readchannel(server)
272 readchannel(server)
273
273
274 runcommand(server, ['up', 'null'])
274 runcommand(server, ['up', 'null'])
275 runcommand(server, ['phase', '-df', 'tip'])
275 runcommand(server, ['phase', '-df', 'tip'])
276 cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
276 cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
277 if os.name == 'nt':
277 if os.name == 'nt':
278 cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
278 cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
279 os.system(cmd)
279 os.system(cmd)
280 runcommand(server, ['log', '--hidden'])
280 runcommand(server, ['log', '--hidden'])
281 runcommand(server, ['log'])
281 runcommand(server, ['log'])
282
282
283 def mqoutsidechanges(server):
283 def mqoutsidechanges(server):
284 readchannel(server)
284 readchannel(server)
285
285
286 # load repo.mq
286 # load repo.mq
287 runcommand(server, ['qapplied'])
287 runcommand(server, ['qapplied'])
288 os.system('hg qnew 0.diff')
288 os.system('hg qnew 0.diff')
289 # repo.mq should be invalidated
289 # repo.mq should be invalidated
290 runcommand(server, ['qapplied'])
290 runcommand(server, ['qapplied'])
291
291
292 runcommand(server, ['qpop', '--all'])
292 runcommand(server, ['qpop', '--all'])
293 os.system('hg qqueue --create foo')
293 os.system('hg qqueue --create foo')
294 # repo.mq should be recreated to point to new queue
294 # repo.mq should be recreated to point to new queue
295 runcommand(server, ['qqueue', '--active'])
295 runcommand(server, ['qqueue', '--active'])
296
296
297 def getpass(server):
298 readchannel(server)
299 runcommand(server, ['debuggetpass', '--config', 'ui.interactive=True'],
300 input=cStringIO.StringIO('1234\n'))
301
297 def startwithoutrepo(server):
302 def startwithoutrepo(server):
298 readchannel(server)
303 readchannel(server)
299 runcommand(server, ['init', 'repo2'])
304 runcommand(server, ['init', 'repo2'])
300 runcommand(server, ['id', '-R', 'repo2'])
305 runcommand(server, ['id', '-R', 'repo2'])
301
306
302 if __name__ == '__main__':
307 if __name__ == '__main__':
303 os.system('hg init repo')
308 os.system('hg init repo')
304 os.chdir('repo')
309 os.chdir('repo')
305
310
306 check(hellomessage)
311 check(hellomessage)
307 check(unknowncommand)
312 check(unknowncommand)
308 check(checkruncommand)
313 check(checkruncommand)
309 check(inputeof)
314 check(inputeof)
310 check(serverinput)
315 check(serverinput)
311 check(cwd)
316 check(cwd)
312
317
313 hgrc = open('.hg/hgrc', 'a')
318 hgrc = open('.hg/hgrc', 'a')
314 hgrc.write('[ui]\nfoo=bar\n')
319 hgrc.write('[ui]\nfoo=bar\n')
315 hgrc.close()
320 hgrc.close()
316 check(localhgrc)
321 check(localhgrc)
317 check(hookoutput)
322 check(hookoutput)
318 check(outsidechanges)
323 check(outsidechanges)
319 check(bookmarks)
324 check(bookmarks)
320 check(tagscache)
325 check(tagscache)
321 check(setphase)
326 check(setphase)
322 check(rollback)
327 check(rollback)
323 check(branch)
328 check(branch)
324 check(hgignore)
329 check(hgignore)
325 check(phasecacheafterstrip)
330 check(phasecacheafterstrip)
326 obs = open('obs.py', 'w')
331 obs = open('obs.py', 'w')
327 obs.write('import mercurial.obsolete\nmercurial.obsolete._enabled = True\n')
332 obs.write('import mercurial.obsolete\nmercurial.obsolete._enabled = True\n')
328 obs.close()
333 obs.close()
329 hgrc = open('.hg/hgrc', 'a')
334 hgrc = open('.hg/hgrc', 'a')
330 hgrc.write('[extensions]\nobs=obs.py\n')
335 hgrc.write('[extensions]\nobs=obs.py\n')
331 hgrc.close()
336 hgrc.close()
332 check(obsolete)
337 check(obsolete)
333 hgrc = open('.hg/hgrc', 'a')
338 hgrc = open('.hg/hgrc', 'a')
334 hgrc.write('[extensions]\nmq=\n')
339 hgrc.write('[extensions]\nmq=\n')
335 hgrc.close()
340 hgrc.close()
336 check(mqoutsidechanges)
341 check(mqoutsidechanges)
342 dbg = open('dbgui.py', 'w')
343 dbg.write('from mercurial import cmdutil, commands\n'
344 'commands.norepo += " debuggetpass"\n'
345 'cmdtable = {}\n'
346 'command = cmdutil.command(cmdtable)\n'
347 '@command("debuggetpass")\n'
348 'def debuggetpass(ui):\n'
349 ' ui.write("%s\\n" % ui.getpass())\n')
350 dbg.close()
351 hgrc = open('.hg/hgrc', 'a')
352 hgrc.write('[extensions]\ndbgui=dbgui.py\n')
353 hgrc.close()
354 check(getpass)
337
355
338 os.chdir('..')
356 os.chdir('..')
339 check(hellomessage)
357 check(hellomessage)
340 check(startwithoutrepo)
358 check(startwithoutrepo)
@@ -1,252 +1,259 b''
1
1
2 testing hellomessage:
2 testing hellomessage:
3
3
4 o, 'capabilities: getencoding runcommand\nencoding: ***'
4 o, 'capabilities: getencoding runcommand\nencoding: ***'
5 runcommand id
5 runcommand id
6 000000000000 tip
6 000000000000 tip
7
7
8 testing unknowncommand:
8 testing unknowncommand:
9
9
10 abort: unknown command unknowncommand
10 abort: unknown command unknowncommand
11
11
12 testing checkruncommand:
12 testing checkruncommand:
13
13
14 runcommand
14 runcommand
15 Mercurial Distributed SCM
15 Mercurial Distributed SCM
16
16
17 basic commands:
17 basic commands:
18
18
19 add add the specified files on the next commit
19 add add the specified files on the next commit
20 annotate show changeset information by line for each file
20 annotate show changeset information by line for each file
21 clone make a copy of an existing repository
21 clone make a copy of an existing repository
22 commit commit the specified files or all outstanding changes
22 commit commit the specified files or all outstanding changes
23 diff diff repository (or selected files)
23 diff diff repository (or selected files)
24 export dump the header and diffs for one or more changesets
24 export dump the header and diffs for one or more changesets
25 forget forget the specified files on the next commit
25 forget forget the specified files on the next commit
26 init create a new repository in the given directory
26 init create a new repository in the given directory
27 log show revision history of entire repository or files
27 log show revision history of entire repository or files
28 merge merge working directory with another revision
28 merge merge working directory with another revision
29 pull pull changes from the specified source
29 pull pull changes from the specified source
30 push push changes to the specified destination
30 push push changes to the specified destination
31 remove remove the specified files on the next commit
31 remove remove the specified files on the next commit
32 serve start stand-alone webserver
32 serve start stand-alone webserver
33 status show changed files in the working directory
33 status show changed files in the working directory
34 summary summarize working directory state
34 summary summarize working directory state
35 update update working directory (or switch revisions)
35 update update working directory (or switch revisions)
36
36
37 use "hg help" for the full list of commands or "hg -v" for details
37 use "hg help" for the full list of commands or "hg -v" for details
38 runcommand id --quiet
38 runcommand id --quiet
39 000000000000
39 000000000000
40 runcommand id
40 runcommand id
41 000000000000 tip
41 000000000000 tip
42 runcommand id --config ui.quiet=True
42 runcommand id --config ui.quiet=True
43 000000000000
43 000000000000
44 runcommand id
44 runcommand id
45 000000000000 tip
45 000000000000 tip
46 runcommand id -runknown
46 runcommand id -runknown
47 abort: unknown revision 'unknown'!
47 abort: unknown revision 'unknown'!
48 [255]
48 [255]
49
49
50 testing inputeof:
50 testing inputeof:
51
51
52 server exit code = 1
52 server exit code = 1
53
53
54 testing serverinput:
54 testing serverinput:
55
55
56 runcommand import -
56 runcommand import -
57 applying patch from stdin
57 applying patch from stdin
58 runcommand log
58 runcommand log
59 changeset: 0:eff892de26ec
59 changeset: 0:eff892de26ec
60 tag: tip
60 tag: tip
61 user: test
61 user: test
62 date: Thu Jan 01 00:00:00 1970 +0000
62 date: Thu Jan 01 00:00:00 1970 +0000
63 summary: 1
63 summary: 1
64
64
65
65
66 testing cwd:
66 testing cwd:
67
67
68 runcommand --cwd foo st bar
68 runcommand --cwd foo st bar
69 ? bar
69 ? bar
70 runcommand st foo/bar
70 runcommand st foo/bar
71 ? foo/bar
71 ? foo/bar
72
72
73 testing localhgrc:
73 testing localhgrc:
74
74
75 runcommand showconfig
75 runcommand showconfig
76 bundle.mainreporoot=$TESTTMP/repo
76 bundle.mainreporoot=$TESTTMP/repo
77 defaults.backout=-d "0 0"
77 defaults.backout=-d "0 0"
78 defaults.commit=-d "0 0"
78 defaults.commit=-d "0 0"
79 defaults.shelve=--date "0 0"
79 defaults.shelve=--date "0 0"
80 defaults.tag=-d "0 0"
80 defaults.tag=-d "0 0"
81 ui.slash=True
81 ui.slash=True
82 ui.interactive=False
82 ui.interactive=False
83 ui.foo=bar
83 ui.foo=bar
84 ui.nontty=true
84 runcommand init foo
85 runcommand init foo
85 runcommand -R foo showconfig ui defaults
86 runcommand -R foo showconfig ui defaults
86 defaults.backout=-d "0 0"
87 defaults.backout=-d "0 0"
87 defaults.commit=-d "0 0"
88 defaults.commit=-d "0 0"
88 defaults.shelve=--date "0 0"
89 defaults.shelve=--date "0 0"
89 defaults.tag=-d "0 0"
90 defaults.tag=-d "0 0"
90 ui.slash=True
91 ui.slash=True
91 ui.interactive=False
92 ui.interactive=False
93 ui.nontty=true
92
94
93 testing hookoutput:
95 testing hookoutput:
94
96
95 runcommand --config hooks.pre-identify=python:test-commandserver.hook id
97 runcommand --config hooks.pre-identify=python:test-commandserver.hook id
96 hook talking
98 hook talking
97 now try to read something: 'some input'
99 now try to read something: 'some input'
98 eff892de26ec tip
100 eff892de26ec tip
99
101
100 testing outsidechanges:
102 testing outsidechanges:
101
103
102 runcommand status
104 runcommand status
103 M a
105 M a
104 runcommand tip
106 runcommand tip
105 changeset: 1:d3a0a68be6de
107 changeset: 1:d3a0a68be6de
106 tag: tip
108 tag: tip
107 user: test
109 user: test
108 date: Thu Jan 01 00:00:00 1970 +0000
110 date: Thu Jan 01 00:00:00 1970 +0000
109 summary: 2
111 summary: 2
110
112
111 runcommand status
113 runcommand status
112
114
113 testing bookmarks:
115 testing bookmarks:
114
116
115 runcommand bookmarks
117 runcommand bookmarks
116 no bookmarks set
118 no bookmarks set
117 runcommand bookmarks
119 runcommand bookmarks
118 bm1 1:d3a0a68be6de
120 bm1 1:d3a0a68be6de
119 bm2 1:d3a0a68be6de
121 bm2 1:d3a0a68be6de
120 runcommand bookmarks
122 runcommand bookmarks
121 * bm1 1:d3a0a68be6de
123 * bm1 1:d3a0a68be6de
122 bm2 1:d3a0a68be6de
124 bm2 1:d3a0a68be6de
123 runcommand bookmarks bm3
125 runcommand bookmarks bm3
124 runcommand commit -Amm
126 runcommand commit -Amm
125 runcommand bookmarks
127 runcommand bookmarks
126 bm1 1:d3a0a68be6de
128 bm1 1:d3a0a68be6de
127 bm2 1:d3a0a68be6de
129 bm2 1:d3a0a68be6de
128 * bm3 2:aef17e88f5f0
130 * bm3 2:aef17e88f5f0
129
131
130 testing tagscache:
132 testing tagscache:
131
133
132 runcommand id -t -r 0
134 runcommand id -t -r 0
133
135
134 runcommand id -t -r 0
136 runcommand id -t -r 0
135 foo
137 foo
136
138
137 testing setphase:
139 testing setphase:
138
140
139 runcommand phase -r .
141 runcommand phase -r .
140 3: draft
142 3: draft
141 runcommand phase -r .
143 runcommand phase -r .
142 3: public
144 3: public
143
145
144 testing rollback:
146 testing rollback:
145
147
146 runcommand phase -r . -p
148 runcommand phase -r . -p
147 no phases changed
149 no phases changed
148 [1]
150 [1]
149 runcommand commit -Am.
151 runcommand commit -Am.
150 runcommand rollback
152 runcommand rollback
151 repository tip rolled back to revision 3 (undo commit)
153 repository tip rolled back to revision 3 (undo commit)
152 working directory now based on revision 3
154 working directory now based on revision 3
153 runcommand phase -r .
155 runcommand phase -r .
154 3: public
156 3: public
155
157
156 testing branch:
158 testing branch:
157
159
158 runcommand branch
160 runcommand branch
159 default
161 default
160 marked working directory as branch foo
162 marked working directory as branch foo
161 (branches are permanent and global, did you want a bookmark?)
163 (branches are permanent and global, did you want a bookmark?)
162 runcommand branch
164 runcommand branch
163 foo
165 foo
164 marked working directory as branch default
166 marked working directory as branch default
165 (branches are permanent and global, did you want a bookmark?)
167 (branches are permanent and global, did you want a bookmark?)
166
168
167 testing hgignore:
169 testing hgignore:
168
170
169 runcommand commit -Am.
171 runcommand commit -Am.
170 adding .hgignore
172 adding .hgignore
171 runcommand status -i -u
173 runcommand status -i -u
172 I ignored-file
174 I ignored-file
173
175
174 testing phasecacheafterstrip:
176 testing phasecacheafterstrip:
175
177
176 runcommand update -C 0
178 runcommand update -C 0
177 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
179 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
178 runcommand commit -Am. a
180 runcommand commit -Am. a
179 created new head
181 created new head
180 runcommand log -Gq
182 runcommand log -Gq
181 @ 5:731265503d86
183 @ 5:731265503d86
182 |
184 |
183 | o 4:7966c8e3734d
185 | o 4:7966c8e3734d
184 | |
186 | |
185 | o 3:b9b85890c400
187 | o 3:b9b85890c400
186 | |
188 | |
187 | o 2:aef17e88f5f0
189 | o 2:aef17e88f5f0
188 | |
190 | |
189 | o 1:d3a0a68be6de
191 | o 1:d3a0a68be6de
190 |/
192 |/
191 o 0:eff892de26ec
193 o 0:eff892de26ec
192
194
193 runcommand phase -p .
195 runcommand phase -p .
194 runcommand phase .
196 runcommand phase .
195 5: public
197 5: public
196 runcommand branches
198 runcommand branches
197 default 1:731265503d86
199 default 1:731265503d86
198
200
199 testing obsolete:
201 testing obsolete:
200
202
201 runcommand up null
203 runcommand up null
202 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
204 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
203 runcommand phase -df tip
205 runcommand phase -df tip
204 runcommand log --hidden
206 runcommand log --hidden
205 changeset: 1:731265503d86
207 changeset: 1:731265503d86
206 tag: tip
208 tag: tip
207 user: test
209 user: test
208 date: Thu Jan 01 00:00:00 1970 +0000
210 date: Thu Jan 01 00:00:00 1970 +0000
209 summary: .
211 summary: .
210
212
211 changeset: 0:eff892de26ec
213 changeset: 0:eff892de26ec
212 bookmark: bm1
214 bookmark: bm1
213 bookmark: bm2
215 bookmark: bm2
214 bookmark: bm3
216 bookmark: bm3
215 user: test
217 user: test
216 date: Thu Jan 01 00:00:00 1970 +0000
218 date: Thu Jan 01 00:00:00 1970 +0000
217 summary: 1
219 summary: 1
218
220
219 runcommand log
221 runcommand log
220 changeset: 0:eff892de26ec
222 changeset: 0:eff892de26ec
221 bookmark: bm1
223 bookmark: bm1
222 bookmark: bm2
224 bookmark: bm2
223 bookmark: bm3
225 bookmark: bm3
224 tag: tip
226 tag: tip
225 user: test
227 user: test
226 date: Thu Jan 01 00:00:00 1970 +0000
228 date: Thu Jan 01 00:00:00 1970 +0000
227 summary: 1
229 summary: 1
228
230
229
231
230 testing mqoutsidechanges:
232 testing mqoutsidechanges:
231
233
232 runcommand qapplied
234 runcommand qapplied
233 runcommand qapplied
235 runcommand qapplied
234 0.diff
236 0.diff
235 runcommand qpop --all
237 runcommand qpop --all
236 popping 0.diff
238 popping 0.diff
237 patch queue now empty
239 patch queue now empty
238 runcommand qqueue --active
240 runcommand qqueue --active
239 foo
241 foo
240
242
243 testing getpass:
244
245 runcommand debuggetpass --config ui.interactive=True
246 password: 1234
247
241 testing hellomessage:
248 testing hellomessage:
242
249
243 o, 'capabilities: getencoding runcommand\nencoding: ***'
250 o, 'capabilities: getencoding runcommand\nencoding: ***'
244 runcommand id
251 runcommand id
245 abort: there is no Mercurial repository here (.hg not found)
252 abort: there is no Mercurial repository here (.hg not found)
246 [255]
253 [255]
247
254
248 testing startwithoutrepo:
255 testing startwithoutrepo:
249
256
250 runcommand init repo2
257 runcommand init repo2
251 runcommand id -R repo2
258 runcommand id -R repo2
252 000000000000 tip
259 000000000000 tip
General Comments 0
You need to be logged in to leave comments. Login now