##// END OF EJS Templates
config: move message about leading spaces in config to config.py...
Martin von Zweigbergk -
r46362:0883413e default
parent child Browse files
Show More
@@ -1,311 +1,314 b''
1 # config.py - configuration parsing for Mercurial
1 # config.py - configuration parsing for Mercurial
2 #
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12
12
13 from .i18n import _
13 from .i18n import _
14 from .pycompat import getattr
14 from .pycompat import getattr
15 from . import (
15 from . import (
16 encoding,
16 encoding,
17 error,
17 error,
18 pycompat,
18 pycompat,
19 util,
19 util,
20 )
20 )
21
21
22
22
23 class config(object):
23 class config(object):
24 def __init__(self, data=None):
24 def __init__(self, data=None):
25 self._data = {}
25 self._data = {}
26 self._unset = []
26 self._unset = []
27 if data:
27 if data:
28 for k in data._data:
28 for k in data._data:
29 self._data[k] = data[k].copy()
29 self._data[k] = data[k].copy()
30 self._source = data._source.copy()
30 self._source = data._source.copy()
31 else:
31 else:
32 self._source = util.cowdict()
32 self._source = util.cowdict()
33
33
34 def copy(self):
34 def copy(self):
35 return config(self)
35 return config(self)
36
36
37 def __contains__(self, section):
37 def __contains__(self, section):
38 return section in self._data
38 return section in self._data
39
39
40 def hasitem(self, section, item):
40 def hasitem(self, section, item):
41 return item in self._data.get(section, {})
41 return item in self._data.get(section, {})
42
42
43 def __getitem__(self, section):
43 def __getitem__(self, section):
44 return self._data.get(section, {})
44 return self._data.get(section, {})
45
45
46 def __iter__(self):
46 def __iter__(self):
47 for d in self.sections():
47 for d in self.sections():
48 yield d
48 yield d
49
49
50 def update(self, src):
50 def update(self, src):
51 self._source = self._source.preparewrite()
51 self._source = self._source.preparewrite()
52 for s, n in src._unset:
52 for s, n in src._unset:
53 ds = self._data.get(s, None)
53 ds = self._data.get(s, None)
54 if ds is not None and n in ds:
54 if ds is not None and n in ds:
55 self._data[s] = ds.preparewrite()
55 self._data[s] = ds.preparewrite()
56 del self._data[s][n]
56 del self._data[s][n]
57 del self._source[(s, n)]
57 del self._source[(s, n)]
58 for s in src:
58 for s in src:
59 ds = self._data.get(s, None)
59 ds = self._data.get(s, None)
60 if ds:
60 if ds:
61 self._data[s] = ds.preparewrite()
61 self._data[s] = ds.preparewrite()
62 else:
62 else:
63 self._data[s] = util.cowsortdict()
63 self._data[s] = util.cowsortdict()
64 self._data[s].update(src._data[s])
64 self._data[s].update(src._data[s])
65 self._source.update(src._source)
65 self._source.update(src._source)
66
66
67 def get(self, section, item, default=None):
67 def get(self, section, item, default=None):
68 return self._data.get(section, {}).get(item, default)
68 return self._data.get(section, {}).get(item, default)
69
69
70 def backup(self, section, item):
70 def backup(self, section, item):
71 """return a tuple allowing restore to reinstall a previous value
71 """return a tuple allowing restore to reinstall a previous value
72
72
73 The main reason we need it is because it handles the "no data" case.
73 The main reason we need it is because it handles the "no data" case.
74 """
74 """
75 try:
75 try:
76 value = self._data[section][item]
76 value = self._data[section][item]
77 source = self.source(section, item)
77 source = self.source(section, item)
78 return (section, item, value, source)
78 return (section, item, value, source)
79 except KeyError:
79 except KeyError:
80 return (section, item)
80 return (section, item)
81
81
82 def source(self, section, item):
82 def source(self, section, item):
83 return self._source.get((section, item), b"")
83 return self._source.get((section, item), b"")
84
84
85 def sections(self):
85 def sections(self):
86 return sorted(self._data.keys())
86 return sorted(self._data.keys())
87
87
88 def items(self, section):
88 def items(self, section):
89 return list(pycompat.iteritems(self._data.get(section, {})))
89 return list(pycompat.iteritems(self._data.get(section, {})))
90
90
91 def set(self, section, item, value, source=b""):
91 def set(self, section, item, value, source=b""):
92 if pycompat.ispy3:
92 if pycompat.ispy3:
93 assert not isinstance(
93 assert not isinstance(
94 section, str
94 section, str
95 ), b'config section may not be unicode strings on Python 3'
95 ), b'config section may not be unicode strings on Python 3'
96 assert not isinstance(
96 assert not isinstance(
97 item, str
97 item, str
98 ), b'config item may not be unicode strings on Python 3'
98 ), b'config item may not be unicode strings on Python 3'
99 assert not isinstance(
99 assert not isinstance(
100 value, str
100 value, str
101 ), b'config values may not be unicode strings on Python 3'
101 ), b'config values may not be unicode strings on Python 3'
102 if section not in self:
102 if section not in self:
103 self._data[section] = util.cowsortdict()
103 self._data[section] = util.cowsortdict()
104 else:
104 else:
105 self._data[section] = self._data[section].preparewrite()
105 self._data[section] = self._data[section].preparewrite()
106 self._data[section][item] = value
106 self._data[section][item] = value
107 if source:
107 if source:
108 self._source = self._source.preparewrite()
108 self._source = self._source.preparewrite()
109 self._source[(section, item)] = source
109 self._source[(section, item)] = source
110
110
111 def restore(self, data):
111 def restore(self, data):
112 """restore data returned by self.backup"""
112 """restore data returned by self.backup"""
113 self._source = self._source.preparewrite()
113 self._source = self._source.preparewrite()
114 if len(data) == 4:
114 if len(data) == 4:
115 # restore old data
115 # restore old data
116 section, item, value, source = data
116 section, item, value, source = data
117 self._data[section] = self._data[section].preparewrite()
117 self._data[section] = self._data[section].preparewrite()
118 self._data[section][item] = value
118 self._data[section][item] = value
119 self._source[(section, item)] = source
119 self._source[(section, item)] = source
120 else:
120 else:
121 # no data before, remove everything
121 # no data before, remove everything
122 section, item = data
122 section, item = data
123 if section in self._data:
123 if section in self._data:
124 self._data[section].pop(item, None)
124 self._data[section].pop(item, None)
125 self._source.pop((section, item), None)
125 self._source.pop((section, item), None)
126
126
127 def parse(self, src, data, sections=None, remap=None, include=None):
127 def parse(self, src, data, sections=None, remap=None, include=None):
128 sectionre = util.re.compile(br'\[([^\[]+)\]')
128 sectionre = util.re.compile(br'\[([^\[]+)\]')
129 itemre = util.re.compile(br'([^=\s][^=]*?)\s*=\s*(.*\S|)')
129 itemre = util.re.compile(br'([^=\s][^=]*?)\s*=\s*(.*\S|)')
130 contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
130 contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
131 emptyre = util.re.compile(br'(;|#|\s*$)')
131 emptyre = util.re.compile(br'(;|#|\s*$)')
132 commentre = util.re.compile(br'(;|#)')
132 commentre = util.re.compile(br'(;|#)')
133 unsetre = util.re.compile(br'%unset\s+(\S+)')
133 unsetre = util.re.compile(br'%unset\s+(\S+)')
134 includere = util.re.compile(br'%include\s+(\S|\S.*\S)\s*$')
134 includere = util.re.compile(br'%include\s+(\S|\S.*\S)\s*$')
135 section = b""
135 section = b""
136 item = None
136 item = None
137 line = 0
137 line = 0
138 cont = False
138 cont = False
139
139
140 if remap:
140 if remap:
141 section = remap.get(section, section)
141 section = remap.get(section, section)
142
142
143 for l in data.splitlines(True):
143 for l in data.splitlines(True):
144 line += 1
144 line += 1
145 if line == 1 and l.startswith(b'\xef\xbb\xbf'):
145 if line == 1 and l.startswith(b'\xef\xbb\xbf'):
146 # Someone set us up the BOM
146 # Someone set us up the BOM
147 l = l[3:]
147 l = l[3:]
148 if cont:
148 if cont:
149 if commentre.match(l):
149 if commentre.match(l):
150 continue
150 continue
151 m = contre.match(l)
151 m = contre.match(l)
152 if m:
152 if m:
153 if sections and section not in sections:
153 if sections and section not in sections:
154 continue
154 continue
155 v = self.get(section, item) + b"\n" + m.group(1)
155 v = self.get(section, item) + b"\n" + m.group(1)
156 self.set(section, item, v, b"%s:%d" % (src, line))
156 self.set(section, item, v, b"%s:%d" % (src, line))
157 continue
157 continue
158 item = None
158 item = None
159 cont = False
159 cont = False
160 m = includere.match(l)
160 m = includere.match(l)
161
161
162 if m and include:
162 if m and include:
163 expanded = util.expandpath(m.group(1))
163 expanded = util.expandpath(m.group(1))
164 try:
164 try:
165 include(expanded, remap=remap, sections=sections)
165 include(expanded, remap=remap, sections=sections)
166 except IOError as inst:
166 except IOError as inst:
167 if inst.errno != errno.ENOENT:
167 if inst.errno != errno.ENOENT:
168 raise error.ParseError(
168 raise error.ParseError(
169 _(b"cannot include %s (%s)")
169 _(b"cannot include %s (%s)")
170 % (expanded, encoding.strtolocal(inst.strerror)),
170 % (expanded, encoding.strtolocal(inst.strerror)),
171 b"%s:%d" % (src, line),
171 b"%s:%d" % (src, line),
172 )
172 )
173 continue
173 continue
174 if emptyre.match(l):
174 if emptyre.match(l):
175 continue
175 continue
176 m = sectionre.match(l)
176 m = sectionre.match(l)
177 if m:
177 if m:
178 section = m.group(1)
178 section = m.group(1)
179 if remap:
179 if remap:
180 section = remap.get(section, section)
180 section = remap.get(section, section)
181 if section not in self:
181 if section not in self:
182 self._data[section] = util.cowsortdict()
182 self._data[section] = util.cowsortdict()
183 continue
183 continue
184 m = itemre.match(l)
184 m = itemre.match(l)
185 if m:
185 if m:
186 item = m.group(1)
186 item = m.group(1)
187 cont = True
187 cont = True
188 if sections and section not in sections:
188 if sections and section not in sections:
189 continue
189 continue
190 self.set(section, item, m.group(2), b"%s:%d" % (src, line))
190 self.set(section, item, m.group(2), b"%s:%d" % (src, line))
191 continue
191 continue
192 m = unsetre.match(l)
192 m = unsetre.match(l)
193 if m:
193 if m:
194 name = m.group(1)
194 name = m.group(1)
195 if sections and section not in sections:
195 if sections and section not in sections:
196 continue
196 continue
197 if self.get(section, name) is not None:
197 if self.get(section, name) is not None:
198 self._data[section] = self._data[section].preparewrite()
198 self._data[section] = self._data[section].preparewrite()
199 del self._data[section][name]
199 del self._data[section][name]
200 self._unset.append((section, name))
200 self._unset.append((section, name))
201 continue
201 continue
202
202
203 raise error.ParseError(l.rstrip(), (b"%s:%d" % (src, line)))
203 message = l.rstrip()
204 if l.startswith(b' '):
205 message = b"unexpected leading whitespace: %s" % message
206 raise error.ParseError(message, (b"%s:%d" % (src, line)))
204
207
205 def read(self, path, fp=None, sections=None, remap=None):
208 def read(self, path, fp=None, sections=None, remap=None):
206 if not fp:
209 if not fp:
207 fp = util.posixfile(path, b'rb')
210 fp = util.posixfile(path, b'rb')
208 assert getattr(fp, 'mode', 'rb') == 'rb', (
211 assert getattr(fp, 'mode', 'rb') == 'rb', (
209 b'config files must be opened in binary mode, got fp=%r mode=%r'
212 b'config files must be opened in binary mode, got fp=%r mode=%r'
210 % (fp, fp.mode,)
213 % (fp, fp.mode,)
211 )
214 )
212
215
213 dir = os.path.dirname(path)
216 dir = os.path.dirname(path)
214
217
215 def include(rel, remap, sections):
218 def include(rel, remap, sections):
216 abs = os.path.normpath(os.path.join(dir, rel))
219 abs = os.path.normpath(os.path.join(dir, rel))
217 self.read(abs, remap=remap, sections=sections)
220 self.read(abs, remap=remap, sections=sections)
218
221
219 self.parse(
222 self.parse(
220 path, fp.read(), sections=sections, remap=remap, include=include
223 path, fp.read(), sections=sections, remap=remap, include=include
221 )
224 )
222
225
223
226
224 def parselist(value):
227 def parselist(value):
225 """parse a configuration value as a list of comma/space separated strings
228 """parse a configuration value as a list of comma/space separated strings
226
229
227 >>> parselist(b'this,is "a small" ,test')
230 >>> parselist(b'this,is "a small" ,test')
228 ['this', 'is', 'a small', 'test']
231 ['this', 'is', 'a small', 'test']
229 """
232 """
230
233
231 def _parse_plain(parts, s, offset):
234 def _parse_plain(parts, s, offset):
232 whitespace = False
235 whitespace = False
233 while offset < len(s) and (
236 while offset < len(s) and (
234 s[offset : offset + 1].isspace() or s[offset : offset + 1] == b','
237 s[offset : offset + 1].isspace() or s[offset : offset + 1] == b','
235 ):
238 ):
236 whitespace = True
239 whitespace = True
237 offset += 1
240 offset += 1
238 if offset >= len(s):
241 if offset >= len(s):
239 return None, parts, offset
242 return None, parts, offset
240 if whitespace:
243 if whitespace:
241 parts.append(b'')
244 parts.append(b'')
242 if s[offset : offset + 1] == b'"' and not parts[-1]:
245 if s[offset : offset + 1] == b'"' and not parts[-1]:
243 return _parse_quote, parts, offset + 1
246 return _parse_quote, parts, offset + 1
244 elif s[offset : offset + 1] == b'"' and parts[-1][-1:] == b'\\':
247 elif s[offset : offset + 1] == b'"' and parts[-1][-1:] == b'\\':
245 parts[-1] = parts[-1][:-1] + s[offset : offset + 1]
248 parts[-1] = parts[-1][:-1] + s[offset : offset + 1]
246 return _parse_plain, parts, offset + 1
249 return _parse_plain, parts, offset + 1
247 parts[-1] += s[offset : offset + 1]
250 parts[-1] += s[offset : offset + 1]
248 return _parse_plain, parts, offset + 1
251 return _parse_plain, parts, offset + 1
249
252
250 def _parse_quote(parts, s, offset):
253 def _parse_quote(parts, s, offset):
251 if offset < len(s) and s[offset : offset + 1] == b'"': # ""
254 if offset < len(s) and s[offset : offset + 1] == b'"': # ""
252 parts.append(b'')
255 parts.append(b'')
253 offset += 1
256 offset += 1
254 while offset < len(s) and (
257 while offset < len(s) and (
255 s[offset : offset + 1].isspace()
258 s[offset : offset + 1].isspace()
256 or s[offset : offset + 1] == b','
259 or s[offset : offset + 1] == b','
257 ):
260 ):
258 offset += 1
261 offset += 1
259 return _parse_plain, parts, offset
262 return _parse_plain, parts, offset
260
263
261 while offset < len(s) and s[offset : offset + 1] != b'"':
264 while offset < len(s) and s[offset : offset + 1] != b'"':
262 if (
265 if (
263 s[offset : offset + 1] == b'\\'
266 s[offset : offset + 1] == b'\\'
264 and offset + 1 < len(s)
267 and offset + 1 < len(s)
265 and s[offset + 1 : offset + 2] == b'"'
268 and s[offset + 1 : offset + 2] == b'"'
266 ):
269 ):
267 offset += 1
270 offset += 1
268 parts[-1] += b'"'
271 parts[-1] += b'"'
269 else:
272 else:
270 parts[-1] += s[offset : offset + 1]
273 parts[-1] += s[offset : offset + 1]
271 offset += 1
274 offset += 1
272
275
273 if offset >= len(s):
276 if offset >= len(s):
274 real_parts = _configlist(parts[-1])
277 real_parts = _configlist(parts[-1])
275 if not real_parts:
278 if not real_parts:
276 parts[-1] = b'"'
279 parts[-1] = b'"'
277 else:
280 else:
278 real_parts[0] = b'"' + real_parts[0]
281 real_parts[0] = b'"' + real_parts[0]
279 parts = parts[:-1]
282 parts = parts[:-1]
280 parts.extend(real_parts)
283 parts.extend(real_parts)
281 return None, parts, offset
284 return None, parts, offset
282
285
283 offset += 1
286 offset += 1
284 while offset < len(s) and s[offset : offset + 1] in [b' ', b',']:
287 while offset < len(s) and s[offset : offset + 1] in [b' ', b',']:
285 offset += 1
288 offset += 1
286
289
287 if offset < len(s):
290 if offset < len(s):
288 if offset + 1 == len(s) and s[offset : offset + 1] == b'"':
291 if offset + 1 == len(s) and s[offset : offset + 1] == b'"':
289 parts[-1] += b'"'
292 parts[-1] += b'"'
290 offset += 1
293 offset += 1
291 else:
294 else:
292 parts.append(b'')
295 parts.append(b'')
293 else:
296 else:
294 return None, parts, offset
297 return None, parts, offset
295
298
296 return _parse_plain, parts, offset
299 return _parse_plain, parts, offset
297
300
298 def _configlist(s):
301 def _configlist(s):
299 s = s.rstrip(b' ,')
302 s = s.rstrip(b' ,')
300 if not s:
303 if not s:
301 return []
304 return []
302 parser, parts, offset = _parse_plain, [b''], 0
305 parser, parts, offset = _parse_plain, [b''], 0
303 while parser:
306 while parser:
304 parser, parts, offset = parser(parts, s, offset)
307 parser, parts, offset = parser(parts, s, offset)
305 return parts
308 return parts
306
309
307 if value is not None and isinstance(value, bytes):
310 if value is not None and isinstance(value, bytes):
308 result = _configlist(value.lstrip(b' ,\n'))
311 result = _configlist(value.lstrip(b' ,\n'))
309 else:
312 else:
310 result = value
313 result = value
311 return result or []
314 return result or []
@@ -1,1337 +1,1335 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching 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 __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import getopt
12 import getopt
13 import io
13 import io
14 import os
14 import os
15 import pdb
15 import pdb
16 import re
16 import re
17 import signal
17 import signal
18 import sys
18 import sys
19 import traceback
19 import traceback
20
20
21
21
22 from .i18n import _
22 from .i18n import _
23 from .pycompat import getattr
23 from .pycompat import getattr
24
24
25 from hgdemandimport import tracing
25 from hgdemandimport import tracing
26
26
27 from . import (
27 from . import (
28 cmdutil,
28 cmdutil,
29 color,
29 color,
30 commands,
30 commands,
31 demandimport,
31 demandimport,
32 encoding,
32 encoding,
33 error,
33 error,
34 extensions,
34 extensions,
35 fancyopts,
35 fancyopts,
36 help,
36 help,
37 hg,
37 hg,
38 hook,
38 hook,
39 profiling,
39 profiling,
40 pycompat,
40 pycompat,
41 rcutil,
41 rcutil,
42 registrar,
42 registrar,
43 scmutil,
43 scmutil,
44 ui as uimod,
44 ui as uimod,
45 util,
45 util,
46 )
46 )
47
47
48 from .utils import (
48 from .utils import (
49 procutil,
49 procutil,
50 stringutil,
50 stringutil,
51 )
51 )
52
52
53
53
54 class request(object):
54 class request(object):
55 def __init__(
55 def __init__(
56 self,
56 self,
57 args,
57 args,
58 ui=None,
58 ui=None,
59 repo=None,
59 repo=None,
60 fin=None,
60 fin=None,
61 fout=None,
61 fout=None,
62 ferr=None,
62 ferr=None,
63 fmsg=None,
63 fmsg=None,
64 prereposetups=None,
64 prereposetups=None,
65 ):
65 ):
66 self.args = args
66 self.args = args
67 self.ui = ui
67 self.ui = ui
68 self.repo = repo
68 self.repo = repo
69
69
70 # input/output/error streams
70 # input/output/error streams
71 self.fin = fin
71 self.fin = fin
72 self.fout = fout
72 self.fout = fout
73 self.ferr = ferr
73 self.ferr = ferr
74 # separate stream for status/error messages
74 # separate stream for status/error messages
75 self.fmsg = fmsg
75 self.fmsg = fmsg
76
76
77 # remember options pre-parsed by _earlyparseopts()
77 # remember options pre-parsed by _earlyparseopts()
78 self.earlyoptions = {}
78 self.earlyoptions = {}
79
79
80 # reposetups which run before extensions, useful for chg to pre-fill
80 # reposetups which run before extensions, useful for chg to pre-fill
81 # low-level repo state (for example, changelog) before extensions.
81 # low-level repo state (for example, changelog) before extensions.
82 self.prereposetups = prereposetups or []
82 self.prereposetups = prereposetups or []
83
83
84 # store the parsed and canonical command
84 # store the parsed and canonical command
85 self.canonical_command = None
85 self.canonical_command = None
86
86
87 def _runexithandlers(self):
87 def _runexithandlers(self):
88 exc = None
88 exc = None
89 handlers = self.ui._exithandlers
89 handlers = self.ui._exithandlers
90 try:
90 try:
91 while handlers:
91 while handlers:
92 func, args, kwargs = handlers.pop()
92 func, args, kwargs = handlers.pop()
93 try:
93 try:
94 func(*args, **kwargs)
94 func(*args, **kwargs)
95 except: # re-raises below
95 except: # re-raises below
96 if exc is None:
96 if exc is None:
97 exc = sys.exc_info()[1]
97 exc = sys.exc_info()[1]
98 self.ui.warnnoi18n(b'error in exit handlers:\n')
98 self.ui.warnnoi18n(b'error in exit handlers:\n')
99 self.ui.traceback(force=True)
99 self.ui.traceback(force=True)
100 finally:
100 finally:
101 if exc is not None:
101 if exc is not None:
102 raise exc
102 raise exc
103
103
104
104
105 def run():
105 def run():
106 """run the command in sys.argv"""
106 """run the command in sys.argv"""
107 try:
107 try:
108 initstdio()
108 initstdio()
109 with tracing.log('parse args into request'):
109 with tracing.log('parse args into request'):
110 req = request(pycompat.sysargv[1:])
110 req = request(pycompat.sysargv[1:])
111 err = None
111 err = None
112 try:
112 try:
113 status = dispatch(req)
113 status = dispatch(req)
114 except error.StdioError as e:
114 except error.StdioError as e:
115 err = e
115 err = e
116 status = -1
116 status = -1
117
117
118 # In all cases we try to flush stdio streams.
118 # In all cases we try to flush stdio streams.
119 if util.safehasattr(req.ui, b'fout'):
119 if util.safehasattr(req.ui, b'fout'):
120 assert req.ui is not None # help pytype
120 assert req.ui is not None # help pytype
121 assert req.ui.fout is not None # help pytype
121 assert req.ui.fout is not None # help pytype
122 try:
122 try:
123 req.ui.fout.flush()
123 req.ui.fout.flush()
124 except IOError as e:
124 except IOError as e:
125 err = e
125 err = e
126 status = -1
126 status = -1
127
127
128 if util.safehasattr(req.ui, b'ferr'):
128 if util.safehasattr(req.ui, b'ferr'):
129 assert req.ui is not None # help pytype
129 assert req.ui is not None # help pytype
130 assert req.ui.ferr is not None # help pytype
130 assert req.ui.ferr is not None # help pytype
131 try:
131 try:
132 if err is not None and err.errno != errno.EPIPE:
132 if err is not None and err.errno != errno.EPIPE:
133 req.ui.ferr.write(
133 req.ui.ferr.write(
134 b'abort: %s\n' % encoding.strtolocal(err.strerror)
134 b'abort: %s\n' % encoding.strtolocal(err.strerror)
135 )
135 )
136 req.ui.ferr.flush()
136 req.ui.ferr.flush()
137 # There's not much we can do about an I/O error here. So (possibly)
137 # There's not much we can do about an I/O error here. So (possibly)
138 # change the status code and move on.
138 # change the status code and move on.
139 except IOError:
139 except IOError:
140 status = -1
140 status = -1
141
141
142 _silencestdio()
142 _silencestdio()
143 except KeyboardInterrupt:
143 except KeyboardInterrupt:
144 # Catch early/late KeyboardInterrupt as last ditch. Here nothing will
144 # Catch early/late KeyboardInterrupt as last ditch. Here nothing will
145 # be printed to console to avoid another IOError/KeyboardInterrupt.
145 # be printed to console to avoid another IOError/KeyboardInterrupt.
146 status = -1
146 status = -1
147 sys.exit(status & 255)
147 sys.exit(status & 255)
148
148
149
149
150 if pycompat.ispy3:
150 if pycompat.ispy3:
151
151
152 def initstdio():
152 def initstdio():
153 # stdio streams on Python 3 are io.TextIOWrapper instances proxying another
153 # stdio streams on Python 3 are io.TextIOWrapper instances proxying another
154 # buffer. These streams will normalize \n to \r\n by default. Mercurial's
154 # buffer. These streams will normalize \n to \r\n by default. Mercurial's
155 # preferred mechanism for writing output (ui.write()) uses io.BufferedWriter
155 # preferred mechanism for writing output (ui.write()) uses io.BufferedWriter
156 # instances, which write to the underlying stdio file descriptor in binary
156 # instances, which write to the underlying stdio file descriptor in binary
157 # mode. ui.write() uses \n for line endings and no line ending normalization
157 # mode. ui.write() uses \n for line endings and no line ending normalization
158 # is attempted through this interface. This "just works," even if the system
158 # is attempted through this interface. This "just works," even if the system
159 # preferred line ending is not \n.
159 # preferred line ending is not \n.
160 #
160 #
161 # But some parts of Mercurial (e.g. hooks) can still send data to sys.stdout
161 # But some parts of Mercurial (e.g. hooks) can still send data to sys.stdout
162 # and sys.stderr. They will inherit the line ending normalization settings,
162 # and sys.stderr. They will inherit the line ending normalization settings,
163 # potentially causing e.g. \r\n to be emitted. Since emitting \n should
163 # potentially causing e.g. \r\n to be emitted. Since emitting \n should
164 # "just work," here we change the sys.* streams to disable line ending
164 # "just work," here we change the sys.* streams to disable line ending
165 # normalization, ensuring compatibility with our ui type.
165 # normalization, ensuring compatibility with our ui type.
166
166
167 # write_through is new in Python 3.7.
167 # write_through is new in Python 3.7.
168 kwargs = {
168 kwargs = {
169 "newline": "\n",
169 "newline": "\n",
170 "line_buffering": sys.stdout.line_buffering,
170 "line_buffering": sys.stdout.line_buffering,
171 }
171 }
172 if util.safehasattr(sys.stdout, "write_through"):
172 if util.safehasattr(sys.stdout, "write_through"):
173 kwargs["write_through"] = sys.stdout.write_through
173 kwargs["write_through"] = sys.stdout.write_through
174 sys.stdout = io.TextIOWrapper(
174 sys.stdout = io.TextIOWrapper(
175 sys.stdout.buffer, sys.stdout.encoding, sys.stdout.errors, **kwargs
175 sys.stdout.buffer, sys.stdout.encoding, sys.stdout.errors, **kwargs
176 )
176 )
177
177
178 kwargs = {
178 kwargs = {
179 "newline": "\n",
179 "newline": "\n",
180 "line_buffering": sys.stderr.line_buffering,
180 "line_buffering": sys.stderr.line_buffering,
181 }
181 }
182 if util.safehasattr(sys.stderr, "write_through"):
182 if util.safehasattr(sys.stderr, "write_through"):
183 kwargs["write_through"] = sys.stderr.write_through
183 kwargs["write_through"] = sys.stderr.write_through
184 sys.stderr = io.TextIOWrapper(
184 sys.stderr = io.TextIOWrapper(
185 sys.stderr.buffer, sys.stderr.encoding, sys.stderr.errors, **kwargs
185 sys.stderr.buffer, sys.stderr.encoding, sys.stderr.errors, **kwargs
186 )
186 )
187
187
188 # No write_through on read-only stream.
188 # No write_through on read-only stream.
189 sys.stdin = io.TextIOWrapper(
189 sys.stdin = io.TextIOWrapper(
190 sys.stdin.buffer,
190 sys.stdin.buffer,
191 sys.stdin.encoding,
191 sys.stdin.encoding,
192 sys.stdin.errors,
192 sys.stdin.errors,
193 # None is universal newlines mode.
193 # None is universal newlines mode.
194 newline=None,
194 newline=None,
195 line_buffering=sys.stdin.line_buffering,
195 line_buffering=sys.stdin.line_buffering,
196 )
196 )
197
197
198 def _silencestdio():
198 def _silencestdio():
199 for fp in (sys.stdout, sys.stderr):
199 for fp in (sys.stdout, sys.stderr):
200 # Check if the file is okay
200 # Check if the file is okay
201 try:
201 try:
202 fp.flush()
202 fp.flush()
203 continue
203 continue
204 except IOError:
204 except IOError:
205 pass
205 pass
206 # Otherwise mark it as closed to silence "Exception ignored in"
206 # Otherwise mark it as closed to silence "Exception ignored in"
207 # message emitted by the interpreter finalizer. Be careful to
207 # message emitted by the interpreter finalizer. Be careful to
208 # not close procutil.stdout, which may be a fdopen-ed file object
208 # not close procutil.stdout, which may be a fdopen-ed file object
209 # and its close() actually closes the underlying file descriptor.
209 # and its close() actually closes the underlying file descriptor.
210 try:
210 try:
211 fp.close()
211 fp.close()
212 except IOError:
212 except IOError:
213 pass
213 pass
214
214
215
215
216 else:
216 else:
217
217
218 def initstdio():
218 def initstdio():
219 for fp in (sys.stdin, sys.stdout, sys.stderr):
219 for fp in (sys.stdin, sys.stdout, sys.stderr):
220 procutil.setbinary(fp)
220 procutil.setbinary(fp)
221
221
222 def _silencestdio():
222 def _silencestdio():
223 pass
223 pass
224
224
225
225
226 def _getsimilar(symbols, value):
226 def _getsimilar(symbols, value):
227 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
227 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
228 # The cutoff for similarity here is pretty arbitrary. It should
228 # The cutoff for similarity here is pretty arbitrary. It should
229 # probably be investigated and tweaked.
229 # probably be investigated and tweaked.
230 return [s for s in symbols if sim(s) > 0.6]
230 return [s for s in symbols if sim(s) > 0.6]
231
231
232
232
233 def _reportsimilar(write, similar):
233 def _reportsimilar(write, similar):
234 if len(similar) == 1:
234 if len(similar) == 1:
235 write(_(b"(did you mean %s?)\n") % similar[0])
235 write(_(b"(did you mean %s?)\n") % similar[0])
236 elif similar:
236 elif similar:
237 ss = b", ".join(sorted(similar))
237 ss = b", ".join(sorted(similar))
238 write(_(b"(did you mean one of %s?)\n") % ss)
238 write(_(b"(did you mean one of %s?)\n") % ss)
239
239
240
240
241 def _formatparse(write, inst):
241 def _formatparse(write, inst):
242 similar = []
242 similar = []
243 if isinstance(inst, error.UnknownIdentifier):
243 if isinstance(inst, error.UnknownIdentifier):
244 # make sure to check fileset first, as revset can invoke fileset
244 # make sure to check fileset first, as revset can invoke fileset
245 similar = _getsimilar(inst.symbols, inst.function)
245 similar = _getsimilar(inst.symbols, inst.function)
246 if inst.location is not None:
246 if inst.location is not None:
247 write(
247 write(
248 _(b"hg: parse error at %s: %s\n")
248 _(b"hg: parse error at %s: %s\n")
249 % (pycompat.bytestr(inst.location), inst.message)
249 % (pycompat.bytestr(inst.location), inst.message)
250 )
250 )
251 if inst.message.startswith(b' '):
252 write(_(b"unexpected leading whitespace\n"))
253 else:
251 else:
254 write(_(b"hg: parse error: %s\n") % inst.message)
252 write(_(b"hg: parse error: %s\n") % inst.message)
255 _reportsimilar(write, similar)
253 _reportsimilar(write, similar)
256 if inst.hint:
254 if inst.hint:
257 write(_(b"(%s)\n") % inst.hint)
255 write(_(b"(%s)\n") % inst.hint)
258
256
259
257
260 def _formatargs(args):
258 def _formatargs(args):
261 return b' '.join(procutil.shellquote(a) for a in args)
259 return b' '.join(procutil.shellquote(a) for a in args)
262
260
263
261
264 def dispatch(req):
262 def dispatch(req):
265 """run the command specified in req.args; returns an integer status code"""
263 """run the command specified in req.args; returns an integer status code"""
266 with tracing.log('dispatch.dispatch'):
264 with tracing.log('dispatch.dispatch'):
267 if req.ferr:
265 if req.ferr:
268 ferr = req.ferr
266 ferr = req.ferr
269 elif req.ui:
267 elif req.ui:
270 ferr = req.ui.ferr
268 ferr = req.ui.ferr
271 else:
269 else:
272 ferr = procutil.stderr
270 ferr = procutil.stderr
273
271
274 try:
272 try:
275 if not req.ui:
273 if not req.ui:
276 req.ui = uimod.ui.load()
274 req.ui = uimod.ui.load()
277 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
275 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
278 if req.earlyoptions[b'traceback']:
276 if req.earlyoptions[b'traceback']:
279 req.ui.setconfig(b'ui', b'traceback', b'on', b'--traceback')
277 req.ui.setconfig(b'ui', b'traceback', b'on', b'--traceback')
280
278
281 # set ui streams from the request
279 # set ui streams from the request
282 if req.fin:
280 if req.fin:
283 req.ui.fin = req.fin
281 req.ui.fin = req.fin
284 if req.fout:
282 if req.fout:
285 req.ui.fout = req.fout
283 req.ui.fout = req.fout
286 if req.ferr:
284 if req.ferr:
287 req.ui.ferr = req.ferr
285 req.ui.ferr = req.ferr
288 if req.fmsg:
286 if req.fmsg:
289 req.ui.fmsg = req.fmsg
287 req.ui.fmsg = req.fmsg
290 except error.Abort as inst:
288 except error.Abort as inst:
291 ferr.write(_(b"abort: %s\n") % inst.message)
289 ferr.write(_(b"abort: %s\n") % inst.message)
292 if inst.hint:
290 if inst.hint:
293 ferr.write(_(b"(%s)\n") % inst.hint)
291 ferr.write(_(b"(%s)\n") % inst.hint)
294 return -1
292 return -1
295 except error.ParseError as inst:
293 except error.ParseError as inst:
296 _formatparse(ferr.write, inst)
294 _formatparse(ferr.write, inst)
297 return -1
295 return -1
298
296
299 msg = _formatargs(req.args)
297 msg = _formatargs(req.args)
300 starttime = util.timer()
298 starttime = util.timer()
301 ret = 1 # default of Python exit code on unhandled exception
299 ret = 1 # default of Python exit code on unhandled exception
302 try:
300 try:
303 ret = _runcatch(req) or 0
301 ret = _runcatch(req) or 0
304 except error.ProgrammingError as inst:
302 except error.ProgrammingError as inst:
305 req.ui.error(_(b'** ProgrammingError: %s\n') % inst)
303 req.ui.error(_(b'** ProgrammingError: %s\n') % inst)
306 if inst.hint:
304 if inst.hint:
307 req.ui.error(_(b'** (%s)\n') % inst.hint)
305 req.ui.error(_(b'** (%s)\n') % inst.hint)
308 raise
306 raise
309 except KeyboardInterrupt as inst:
307 except KeyboardInterrupt as inst:
310 try:
308 try:
311 if isinstance(inst, error.SignalInterrupt):
309 if isinstance(inst, error.SignalInterrupt):
312 msg = _(b"killed!\n")
310 msg = _(b"killed!\n")
313 else:
311 else:
314 msg = _(b"interrupted!\n")
312 msg = _(b"interrupted!\n")
315 req.ui.error(msg)
313 req.ui.error(msg)
316 except error.SignalInterrupt:
314 except error.SignalInterrupt:
317 # maybe pager would quit without consuming all the output, and
315 # maybe pager would quit without consuming all the output, and
318 # SIGPIPE was raised. we cannot print anything in this case.
316 # SIGPIPE was raised. we cannot print anything in this case.
319 pass
317 pass
320 except IOError as inst:
318 except IOError as inst:
321 if inst.errno != errno.EPIPE:
319 if inst.errno != errno.EPIPE:
322 raise
320 raise
323 ret = -1
321 ret = -1
324 finally:
322 finally:
325 duration = util.timer() - starttime
323 duration = util.timer() - starttime
326 req.ui.flush() # record blocked times
324 req.ui.flush() # record blocked times
327 if req.ui.logblockedtimes:
325 if req.ui.logblockedtimes:
328 req.ui._blockedtimes[b'command_duration'] = duration * 1000
326 req.ui._blockedtimes[b'command_duration'] = duration * 1000
329 req.ui.log(
327 req.ui.log(
330 b'uiblocked',
328 b'uiblocked',
331 b'ui blocked ms\n',
329 b'ui blocked ms\n',
332 **pycompat.strkwargs(req.ui._blockedtimes)
330 **pycompat.strkwargs(req.ui._blockedtimes)
333 )
331 )
334 return_code = ret & 255
332 return_code = ret & 255
335 req.ui.log(
333 req.ui.log(
336 b"commandfinish",
334 b"commandfinish",
337 b"%s exited %d after %0.2f seconds\n",
335 b"%s exited %d after %0.2f seconds\n",
338 msg,
336 msg,
339 return_code,
337 return_code,
340 duration,
338 duration,
341 return_code=return_code,
339 return_code=return_code,
342 duration=duration,
340 duration=duration,
343 canonical_command=req.canonical_command,
341 canonical_command=req.canonical_command,
344 )
342 )
345 try:
343 try:
346 req._runexithandlers()
344 req._runexithandlers()
347 except: # exiting, so no re-raises
345 except: # exiting, so no re-raises
348 ret = ret or -1
346 ret = ret or -1
349 # do flush again since ui.log() and exit handlers may write to ui
347 # do flush again since ui.log() and exit handlers may write to ui
350 req.ui.flush()
348 req.ui.flush()
351 return ret
349 return ret
352
350
353
351
354 def _runcatch(req):
352 def _runcatch(req):
355 with tracing.log('dispatch._runcatch'):
353 with tracing.log('dispatch._runcatch'):
356
354
357 def catchterm(*args):
355 def catchterm(*args):
358 raise error.SignalInterrupt
356 raise error.SignalInterrupt
359
357
360 ui = req.ui
358 ui = req.ui
361 try:
359 try:
362 for name in b'SIGBREAK', b'SIGHUP', b'SIGTERM':
360 for name in b'SIGBREAK', b'SIGHUP', b'SIGTERM':
363 num = getattr(signal, name, None)
361 num = getattr(signal, name, None)
364 if num:
362 if num:
365 signal.signal(num, catchterm)
363 signal.signal(num, catchterm)
366 except ValueError:
364 except ValueError:
367 pass # happens if called in a thread
365 pass # happens if called in a thread
368
366
369 def _runcatchfunc():
367 def _runcatchfunc():
370 realcmd = None
368 realcmd = None
371 try:
369 try:
372 cmdargs = fancyopts.fancyopts(
370 cmdargs = fancyopts.fancyopts(
373 req.args[:], commands.globalopts, {}
371 req.args[:], commands.globalopts, {}
374 )
372 )
375 cmd = cmdargs[0]
373 cmd = cmdargs[0]
376 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
374 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
377 realcmd = aliases[0]
375 realcmd = aliases[0]
378 except (
376 except (
379 error.UnknownCommand,
377 error.UnknownCommand,
380 error.AmbiguousCommand,
378 error.AmbiguousCommand,
381 IndexError,
379 IndexError,
382 getopt.GetoptError,
380 getopt.GetoptError,
383 ):
381 ):
384 # Don't handle this here. We know the command is
382 # Don't handle this here. We know the command is
385 # invalid, but all we're worried about for now is that
383 # invalid, but all we're worried about for now is that
386 # it's not a command that server operators expect to
384 # it's not a command that server operators expect to
387 # be safe to offer to users in a sandbox.
385 # be safe to offer to users in a sandbox.
388 pass
386 pass
389 if realcmd == b'serve' and b'--stdio' in cmdargs:
387 if realcmd == b'serve' and b'--stdio' in cmdargs:
390 # We want to constrain 'hg serve --stdio' instances pretty
388 # We want to constrain 'hg serve --stdio' instances pretty
391 # closely, as many shared-ssh access tools want to grant
389 # closely, as many shared-ssh access tools want to grant
392 # access to run *only* 'hg -R $repo serve --stdio'. We
390 # access to run *only* 'hg -R $repo serve --stdio'. We
393 # restrict to exactly that set of arguments, and prohibit
391 # restrict to exactly that set of arguments, and prohibit
394 # any repo name that starts with '--' to prevent
392 # any repo name that starts with '--' to prevent
395 # shenanigans wherein a user does something like pass
393 # shenanigans wherein a user does something like pass
396 # --debugger or --config=ui.debugger=1 as a repo
394 # --debugger or --config=ui.debugger=1 as a repo
397 # name. This used to actually run the debugger.
395 # name. This used to actually run the debugger.
398 if (
396 if (
399 len(req.args) != 4
397 len(req.args) != 4
400 or req.args[0] != b'-R'
398 or req.args[0] != b'-R'
401 or req.args[1].startswith(b'--')
399 or req.args[1].startswith(b'--')
402 or req.args[2] != b'serve'
400 or req.args[2] != b'serve'
403 or req.args[3] != b'--stdio'
401 or req.args[3] != b'--stdio'
404 ):
402 ):
405 raise error.Abort(
403 raise error.Abort(
406 _(b'potentially unsafe serve --stdio invocation: %s')
404 _(b'potentially unsafe serve --stdio invocation: %s')
407 % (stringutil.pprint(req.args),)
405 % (stringutil.pprint(req.args),)
408 )
406 )
409
407
410 try:
408 try:
411 debugger = b'pdb'
409 debugger = b'pdb'
412 debugtrace = {b'pdb': pdb.set_trace}
410 debugtrace = {b'pdb': pdb.set_trace}
413 debugmortem = {b'pdb': pdb.post_mortem}
411 debugmortem = {b'pdb': pdb.post_mortem}
414
412
415 # read --config before doing anything else
413 # read --config before doing anything else
416 # (e.g. to change trust settings for reading .hg/hgrc)
414 # (e.g. to change trust settings for reading .hg/hgrc)
417 cfgs = _parseconfig(req.ui, req.earlyoptions[b'config'])
415 cfgs = _parseconfig(req.ui, req.earlyoptions[b'config'])
418
416
419 if req.repo:
417 if req.repo:
420 # copy configs that were passed on the cmdline (--config) to
418 # copy configs that were passed on the cmdline (--config) to
421 # the repo ui
419 # the repo ui
422 for sec, name, val in cfgs:
420 for sec, name, val in cfgs:
423 req.repo.ui.setconfig(
421 req.repo.ui.setconfig(
424 sec, name, val, source=b'--config'
422 sec, name, val, source=b'--config'
425 )
423 )
426
424
427 # developer config: ui.debugger
425 # developer config: ui.debugger
428 debugger = ui.config(b"ui", b"debugger")
426 debugger = ui.config(b"ui", b"debugger")
429 debugmod = pdb
427 debugmod = pdb
430 if not debugger or ui.plain():
428 if not debugger or ui.plain():
431 # if we are in HGPLAIN mode, then disable custom debugging
429 # if we are in HGPLAIN mode, then disable custom debugging
432 debugger = b'pdb'
430 debugger = b'pdb'
433 elif req.earlyoptions[b'debugger']:
431 elif req.earlyoptions[b'debugger']:
434 # This import can be slow for fancy debuggers, so only
432 # This import can be slow for fancy debuggers, so only
435 # do it when absolutely necessary, i.e. when actual
433 # do it when absolutely necessary, i.e. when actual
436 # debugging has been requested
434 # debugging has been requested
437 with demandimport.deactivated():
435 with demandimport.deactivated():
438 try:
436 try:
439 debugmod = __import__(debugger)
437 debugmod = __import__(debugger)
440 except ImportError:
438 except ImportError:
441 pass # Leave debugmod = pdb
439 pass # Leave debugmod = pdb
442
440
443 debugtrace[debugger] = debugmod.set_trace
441 debugtrace[debugger] = debugmod.set_trace
444 debugmortem[debugger] = debugmod.post_mortem
442 debugmortem[debugger] = debugmod.post_mortem
445
443
446 # enter the debugger before command execution
444 # enter the debugger before command execution
447 if req.earlyoptions[b'debugger']:
445 if req.earlyoptions[b'debugger']:
448 ui.warn(
446 ui.warn(
449 _(
447 _(
450 b"entering debugger - "
448 b"entering debugger - "
451 b"type c to continue starting hg or h for help\n"
449 b"type c to continue starting hg or h for help\n"
452 )
450 )
453 )
451 )
454
452
455 if (
453 if (
456 debugger != b'pdb'
454 debugger != b'pdb'
457 and debugtrace[debugger] == debugtrace[b'pdb']
455 and debugtrace[debugger] == debugtrace[b'pdb']
458 ):
456 ):
459 ui.warn(
457 ui.warn(
460 _(
458 _(
461 b"%s debugger specified "
459 b"%s debugger specified "
462 b"but its module was not found\n"
460 b"but its module was not found\n"
463 )
461 )
464 % debugger
462 % debugger
465 )
463 )
466 with demandimport.deactivated():
464 with demandimport.deactivated():
467 debugtrace[debugger]()
465 debugtrace[debugger]()
468 try:
466 try:
469 return _dispatch(req)
467 return _dispatch(req)
470 finally:
468 finally:
471 ui.flush()
469 ui.flush()
472 except: # re-raises
470 except: # re-raises
473 # enter the debugger when we hit an exception
471 # enter the debugger when we hit an exception
474 if req.earlyoptions[b'debugger']:
472 if req.earlyoptions[b'debugger']:
475 traceback.print_exc()
473 traceback.print_exc()
476 debugmortem[debugger](sys.exc_info()[2])
474 debugmortem[debugger](sys.exc_info()[2])
477 raise
475 raise
478
476
479 return _callcatch(ui, _runcatchfunc)
477 return _callcatch(ui, _runcatchfunc)
480
478
481
479
482 def _callcatch(ui, func):
480 def _callcatch(ui, func):
483 """like scmutil.callcatch but handles more high-level exceptions about
481 """like scmutil.callcatch but handles more high-level exceptions about
484 config parsing and commands. besides, use handlecommandexception to handle
482 config parsing and commands. besides, use handlecommandexception to handle
485 uncaught exceptions.
483 uncaught exceptions.
486 """
484 """
487 try:
485 try:
488 return scmutil.callcatch(ui, func)
486 return scmutil.callcatch(ui, func)
489 except error.AmbiguousCommand as inst:
487 except error.AmbiguousCommand as inst:
490 ui.warn(
488 ui.warn(
491 _(b"hg: command '%s' is ambiguous:\n %s\n")
489 _(b"hg: command '%s' is ambiguous:\n %s\n")
492 % (inst.prefix, b" ".join(inst.matches))
490 % (inst.prefix, b" ".join(inst.matches))
493 )
491 )
494 except error.CommandError as inst:
492 except error.CommandError as inst:
495 if inst.command:
493 if inst.command:
496 ui.pager(b'help')
494 ui.pager(b'help')
497 msgbytes = pycompat.bytestr(inst.message)
495 msgbytes = pycompat.bytestr(inst.message)
498 ui.warn(_(b"hg %s: %s\n") % (inst.command, msgbytes))
496 ui.warn(_(b"hg %s: %s\n") % (inst.command, msgbytes))
499 commands.help_(ui, inst.command, full=False, command=True)
497 commands.help_(ui, inst.command, full=False, command=True)
500 else:
498 else:
501 ui.warn(_(b"hg: %s\n") % inst.message)
499 ui.warn(_(b"hg: %s\n") % inst.message)
502 ui.warn(_(b"(use 'hg help -v' for a list of global options)\n"))
500 ui.warn(_(b"(use 'hg help -v' for a list of global options)\n"))
503 except error.ParseError as inst:
501 except error.ParseError as inst:
504 _formatparse(ui.warn, inst)
502 _formatparse(ui.warn, inst)
505 return -1
503 return -1
506 except error.UnknownCommand as inst:
504 except error.UnknownCommand as inst:
507 nocmdmsg = _(b"hg: unknown command '%s'\n") % inst.command
505 nocmdmsg = _(b"hg: unknown command '%s'\n") % inst.command
508 try:
506 try:
509 # check if the command is in a disabled extension
507 # check if the command is in a disabled extension
510 # (but don't check for extensions themselves)
508 # (but don't check for extensions themselves)
511 formatted = help.formattedhelp(
509 formatted = help.formattedhelp(
512 ui, commands, inst.command, unknowncmd=True
510 ui, commands, inst.command, unknowncmd=True
513 )
511 )
514 ui.warn(nocmdmsg)
512 ui.warn(nocmdmsg)
515 ui.write(formatted)
513 ui.write(formatted)
516 except (error.UnknownCommand, error.Abort):
514 except (error.UnknownCommand, error.Abort):
517 suggested = False
515 suggested = False
518 if inst.all_commands:
516 if inst.all_commands:
519 sim = _getsimilar(inst.all_commands, inst.command)
517 sim = _getsimilar(inst.all_commands, inst.command)
520 if sim:
518 if sim:
521 ui.warn(nocmdmsg)
519 ui.warn(nocmdmsg)
522 _reportsimilar(ui.warn, sim)
520 _reportsimilar(ui.warn, sim)
523 suggested = True
521 suggested = True
524 if not suggested:
522 if not suggested:
525 ui.warn(nocmdmsg)
523 ui.warn(nocmdmsg)
526 ui.warn(_(b"(use 'hg help' for a list of commands)\n"))
524 ui.warn(_(b"(use 'hg help' for a list of commands)\n"))
527 except IOError:
525 except IOError:
528 raise
526 raise
529 except KeyboardInterrupt:
527 except KeyboardInterrupt:
530 raise
528 raise
531 except: # probably re-raises
529 except: # probably re-raises
532 if not handlecommandexception(ui):
530 if not handlecommandexception(ui):
533 raise
531 raise
534
532
535 return -1
533 return -1
536
534
537
535
538 def aliasargs(fn, givenargs):
536 def aliasargs(fn, givenargs):
539 args = []
537 args = []
540 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction
538 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction
541 if not util.safehasattr(fn, b'_origfunc'):
539 if not util.safehasattr(fn, b'_origfunc'):
542 args = getattr(fn, 'args', args)
540 args = getattr(fn, 'args', args)
543 if args:
541 if args:
544 cmd = b' '.join(map(procutil.shellquote, args))
542 cmd = b' '.join(map(procutil.shellquote, args))
545
543
546 nums = []
544 nums = []
547
545
548 def replacer(m):
546 def replacer(m):
549 num = int(m.group(1)) - 1
547 num = int(m.group(1)) - 1
550 nums.append(num)
548 nums.append(num)
551 if num < len(givenargs):
549 if num < len(givenargs):
552 return givenargs[num]
550 return givenargs[num]
553 raise error.Abort(_(b'too few arguments for command alias'))
551 raise error.Abort(_(b'too few arguments for command alias'))
554
552
555 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
553 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
556 givenargs = [x for i, x in enumerate(givenargs) if i not in nums]
554 givenargs = [x for i, x in enumerate(givenargs) if i not in nums]
557 args = pycompat.shlexsplit(cmd)
555 args = pycompat.shlexsplit(cmd)
558 return args + givenargs
556 return args + givenargs
559
557
560
558
561 def aliasinterpolate(name, args, cmd):
559 def aliasinterpolate(name, args, cmd):
562 '''interpolate args into cmd for shell aliases
560 '''interpolate args into cmd for shell aliases
563
561
564 This also handles $0, $@ and "$@".
562 This also handles $0, $@ and "$@".
565 '''
563 '''
566 # util.interpolate can't deal with "$@" (with quotes) because it's only
564 # util.interpolate can't deal with "$@" (with quotes) because it's only
567 # built to match prefix + patterns.
565 # built to match prefix + patterns.
568 replacemap = {b'$%d' % (i + 1): arg for i, arg in enumerate(args)}
566 replacemap = {b'$%d' % (i + 1): arg for i, arg in enumerate(args)}
569 replacemap[b'$0'] = name
567 replacemap[b'$0'] = name
570 replacemap[b'$$'] = b'$'
568 replacemap[b'$$'] = b'$'
571 replacemap[b'$@'] = b' '.join(args)
569 replacemap[b'$@'] = b' '.join(args)
572 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
570 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
573 # parameters, separated out into words. Emulate the same behavior here by
571 # parameters, separated out into words. Emulate the same behavior here by
574 # quoting the arguments individually. POSIX shells will then typically
572 # quoting the arguments individually. POSIX shells will then typically
575 # tokenize each argument into exactly one word.
573 # tokenize each argument into exactly one word.
576 replacemap[b'"$@"'] = b' '.join(procutil.shellquote(arg) for arg in args)
574 replacemap[b'"$@"'] = b' '.join(procutil.shellquote(arg) for arg in args)
577 # escape '\$' for regex
575 # escape '\$' for regex
578 regex = b'|'.join(replacemap.keys()).replace(b'$', br'\$')
576 regex = b'|'.join(replacemap.keys()).replace(b'$', br'\$')
579 r = re.compile(regex)
577 r = re.compile(regex)
580 return r.sub(lambda x: replacemap[x.group()], cmd)
578 return r.sub(lambda x: replacemap[x.group()], cmd)
581
579
582
580
583 class cmdalias(object):
581 class cmdalias(object):
584 def __init__(self, ui, name, definition, cmdtable, source):
582 def __init__(self, ui, name, definition, cmdtable, source):
585 self.name = self.cmd = name
583 self.name = self.cmd = name
586 self.cmdname = b''
584 self.cmdname = b''
587 self.definition = definition
585 self.definition = definition
588 self.fn = None
586 self.fn = None
589 self.givenargs = []
587 self.givenargs = []
590 self.opts = []
588 self.opts = []
591 self.help = b''
589 self.help = b''
592 self.badalias = None
590 self.badalias = None
593 self.unknowncmd = False
591 self.unknowncmd = False
594 self.source = source
592 self.source = source
595
593
596 try:
594 try:
597 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
595 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
598 for alias, e in pycompat.iteritems(cmdtable):
596 for alias, e in pycompat.iteritems(cmdtable):
599 if e is entry:
597 if e is entry:
600 self.cmd = alias
598 self.cmd = alias
601 break
599 break
602 self.shadows = True
600 self.shadows = True
603 except error.UnknownCommand:
601 except error.UnknownCommand:
604 self.shadows = False
602 self.shadows = False
605
603
606 if not self.definition:
604 if not self.definition:
607 self.badalias = _(b"no definition for alias '%s'") % self.name
605 self.badalias = _(b"no definition for alias '%s'") % self.name
608 return
606 return
609
607
610 if self.definition.startswith(b'!'):
608 if self.definition.startswith(b'!'):
611 shdef = self.definition[1:]
609 shdef = self.definition[1:]
612 self.shell = True
610 self.shell = True
613
611
614 def fn(ui, *args):
612 def fn(ui, *args):
615 env = {b'HG_ARGS': b' '.join((self.name,) + args)}
613 env = {b'HG_ARGS': b' '.join((self.name,) + args)}
616
614
617 def _checkvar(m):
615 def _checkvar(m):
618 if m.groups()[0] == b'$':
616 if m.groups()[0] == b'$':
619 return m.group()
617 return m.group()
620 elif int(m.groups()[0]) <= len(args):
618 elif int(m.groups()[0]) <= len(args):
621 return m.group()
619 return m.group()
622 else:
620 else:
623 ui.debug(
621 ui.debug(
624 b"No argument found for substitution "
622 b"No argument found for substitution "
625 b"of %i variable in alias '%s' definition.\n"
623 b"of %i variable in alias '%s' definition.\n"
626 % (int(m.groups()[0]), self.name)
624 % (int(m.groups()[0]), self.name)
627 )
625 )
628 return b''
626 return b''
629
627
630 cmd = re.sub(br'\$(\d+|\$)', _checkvar, shdef)
628 cmd = re.sub(br'\$(\d+|\$)', _checkvar, shdef)
631 cmd = aliasinterpolate(self.name, args, cmd)
629 cmd = aliasinterpolate(self.name, args, cmd)
632 return ui.system(
630 return ui.system(
633 cmd, environ=env, blockedtag=b'alias_%s' % self.name
631 cmd, environ=env, blockedtag=b'alias_%s' % self.name
634 )
632 )
635
633
636 self.fn = fn
634 self.fn = fn
637 self.alias = True
635 self.alias = True
638 self._populatehelp(ui, name, shdef, self.fn)
636 self._populatehelp(ui, name, shdef, self.fn)
639 return
637 return
640
638
641 try:
639 try:
642 args = pycompat.shlexsplit(self.definition)
640 args = pycompat.shlexsplit(self.definition)
643 except ValueError as inst:
641 except ValueError as inst:
644 self.badalias = _(b"error in definition for alias '%s': %s") % (
642 self.badalias = _(b"error in definition for alias '%s': %s") % (
645 self.name,
643 self.name,
646 stringutil.forcebytestr(inst),
644 stringutil.forcebytestr(inst),
647 )
645 )
648 return
646 return
649 earlyopts, args = _earlysplitopts(args)
647 earlyopts, args = _earlysplitopts(args)
650 if earlyopts:
648 if earlyopts:
651 self.badalias = _(
649 self.badalias = _(
652 b"error in definition for alias '%s': %s may "
650 b"error in definition for alias '%s': %s may "
653 b"only be given on the command line"
651 b"only be given on the command line"
654 ) % (self.name, b'/'.join(pycompat.ziplist(*earlyopts)[0]))
652 ) % (self.name, b'/'.join(pycompat.ziplist(*earlyopts)[0]))
655 return
653 return
656 self.cmdname = cmd = args.pop(0)
654 self.cmdname = cmd = args.pop(0)
657 self.givenargs = args
655 self.givenargs = args
658
656
659 try:
657 try:
660 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
658 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
661 if len(tableentry) > 2:
659 if len(tableentry) > 2:
662 self.fn, self.opts, cmdhelp = tableentry
660 self.fn, self.opts, cmdhelp = tableentry
663 else:
661 else:
664 self.fn, self.opts = tableentry
662 self.fn, self.opts = tableentry
665 cmdhelp = None
663 cmdhelp = None
666
664
667 self.alias = True
665 self.alias = True
668 self._populatehelp(ui, name, cmd, self.fn, cmdhelp)
666 self._populatehelp(ui, name, cmd, self.fn, cmdhelp)
669
667
670 except error.UnknownCommand:
668 except error.UnknownCommand:
671 self.badalias = _(
669 self.badalias = _(
672 b"alias '%s' resolves to unknown command '%s'"
670 b"alias '%s' resolves to unknown command '%s'"
673 ) % (self.name, cmd,)
671 ) % (self.name, cmd,)
674 self.unknowncmd = True
672 self.unknowncmd = True
675 except error.AmbiguousCommand:
673 except error.AmbiguousCommand:
676 self.badalias = _(
674 self.badalias = _(
677 b"alias '%s' resolves to ambiguous command '%s'"
675 b"alias '%s' resolves to ambiguous command '%s'"
678 ) % (self.name, cmd,)
676 ) % (self.name, cmd,)
679
677
680 def _populatehelp(self, ui, name, cmd, fn, defaulthelp=None):
678 def _populatehelp(self, ui, name, cmd, fn, defaulthelp=None):
681 # confine strings to be passed to i18n.gettext()
679 # confine strings to be passed to i18n.gettext()
682 cfg = {}
680 cfg = {}
683 for k in (b'doc', b'help', b'category'):
681 for k in (b'doc', b'help', b'category'):
684 v = ui.config(b'alias', b'%s:%s' % (name, k), None)
682 v = ui.config(b'alias', b'%s:%s' % (name, k), None)
685 if v is None:
683 if v is None:
686 continue
684 continue
687 if not encoding.isasciistr(v):
685 if not encoding.isasciistr(v):
688 self.badalias = _(
686 self.badalias = _(
689 b"non-ASCII character in alias definition '%s:%s'"
687 b"non-ASCII character in alias definition '%s:%s'"
690 ) % (name, k)
688 ) % (name, k)
691 return
689 return
692 cfg[k] = v
690 cfg[k] = v
693
691
694 self.help = cfg.get(b'help', defaulthelp or b'')
692 self.help = cfg.get(b'help', defaulthelp or b'')
695 if self.help and self.help.startswith(b"hg " + cmd):
693 if self.help and self.help.startswith(b"hg " + cmd):
696 # drop prefix in old-style help lines so hg shows the alias
694 # drop prefix in old-style help lines so hg shows the alias
697 self.help = self.help[4 + len(cmd) :]
695 self.help = self.help[4 + len(cmd) :]
698
696
699 self.owndoc = b'doc' in cfg
697 self.owndoc = b'doc' in cfg
700 doc = cfg.get(b'doc', pycompat.getdoc(fn))
698 doc = cfg.get(b'doc', pycompat.getdoc(fn))
701 if doc is not None:
699 if doc is not None:
702 doc = pycompat.sysstr(doc)
700 doc = pycompat.sysstr(doc)
703 self.__doc__ = doc
701 self.__doc__ = doc
704
702
705 self.helpcategory = cfg.get(
703 self.helpcategory = cfg.get(
706 b'category', registrar.command.CATEGORY_NONE
704 b'category', registrar.command.CATEGORY_NONE
707 )
705 )
708
706
709 @property
707 @property
710 def args(self):
708 def args(self):
711 args = pycompat.maplist(util.expandpath, self.givenargs)
709 args = pycompat.maplist(util.expandpath, self.givenargs)
712 return aliasargs(self.fn, args)
710 return aliasargs(self.fn, args)
713
711
714 def __getattr__(self, name):
712 def __getattr__(self, name):
715 adefaults = {
713 adefaults = {
716 'norepo': True,
714 'norepo': True,
717 'intents': set(),
715 'intents': set(),
718 'optionalrepo': False,
716 'optionalrepo': False,
719 'inferrepo': False,
717 'inferrepo': False,
720 }
718 }
721 if name not in adefaults:
719 if name not in adefaults:
722 raise AttributeError(name)
720 raise AttributeError(name)
723 if self.badalias or util.safehasattr(self, b'shell'):
721 if self.badalias or util.safehasattr(self, b'shell'):
724 return adefaults[name]
722 return adefaults[name]
725 return getattr(self.fn, name)
723 return getattr(self.fn, name)
726
724
727 def __call__(self, ui, *args, **opts):
725 def __call__(self, ui, *args, **opts):
728 if self.badalias:
726 if self.badalias:
729 hint = None
727 hint = None
730 if self.unknowncmd:
728 if self.unknowncmd:
731 try:
729 try:
732 # check if the command is in a disabled extension
730 # check if the command is in a disabled extension
733 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
731 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
734 hint = _(b"'%s' is provided by '%s' extension") % (cmd, ext)
732 hint = _(b"'%s' is provided by '%s' extension") % (cmd, ext)
735 except error.UnknownCommand:
733 except error.UnknownCommand:
736 pass
734 pass
737 raise error.Abort(self.badalias, hint=hint)
735 raise error.Abort(self.badalias, hint=hint)
738 if self.shadows:
736 if self.shadows:
739 ui.debug(
737 ui.debug(
740 b"alias '%s' shadows command '%s'\n" % (self.name, self.cmdname)
738 b"alias '%s' shadows command '%s'\n" % (self.name, self.cmdname)
741 )
739 )
742
740
743 ui.log(
741 ui.log(
744 b'commandalias',
742 b'commandalias',
745 b"alias '%s' expands to '%s'\n",
743 b"alias '%s' expands to '%s'\n",
746 self.name,
744 self.name,
747 self.definition,
745 self.definition,
748 )
746 )
749 if util.safehasattr(self, b'shell'):
747 if util.safehasattr(self, b'shell'):
750 return self.fn(ui, *args, **opts)
748 return self.fn(ui, *args, **opts)
751 else:
749 else:
752 try:
750 try:
753 return util.checksignature(self.fn)(ui, *args, **opts)
751 return util.checksignature(self.fn)(ui, *args, **opts)
754 except error.SignatureError:
752 except error.SignatureError:
755 args = b' '.join([self.cmdname] + self.args)
753 args = b' '.join([self.cmdname] + self.args)
756 ui.debug(b"alias '%s' expands to '%s'\n" % (self.name, args))
754 ui.debug(b"alias '%s' expands to '%s'\n" % (self.name, args))
757 raise
755 raise
758
756
759
757
760 class lazyaliasentry(object):
758 class lazyaliasentry(object):
761 """like a typical command entry (func, opts, help), but is lazy"""
759 """like a typical command entry (func, opts, help), but is lazy"""
762
760
763 def __init__(self, ui, name, definition, cmdtable, source):
761 def __init__(self, ui, name, definition, cmdtable, source):
764 self.ui = ui
762 self.ui = ui
765 self.name = name
763 self.name = name
766 self.definition = definition
764 self.definition = definition
767 self.cmdtable = cmdtable.copy()
765 self.cmdtable = cmdtable.copy()
768 self.source = source
766 self.source = source
769 self.alias = True
767 self.alias = True
770
768
771 @util.propertycache
769 @util.propertycache
772 def _aliasdef(self):
770 def _aliasdef(self):
773 return cmdalias(
771 return cmdalias(
774 self.ui, self.name, self.definition, self.cmdtable, self.source
772 self.ui, self.name, self.definition, self.cmdtable, self.source
775 )
773 )
776
774
777 def __getitem__(self, n):
775 def __getitem__(self, n):
778 aliasdef = self._aliasdef
776 aliasdef = self._aliasdef
779 if n == 0:
777 if n == 0:
780 return aliasdef
778 return aliasdef
781 elif n == 1:
779 elif n == 1:
782 return aliasdef.opts
780 return aliasdef.opts
783 elif n == 2:
781 elif n == 2:
784 return aliasdef.help
782 return aliasdef.help
785 else:
783 else:
786 raise IndexError
784 raise IndexError
787
785
788 def __iter__(self):
786 def __iter__(self):
789 for i in range(3):
787 for i in range(3):
790 yield self[i]
788 yield self[i]
791
789
792 def __len__(self):
790 def __len__(self):
793 return 3
791 return 3
794
792
795
793
796 def addaliases(ui, cmdtable):
794 def addaliases(ui, cmdtable):
797 # aliases are processed after extensions have been loaded, so they
795 # aliases are processed after extensions have been loaded, so they
798 # may use extension commands. Aliases can also use other alias definitions,
796 # may use extension commands. Aliases can also use other alias definitions,
799 # but only if they have been defined prior to the current definition.
797 # but only if they have been defined prior to the current definition.
800 for alias, definition in ui.configitems(b'alias', ignoresub=True):
798 for alias, definition in ui.configitems(b'alias', ignoresub=True):
801 try:
799 try:
802 if cmdtable[alias].definition == definition:
800 if cmdtable[alias].definition == definition:
803 continue
801 continue
804 except (KeyError, AttributeError):
802 except (KeyError, AttributeError):
805 # definition might not exist or it might not be a cmdalias
803 # definition might not exist or it might not be a cmdalias
806 pass
804 pass
807
805
808 source = ui.configsource(b'alias', alias)
806 source = ui.configsource(b'alias', alias)
809 entry = lazyaliasentry(ui, alias, definition, cmdtable, source)
807 entry = lazyaliasentry(ui, alias, definition, cmdtable, source)
810 cmdtable[alias] = entry
808 cmdtable[alias] = entry
811
809
812
810
813 def _parse(ui, args):
811 def _parse(ui, args):
814 options = {}
812 options = {}
815 cmdoptions = {}
813 cmdoptions = {}
816
814
817 try:
815 try:
818 args = fancyopts.fancyopts(args, commands.globalopts, options)
816 args = fancyopts.fancyopts(args, commands.globalopts, options)
819 except getopt.GetoptError as inst:
817 except getopt.GetoptError as inst:
820 raise error.CommandError(None, stringutil.forcebytestr(inst))
818 raise error.CommandError(None, stringutil.forcebytestr(inst))
821
819
822 if args:
820 if args:
823 cmd, args = args[0], args[1:]
821 cmd, args = args[0], args[1:]
824 aliases, entry = cmdutil.findcmd(
822 aliases, entry = cmdutil.findcmd(
825 cmd, commands.table, ui.configbool(b"ui", b"strict")
823 cmd, commands.table, ui.configbool(b"ui", b"strict")
826 )
824 )
827 cmd = aliases[0]
825 cmd = aliases[0]
828 args = aliasargs(entry[0], args)
826 args = aliasargs(entry[0], args)
829 defaults = ui.config(b"defaults", cmd)
827 defaults = ui.config(b"defaults", cmd)
830 if defaults:
828 if defaults:
831 args = (
829 args = (
832 pycompat.maplist(util.expandpath, pycompat.shlexsplit(defaults))
830 pycompat.maplist(util.expandpath, pycompat.shlexsplit(defaults))
833 + args
831 + args
834 )
832 )
835 c = list(entry[1])
833 c = list(entry[1])
836 else:
834 else:
837 cmd = None
835 cmd = None
838 c = []
836 c = []
839
837
840 # combine global options into local
838 # combine global options into local
841 for o in commands.globalopts:
839 for o in commands.globalopts:
842 c.append((o[0], o[1], options[o[1]], o[3]))
840 c.append((o[0], o[1], options[o[1]], o[3]))
843
841
844 try:
842 try:
845 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
843 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
846 except getopt.GetoptError as inst:
844 except getopt.GetoptError as inst:
847 raise error.CommandError(cmd, stringutil.forcebytestr(inst))
845 raise error.CommandError(cmd, stringutil.forcebytestr(inst))
848
846
849 # separate global options back out
847 # separate global options back out
850 for o in commands.globalopts:
848 for o in commands.globalopts:
851 n = o[1]
849 n = o[1]
852 options[n] = cmdoptions[n]
850 options[n] = cmdoptions[n]
853 del cmdoptions[n]
851 del cmdoptions[n]
854
852
855 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
853 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
856
854
857
855
858 def _parseconfig(ui, config):
856 def _parseconfig(ui, config):
859 """parse the --config options from the command line"""
857 """parse the --config options from the command line"""
860 configs = []
858 configs = []
861
859
862 for cfg in config:
860 for cfg in config:
863 try:
861 try:
864 name, value = [cfgelem.strip() for cfgelem in cfg.split(b'=', 1)]
862 name, value = [cfgelem.strip() for cfgelem in cfg.split(b'=', 1)]
865 section, name = name.split(b'.', 1)
863 section, name = name.split(b'.', 1)
866 if not section or not name:
864 if not section or not name:
867 raise IndexError
865 raise IndexError
868 ui.setconfig(section, name, value, b'--config')
866 ui.setconfig(section, name, value, b'--config')
869 configs.append((section, name, value))
867 configs.append((section, name, value))
870 except (IndexError, ValueError):
868 except (IndexError, ValueError):
871 raise error.Abort(
869 raise error.Abort(
872 _(
870 _(
873 b'malformed --config option: %r '
871 b'malformed --config option: %r '
874 b'(use --config section.name=value)'
872 b'(use --config section.name=value)'
875 )
873 )
876 % pycompat.bytestr(cfg)
874 % pycompat.bytestr(cfg)
877 )
875 )
878
876
879 return configs
877 return configs
880
878
881
879
882 def _earlyparseopts(ui, args):
880 def _earlyparseopts(ui, args):
883 options = {}
881 options = {}
884 fancyopts.fancyopts(
882 fancyopts.fancyopts(
885 args,
883 args,
886 commands.globalopts,
884 commands.globalopts,
887 options,
885 options,
888 gnu=not ui.plain(b'strictflags'),
886 gnu=not ui.plain(b'strictflags'),
889 early=True,
887 early=True,
890 optaliases={b'repository': [b'repo']},
888 optaliases={b'repository': [b'repo']},
891 )
889 )
892 return options
890 return options
893
891
894
892
895 def _earlysplitopts(args):
893 def _earlysplitopts(args):
896 """Split args into a list of possible early options and remainder args"""
894 """Split args into a list of possible early options and remainder args"""
897 shortoptions = b'R:'
895 shortoptions = b'R:'
898 # TODO: perhaps 'debugger' should be included
896 # TODO: perhaps 'debugger' should be included
899 longoptions = [b'cwd=', b'repository=', b'repo=', b'config=']
897 longoptions = [b'cwd=', b'repository=', b'repo=', b'config=']
900 return fancyopts.earlygetopt(
898 return fancyopts.earlygetopt(
901 args, shortoptions, longoptions, gnu=True, keepsep=True
899 args, shortoptions, longoptions, gnu=True, keepsep=True
902 )
900 )
903
901
904
902
905 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
903 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
906 # run pre-hook, and abort if it fails
904 # run pre-hook, and abort if it fails
907 hook.hook(
905 hook.hook(
908 lui,
906 lui,
909 repo,
907 repo,
910 b"pre-%s" % cmd,
908 b"pre-%s" % cmd,
911 True,
909 True,
912 args=b" ".join(fullargs),
910 args=b" ".join(fullargs),
913 pats=cmdpats,
911 pats=cmdpats,
914 opts=cmdoptions,
912 opts=cmdoptions,
915 )
913 )
916 try:
914 try:
917 ret = _runcommand(ui, options, cmd, d)
915 ret = _runcommand(ui, options, cmd, d)
918 # run post-hook, passing command result
916 # run post-hook, passing command result
919 hook.hook(
917 hook.hook(
920 lui,
918 lui,
921 repo,
919 repo,
922 b"post-%s" % cmd,
920 b"post-%s" % cmd,
923 False,
921 False,
924 args=b" ".join(fullargs),
922 args=b" ".join(fullargs),
925 result=ret,
923 result=ret,
926 pats=cmdpats,
924 pats=cmdpats,
927 opts=cmdoptions,
925 opts=cmdoptions,
928 )
926 )
929 except Exception:
927 except Exception:
930 # run failure hook and re-raise
928 # run failure hook and re-raise
931 hook.hook(
929 hook.hook(
932 lui,
930 lui,
933 repo,
931 repo,
934 b"fail-%s" % cmd,
932 b"fail-%s" % cmd,
935 False,
933 False,
936 args=b" ".join(fullargs),
934 args=b" ".join(fullargs),
937 pats=cmdpats,
935 pats=cmdpats,
938 opts=cmdoptions,
936 opts=cmdoptions,
939 )
937 )
940 raise
938 raise
941 return ret
939 return ret
942
940
943
941
944 def _getlocal(ui, rpath, wd=None):
942 def _getlocal(ui, rpath, wd=None):
945 """Return (path, local ui object) for the given target path.
943 """Return (path, local ui object) for the given target path.
946
944
947 Takes paths in [cwd]/.hg/hgrc into account."
945 Takes paths in [cwd]/.hg/hgrc into account."
948 """
946 """
949 if wd is None:
947 if wd is None:
950 try:
948 try:
951 wd = encoding.getcwd()
949 wd = encoding.getcwd()
952 except OSError as e:
950 except OSError as e:
953 raise error.Abort(
951 raise error.Abort(
954 _(b"error getting current working directory: %s")
952 _(b"error getting current working directory: %s")
955 % encoding.strtolocal(e.strerror)
953 % encoding.strtolocal(e.strerror)
956 )
954 )
957
955
958 path = cmdutil.findrepo(wd) or b""
956 path = cmdutil.findrepo(wd) or b""
959 if not path:
957 if not path:
960 lui = ui
958 lui = ui
961 else:
959 else:
962 lui = ui.copy()
960 lui = ui.copy()
963 if rcutil.use_repo_hgrc():
961 if rcutil.use_repo_hgrc():
964 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
962 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
965
963
966 if rpath:
964 if rpath:
967 path = lui.expandpath(rpath)
965 path = lui.expandpath(rpath)
968 lui = ui.copy()
966 lui = ui.copy()
969 if rcutil.use_repo_hgrc():
967 if rcutil.use_repo_hgrc():
970 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
968 lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path)
971
969
972 return path, lui
970 return path, lui
973
971
974
972
975 def _checkshellalias(lui, ui, args):
973 def _checkshellalias(lui, ui, args):
976 """Return the function to run the shell alias, if it is required"""
974 """Return the function to run the shell alias, if it is required"""
977 options = {}
975 options = {}
978
976
979 try:
977 try:
980 args = fancyopts.fancyopts(args, commands.globalopts, options)
978 args = fancyopts.fancyopts(args, commands.globalopts, options)
981 except getopt.GetoptError:
979 except getopt.GetoptError:
982 return
980 return
983
981
984 if not args:
982 if not args:
985 return
983 return
986
984
987 cmdtable = commands.table
985 cmdtable = commands.table
988
986
989 cmd = args[0]
987 cmd = args[0]
990 try:
988 try:
991 strict = ui.configbool(b"ui", b"strict")
989 strict = ui.configbool(b"ui", b"strict")
992 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
990 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
993 except (error.AmbiguousCommand, error.UnknownCommand):
991 except (error.AmbiguousCommand, error.UnknownCommand):
994 return
992 return
995
993
996 cmd = aliases[0]
994 cmd = aliases[0]
997 fn = entry[0]
995 fn = entry[0]
998
996
999 if cmd and util.safehasattr(fn, b'shell'):
997 if cmd and util.safehasattr(fn, b'shell'):
1000 # shell alias shouldn't receive early options which are consumed by hg
998 # shell alias shouldn't receive early options which are consumed by hg
1001 _earlyopts, args = _earlysplitopts(args)
999 _earlyopts, args = _earlysplitopts(args)
1002 d = lambda: fn(ui, *args[1:])
1000 d = lambda: fn(ui, *args[1:])
1003 return lambda: runcommand(
1001 return lambda: runcommand(
1004 lui, None, cmd, args[:1], ui, options, d, [], {}
1002 lui, None, cmd, args[:1], ui, options, d, [], {}
1005 )
1003 )
1006
1004
1007
1005
1008 def _dispatch(req):
1006 def _dispatch(req):
1009 args = req.args
1007 args = req.args
1010 ui = req.ui
1008 ui = req.ui
1011
1009
1012 # check for cwd
1010 # check for cwd
1013 cwd = req.earlyoptions[b'cwd']
1011 cwd = req.earlyoptions[b'cwd']
1014 if cwd:
1012 if cwd:
1015 os.chdir(cwd)
1013 os.chdir(cwd)
1016
1014
1017 rpath = req.earlyoptions[b'repository']
1015 rpath = req.earlyoptions[b'repository']
1018 path, lui = _getlocal(ui, rpath)
1016 path, lui = _getlocal(ui, rpath)
1019
1017
1020 uis = {ui, lui}
1018 uis = {ui, lui}
1021
1019
1022 if req.repo:
1020 if req.repo:
1023 uis.add(req.repo.ui)
1021 uis.add(req.repo.ui)
1024
1022
1025 if (
1023 if (
1026 req.earlyoptions[b'verbose']
1024 req.earlyoptions[b'verbose']
1027 or req.earlyoptions[b'debug']
1025 or req.earlyoptions[b'debug']
1028 or req.earlyoptions[b'quiet']
1026 or req.earlyoptions[b'quiet']
1029 ):
1027 ):
1030 for opt in (b'verbose', b'debug', b'quiet'):
1028 for opt in (b'verbose', b'debug', b'quiet'):
1031 val = pycompat.bytestr(bool(req.earlyoptions[opt]))
1029 val = pycompat.bytestr(bool(req.earlyoptions[opt]))
1032 for ui_ in uis:
1030 for ui_ in uis:
1033 ui_.setconfig(b'ui', opt, val, b'--' + opt)
1031 ui_.setconfig(b'ui', opt, val, b'--' + opt)
1034
1032
1035 if req.earlyoptions[b'profile']:
1033 if req.earlyoptions[b'profile']:
1036 for ui_ in uis:
1034 for ui_ in uis:
1037 ui_.setconfig(b'profiling', b'enabled', b'true', b'--profile')
1035 ui_.setconfig(b'profiling', b'enabled', b'true', b'--profile')
1038
1036
1039 profile = lui.configbool(b'profiling', b'enabled')
1037 profile = lui.configbool(b'profiling', b'enabled')
1040 with profiling.profile(lui, enabled=profile) as profiler:
1038 with profiling.profile(lui, enabled=profile) as profiler:
1041 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
1039 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
1042 # reposetup
1040 # reposetup
1043 extensions.loadall(lui)
1041 extensions.loadall(lui)
1044 # Propagate any changes to lui.__class__ by extensions
1042 # Propagate any changes to lui.__class__ by extensions
1045 ui.__class__ = lui.__class__
1043 ui.__class__ = lui.__class__
1046
1044
1047 # (uisetup and extsetup are handled in extensions.loadall)
1045 # (uisetup and extsetup are handled in extensions.loadall)
1048
1046
1049 # (reposetup is handled in hg.repository)
1047 # (reposetup is handled in hg.repository)
1050
1048
1051 addaliases(lui, commands.table)
1049 addaliases(lui, commands.table)
1052
1050
1053 # All aliases and commands are completely defined, now.
1051 # All aliases and commands are completely defined, now.
1054 # Check abbreviation/ambiguity of shell alias.
1052 # Check abbreviation/ambiguity of shell alias.
1055 shellaliasfn = _checkshellalias(lui, ui, args)
1053 shellaliasfn = _checkshellalias(lui, ui, args)
1056 if shellaliasfn:
1054 if shellaliasfn:
1057 # no additional configs will be set, set up the ui instances
1055 # no additional configs will be set, set up the ui instances
1058 for ui_ in uis:
1056 for ui_ in uis:
1059 extensions.populateui(ui_)
1057 extensions.populateui(ui_)
1060 return shellaliasfn()
1058 return shellaliasfn()
1061
1059
1062 # check for fallback encoding
1060 # check for fallback encoding
1063 fallback = lui.config(b'ui', b'fallbackencoding')
1061 fallback = lui.config(b'ui', b'fallbackencoding')
1064 if fallback:
1062 if fallback:
1065 encoding.fallbackencoding = fallback
1063 encoding.fallbackencoding = fallback
1066
1064
1067 fullargs = args
1065 fullargs = args
1068 cmd, func, args, options, cmdoptions = _parse(lui, args)
1066 cmd, func, args, options, cmdoptions = _parse(lui, args)
1069
1067
1070 # store the canonical command name in request object for later access
1068 # store the canonical command name in request object for later access
1071 req.canonical_command = cmd
1069 req.canonical_command = cmd
1072
1070
1073 if options[b"config"] != req.earlyoptions[b"config"]:
1071 if options[b"config"] != req.earlyoptions[b"config"]:
1074 raise error.Abort(_(b"option --config may not be abbreviated!"))
1072 raise error.Abort(_(b"option --config may not be abbreviated!"))
1075 if options[b"cwd"] != req.earlyoptions[b"cwd"]:
1073 if options[b"cwd"] != req.earlyoptions[b"cwd"]:
1076 raise error.Abort(_(b"option --cwd may not be abbreviated!"))
1074 raise error.Abort(_(b"option --cwd may not be abbreviated!"))
1077 if options[b"repository"] != req.earlyoptions[b"repository"]:
1075 if options[b"repository"] != req.earlyoptions[b"repository"]:
1078 raise error.Abort(
1076 raise error.Abort(
1079 _(
1077 _(
1080 b"option -R has to be separated from other options (e.g. not "
1078 b"option -R has to be separated from other options (e.g. not "
1081 b"-qR) and --repository may only be abbreviated as --repo!"
1079 b"-qR) and --repository may only be abbreviated as --repo!"
1082 )
1080 )
1083 )
1081 )
1084 if options[b"debugger"] != req.earlyoptions[b"debugger"]:
1082 if options[b"debugger"] != req.earlyoptions[b"debugger"]:
1085 raise error.Abort(_(b"option --debugger may not be abbreviated!"))
1083 raise error.Abort(_(b"option --debugger may not be abbreviated!"))
1086 # don't validate --profile/--traceback, which can be enabled from now
1084 # don't validate --profile/--traceback, which can be enabled from now
1087
1085
1088 if options[b"encoding"]:
1086 if options[b"encoding"]:
1089 encoding.encoding = options[b"encoding"]
1087 encoding.encoding = options[b"encoding"]
1090 if options[b"encodingmode"]:
1088 if options[b"encodingmode"]:
1091 encoding.encodingmode = options[b"encodingmode"]
1089 encoding.encodingmode = options[b"encodingmode"]
1092 if options[b"time"]:
1090 if options[b"time"]:
1093
1091
1094 def get_times():
1092 def get_times():
1095 t = os.times()
1093 t = os.times()
1096 if t[4] == 0.0:
1094 if t[4] == 0.0:
1097 # Windows leaves this as zero, so use time.perf_counter()
1095 # Windows leaves this as zero, so use time.perf_counter()
1098 t = (t[0], t[1], t[2], t[3], util.timer())
1096 t = (t[0], t[1], t[2], t[3], util.timer())
1099 return t
1097 return t
1100
1098
1101 s = get_times()
1099 s = get_times()
1102
1100
1103 def print_time():
1101 def print_time():
1104 t = get_times()
1102 t = get_times()
1105 ui.warn(
1103 ui.warn(
1106 _(b"time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n")
1104 _(b"time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n")
1107 % (
1105 % (
1108 t[4] - s[4],
1106 t[4] - s[4],
1109 t[0] - s[0],
1107 t[0] - s[0],
1110 t[2] - s[2],
1108 t[2] - s[2],
1111 t[1] - s[1],
1109 t[1] - s[1],
1112 t[3] - s[3],
1110 t[3] - s[3],
1113 )
1111 )
1114 )
1112 )
1115
1113
1116 ui.atexit(print_time)
1114 ui.atexit(print_time)
1117 if options[b"profile"]:
1115 if options[b"profile"]:
1118 profiler.start()
1116 profiler.start()
1119
1117
1120 # if abbreviated version of this were used, take them in account, now
1118 # if abbreviated version of this were used, take them in account, now
1121 if options[b'verbose'] or options[b'debug'] or options[b'quiet']:
1119 if options[b'verbose'] or options[b'debug'] or options[b'quiet']:
1122 for opt in (b'verbose', b'debug', b'quiet'):
1120 for opt in (b'verbose', b'debug', b'quiet'):
1123 if options[opt] == req.earlyoptions[opt]:
1121 if options[opt] == req.earlyoptions[opt]:
1124 continue
1122 continue
1125 val = pycompat.bytestr(bool(options[opt]))
1123 val = pycompat.bytestr(bool(options[opt]))
1126 for ui_ in uis:
1124 for ui_ in uis:
1127 ui_.setconfig(b'ui', opt, val, b'--' + opt)
1125 ui_.setconfig(b'ui', opt, val, b'--' + opt)
1128
1126
1129 if options[b'traceback']:
1127 if options[b'traceback']:
1130 for ui_ in uis:
1128 for ui_ in uis:
1131 ui_.setconfig(b'ui', b'traceback', b'on', b'--traceback')
1129 ui_.setconfig(b'ui', b'traceback', b'on', b'--traceback')
1132
1130
1133 if options[b'noninteractive']:
1131 if options[b'noninteractive']:
1134 for ui_ in uis:
1132 for ui_ in uis:
1135 ui_.setconfig(b'ui', b'interactive', b'off', b'-y')
1133 ui_.setconfig(b'ui', b'interactive', b'off', b'-y')
1136
1134
1137 if cmdoptions.get(b'insecure', False):
1135 if cmdoptions.get(b'insecure', False):
1138 for ui_ in uis:
1136 for ui_ in uis:
1139 ui_.insecureconnections = True
1137 ui_.insecureconnections = True
1140
1138
1141 # setup color handling before pager, because setting up pager
1139 # setup color handling before pager, because setting up pager
1142 # might cause incorrect console information
1140 # might cause incorrect console information
1143 coloropt = options[b'color']
1141 coloropt = options[b'color']
1144 for ui_ in uis:
1142 for ui_ in uis:
1145 if coloropt:
1143 if coloropt:
1146 ui_.setconfig(b'ui', b'color', coloropt, b'--color')
1144 ui_.setconfig(b'ui', b'color', coloropt, b'--color')
1147 color.setup(ui_)
1145 color.setup(ui_)
1148
1146
1149 if stringutil.parsebool(options[b'pager']):
1147 if stringutil.parsebool(options[b'pager']):
1150 # ui.pager() expects 'internal-always-' prefix in this case
1148 # ui.pager() expects 'internal-always-' prefix in this case
1151 ui.pager(b'internal-always-' + cmd)
1149 ui.pager(b'internal-always-' + cmd)
1152 elif options[b'pager'] != b'auto':
1150 elif options[b'pager'] != b'auto':
1153 for ui_ in uis:
1151 for ui_ in uis:
1154 ui_.disablepager()
1152 ui_.disablepager()
1155
1153
1156 # configs are fully loaded, set up the ui instances
1154 # configs are fully loaded, set up the ui instances
1157 for ui_ in uis:
1155 for ui_ in uis:
1158 extensions.populateui(ui_)
1156 extensions.populateui(ui_)
1159
1157
1160 if options[b'version']:
1158 if options[b'version']:
1161 return commands.version_(ui)
1159 return commands.version_(ui)
1162 if options[b'help']:
1160 if options[b'help']:
1163 return commands.help_(ui, cmd, command=cmd is not None)
1161 return commands.help_(ui, cmd, command=cmd is not None)
1164 elif not cmd:
1162 elif not cmd:
1165 return commands.help_(ui, b'shortlist')
1163 return commands.help_(ui, b'shortlist')
1166
1164
1167 repo = None
1165 repo = None
1168 cmdpats = args[:]
1166 cmdpats = args[:]
1169 assert func is not None # help out pytype
1167 assert func is not None # help out pytype
1170 if not func.norepo:
1168 if not func.norepo:
1171 # use the repo from the request only if we don't have -R
1169 # use the repo from the request only if we don't have -R
1172 if not rpath and not cwd:
1170 if not rpath and not cwd:
1173 repo = req.repo
1171 repo = req.repo
1174
1172
1175 if repo:
1173 if repo:
1176 # set the descriptors of the repo ui to those of ui
1174 # set the descriptors of the repo ui to those of ui
1177 repo.ui.fin = ui.fin
1175 repo.ui.fin = ui.fin
1178 repo.ui.fout = ui.fout
1176 repo.ui.fout = ui.fout
1179 repo.ui.ferr = ui.ferr
1177 repo.ui.ferr = ui.ferr
1180 repo.ui.fmsg = ui.fmsg
1178 repo.ui.fmsg = ui.fmsg
1181 else:
1179 else:
1182 try:
1180 try:
1183 repo = hg.repository(
1181 repo = hg.repository(
1184 ui,
1182 ui,
1185 path=path,
1183 path=path,
1186 presetupfuncs=req.prereposetups,
1184 presetupfuncs=req.prereposetups,
1187 intents=func.intents,
1185 intents=func.intents,
1188 )
1186 )
1189 if not repo.local():
1187 if not repo.local():
1190 raise error.Abort(
1188 raise error.Abort(
1191 _(b"repository '%s' is not local") % path
1189 _(b"repository '%s' is not local") % path
1192 )
1190 )
1193 repo.ui.setconfig(
1191 repo.ui.setconfig(
1194 b"bundle", b"mainreporoot", repo.root, b'repo'
1192 b"bundle", b"mainreporoot", repo.root, b'repo'
1195 )
1193 )
1196 except error.RequirementError:
1194 except error.RequirementError:
1197 raise
1195 raise
1198 except error.RepoError:
1196 except error.RepoError:
1199 if rpath: # invalid -R path
1197 if rpath: # invalid -R path
1200 raise
1198 raise
1201 if not func.optionalrepo:
1199 if not func.optionalrepo:
1202 if func.inferrepo and args and not path:
1200 if func.inferrepo and args and not path:
1203 # try to infer -R from command args
1201 # try to infer -R from command args
1204 repos = pycompat.maplist(cmdutil.findrepo, args)
1202 repos = pycompat.maplist(cmdutil.findrepo, args)
1205 guess = repos[0]
1203 guess = repos[0]
1206 if guess and repos.count(guess) == len(repos):
1204 if guess and repos.count(guess) == len(repos):
1207 req.args = [b'--repository', guess] + fullargs
1205 req.args = [b'--repository', guess] + fullargs
1208 req.earlyoptions[b'repository'] = guess
1206 req.earlyoptions[b'repository'] = guess
1209 return _dispatch(req)
1207 return _dispatch(req)
1210 if not path:
1208 if not path:
1211 raise error.RepoError(
1209 raise error.RepoError(
1212 _(
1210 _(
1213 b"no repository found in"
1211 b"no repository found in"
1214 b" '%s' (.hg not found)"
1212 b" '%s' (.hg not found)"
1215 )
1213 )
1216 % encoding.getcwd()
1214 % encoding.getcwd()
1217 )
1215 )
1218 raise
1216 raise
1219 if repo:
1217 if repo:
1220 ui = repo.ui
1218 ui = repo.ui
1221 if options[b'hidden']:
1219 if options[b'hidden']:
1222 repo = repo.unfiltered()
1220 repo = repo.unfiltered()
1223 args.insert(0, repo)
1221 args.insert(0, repo)
1224 elif rpath:
1222 elif rpath:
1225 ui.warn(_(b"warning: --repository ignored\n"))
1223 ui.warn(_(b"warning: --repository ignored\n"))
1226
1224
1227 msg = _formatargs(fullargs)
1225 msg = _formatargs(fullargs)
1228 ui.log(b"command", b'%s\n', msg)
1226 ui.log(b"command", b'%s\n', msg)
1229 strcmdopt = pycompat.strkwargs(cmdoptions)
1227 strcmdopt = pycompat.strkwargs(cmdoptions)
1230 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
1228 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
1231 try:
1229 try:
1232 return runcommand(
1230 return runcommand(
1233 lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions
1231 lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions
1234 )
1232 )
1235 finally:
1233 finally:
1236 if repo and repo != req.repo:
1234 if repo and repo != req.repo:
1237 repo.close()
1235 repo.close()
1238
1236
1239
1237
1240 def _runcommand(ui, options, cmd, cmdfunc):
1238 def _runcommand(ui, options, cmd, cmdfunc):
1241 """Run a command function, possibly with profiling enabled."""
1239 """Run a command function, possibly with profiling enabled."""
1242 try:
1240 try:
1243 with tracing.log("Running %s command" % cmd):
1241 with tracing.log("Running %s command" % cmd):
1244 return cmdfunc()
1242 return cmdfunc()
1245 except error.SignatureError:
1243 except error.SignatureError:
1246 raise error.CommandError(cmd, _(b'invalid arguments'))
1244 raise error.CommandError(cmd, _(b'invalid arguments'))
1247
1245
1248
1246
1249 def _exceptionwarning(ui):
1247 def _exceptionwarning(ui):
1250 """Produce a warning message for the current active exception"""
1248 """Produce a warning message for the current active exception"""
1251
1249
1252 # For compatibility checking, we discard the portion of the hg
1250 # For compatibility checking, we discard the portion of the hg
1253 # version after the + on the assumption that if a "normal
1251 # version after the + on the assumption that if a "normal
1254 # user" is running a build with a + in it the packager
1252 # user" is running a build with a + in it the packager
1255 # probably built from fairly close to a tag and anyone with a
1253 # probably built from fairly close to a tag and anyone with a
1256 # 'make local' copy of hg (where the version number can be out
1254 # 'make local' copy of hg (where the version number can be out
1257 # of date) will be clueful enough to notice the implausible
1255 # of date) will be clueful enough to notice the implausible
1258 # version number and try updating.
1256 # version number and try updating.
1259 ct = util.versiontuple(n=2)
1257 ct = util.versiontuple(n=2)
1260 worst = None, ct, b''
1258 worst = None, ct, b''
1261 if ui.config(b'ui', b'supportcontact') is None:
1259 if ui.config(b'ui', b'supportcontact') is None:
1262 for name, mod in extensions.extensions():
1260 for name, mod in extensions.extensions():
1263 # 'testedwith' should be bytes, but not all extensions are ported
1261 # 'testedwith' should be bytes, but not all extensions are ported
1264 # to py3 and we don't want UnicodeException because of that.
1262 # to py3 and we don't want UnicodeException because of that.
1265 testedwith = stringutil.forcebytestr(
1263 testedwith = stringutil.forcebytestr(
1266 getattr(mod, 'testedwith', b'')
1264 getattr(mod, 'testedwith', b'')
1267 )
1265 )
1268 report = getattr(mod, 'buglink', _(b'the extension author.'))
1266 report = getattr(mod, 'buglink', _(b'the extension author.'))
1269 if not testedwith.strip():
1267 if not testedwith.strip():
1270 # We found an untested extension. It's likely the culprit.
1268 # We found an untested extension. It's likely the culprit.
1271 worst = name, b'unknown', report
1269 worst = name, b'unknown', report
1272 break
1270 break
1273
1271
1274 # Never blame on extensions bundled with Mercurial.
1272 # Never blame on extensions bundled with Mercurial.
1275 if extensions.ismoduleinternal(mod):
1273 if extensions.ismoduleinternal(mod):
1276 continue
1274 continue
1277
1275
1278 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
1276 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
1279 if ct in tested:
1277 if ct in tested:
1280 continue
1278 continue
1281
1279
1282 lower = [t for t in tested if t < ct]
1280 lower = [t for t in tested if t < ct]
1283 nearest = max(lower or tested)
1281 nearest = max(lower or tested)
1284 if worst[0] is None or nearest < worst[1]:
1282 if worst[0] is None or nearest < worst[1]:
1285 worst = name, nearest, report
1283 worst = name, nearest, report
1286 if worst[0] is not None:
1284 if worst[0] is not None:
1287 name, testedwith, report = worst
1285 name, testedwith, report = worst
1288 if not isinstance(testedwith, (bytes, str)):
1286 if not isinstance(testedwith, (bytes, str)):
1289 testedwith = b'.'.join(
1287 testedwith = b'.'.join(
1290 [stringutil.forcebytestr(c) for c in testedwith]
1288 [stringutil.forcebytestr(c) for c in testedwith]
1291 )
1289 )
1292 warning = _(
1290 warning = _(
1293 b'** Unknown exception encountered with '
1291 b'** Unknown exception encountered with '
1294 b'possibly-broken third-party extension %s\n'
1292 b'possibly-broken third-party extension %s\n'
1295 b'** which supports versions %s of Mercurial.\n'
1293 b'** which supports versions %s of Mercurial.\n'
1296 b'** Please disable %s and try your action again.\n'
1294 b'** Please disable %s and try your action again.\n'
1297 b'** If that fixes the bug please report it to %s\n'
1295 b'** If that fixes the bug please report it to %s\n'
1298 ) % (name, testedwith, name, stringutil.forcebytestr(report))
1296 ) % (name, testedwith, name, stringutil.forcebytestr(report))
1299 else:
1297 else:
1300 bugtracker = ui.config(b'ui', b'supportcontact')
1298 bugtracker = ui.config(b'ui', b'supportcontact')
1301 if bugtracker is None:
1299 if bugtracker is None:
1302 bugtracker = _(b"https://mercurial-scm.org/wiki/BugTracker")
1300 bugtracker = _(b"https://mercurial-scm.org/wiki/BugTracker")
1303 warning = (
1301 warning = (
1304 _(
1302 _(
1305 b"** unknown exception encountered, "
1303 b"** unknown exception encountered, "
1306 b"please report by visiting\n** "
1304 b"please report by visiting\n** "
1307 )
1305 )
1308 + bugtracker
1306 + bugtracker
1309 + b'\n'
1307 + b'\n'
1310 )
1308 )
1311 sysversion = pycompat.sysbytes(sys.version).replace(b'\n', b'')
1309 sysversion = pycompat.sysbytes(sys.version).replace(b'\n', b'')
1312 warning += (
1310 warning += (
1313 (_(b"** Python %s\n") % sysversion)
1311 (_(b"** Python %s\n") % sysversion)
1314 + (_(b"** Mercurial Distributed SCM (version %s)\n") % util.version())
1312 + (_(b"** Mercurial Distributed SCM (version %s)\n") % util.version())
1315 + (
1313 + (
1316 _(b"** Extensions loaded: %s\n")
1314 _(b"** Extensions loaded: %s\n")
1317 % b", ".join([x[0] for x in extensions.extensions()])
1315 % b", ".join([x[0] for x in extensions.extensions()])
1318 )
1316 )
1319 )
1317 )
1320 return warning
1318 return warning
1321
1319
1322
1320
1323 def handlecommandexception(ui):
1321 def handlecommandexception(ui):
1324 """Produce a warning message for broken commands
1322 """Produce a warning message for broken commands
1325
1323
1326 Called when handling an exception; the exception is reraised if
1324 Called when handling an exception; the exception is reraised if
1327 this function returns False, ignored otherwise.
1325 this function returns False, ignored otherwise.
1328 """
1326 """
1329 warning = _exceptionwarning(ui)
1327 warning = _exceptionwarning(ui)
1330 ui.log(
1328 ui.log(
1331 b"commandexception",
1329 b"commandexception",
1332 b"%s\n%s\n",
1330 b"%s\n%s\n",
1333 warning,
1331 warning,
1334 pycompat.sysbytes(traceback.format_exc()),
1332 pycompat.sysbytes(traceback.format_exc()),
1335 )
1333 )
1336 ui.warn(warning)
1334 ui.warn(warning)
1337 return False # re-raise the exception
1335 return False # re-raise the exception
@@ -1,392 +1,390 b''
1 hide outer repo
1 hide outer repo
2 $ hg init
2 $ hg init
3
3
4 Invalid syntax: no value
4 Invalid syntax: no value
5
5
6 $ cat > .hg/hgrc << EOF
6 $ cat > .hg/hgrc << EOF
7 > novaluekey
7 > novaluekey
8 > EOF
8 > EOF
9 $ hg showconfig
9 $ hg showconfig
10 hg: parse error at $TESTTMP/.hg/hgrc:1: novaluekey
10 hg: parse error at $TESTTMP/.hg/hgrc:1: novaluekey
11 [255]
11 [255]
12
12
13 Invalid syntax: no key
13 Invalid syntax: no key
14
14
15 $ cat > .hg/hgrc << EOF
15 $ cat > .hg/hgrc << EOF
16 > =nokeyvalue
16 > =nokeyvalue
17 > EOF
17 > EOF
18 $ hg showconfig
18 $ hg showconfig
19 hg: parse error at $TESTTMP/.hg/hgrc:1: =nokeyvalue
19 hg: parse error at $TESTTMP/.hg/hgrc:1: =nokeyvalue
20 [255]
20 [255]
21
21
22 Test hint about invalid syntax from leading white space
22 Test hint about invalid syntax from leading white space
23
23
24 $ cat > .hg/hgrc << EOF
24 $ cat > .hg/hgrc << EOF
25 > key=value
25 > key=value
26 > EOF
26 > EOF
27 $ hg showconfig
27 $ hg showconfig
28 hg: parse error at $TESTTMP/.hg/hgrc:1: key=value
28 hg: parse error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace: key=value
29 unexpected leading whitespace
30 [255]
29 [255]
31
30
32 $ cat > .hg/hgrc << EOF
31 $ cat > .hg/hgrc << EOF
33 > [section]
32 > [section]
34 > key=value
33 > key=value
35 > EOF
34 > EOF
36 $ hg showconfig
35 $ hg showconfig
37 hg: parse error at $TESTTMP/.hg/hgrc:1: [section]
36 hg: parse error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace: [section]
38 unexpected leading whitespace
39 [255]
37 [255]
40
38
41 Reset hgrc
39 Reset hgrc
42
40
43 $ echo > .hg/hgrc
41 $ echo > .hg/hgrc
44
42
45 Test case sensitive configuration
43 Test case sensitive configuration
46
44
47 $ cat <<EOF >> $HGRCPATH
45 $ cat <<EOF >> $HGRCPATH
48 > [Section]
46 > [Section]
49 > KeY = Case Sensitive
47 > KeY = Case Sensitive
50 > key = lower case
48 > key = lower case
51 > EOF
49 > EOF
52
50
53 $ hg showconfig Section
51 $ hg showconfig Section
54 Section.KeY=Case Sensitive
52 Section.KeY=Case Sensitive
55 Section.key=lower case
53 Section.key=lower case
56
54
57 $ hg showconfig Section -Tjson
55 $ hg showconfig Section -Tjson
58 [
56 [
59 {
57 {
60 "defaultvalue": null,
58 "defaultvalue": null,
61 "name": "Section.KeY",
59 "name": "Section.KeY",
62 "source": "*.hgrc:*", (glob)
60 "source": "*.hgrc:*", (glob)
63 "value": "Case Sensitive"
61 "value": "Case Sensitive"
64 },
62 },
65 {
63 {
66 "defaultvalue": null,
64 "defaultvalue": null,
67 "name": "Section.key",
65 "name": "Section.key",
68 "source": "*.hgrc:*", (glob)
66 "source": "*.hgrc:*", (glob)
69 "value": "lower case"
67 "value": "lower case"
70 }
68 }
71 ]
69 ]
72 $ hg showconfig Section.KeY -Tjson
70 $ hg showconfig Section.KeY -Tjson
73 [
71 [
74 {
72 {
75 "defaultvalue": null,
73 "defaultvalue": null,
76 "name": "Section.KeY",
74 "name": "Section.KeY",
77 "source": "*.hgrc:*", (glob)
75 "source": "*.hgrc:*", (glob)
78 "value": "Case Sensitive"
76 "value": "Case Sensitive"
79 }
77 }
80 ]
78 ]
81 $ hg showconfig -Tjson | tail -7
79 $ hg showconfig -Tjson | tail -7
82 {
80 {
83 "defaultvalue": null,
81 "defaultvalue": null,
84 "name": "*", (glob)
82 "name": "*", (glob)
85 "source": "*", (glob)
83 "source": "*", (glob)
86 "value": "*" (glob)
84 "value": "*" (glob)
87 }
85 }
88 ]
86 ]
89
87
90 Test config default of various types:
88 Test config default of various types:
91
89
92 {"defaultvalue": ""} for -T'json(defaultvalue)' looks weird, but that's
90 {"defaultvalue": ""} for -T'json(defaultvalue)' looks weird, but that's
93 how the templater works. Unknown keywords are evaluated to "".
91 how the templater works. Unknown keywords are evaluated to "".
94
92
95 dynamicdefault
93 dynamicdefault
96
94
97 $ hg config --config alias.foo= alias -Tjson
95 $ hg config --config alias.foo= alias -Tjson
98 [
96 [
99 {
97 {
100 "name": "alias.foo",
98 "name": "alias.foo",
101 "source": "--config",
99 "source": "--config",
102 "value": ""
100 "value": ""
103 }
101 }
104 ]
102 ]
105 $ hg config --config alias.foo= alias -T'json(defaultvalue)'
103 $ hg config --config alias.foo= alias -T'json(defaultvalue)'
106 [
104 [
107 {"defaultvalue": ""}
105 {"defaultvalue": ""}
108 ]
106 ]
109 $ hg config --config alias.foo= alias -T'{defaultvalue}\n'
107 $ hg config --config alias.foo= alias -T'{defaultvalue}\n'
110
108
111
109
112 null
110 null
113
111
114 $ hg config --config auth.cookiefile= auth -Tjson
112 $ hg config --config auth.cookiefile= auth -Tjson
115 [
113 [
116 {
114 {
117 "defaultvalue": null,
115 "defaultvalue": null,
118 "name": "auth.cookiefile",
116 "name": "auth.cookiefile",
119 "source": "--config",
117 "source": "--config",
120 "value": ""
118 "value": ""
121 }
119 }
122 ]
120 ]
123 $ hg config --config auth.cookiefile= auth -T'json(defaultvalue)'
121 $ hg config --config auth.cookiefile= auth -T'json(defaultvalue)'
124 [
122 [
125 {"defaultvalue": null}
123 {"defaultvalue": null}
126 ]
124 ]
127 $ hg config --config auth.cookiefile= auth -T'{defaultvalue}\n'
125 $ hg config --config auth.cookiefile= auth -T'{defaultvalue}\n'
128
126
129
127
130 false
128 false
131
129
132 $ hg config --config commands.commit.post-status= commands -Tjson
130 $ hg config --config commands.commit.post-status= commands -Tjson
133 [
131 [
134 {
132 {
135 "defaultvalue": false,
133 "defaultvalue": false,
136 "name": "commands.commit.post-status",
134 "name": "commands.commit.post-status",
137 "source": "--config",
135 "source": "--config",
138 "value": ""
136 "value": ""
139 }
137 }
140 ]
138 ]
141 $ hg config --config commands.commit.post-status= commands -T'json(defaultvalue)'
139 $ hg config --config commands.commit.post-status= commands -T'json(defaultvalue)'
142 [
140 [
143 {"defaultvalue": false}
141 {"defaultvalue": false}
144 ]
142 ]
145 $ hg config --config commands.commit.post-status= commands -T'{defaultvalue}\n'
143 $ hg config --config commands.commit.post-status= commands -T'{defaultvalue}\n'
146 False
144 False
147
145
148 true
146 true
149
147
150 $ hg config --config format.dotencode= format -Tjson
148 $ hg config --config format.dotencode= format -Tjson
151 [
149 [
152 {
150 {
153 "defaultvalue": true,
151 "defaultvalue": true,
154 "name": "format.dotencode",
152 "name": "format.dotencode",
155 "source": "--config",
153 "source": "--config",
156 "value": ""
154 "value": ""
157 }
155 }
158 ]
156 ]
159 $ hg config --config format.dotencode= format -T'json(defaultvalue)'
157 $ hg config --config format.dotencode= format -T'json(defaultvalue)'
160 [
158 [
161 {"defaultvalue": true}
159 {"defaultvalue": true}
162 ]
160 ]
163 $ hg config --config format.dotencode= format -T'{defaultvalue}\n'
161 $ hg config --config format.dotencode= format -T'{defaultvalue}\n'
164 True
162 True
165
163
166 bytes
164 bytes
167
165
168 $ hg config --config commands.resolve.mark-check= commands -Tjson
166 $ hg config --config commands.resolve.mark-check= commands -Tjson
169 [
167 [
170 {
168 {
171 "defaultvalue": "none",
169 "defaultvalue": "none",
172 "name": "commands.resolve.mark-check",
170 "name": "commands.resolve.mark-check",
173 "source": "--config",
171 "source": "--config",
174 "value": ""
172 "value": ""
175 }
173 }
176 ]
174 ]
177 $ hg config --config commands.resolve.mark-check= commands -T'json(defaultvalue)'
175 $ hg config --config commands.resolve.mark-check= commands -T'json(defaultvalue)'
178 [
176 [
179 {"defaultvalue": "none"}
177 {"defaultvalue": "none"}
180 ]
178 ]
181 $ hg config --config commands.resolve.mark-check= commands -T'{defaultvalue}\n'
179 $ hg config --config commands.resolve.mark-check= commands -T'{defaultvalue}\n'
182 none
180 none
183
181
184 empty list
182 empty list
185
183
186 $ hg config --config commands.show.aliasprefix= commands -Tjson
184 $ hg config --config commands.show.aliasprefix= commands -Tjson
187 [
185 [
188 {
186 {
189 "defaultvalue": [],
187 "defaultvalue": [],
190 "name": "commands.show.aliasprefix",
188 "name": "commands.show.aliasprefix",
191 "source": "--config",
189 "source": "--config",
192 "value": ""
190 "value": ""
193 }
191 }
194 ]
192 ]
195 $ hg config --config commands.show.aliasprefix= commands -T'json(defaultvalue)'
193 $ hg config --config commands.show.aliasprefix= commands -T'json(defaultvalue)'
196 [
194 [
197 {"defaultvalue": []}
195 {"defaultvalue": []}
198 ]
196 ]
199 $ hg config --config commands.show.aliasprefix= commands -T'{defaultvalue}\n'
197 $ hg config --config commands.show.aliasprefix= commands -T'{defaultvalue}\n'
200
198
201
199
202 nonempty list
200 nonempty list
203
201
204 $ hg config --config progress.format= progress -Tjson
202 $ hg config --config progress.format= progress -Tjson
205 [
203 [
206 {
204 {
207 "defaultvalue": ["topic", "bar", "number", "estimate"],
205 "defaultvalue": ["topic", "bar", "number", "estimate"],
208 "name": "progress.format",
206 "name": "progress.format",
209 "source": "--config",
207 "source": "--config",
210 "value": ""
208 "value": ""
211 }
209 }
212 ]
210 ]
213 $ hg config --config progress.format= progress -T'json(defaultvalue)'
211 $ hg config --config progress.format= progress -T'json(defaultvalue)'
214 [
212 [
215 {"defaultvalue": ["topic", "bar", "number", "estimate"]}
213 {"defaultvalue": ["topic", "bar", "number", "estimate"]}
216 ]
214 ]
217 $ hg config --config progress.format= progress -T'{defaultvalue}\n'
215 $ hg config --config progress.format= progress -T'{defaultvalue}\n'
218 topic bar number estimate
216 topic bar number estimate
219
217
220 int
218 int
221
219
222 $ hg config --config profiling.freq= profiling -Tjson
220 $ hg config --config profiling.freq= profiling -Tjson
223 [
221 [
224 {
222 {
225 "defaultvalue": 1000,
223 "defaultvalue": 1000,
226 "name": "profiling.freq",
224 "name": "profiling.freq",
227 "source": "--config",
225 "source": "--config",
228 "value": ""
226 "value": ""
229 }
227 }
230 ]
228 ]
231 $ hg config --config profiling.freq= profiling -T'json(defaultvalue)'
229 $ hg config --config profiling.freq= profiling -T'json(defaultvalue)'
232 [
230 [
233 {"defaultvalue": 1000}
231 {"defaultvalue": 1000}
234 ]
232 ]
235 $ hg config --config profiling.freq= profiling -T'{defaultvalue}\n'
233 $ hg config --config profiling.freq= profiling -T'{defaultvalue}\n'
236 1000
234 1000
237
235
238 float
236 float
239
237
240 $ hg config --config profiling.showmax= profiling -Tjson
238 $ hg config --config profiling.showmax= profiling -Tjson
241 [
239 [
242 {
240 {
243 "defaultvalue": 0.999,
241 "defaultvalue": 0.999,
244 "name": "profiling.showmax",
242 "name": "profiling.showmax",
245 "source": "--config",
243 "source": "--config",
246 "value": ""
244 "value": ""
247 }
245 }
248 ]
246 ]
249 $ hg config --config profiling.showmax= profiling -T'json(defaultvalue)'
247 $ hg config --config profiling.showmax= profiling -T'json(defaultvalue)'
250 [
248 [
251 {"defaultvalue": 0.999}
249 {"defaultvalue": 0.999}
252 ]
250 ]
253 $ hg config --config profiling.showmax= profiling -T'{defaultvalue}\n'
251 $ hg config --config profiling.showmax= profiling -T'{defaultvalue}\n'
254 0.999
252 0.999
255
253
256 Test empty config source:
254 Test empty config source:
257
255
258 $ cat <<EOF > emptysource.py
256 $ cat <<EOF > emptysource.py
259 > def reposetup(ui, repo):
257 > def reposetup(ui, repo):
260 > ui.setconfig(b'empty', b'source', b'value')
258 > ui.setconfig(b'empty', b'source', b'value')
261 > EOF
259 > EOF
262 $ cp .hg/hgrc .hg/hgrc.orig
260 $ cp .hg/hgrc .hg/hgrc.orig
263 $ cat <<EOF >> .hg/hgrc
261 $ cat <<EOF >> .hg/hgrc
264 > [extensions]
262 > [extensions]
265 > emptysource = `pwd`/emptysource.py
263 > emptysource = `pwd`/emptysource.py
266 > EOF
264 > EOF
267
265
268 $ hg config --debug empty.source
266 $ hg config --debug empty.source
269 read config from: * (glob)
267 read config from: * (glob)
270 none: value
268 none: value
271 $ hg config empty.source -Tjson
269 $ hg config empty.source -Tjson
272 [
270 [
273 {
271 {
274 "defaultvalue": null,
272 "defaultvalue": null,
275 "name": "empty.source",
273 "name": "empty.source",
276 "source": "",
274 "source": "",
277 "value": "value"
275 "value": "value"
278 }
276 }
279 ]
277 ]
280
278
281 $ cp .hg/hgrc.orig .hg/hgrc
279 $ cp .hg/hgrc.orig .hg/hgrc
282
280
283 Test "%unset"
281 Test "%unset"
284
282
285 $ cat >> $HGRCPATH <<EOF
283 $ cat >> $HGRCPATH <<EOF
286 > [unsettest]
284 > [unsettest]
287 > local-hgrcpath = should be unset (HGRCPATH)
285 > local-hgrcpath = should be unset (HGRCPATH)
288 > %unset local-hgrcpath
286 > %unset local-hgrcpath
289 >
287 >
290 > global = should be unset (HGRCPATH)
288 > global = should be unset (HGRCPATH)
291 >
289 >
292 > both = should be unset (HGRCPATH)
290 > both = should be unset (HGRCPATH)
293 >
291 >
294 > set-after-unset = should be unset (HGRCPATH)
292 > set-after-unset = should be unset (HGRCPATH)
295 > EOF
293 > EOF
296
294
297 $ cat >> .hg/hgrc <<EOF
295 $ cat >> .hg/hgrc <<EOF
298 > [unsettest]
296 > [unsettest]
299 > local-hgrc = should be unset (.hg/hgrc)
297 > local-hgrc = should be unset (.hg/hgrc)
300 > %unset local-hgrc
298 > %unset local-hgrc
301 >
299 >
302 > %unset global
300 > %unset global
303 >
301 >
304 > both = should be unset (.hg/hgrc)
302 > both = should be unset (.hg/hgrc)
305 > %unset both
303 > %unset both
306 >
304 >
307 > set-after-unset = should be unset (.hg/hgrc)
305 > set-after-unset = should be unset (.hg/hgrc)
308 > %unset set-after-unset
306 > %unset set-after-unset
309 > set-after-unset = should be set (.hg/hgrc)
307 > set-after-unset = should be set (.hg/hgrc)
310 > EOF
308 > EOF
311
309
312 $ hg showconfig unsettest
310 $ hg showconfig unsettest
313 unsettest.set-after-unset=should be set (.hg/hgrc)
311 unsettest.set-after-unset=should be set (.hg/hgrc)
314
312
315 Test exit code when no config matches
313 Test exit code when no config matches
316
314
317 $ hg config Section.idontexist
315 $ hg config Section.idontexist
318 [1]
316 [1]
319
317
320 sub-options in [paths] aren't expanded
318 sub-options in [paths] aren't expanded
321
319
322 $ cat > .hg/hgrc << EOF
320 $ cat > .hg/hgrc << EOF
323 > [paths]
321 > [paths]
324 > foo = ~/foo
322 > foo = ~/foo
325 > foo:suboption = ~/foo
323 > foo:suboption = ~/foo
326 > EOF
324 > EOF
327
325
328 $ hg showconfig paths
326 $ hg showconfig paths
329 paths.foo:suboption=~/foo
327 paths.foo:suboption=~/foo
330 paths.foo=$TESTTMP/foo
328 paths.foo=$TESTTMP/foo
331
329
332 edit failure
330 edit failure
333
331
334 $ HGEDITOR=false hg config --edit
332 $ HGEDITOR=false hg config --edit
335 abort: edit failed: false exited with status 1
333 abort: edit failed: false exited with status 1
336 [255]
334 [255]
337
335
338 config affected by environment variables
336 config affected by environment variables
339
337
340 $ EDITOR=e1 VISUAL=e2 hg config --debug | grep 'ui\.editor'
338 $ EDITOR=e1 VISUAL=e2 hg config --debug | grep 'ui\.editor'
341 $VISUAL: ui.editor=e2
339 $VISUAL: ui.editor=e2
342
340
343 $ VISUAL=e2 hg config --debug --config ui.editor=e3 | grep 'ui\.editor'
341 $ VISUAL=e2 hg config --debug --config ui.editor=e3 | grep 'ui\.editor'
344 --config: ui.editor=e3
342 --config: ui.editor=e3
345
343
346 $ PAGER=p1 hg config --debug | grep 'pager\.pager'
344 $ PAGER=p1 hg config --debug | grep 'pager\.pager'
347 $PAGER: pager.pager=p1
345 $PAGER: pager.pager=p1
348
346
349 $ PAGER=p1 hg config --debug --config pager.pager=p2 | grep 'pager\.pager'
347 $ PAGER=p1 hg config --debug --config pager.pager=p2 | grep 'pager\.pager'
350 --config: pager.pager=p2
348 --config: pager.pager=p2
351
349
352 verify that aliases are evaluated as well
350 verify that aliases are evaluated as well
353
351
354 $ hg init aliastest
352 $ hg init aliastest
355 $ cd aliastest
353 $ cd aliastest
356 $ cat > .hg/hgrc << EOF
354 $ cat > .hg/hgrc << EOF
357 > [ui]
355 > [ui]
358 > user = repo user
356 > user = repo user
359 > EOF
357 > EOF
360 $ touch index
358 $ touch index
361 $ unset HGUSER
359 $ unset HGUSER
362 $ hg ci -Am test
360 $ hg ci -Am test
363 adding index
361 adding index
364 $ hg log --template '{author}\n'
362 $ hg log --template '{author}\n'
365 repo user
363 repo user
366 $ cd ..
364 $ cd ..
367
365
368 alias has lower priority
366 alias has lower priority
369
367
370 $ hg init aliaspriority
368 $ hg init aliaspriority
371 $ cd aliaspriority
369 $ cd aliaspriority
372 $ cat > .hg/hgrc << EOF
370 $ cat > .hg/hgrc << EOF
373 > [ui]
371 > [ui]
374 > user = alias user
372 > user = alias user
375 > username = repo user
373 > username = repo user
376 > EOF
374 > EOF
377 $ touch index
375 $ touch index
378 $ unset HGUSER
376 $ unset HGUSER
379 $ hg ci -Am test
377 $ hg ci -Am test
380 adding index
378 adding index
381 $ hg log --template '{author}\n'
379 $ hg log --template '{author}\n'
382 repo user
380 repo user
383 $ cd ..
381 $ cd ..
384
382
385 configs should be read in lexicographical order
383 configs should be read in lexicographical order
386
384
387 $ mkdir configs
385 $ mkdir configs
388 $ for i in `$TESTDIR/seq.py 10 99`; do
386 $ for i in `$TESTDIR/seq.py 10 99`; do
389 > printf "[section]\nkey=$i" > configs/$i.rc
387 > printf "[section]\nkey=$i" > configs/$i.rc
390 > done
388 > done
391 $ HGRCPATH=configs hg config section.key
389 $ HGRCPATH=configs hg config section.key
392 99
390 99
@@ -1,309 +1,308 b''
1 Use hgrc within $TESTTMP
1 Use hgrc within $TESTTMP
2
2
3 $ HGRCPATH=`pwd`/hgrc
3 $ HGRCPATH=`pwd`/hgrc
4 $ export HGRCPATH
4 $ export HGRCPATH
5
5
6 hide outer repo
6 hide outer repo
7 $ hg init
7 $ hg init
8
8
9 Use an alternate var for scribbling on hgrc to keep check-code from
9 Use an alternate var for scribbling on hgrc to keep check-code from
10 complaining about the important settings we may be overwriting:
10 complaining about the important settings we may be overwriting:
11
11
12 $ HGRC=`pwd`/hgrc
12 $ HGRC=`pwd`/hgrc
13 $ export HGRC
13 $ export HGRC
14
14
15 Basic syntax error
15 Basic syntax error
16
16
17 $ echo "invalid" > $HGRC
17 $ echo "invalid" > $HGRC
18 $ hg version
18 $ hg version
19 hg: parse error at $TESTTMP/hgrc:1: invalid
19 hg: parse error at $TESTTMP/hgrc:1: invalid
20 [255]
20 [255]
21 $ echo "" > $HGRC
21 $ echo "" > $HGRC
22
22
23 Issue1199: Can't use '%' in hgrc (eg url encoded username)
23 Issue1199: Can't use '%' in hgrc (eg url encoded username)
24
24
25 $ hg init "foo%bar"
25 $ hg init "foo%bar"
26 $ hg clone "foo%bar" foobar
26 $ hg clone "foo%bar" foobar
27 updating to branch default
27 updating to branch default
28 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 $ cd foobar
29 $ cd foobar
30 $ cat .hg/hgrc
30 $ cat .hg/hgrc
31 # example repository config (see 'hg help config' for more info)
31 # example repository config (see 'hg help config' for more info)
32 [paths]
32 [paths]
33 default = $TESTTMP/foo%bar
33 default = $TESTTMP/foo%bar
34
34
35 # path aliases to other clones of this repo in URLs or filesystem paths
35 # path aliases to other clones of this repo in URLs or filesystem paths
36 # (see 'hg help config.paths' for more info)
36 # (see 'hg help config.paths' for more info)
37 #
37 #
38 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
38 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
39 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
39 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
40 # my-clone = /home/jdoe/jdoes-clone
40 # my-clone = /home/jdoe/jdoes-clone
41
41
42 [ui]
42 [ui]
43 # name and email (local to this repository, optional), e.g.
43 # name and email (local to this repository, optional), e.g.
44 # username = Jane Doe <jdoe@example.com>
44 # username = Jane Doe <jdoe@example.com>
45 $ hg paths
45 $ hg paths
46 default = $TESTTMP/foo%bar
46 default = $TESTTMP/foo%bar
47 $ hg showconfig
47 $ hg showconfig
48 bundle.mainreporoot=$TESTTMP/foobar
48 bundle.mainreporoot=$TESTTMP/foobar
49 paths.default=$TESTTMP/foo%bar
49 paths.default=$TESTTMP/foo%bar
50 $ cd ..
50 $ cd ..
51
51
52 Check %include
52 Check %include
53
53
54 $ echo '[section]' > $TESTTMP/included
54 $ echo '[section]' > $TESTTMP/included
55 $ echo 'option = value' >> $TESTTMP/included
55 $ echo 'option = value' >> $TESTTMP/included
56 $ echo '%include $TESTTMP/included' >> $HGRC
56 $ echo '%include $TESTTMP/included' >> $HGRC
57 $ hg showconfig section
57 $ hg showconfig section
58 section.option=value
58 section.option=value
59 #if unix-permissions no-root
59 #if unix-permissions no-root
60 $ chmod u-r $TESTTMP/included
60 $ chmod u-r $TESTTMP/included
61 $ hg showconfig section
61 $ hg showconfig section
62 hg: parse error at $TESTTMP/hgrc:2: cannot include $TESTTMP/included (Permission denied)
62 hg: parse error at $TESTTMP/hgrc:2: cannot include $TESTTMP/included (Permission denied)
63 [255]
63 [255]
64 #endif
64 #endif
65
65
66 issue1829: wrong indentation
66 issue1829: wrong indentation
67
67
68 $ echo '[foo]' > $HGRC
68 $ echo '[foo]' > $HGRC
69 $ echo ' x = y' >> $HGRC
69 $ echo ' x = y' >> $HGRC
70 $ hg version
70 $ hg version
71 hg: parse error at $TESTTMP/hgrc:2: x = y
71 hg: parse error at $TESTTMP/hgrc:2: unexpected leading whitespace: x = y
72 unexpected leading whitespace
73 [255]
72 [255]
74
73
75 $ "$PYTHON" -c "from __future__ import print_function; print('[foo]\nbar = a\n b\n c \n de\n fg \nbaz = bif cb \n')" \
74 $ "$PYTHON" -c "from __future__ import print_function; print('[foo]\nbar = a\n b\n c \n de\n fg \nbaz = bif cb \n')" \
76 > > $HGRC
75 > > $HGRC
77 $ hg showconfig foo
76 $ hg showconfig foo
78 foo.bar=a\nb\nc\nde\nfg
77 foo.bar=a\nb\nc\nde\nfg
79 foo.baz=bif cb
78 foo.baz=bif cb
80
79
81 $ FAKEPATH=/path/to/nowhere
80 $ FAKEPATH=/path/to/nowhere
82 $ export FAKEPATH
81 $ export FAKEPATH
83 $ echo '%include $FAKEPATH/no-such-file' > $HGRC
82 $ echo '%include $FAKEPATH/no-such-file' > $HGRC
84 $ hg version
83 $ hg version
85 Mercurial Distributed SCM (version *) (glob)
84 Mercurial Distributed SCM (version *) (glob)
86 (see https://mercurial-scm.org for more information)
85 (see https://mercurial-scm.org for more information)
87
86
88 Copyright (C) 2005-* Matt Mackall and others (glob)
87 Copyright (C) 2005-* Matt Mackall and others (glob)
89 This is free software; see the source for copying conditions. There is NO
88 This is free software; see the source for copying conditions. There is NO
90 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
89 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
91 $ unset FAKEPATH
90 $ unset FAKEPATH
92
91
93 make sure global options given on the cmdline take precedence
92 make sure global options given on the cmdline take precedence
94
93
95 $ hg showconfig --config ui.verbose=True --quiet
94 $ hg showconfig --config ui.verbose=True --quiet
96 bundle.mainreporoot=$TESTTMP
95 bundle.mainreporoot=$TESTTMP
97 ui.verbose=False
96 ui.verbose=False
98 ui.debug=False
97 ui.debug=False
99 ui.quiet=True
98 ui.quiet=True
100
99
101 $ touch foobar/untracked
100 $ touch foobar/untracked
102 $ cat >> foobar/.hg/hgrc <<EOF
101 $ cat >> foobar/.hg/hgrc <<EOF
103 > [ui]
102 > [ui]
104 > verbose=True
103 > verbose=True
105 > EOF
104 > EOF
106 $ hg -R foobar st -q
105 $ hg -R foobar st -q
107
106
108 username expansion
107 username expansion
109
108
110 $ olduser=$HGUSER
109 $ olduser=$HGUSER
111 $ unset HGUSER
110 $ unset HGUSER
112
111
113 $ FAKEUSER='John Doe'
112 $ FAKEUSER='John Doe'
114 $ export FAKEUSER
113 $ export FAKEUSER
115 $ echo '[ui]' > $HGRC
114 $ echo '[ui]' > $HGRC
116 $ echo 'username = $FAKEUSER' >> $HGRC
115 $ echo 'username = $FAKEUSER' >> $HGRC
117
116
118 $ hg init usertest
117 $ hg init usertest
119 $ cd usertest
118 $ cd usertest
120 $ touch bar
119 $ touch bar
121 $ hg commit --addremove --quiet -m "added bar"
120 $ hg commit --addremove --quiet -m "added bar"
122 $ hg log --template "{author}\n"
121 $ hg log --template "{author}\n"
123 John Doe
122 John Doe
124 $ cd ..
123 $ cd ..
125
124
126 $ hg showconfig
125 $ hg showconfig
127 bundle.mainreporoot=$TESTTMP
126 bundle.mainreporoot=$TESTTMP
128 ui.username=$FAKEUSER
127 ui.username=$FAKEUSER
129
128
130 $ unset FAKEUSER
129 $ unset FAKEUSER
131 $ HGUSER=$olduser
130 $ HGUSER=$olduser
132 $ export HGUSER
131 $ export HGUSER
133
132
134 showconfig with multiple arguments
133 showconfig with multiple arguments
135
134
136 $ echo "[alias]" > $HGRC
135 $ echo "[alias]" > $HGRC
137 $ echo "log = log -g" >> $HGRC
136 $ echo "log = log -g" >> $HGRC
138 $ echo "[defaults]" >> $HGRC
137 $ echo "[defaults]" >> $HGRC
139 $ echo "identify = -n" >> $HGRC
138 $ echo "identify = -n" >> $HGRC
140 $ hg showconfig alias defaults
139 $ hg showconfig alias defaults
141 alias.log=log -g
140 alias.log=log -g
142 defaults.identify=-n
141 defaults.identify=-n
143 $ hg showconfig alias alias
142 $ hg showconfig alias alias
144 alias.log=log -g
143 alias.log=log -g
145 $ hg showconfig alias.log alias.log
144 $ hg showconfig alias.log alias.log
146 alias.log=log -g
145 alias.log=log -g
147 $ hg showconfig alias defaults.identify
146 $ hg showconfig alias defaults.identify
148 alias.log=log -g
147 alias.log=log -g
149 defaults.identify=-n
148 defaults.identify=-n
150 $ hg showconfig alias.log defaults.identify
149 $ hg showconfig alias.log defaults.identify
151 alias.log=log -g
150 alias.log=log -g
152 defaults.identify=-n
151 defaults.identify=-n
153
152
154 HGPLAIN
153 HGPLAIN
155
154
156 $ echo "[ui]" > $HGRC
155 $ echo "[ui]" > $HGRC
157 $ echo "debug=true" >> $HGRC
156 $ echo "debug=true" >> $HGRC
158 $ echo "fallbackencoding=ASCII" >> $HGRC
157 $ echo "fallbackencoding=ASCII" >> $HGRC
159 $ echo "quiet=true" >> $HGRC
158 $ echo "quiet=true" >> $HGRC
160 $ echo "slash=true" >> $HGRC
159 $ echo "slash=true" >> $HGRC
161 $ echo "traceback=true" >> $HGRC
160 $ echo "traceback=true" >> $HGRC
162 $ echo "verbose=true" >> $HGRC
161 $ echo "verbose=true" >> $HGRC
163 $ echo "style=~/.hgstyle" >> $HGRC
162 $ echo "style=~/.hgstyle" >> $HGRC
164 $ echo "logtemplate={node}" >> $HGRC
163 $ echo "logtemplate={node}" >> $HGRC
165 $ echo "[defaults]" >> $HGRC
164 $ echo "[defaults]" >> $HGRC
166 $ echo "identify=-n" >> $HGRC
165 $ echo "identify=-n" >> $HGRC
167 $ echo "[alias]" >> $HGRC
166 $ echo "[alias]" >> $HGRC
168 $ echo "log=log -g" >> $HGRC
167 $ echo "log=log -g" >> $HGRC
169
168
170 customized hgrc
169 customized hgrc
171
170
172 $ hg showconfig
171 $ hg showconfig
173 read config from: $TESTTMP/hgrc
172 read config from: $TESTTMP/hgrc
174 $TESTTMP/hgrc:13: alias.log=log -g
173 $TESTTMP/hgrc:13: alias.log=log -g
175 repo: bundle.mainreporoot=$TESTTMP
174 repo: bundle.mainreporoot=$TESTTMP
176 $TESTTMP/hgrc:11: defaults.identify=-n
175 $TESTTMP/hgrc:11: defaults.identify=-n
177 $TESTTMP/hgrc:2: ui.debug=true
176 $TESTTMP/hgrc:2: ui.debug=true
178 $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII
177 $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII
179 $TESTTMP/hgrc:4: ui.quiet=true
178 $TESTTMP/hgrc:4: ui.quiet=true
180 $TESTTMP/hgrc:5: ui.slash=true
179 $TESTTMP/hgrc:5: ui.slash=true
181 $TESTTMP/hgrc:6: ui.traceback=true
180 $TESTTMP/hgrc:6: ui.traceback=true
182 $TESTTMP/hgrc:7: ui.verbose=true
181 $TESTTMP/hgrc:7: ui.verbose=true
183 $TESTTMP/hgrc:8: ui.style=~/.hgstyle
182 $TESTTMP/hgrc:8: ui.style=~/.hgstyle
184 $TESTTMP/hgrc:9: ui.logtemplate={node}
183 $TESTTMP/hgrc:9: ui.logtemplate={node}
185
184
186 plain hgrc
185 plain hgrc
187
186
188 $ HGPLAIN=; export HGPLAIN
187 $ HGPLAIN=; export HGPLAIN
189 $ hg showconfig --config ui.traceback=True --debug
188 $ hg showconfig --config ui.traceback=True --debug
190 read config from: $TESTTMP/hgrc
189 read config from: $TESTTMP/hgrc
191 repo: bundle.mainreporoot=$TESTTMP
190 repo: bundle.mainreporoot=$TESTTMP
192 --config: ui.traceback=True
191 --config: ui.traceback=True
193 --verbose: ui.verbose=False
192 --verbose: ui.verbose=False
194 --debug: ui.debug=True
193 --debug: ui.debug=True
195 --quiet: ui.quiet=False
194 --quiet: ui.quiet=False
196
195
197 with environment variables
196 with environment variables
198
197
199 $ PAGER=p1 EDITOR=e1 VISUAL=e2 hg showconfig --debug
198 $ PAGER=p1 EDITOR=e1 VISUAL=e2 hg showconfig --debug
200 read config from: $TESTTMP/hgrc
199 read config from: $TESTTMP/hgrc
201 repo: bundle.mainreporoot=$TESTTMP
200 repo: bundle.mainreporoot=$TESTTMP
202 $PAGER: pager.pager=p1
201 $PAGER: pager.pager=p1
203 $VISUAL: ui.editor=e2
202 $VISUAL: ui.editor=e2
204 --verbose: ui.verbose=False
203 --verbose: ui.verbose=False
205 --debug: ui.debug=True
204 --debug: ui.debug=True
206 --quiet: ui.quiet=False
205 --quiet: ui.quiet=False
207
206
208 plain mode with exceptions
207 plain mode with exceptions
209
208
210 $ cat > plain.py <<EOF
209 $ cat > plain.py <<EOF
211 > from mercurial import commands, extensions
210 > from mercurial import commands, extensions
212 > def _config(orig, ui, repo, *values, **opts):
211 > def _config(orig, ui, repo, *values, **opts):
213 > ui.write(b'plain: %r\n' % ui.plain())
212 > ui.write(b'plain: %r\n' % ui.plain())
214 > return orig(ui, repo, *values, **opts)
213 > return orig(ui, repo, *values, **opts)
215 > def uisetup(ui):
214 > def uisetup(ui):
216 > extensions.wrapcommand(commands.table, b'config', _config)
215 > extensions.wrapcommand(commands.table, b'config', _config)
217 > EOF
216 > EOF
218 $ echo "[extensions]" >> $HGRC
217 $ echo "[extensions]" >> $HGRC
219 $ echo "plain=./plain.py" >> $HGRC
218 $ echo "plain=./plain.py" >> $HGRC
220 $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
219 $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
221 $ hg showconfig --config ui.traceback=True --debug
220 $ hg showconfig --config ui.traceback=True --debug
222 plain: True
221 plain: True
223 read config from: $TESTTMP/hgrc
222 read config from: $TESTTMP/hgrc
224 repo: bundle.mainreporoot=$TESTTMP
223 repo: bundle.mainreporoot=$TESTTMP
225 $TESTTMP/hgrc:15: extensions.plain=./plain.py
224 $TESTTMP/hgrc:15: extensions.plain=./plain.py
226 --config: ui.traceback=True
225 --config: ui.traceback=True
227 --verbose: ui.verbose=False
226 --verbose: ui.verbose=False
228 --debug: ui.debug=True
227 --debug: ui.debug=True
229 --quiet: ui.quiet=False
228 --quiet: ui.quiet=False
230 $ unset HGPLAIN
229 $ unset HGPLAIN
231 $ hg showconfig --config ui.traceback=True --debug
230 $ hg showconfig --config ui.traceback=True --debug
232 plain: True
231 plain: True
233 read config from: $TESTTMP/hgrc
232 read config from: $TESTTMP/hgrc
234 repo: bundle.mainreporoot=$TESTTMP
233 repo: bundle.mainreporoot=$TESTTMP
235 $TESTTMP/hgrc:15: extensions.plain=./plain.py
234 $TESTTMP/hgrc:15: extensions.plain=./plain.py
236 --config: ui.traceback=True
235 --config: ui.traceback=True
237 --verbose: ui.verbose=False
236 --verbose: ui.verbose=False
238 --debug: ui.debug=True
237 --debug: ui.debug=True
239 --quiet: ui.quiet=False
238 --quiet: ui.quiet=False
240 $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
239 $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
241 $ hg showconfig --config ui.traceback=True --debug
240 $ hg showconfig --config ui.traceback=True --debug
242 plain: True
241 plain: True
243 read config from: $TESTTMP/hgrc
242 read config from: $TESTTMP/hgrc
244 repo: bundle.mainreporoot=$TESTTMP
243 repo: bundle.mainreporoot=$TESTTMP
245 $TESTTMP/hgrc:15: extensions.plain=./plain.py
244 $TESTTMP/hgrc:15: extensions.plain=./plain.py
246 --config: ui.traceback=True
245 --config: ui.traceback=True
247 --verbose: ui.verbose=False
246 --verbose: ui.verbose=False
248 --debug: ui.debug=True
247 --debug: ui.debug=True
249 --quiet: ui.quiet=False
248 --quiet: ui.quiet=False
250
249
251 source of paths is not mangled
250 source of paths is not mangled
252
251
253 $ cat >> $HGRCPATH <<EOF
252 $ cat >> $HGRCPATH <<EOF
254 > [paths]
253 > [paths]
255 > foo = bar
254 > foo = bar
256 > EOF
255 > EOF
257 $ hg showconfig --debug paths
256 $ hg showconfig --debug paths
258 plain: True
257 plain: True
259 read config from: $TESTTMP/hgrc
258 read config from: $TESTTMP/hgrc
260 $TESTTMP/hgrc:17: paths.foo=$TESTTMP/bar
259 $TESTTMP/hgrc:17: paths.foo=$TESTTMP/bar
261
260
262 Test we can skip the user configuration
261 Test we can skip the user configuration
263
262
264 $ cat >> .hg/hgrc <<EOF
263 $ cat >> .hg/hgrc <<EOF
265 > [paths]
264 > [paths]
266 > elephant = babar
265 > elephant = babar
267 > EOF
266 > EOF
268 $ hg path
267 $ hg path
269 elephant = $TESTTMP/babar
268 elephant = $TESTTMP/babar
270 foo = $TESTTMP/bar
269 foo = $TESTTMP/bar
271 $ HGRCSKIPREPO=1 hg path
270 $ HGRCSKIPREPO=1 hg path
272 foo = $TESTTMP/bar
271 foo = $TESTTMP/bar
273
272
274 $ cat >> .hg/hgrc <<EOF
273 $ cat >> .hg/hgrc <<EOF
275 > [broken
274 > [broken
276 > EOF
275 > EOF
277
276
278 $ hg path
277 $ hg path
279 hg: parse error at $TESTTMP/.hg/hgrc:3: [broken
278 hg: parse error at $TESTTMP/.hg/hgrc:3: [broken
280 [255]
279 [255]
281 $ HGRCSKIPREPO=1 hg path
280 $ HGRCSKIPREPO=1 hg path
282 foo = $TESTTMP/bar
281 foo = $TESTTMP/bar
283
282
284 Check that hgweb respect HGRCSKIPREPO=1
283 Check that hgweb respect HGRCSKIPREPO=1
285
284
286 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
285 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
287 hg: parse error at $TESTTMP/.hg/hgrc:3: [broken
286 hg: parse error at $TESTTMP/.hg/hgrc:3: [broken
288 [255]
287 [255]
289 $ test -f hg.pid && (cat hg.pid >> $DAEMON_PIDS)
288 $ test -f hg.pid && (cat hg.pid >> $DAEMON_PIDS)
290 [1]
289 [1]
291 $ killdaemons.py
290 $ killdaemons.py
292 $ test -f access.log && cat access.log
291 $ test -f access.log && cat access.log
293 [1]
292 [1]
294 $ test -f errors.log && cat errors.log
293 $ test -f errors.log && cat errors.log
295 [1]
294 [1]
296
295
297 $ HGRCSKIPREPO=1 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
296 $ HGRCSKIPREPO=1 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
298 $ cat hg.pid >> $DAEMON_PIDS
297 $ cat hg.pid >> $DAEMON_PIDS
299 $ killdaemons.py
298 $ killdaemons.py
300 $ cat access.log
299 $ cat access.log
301 $ cat errors.log
300 $ cat errors.log
302
301
303 Check that zeroconf respect HGRCSKIPREPO=1
302 Check that zeroconf respect HGRCSKIPREPO=1
304
303
305 $ hg paths --config extensions.zeroconf=
304 $ hg paths --config extensions.zeroconf=
306 hg: parse error at $TESTTMP/.hg/hgrc:3: [broken
305 hg: parse error at $TESTTMP/.hg/hgrc:3: [broken
307 [255]
306 [255]
308 $ HGRCSKIPREPO=1 hg paths --config extensions.zeroconf=
307 $ HGRCSKIPREPO=1 hg paths --config extensions.zeroconf=
309 foo = $TESTTMP/bar
308 foo = $TESTTMP/bar
General Comments 0
You need to be logged in to leave comments. Login now