##// END OF EJS Templates
tweakdefaults: remove "bisect" from commands.status.skipstates...
Martin von Zweigbergk -
r37936:c6904da6 default
parent child Browse files
Show More
@@ -1,1870 +1,1868 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import collections
10 import collections
11 import contextlib
11 import contextlib
12 import errno
12 import errno
13 import getpass
13 import getpass
14 import inspect
14 import inspect
15 import os
15 import os
16 import re
16 import re
17 import signal
17 import signal
18 import socket
18 import socket
19 import subprocess
19 import subprocess
20 import sys
20 import sys
21 import tempfile
21 import tempfile
22 import traceback
22 import traceback
23
23
24 from .i18n import _
24 from .i18n import _
25 from .node import hex
25 from .node import hex
26
26
27 from . import (
27 from . import (
28 color,
28 color,
29 config,
29 config,
30 configitems,
30 configitems,
31 encoding,
31 encoding,
32 error,
32 error,
33 formatter,
33 formatter,
34 progress,
34 progress,
35 pycompat,
35 pycompat,
36 rcutil,
36 rcutil,
37 scmutil,
37 scmutil,
38 util,
38 util,
39 )
39 )
40 from .utils import (
40 from .utils import (
41 dateutil,
41 dateutil,
42 procutil,
42 procutil,
43 stringutil,
43 stringutil,
44 )
44 )
45
45
46 urlreq = util.urlreq
46 urlreq = util.urlreq
47
47
48 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
48 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
49 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
49 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
50 if not c.isalnum())
50 if not c.isalnum())
51
51
52 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
52 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
53 tweakrc = b"""
53 tweakrc = b"""
54 [ui]
54 [ui]
55 # The rollback command is dangerous. As a rule, don't use it.
55 # The rollback command is dangerous. As a rule, don't use it.
56 rollback = False
56 rollback = False
57 # Make `hg status` report copy information
57 # Make `hg status` report copy information
58 statuscopies = yes
58 statuscopies = yes
59 # Prefer curses UIs when available. Revert to plain-text with `text`.
59 # Prefer curses UIs when available. Revert to plain-text with `text`.
60 interface = curses
60 interface = curses
61
61
62 [commands]
62 [commands]
63 # Make `hg status` emit cwd-relative paths by default.
63 # Make `hg status` emit cwd-relative paths by default.
64 status.relative = yes
64 status.relative = yes
65 # Refuse to perform an `hg update` that would cause a file content merge
65 # Refuse to perform an `hg update` that would cause a file content merge
66 update.check = noconflict
66 update.check = noconflict
67 # Show conflicts information in `hg status`
67 # Show conflicts information in `hg status`
68 status.verbose = True
68 status.verbose = True
69 # Skip the bisect state in conflicts information in `hg status`
70 status.skipstates = bisect
71
69
72 [diff]
70 [diff]
73 git = 1
71 git = 1
74 showfunc = 1
72 showfunc = 1
75 """
73 """
76
74
77 samplehgrcs = {
75 samplehgrcs = {
78 'user':
76 'user':
79 b"""# example user config (see 'hg help config' for more info)
77 b"""# example user config (see 'hg help config' for more info)
80 [ui]
78 [ui]
81 # name and email, e.g.
79 # name and email, e.g.
82 # username = Jane Doe <jdoe@example.com>
80 # username = Jane Doe <jdoe@example.com>
83 username =
81 username =
84
82
85 # We recommend enabling tweakdefaults to get slight improvements to
83 # We recommend enabling tweakdefaults to get slight improvements to
86 # the UI over time. Make sure to set HGPLAIN in the environment when
84 # the UI over time. Make sure to set HGPLAIN in the environment when
87 # writing scripts!
85 # writing scripts!
88 # tweakdefaults = True
86 # tweakdefaults = True
89
87
90 # uncomment to disable color in command output
88 # uncomment to disable color in command output
91 # (see 'hg help color' for details)
89 # (see 'hg help color' for details)
92 # color = never
90 # color = never
93
91
94 # uncomment to disable command output pagination
92 # uncomment to disable command output pagination
95 # (see 'hg help pager' for details)
93 # (see 'hg help pager' for details)
96 # paginate = never
94 # paginate = never
97
95
98 [extensions]
96 [extensions]
99 # uncomment these lines to enable some popular extensions
97 # uncomment these lines to enable some popular extensions
100 # (see 'hg help extensions' for more info)
98 # (see 'hg help extensions' for more info)
101 #
99 #
102 # churn =
100 # churn =
103 """,
101 """,
104
102
105 'cloned':
103 'cloned':
106 b"""# example repository config (see 'hg help config' for more info)
104 b"""# example repository config (see 'hg help config' for more info)
107 [paths]
105 [paths]
108 default = %s
106 default = %s
109
107
110 # path aliases to other clones of this repo in URLs or filesystem paths
108 # path aliases to other clones of this repo in URLs or filesystem paths
111 # (see 'hg help config.paths' for more info)
109 # (see 'hg help config.paths' for more info)
112 #
110 #
113 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
111 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
114 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
112 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
115 # my-clone = /home/jdoe/jdoes-clone
113 # my-clone = /home/jdoe/jdoes-clone
116
114
117 [ui]
115 [ui]
118 # name and email (local to this repository, optional), e.g.
116 # name and email (local to this repository, optional), e.g.
119 # username = Jane Doe <jdoe@example.com>
117 # username = Jane Doe <jdoe@example.com>
120 """,
118 """,
121
119
122 'local':
120 'local':
123 b"""# example repository config (see 'hg help config' for more info)
121 b"""# example repository config (see 'hg help config' for more info)
124 [paths]
122 [paths]
125 # path aliases to other clones of this repo in URLs or filesystem paths
123 # path aliases to other clones of this repo in URLs or filesystem paths
126 # (see 'hg help config.paths' for more info)
124 # (see 'hg help config.paths' for more info)
127 #
125 #
128 # default = http://example.com/hg/example-repo
126 # default = http://example.com/hg/example-repo
129 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
127 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
130 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
128 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
131 # my-clone = /home/jdoe/jdoes-clone
129 # my-clone = /home/jdoe/jdoes-clone
132
130
133 [ui]
131 [ui]
134 # name and email (local to this repository, optional), e.g.
132 # name and email (local to this repository, optional), e.g.
135 # username = Jane Doe <jdoe@example.com>
133 # username = Jane Doe <jdoe@example.com>
136 """,
134 """,
137
135
138 'global':
136 'global':
139 b"""# example system-wide hg config (see 'hg help config' for more info)
137 b"""# example system-wide hg config (see 'hg help config' for more info)
140
138
141 [ui]
139 [ui]
142 # uncomment to disable color in command output
140 # uncomment to disable color in command output
143 # (see 'hg help color' for details)
141 # (see 'hg help color' for details)
144 # color = never
142 # color = never
145
143
146 # uncomment to disable command output pagination
144 # uncomment to disable command output pagination
147 # (see 'hg help pager' for details)
145 # (see 'hg help pager' for details)
148 # paginate = never
146 # paginate = never
149
147
150 [extensions]
148 [extensions]
151 # uncomment these lines to enable some popular extensions
149 # uncomment these lines to enable some popular extensions
152 # (see 'hg help extensions' for more info)
150 # (see 'hg help extensions' for more info)
153 #
151 #
154 # blackbox =
152 # blackbox =
155 # churn =
153 # churn =
156 """,
154 """,
157 }
155 }
158
156
159 def _maybestrurl(maybebytes):
157 def _maybestrurl(maybebytes):
160 return util.rapply(pycompat.strurl, maybebytes)
158 return util.rapply(pycompat.strurl, maybebytes)
161
159
162 def _maybebytesurl(maybestr):
160 def _maybebytesurl(maybestr):
163 return util.rapply(pycompat.bytesurl, maybestr)
161 return util.rapply(pycompat.bytesurl, maybestr)
164
162
165 class httppasswordmgrdbproxy(object):
163 class httppasswordmgrdbproxy(object):
166 """Delays loading urllib2 until it's needed."""
164 """Delays loading urllib2 until it's needed."""
167 def __init__(self):
165 def __init__(self):
168 self._mgr = None
166 self._mgr = None
169
167
170 def _get_mgr(self):
168 def _get_mgr(self):
171 if self._mgr is None:
169 if self._mgr is None:
172 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
170 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
173 return self._mgr
171 return self._mgr
174
172
175 def add_password(self, realm, uris, user, passwd):
173 def add_password(self, realm, uris, user, passwd):
176 return self._get_mgr().add_password(
174 return self._get_mgr().add_password(
177 _maybestrurl(realm), _maybestrurl(uris),
175 _maybestrurl(realm), _maybestrurl(uris),
178 _maybestrurl(user), _maybestrurl(passwd))
176 _maybestrurl(user), _maybestrurl(passwd))
179
177
180 def find_user_password(self, realm, uri):
178 def find_user_password(self, realm, uri):
181 mgr = self._get_mgr()
179 mgr = self._get_mgr()
182 return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
180 return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
183 _maybestrurl(uri)))
181 _maybestrurl(uri)))
184
182
185 def _catchterm(*args):
183 def _catchterm(*args):
186 raise error.SignalInterrupt
184 raise error.SignalInterrupt
187
185
188 # unique object used to detect no default value has been provided when
186 # unique object used to detect no default value has been provided when
189 # retrieving configuration value.
187 # retrieving configuration value.
190 _unset = object()
188 _unset = object()
191
189
192 # _reqexithandlers: callbacks run at the end of a request
190 # _reqexithandlers: callbacks run at the end of a request
193 _reqexithandlers = []
191 _reqexithandlers = []
194
192
195 class ui(object):
193 class ui(object):
196 def __init__(self, src=None):
194 def __init__(self, src=None):
197 """Create a fresh new ui object if no src given
195 """Create a fresh new ui object if no src given
198
196
199 Use uimod.ui.load() to create a ui which knows global and user configs.
197 Use uimod.ui.load() to create a ui which knows global and user configs.
200 In most cases, you should use ui.copy() to create a copy of an existing
198 In most cases, you should use ui.copy() to create a copy of an existing
201 ui object.
199 ui object.
202 """
200 """
203 # _buffers: used for temporary capture of output
201 # _buffers: used for temporary capture of output
204 self._buffers = []
202 self._buffers = []
205 # 3-tuple describing how each buffer in the stack behaves.
203 # 3-tuple describing how each buffer in the stack behaves.
206 # Values are (capture stderr, capture subprocesses, apply labels).
204 # Values are (capture stderr, capture subprocesses, apply labels).
207 self._bufferstates = []
205 self._bufferstates = []
208 # When a buffer is active, defines whether we are expanding labels.
206 # When a buffer is active, defines whether we are expanding labels.
209 # This exists to prevent an extra list lookup.
207 # This exists to prevent an extra list lookup.
210 self._bufferapplylabels = None
208 self._bufferapplylabels = None
211 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
209 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
212 self._reportuntrusted = True
210 self._reportuntrusted = True
213 self._knownconfig = configitems.coreitems
211 self._knownconfig = configitems.coreitems
214 self._ocfg = config.config() # overlay
212 self._ocfg = config.config() # overlay
215 self._tcfg = config.config() # trusted
213 self._tcfg = config.config() # trusted
216 self._ucfg = config.config() # untrusted
214 self._ucfg = config.config() # untrusted
217 self._trustusers = set()
215 self._trustusers = set()
218 self._trustgroups = set()
216 self._trustgroups = set()
219 self.callhooks = True
217 self.callhooks = True
220 # Insecure server connections requested.
218 # Insecure server connections requested.
221 self.insecureconnections = False
219 self.insecureconnections = False
222 # Blocked time
220 # Blocked time
223 self.logblockedtimes = False
221 self.logblockedtimes = False
224 # color mode: see mercurial/color.py for possible value
222 # color mode: see mercurial/color.py for possible value
225 self._colormode = None
223 self._colormode = None
226 self._terminfoparams = {}
224 self._terminfoparams = {}
227 self._styles = {}
225 self._styles = {}
228
226
229 if src:
227 if src:
230 self.fout = src.fout
228 self.fout = src.fout
231 self.ferr = src.ferr
229 self.ferr = src.ferr
232 self.fin = src.fin
230 self.fin = src.fin
233 self.pageractive = src.pageractive
231 self.pageractive = src.pageractive
234 self._disablepager = src._disablepager
232 self._disablepager = src._disablepager
235 self._tweaked = src._tweaked
233 self._tweaked = src._tweaked
236
234
237 self._tcfg = src._tcfg.copy()
235 self._tcfg = src._tcfg.copy()
238 self._ucfg = src._ucfg.copy()
236 self._ucfg = src._ucfg.copy()
239 self._ocfg = src._ocfg.copy()
237 self._ocfg = src._ocfg.copy()
240 self._trustusers = src._trustusers.copy()
238 self._trustusers = src._trustusers.copy()
241 self._trustgroups = src._trustgroups.copy()
239 self._trustgroups = src._trustgroups.copy()
242 self.environ = src.environ
240 self.environ = src.environ
243 self.callhooks = src.callhooks
241 self.callhooks = src.callhooks
244 self.insecureconnections = src.insecureconnections
242 self.insecureconnections = src.insecureconnections
245 self._colormode = src._colormode
243 self._colormode = src._colormode
246 self._terminfoparams = src._terminfoparams.copy()
244 self._terminfoparams = src._terminfoparams.copy()
247 self._styles = src._styles.copy()
245 self._styles = src._styles.copy()
248
246
249 self.fixconfig()
247 self.fixconfig()
250
248
251 self.httppasswordmgrdb = src.httppasswordmgrdb
249 self.httppasswordmgrdb = src.httppasswordmgrdb
252 self._blockedtimes = src._blockedtimes
250 self._blockedtimes = src._blockedtimes
253 else:
251 else:
254 self.fout = procutil.stdout
252 self.fout = procutil.stdout
255 self.ferr = procutil.stderr
253 self.ferr = procutil.stderr
256 self.fin = procutil.stdin
254 self.fin = procutil.stdin
257 self.pageractive = False
255 self.pageractive = False
258 self._disablepager = False
256 self._disablepager = False
259 self._tweaked = False
257 self._tweaked = False
260
258
261 # shared read-only environment
259 # shared read-only environment
262 self.environ = encoding.environ
260 self.environ = encoding.environ
263
261
264 self.httppasswordmgrdb = httppasswordmgrdbproxy()
262 self.httppasswordmgrdb = httppasswordmgrdbproxy()
265 self._blockedtimes = collections.defaultdict(int)
263 self._blockedtimes = collections.defaultdict(int)
266
264
267 allowed = self.configlist('experimental', 'exportableenviron')
265 allowed = self.configlist('experimental', 'exportableenviron')
268 if '*' in allowed:
266 if '*' in allowed:
269 self._exportableenviron = self.environ
267 self._exportableenviron = self.environ
270 else:
268 else:
271 self._exportableenviron = {}
269 self._exportableenviron = {}
272 for k in allowed:
270 for k in allowed:
273 if k in self.environ:
271 if k in self.environ:
274 self._exportableenviron[k] = self.environ[k]
272 self._exportableenviron[k] = self.environ[k]
275
273
276 @classmethod
274 @classmethod
277 def load(cls):
275 def load(cls):
278 """Create a ui and load global and user configs"""
276 """Create a ui and load global and user configs"""
279 u = cls()
277 u = cls()
280 # we always trust global config files and environment variables
278 # we always trust global config files and environment variables
281 for t, f in rcutil.rccomponents():
279 for t, f in rcutil.rccomponents():
282 if t == 'path':
280 if t == 'path':
283 u.readconfig(f, trust=True)
281 u.readconfig(f, trust=True)
284 elif t == 'items':
282 elif t == 'items':
285 sections = set()
283 sections = set()
286 for section, name, value, source in f:
284 for section, name, value, source in f:
287 # do not set u._ocfg
285 # do not set u._ocfg
288 # XXX clean this up once immutable config object is a thing
286 # XXX clean this up once immutable config object is a thing
289 u._tcfg.set(section, name, value, source)
287 u._tcfg.set(section, name, value, source)
290 u._ucfg.set(section, name, value, source)
288 u._ucfg.set(section, name, value, source)
291 sections.add(section)
289 sections.add(section)
292 for section in sections:
290 for section in sections:
293 u.fixconfig(section=section)
291 u.fixconfig(section=section)
294 else:
292 else:
295 raise error.ProgrammingError('unknown rctype: %s' % t)
293 raise error.ProgrammingError('unknown rctype: %s' % t)
296 u._maybetweakdefaults()
294 u._maybetweakdefaults()
297 return u
295 return u
298
296
299 def _maybetweakdefaults(self):
297 def _maybetweakdefaults(self):
300 if not self.configbool('ui', 'tweakdefaults'):
298 if not self.configbool('ui', 'tweakdefaults'):
301 return
299 return
302 if self._tweaked or self.plain('tweakdefaults'):
300 if self._tweaked or self.plain('tweakdefaults'):
303 return
301 return
304
302
305 # Note: it is SUPER IMPORTANT that you set self._tweaked to
303 # Note: it is SUPER IMPORTANT that you set self._tweaked to
306 # True *before* any calls to setconfig(), otherwise you'll get
304 # True *before* any calls to setconfig(), otherwise you'll get
307 # infinite recursion between setconfig and this method.
305 # infinite recursion between setconfig and this method.
308 #
306 #
309 # TODO: We should extract an inner method in setconfig() to
307 # TODO: We should extract an inner method in setconfig() to
310 # avoid this weirdness.
308 # avoid this weirdness.
311 self._tweaked = True
309 self._tweaked = True
312 tmpcfg = config.config()
310 tmpcfg = config.config()
313 tmpcfg.parse('<tweakdefaults>', tweakrc)
311 tmpcfg.parse('<tweakdefaults>', tweakrc)
314 for section in tmpcfg:
312 for section in tmpcfg:
315 for name, value in tmpcfg.items(section):
313 for name, value in tmpcfg.items(section):
316 if not self.hasconfig(section, name):
314 if not self.hasconfig(section, name):
317 self.setconfig(section, name, value, "<tweakdefaults>")
315 self.setconfig(section, name, value, "<tweakdefaults>")
318
316
319 def copy(self):
317 def copy(self):
320 return self.__class__(self)
318 return self.__class__(self)
321
319
322 def resetstate(self):
320 def resetstate(self):
323 """Clear internal state that shouldn't persist across commands"""
321 """Clear internal state that shouldn't persist across commands"""
324 if self._progbar:
322 if self._progbar:
325 self._progbar.resetstate() # reset last-print time of progress bar
323 self._progbar.resetstate() # reset last-print time of progress bar
326 self.httppasswordmgrdb = httppasswordmgrdbproxy()
324 self.httppasswordmgrdb = httppasswordmgrdbproxy()
327
325
328 @contextlib.contextmanager
326 @contextlib.contextmanager
329 def timeblockedsection(self, key):
327 def timeblockedsection(self, key):
330 # this is open-coded below - search for timeblockedsection to find them
328 # this is open-coded below - search for timeblockedsection to find them
331 starttime = util.timer()
329 starttime = util.timer()
332 try:
330 try:
333 yield
331 yield
334 finally:
332 finally:
335 self._blockedtimes[key + '_blocked'] += \
333 self._blockedtimes[key + '_blocked'] += \
336 (util.timer() - starttime) * 1000
334 (util.timer() - starttime) * 1000
337
335
338 def formatter(self, topic, opts):
336 def formatter(self, topic, opts):
339 return formatter.formatter(self, self, topic, opts)
337 return formatter.formatter(self, self, topic, opts)
340
338
341 def _trusted(self, fp, f):
339 def _trusted(self, fp, f):
342 st = util.fstat(fp)
340 st = util.fstat(fp)
343 if util.isowner(st):
341 if util.isowner(st):
344 return True
342 return True
345
343
346 tusers, tgroups = self._trustusers, self._trustgroups
344 tusers, tgroups = self._trustusers, self._trustgroups
347 if '*' in tusers or '*' in tgroups:
345 if '*' in tusers or '*' in tgroups:
348 return True
346 return True
349
347
350 user = util.username(st.st_uid)
348 user = util.username(st.st_uid)
351 group = util.groupname(st.st_gid)
349 group = util.groupname(st.st_gid)
352 if user in tusers or group in tgroups or user == util.username():
350 if user in tusers or group in tgroups or user == util.username():
353 return True
351 return True
354
352
355 if self._reportuntrusted:
353 if self._reportuntrusted:
356 self.warn(_('not trusting file %s from untrusted '
354 self.warn(_('not trusting file %s from untrusted '
357 'user %s, group %s\n') % (f, user, group))
355 'user %s, group %s\n') % (f, user, group))
358 return False
356 return False
359
357
360 def readconfig(self, filename, root=None, trust=False,
358 def readconfig(self, filename, root=None, trust=False,
361 sections=None, remap=None):
359 sections=None, remap=None):
362 try:
360 try:
363 fp = open(filename, u'rb')
361 fp = open(filename, u'rb')
364 except IOError:
362 except IOError:
365 if not sections: # ignore unless we were looking for something
363 if not sections: # ignore unless we were looking for something
366 return
364 return
367 raise
365 raise
368
366
369 cfg = config.config()
367 cfg = config.config()
370 trusted = sections or trust or self._trusted(fp, filename)
368 trusted = sections or trust or self._trusted(fp, filename)
371
369
372 try:
370 try:
373 cfg.read(filename, fp, sections=sections, remap=remap)
371 cfg.read(filename, fp, sections=sections, remap=remap)
374 fp.close()
372 fp.close()
375 except error.ConfigError as inst:
373 except error.ConfigError as inst:
376 if trusted:
374 if trusted:
377 raise
375 raise
378 self.warn(_("ignored: %s\n") % stringutil.forcebytestr(inst))
376 self.warn(_("ignored: %s\n") % stringutil.forcebytestr(inst))
379
377
380 if self.plain():
378 if self.plain():
381 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
379 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
382 'logtemplate', 'statuscopies', 'style',
380 'logtemplate', 'statuscopies', 'style',
383 'traceback', 'verbose'):
381 'traceback', 'verbose'):
384 if k in cfg['ui']:
382 if k in cfg['ui']:
385 del cfg['ui'][k]
383 del cfg['ui'][k]
386 for k, v in cfg.items('defaults'):
384 for k, v in cfg.items('defaults'):
387 del cfg['defaults'][k]
385 del cfg['defaults'][k]
388 for k, v in cfg.items('commands'):
386 for k, v in cfg.items('commands'):
389 del cfg['commands'][k]
387 del cfg['commands'][k]
390 # Don't remove aliases from the configuration if in the exceptionlist
388 # Don't remove aliases from the configuration if in the exceptionlist
391 if self.plain('alias'):
389 if self.plain('alias'):
392 for k, v in cfg.items('alias'):
390 for k, v in cfg.items('alias'):
393 del cfg['alias'][k]
391 del cfg['alias'][k]
394 if self.plain('revsetalias'):
392 if self.plain('revsetalias'):
395 for k, v in cfg.items('revsetalias'):
393 for k, v in cfg.items('revsetalias'):
396 del cfg['revsetalias'][k]
394 del cfg['revsetalias'][k]
397 if self.plain('templatealias'):
395 if self.plain('templatealias'):
398 for k, v in cfg.items('templatealias'):
396 for k, v in cfg.items('templatealias'):
399 del cfg['templatealias'][k]
397 del cfg['templatealias'][k]
400
398
401 if trusted:
399 if trusted:
402 self._tcfg.update(cfg)
400 self._tcfg.update(cfg)
403 self._tcfg.update(self._ocfg)
401 self._tcfg.update(self._ocfg)
404 self._ucfg.update(cfg)
402 self._ucfg.update(cfg)
405 self._ucfg.update(self._ocfg)
403 self._ucfg.update(self._ocfg)
406
404
407 if root is None:
405 if root is None:
408 root = os.path.expanduser('~')
406 root = os.path.expanduser('~')
409 self.fixconfig(root=root)
407 self.fixconfig(root=root)
410
408
411 def fixconfig(self, root=None, section=None):
409 def fixconfig(self, root=None, section=None):
412 if section in (None, 'paths'):
410 if section in (None, 'paths'):
413 # expand vars and ~
411 # expand vars and ~
414 # translate paths relative to root (or home) into absolute paths
412 # translate paths relative to root (or home) into absolute paths
415 root = root or pycompat.getcwd()
413 root = root or pycompat.getcwd()
416 for c in self._tcfg, self._ucfg, self._ocfg:
414 for c in self._tcfg, self._ucfg, self._ocfg:
417 for n, p in c.items('paths'):
415 for n, p in c.items('paths'):
418 # Ignore sub-options.
416 # Ignore sub-options.
419 if ':' in n:
417 if ':' in n:
420 continue
418 continue
421 if not p:
419 if not p:
422 continue
420 continue
423 if '%%' in p:
421 if '%%' in p:
424 s = self.configsource('paths', n) or 'none'
422 s = self.configsource('paths', n) or 'none'
425 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
423 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
426 % (n, p, s))
424 % (n, p, s))
427 p = p.replace('%%', '%')
425 p = p.replace('%%', '%')
428 p = util.expandpath(p)
426 p = util.expandpath(p)
429 if not util.hasscheme(p) and not os.path.isabs(p):
427 if not util.hasscheme(p) and not os.path.isabs(p):
430 p = os.path.normpath(os.path.join(root, p))
428 p = os.path.normpath(os.path.join(root, p))
431 c.set("paths", n, p)
429 c.set("paths", n, p)
432
430
433 if section in (None, 'ui'):
431 if section in (None, 'ui'):
434 # update ui options
432 # update ui options
435 self.debugflag = self.configbool('ui', 'debug')
433 self.debugflag = self.configbool('ui', 'debug')
436 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
434 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
437 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
435 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
438 if self.verbose and self.quiet:
436 if self.verbose and self.quiet:
439 self.quiet = self.verbose = False
437 self.quiet = self.verbose = False
440 self._reportuntrusted = self.debugflag or self.configbool("ui",
438 self._reportuntrusted = self.debugflag or self.configbool("ui",
441 "report_untrusted")
439 "report_untrusted")
442 self.tracebackflag = self.configbool('ui', 'traceback')
440 self.tracebackflag = self.configbool('ui', 'traceback')
443 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
441 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
444
442
445 if section in (None, 'trusted'):
443 if section in (None, 'trusted'):
446 # update trust information
444 # update trust information
447 self._trustusers.update(self.configlist('trusted', 'users'))
445 self._trustusers.update(self.configlist('trusted', 'users'))
448 self._trustgroups.update(self.configlist('trusted', 'groups'))
446 self._trustgroups.update(self.configlist('trusted', 'groups'))
449
447
450 def backupconfig(self, section, item):
448 def backupconfig(self, section, item):
451 return (self._ocfg.backup(section, item),
449 return (self._ocfg.backup(section, item),
452 self._tcfg.backup(section, item),
450 self._tcfg.backup(section, item),
453 self._ucfg.backup(section, item),)
451 self._ucfg.backup(section, item),)
454 def restoreconfig(self, data):
452 def restoreconfig(self, data):
455 self._ocfg.restore(data[0])
453 self._ocfg.restore(data[0])
456 self._tcfg.restore(data[1])
454 self._tcfg.restore(data[1])
457 self._ucfg.restore(data[2])
455 self._ucfg.restore(data[2])
458
456
459 def setconfig(self, section, name, value, source=''):
457 def setconfig(self, section, name, value, source=''):
460 for cfg in (self._ocfg, self._tcfg, self._ucfg):
458 for cfg in (self._ocfg, self._tcfg, self._ucfg):
461 cfg.set(section, name, value, source)
459 cfg.set(section, name, value, source)
462 self.fixconfig(section=section)
460 self.fixconfig(section=section)
463 self._maybetweakdefaults()
461 self._maybetweakdefaults()
464
462
465 def _data(self, untrusted):
463 def _data(self, untrusted):
466 return untrusted and self._ucfg or self._tcfg
464 return untrusted and self._ucfg or self._tcfg
467
465
468 def configsource(self, section, name, untrusted=False):
466 def configsource(self, section, name, untrusted=False):
469 return self._data(untrusted).source(section, name)
467 return self._data(untrusted).source(section, name)
470
468
471 def config(self, section, name, default=_unset, untrusted=False):
469 def config(self, section, name, default=_unset, untrusted=False):
472 """return the plain string version of a config"""
470 """return the plain string version of a config"""
473 value = self._config(section, name, default=default,
471 value = self._config(section, name, default=default,
474 untrusted=untrusted)
472 untrusted=untrusted)
475 if value is _unset:
473 if value is _unset:
476 return None
474 return None
477 return value
475 return value
478
476
479 def _config(self, section, name, default=_unset, untrusted=False):
477 def _config(self, section, name, default=_unset, untrusted=False):
480 value = itemdefault = default
478 value = itemdefault = default
481 item = self._knownconfig.get(section, {}).get(name)
479 item = self._knownconfig.get(section, {}).get(name)
482 alternates = [(section, name)]
480 alternates = [(section, name)]
483
481
484 if item is not None:
482 if item is not None:
485 alternates.extend(item.alias)
483 alternates.extend(item.alias)
486 if callable(item.default):
484 if callable(item.default):
487 itemdefault = item.default()
485 itemdefault = item.default()
488 else:
486 else:
489 itemdefault = item.default
487 itemdefault = item.default
490 else:
488 else:
491 msg = ("accessing unregistered config item: '%s.%s'")
489 msg = ("accessing unregistered config item: '%s.%s'")
492 msg %= (section, name)
490 msg %= (section, name)
493 self.develwarn(msg, 2, 'warn-config-unknown')
491 self.develwarn(msg, 2, 'warn-config-unknown')
494
492
495 if default is _unset:
493 if default is _unset:
496 if item is None:
494 if item is None:
497 value = default
495 value = default
498 elif item.default is configitems.dynamicdefault:
496 elif item.default is configitems.dynamicdefault:
499 value = None
497 value = None
500 msg = "config item requires an explicit default value: '%s.%s'"
498 msg = "config item requires an explicit default value: '%s.%s'"
501 msg %= (section, name)
499 msg %= (section, name)
502 self.develwarn(msg, 2, 'warn-config-default')
500 self.develwarn(msg, 2, 'warn-config-default')
503 else:
501 else:
504 value = itemdefault
502 value = itemdefault
505 elif (item is not None
503 elif (item is not None
506 and item.default is not configitems.dynamicdefault
504 and item.default is not configitems.dynamicdefault
507 and default != itemdefault):
505 and default != itemdefault):
508 msg = ("specifying a mismatched default value for a registered "
506 msg = ("specifying a mismatched default value for a registered "
509 "config item: '%s.%s' '%s'")
507 "config item: '%s.%s' '%s'")
510 msg %= (section, name, pycompat.bytestr(default))
508 msg %= (section, name, pycompat.bytestr(default))
511 self.develwarn(msg, 2, 'warn-config-default')
509 self.develwarn(msg, 2, 'warn-config-default')
512
510
513 for s, n in alternates:
511 for s, n in alternates:
514 candidate = self._data(untrusted).get(s, n, None)
512 candidate = self._data(untrusted).get(s, n, None)
515 if candidate is not None:
513 if candidate is not None:
516 value = candidate
514 value = candidate
517 section = s
515 section = s
518 name = n
516 name = n
519 break
517 break
520
518
521 if self.debugflag and not untrusted and self._reportuntrusted:
519 if self.debugflag and not untrusted and self._reportuntrusted:
522 for s, n in alternates:
520 for s, n in alternates:
523 uvalue = self._ucfg.get(s, n)
521 uvalue = self._ucfg.get(s, n)
524 if uvalue is not None and uvalue != value:
522 if uvalue is not None and uvalue != value:
525 self.debug("ignoring untrusted configuration option "
523 self.debug("ignoring untrusted configuration option "
526 "%s.%s = %s\n" % (s, n, uvalue))
524 "%s.%s = %s\n" % (s, n, uvalue))
527 return value
525 return value
528
526
529 def configsuboptions(self, section, name, default=_unset, untrusted=False):
527 def configsuboptions(self, section, name, default=_unset, untrusted=False):
530 """Get a config option and all sub-options.
528 """Get a config option and all sub-options.
531
529
532 Some config options have sub-options that are declared with the
530 Some config options have sub-options that are declared with the
533 format "key:opt = value". This method is used to return the main
531 format "key:opt = value". This method is used to return the main
534 option and all its declared sub-options.
532 option and all its declared sub-options.
535
533
536 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
534 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
537 is a dict of defined sub-options where keys and values are strings.
535 is a dict of defined sub-options where keys and values are strings.
538 """
536 """
539 main = self.config(section, name, default, untrusted=untrusted)
537 main = self.config(section, name, default, untrusted=untrusted)
540 data = self._data(untrusted)
538 data = self._data(untrusted)
541 sub = {}
539 sub = {}
542 prefix = '%s:' % name
540 prefix = '%s:' % name
543 for k, v in data.items(section):
541 for k, v in data.items(section):
544 if k.startswith(prefix):
542 if k.startswith(prefix):
545 sub[k[len(prefix):]] = v
543 sub[k[len(prefix):]] = v
546
544
547 if self.debugflag and not untrusted and self._reportuntrusted:
545 if self.debugflag and not untrusted and self._reportuntrusted:
548 for k, v in sub.items():
546 for k, v in sub.items():
549 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
547 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
550 if uvalue is not None and uvalue != v:
548 if uvalue is not None and uvalue != v:
551 self.debug('ignoring untrusted configuration option '
549 self.debug('ignoring untrusted configuration option '
552 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
550 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
553
551
554 return main, sub
552 return main, sub
555
553
556 def configpath(self, section, name, default=_unset, untrusted=False):
554 def configpath(self, section, name, default=_unset, untrusted=False):
557 'get a path config item, expanded relative to repo root or config file'
555 'get a path config item, expanded relative to repo root or config file'
558 v = self.config(section, name, default, untrusted)
556 v = self.config(section, name, default, untrusted)
559 if v is None:
557 if v is None:
560 return None
558 return None
561 if not os.path.isabs(v) or "://" not in v:
559 if not os.path.isabs(v) or "://" not in v:
562 src = self.configsource(section, name, untrusted)
560 src = self.configsource(section, name, untrusted)
563 if ':' in src:
561 if ':' in src:
564 base = os.path.dirname(src.rsplit(':')[0])
562 base = os.path.dirname(src.rsplit(':')[0])
565 v = os.path.join(base, os.path.expanduser(v))
563 v = os.path.join(base, os.path.expanduser(v))
566 return v
564 return v
567
565
568 def configbool(self, section, name, default=_unset, untrusted=False):
566 def configbool(self, section, name, default=_unset, untrusted=False):
569 """parse a configuration element as a boolean
567 """parse a configuration element as a boolean
570
568
571 >>> u = ui(); s = b'foo'
569 >>> u = ui(); s = b'foo'
572 >>> u.setconfig(s, b'true', b'yes')
570 >>> u.setconfig(s, b'true', b'yes')
573 >>> u.configbool(s, b'true')
571 >>> u.configbool(s, b'true')
574 True
572 True
575 >>> u.setconfig(s, b'false', b'no')
573 >>> u.setconfig(s, b'false', b'no')
576 >>> u.configbool(s, b'false')
574 >>> u.configbool(s, b'false')
577 False
575 False
578 >>> u.configbool(s, b'unknown')
576 >>> u.configbool(s, b'unknown')
579 False
577 False
580 >>> u.configbool(s, b'unknown', True)
578 >>> u.configbool(s, b'unknown', True)
581 True
579 True
582 >>> u.setconfig(s, b'invalid', b'somevalue')
580 >>> u.setconfig(s, b'invalid', b'somevalue')
583 >>> u.configbool(s, b'invalid')
581 >>> u.configbool(s, b'invalid')
584 Traceback (most recent call last):
582 Traceback (most recent call last):
585 ...
583 ...
586 ConfigError: foo.invalid is not a boolean ('somevalue')
584 ConfigError: foo.invalid is not a boolean ('somevalue')
587 """
585 """
588
586
589 v = self._config(section, name, default, untrusted=untrusted)
587 v = self._config(section, name, default, untrusted=untrusted)
590 if v is None:
588 if v is None:
591 return v
589 return v
592 if v is _unset:
590 if v is _unset:
593 if default is _unset:
591 if default is _unset:
594 return False
592 return False
595 return default
593 return default
596 if isinstance(v, bool):
594 if isinstance(v, bool):
597 return v
595 return v
598 b = stringutil.parsebool(v)
596 b = stringutil.parsebool(v)
599 if b is None:
597 if b is None:
600 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
598 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
601 % (section, name, v))
599 % (section, name, v))
602 return b
600 return b
603
601
604 def configwith(self, convert, section, name, default=_unset,
602 def configwith(self, convert, section, name, default=_unset,
605 desc=None, untrusted=False):
603 desc=None, untrusted=False):
606 """parse a configuration element with a conversion function
604 """parse a configuration element with a conversion function
607
605
608 >>> u = ui(); s = b'foo'
606 >>> u = ui(); s = b'foo'
609 >>> u.setconfig(s, b'float1', b'42')
607 >>> u.setconfig(s, b'float1', b'42')
610 >>> u.configwith(float, s, b'float1')
608 >>> u.configwith(float, s, b'float1')
611 42.0
609 42.0
612 >>> u.setconfig(s, b'float2', b'-4.25')
610 >>> u.setconfig(s, b'float2', b'-4.25')
613 >>> u.configwith(float, s, b'float2')
611 >>> u.configwith(float, s, b'float2')
614 -4.25
612 -4.25
615 >>> u.configwith(float, s, b'unknown', 7)
613 >>> u.configwith(float, s, b'unknown', 7)
616 7.0
614 7.0
617 >>> u.setconfig(s, b'invalid', b'somevalue')
615 >>> u.setconfig(s, b'invalid', b'somevalue')
618 >>> u.configwith(float, s, b'invalid')
616 >>> u.configwith(float, s, b'invalid')
619 Traceback (most recent call last):
617 Traceback (most recent call last):
620 ...
618 ...
621 ConfigError: foo.invalid is not a valid float ('somevalue')
619 ConfigError: foo.invalid is not a valid float ('somevalue')
622 >>> u.configwith(float, s, b'invalid', desc=b'womble')
620 >>> u.configwith(float, s, b'invalid', desc=b'womble')
623 Traceback (most recent call last):
621 Traceback (most recent call last):
624 ...
622 ...
625 ConfigError: foo.invalid is not a valid womble ('somevalue')
623 ConfigError: foo.invalid is not a valid womble ('somevalue')
626 """
624 """
627
625
628 v = self.config(section, name, default, untrusted)
626 v = self.config(section, name, default, untrusted)
629 if v is None:
627 if v is None:
630 return v # do not attempt to convert None
628 return v # do not attempt to convert None
631 try:
629 try:
632 return convert(v)
630 return convert(v)
633 except (ValueError, error.ParseError):
631 except (ValueError, error.ParseError):
634 if desc is None:
632 if desc is None:
635 desc = pycompat.sysbytes(convert.__name__)
633 desc = pycompat.sysbytes(convert.__name__)
636 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
634 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
637 % (section, name, desc, v))
635 % (section, name, desc, v))
638
636
639 def configint(self, section, name, default=_unset, untrusted=False):
637 def configint(self, section, name, default=_unset, untrusted=False):
640 """parse a configuration element as an integer
638 """parse a configuration element as an integer
641
639
642 >>> u = ui(); s = b'foo'
640 >>> u = ui(); s = b'foo'
643 >>> u.setconfig(s, b'int1', b'42')
641 >>> u.setconfig(s, b'int1', b'42')
644 >>> u.configint(s, b'int1')
642 >>> u.configint(s, b'int1')
645 42
643 42
646 >>> u.setconfig(s, b'int2', b'-42')
644 >>> u.setconfig(s, b'int2', b'-42')
647 >>> u.configint(s, b'int2')
645 >>> u.configint(s, b'int2')
648 -42
646 -42
649 >>> u.configint(s, b'unknown', 7)
647 >>> u.configint(s, b'unknown', 7)
650 7
648 7
651 >>> u.setconfig(s, b'invalid', b'somevalue')
649 >>> u.setconfig(s, b'invalid', b'somevalue')
652 >>> u.configint(s, b'invalid')
650 >>> u.configint(s, b'invalid')
653 Traceback (most recent call last):
651 Traceback (most recent call last):
654 ...
652 ...
655 ConfigError: foo.invalid is not a valid integer ('somevalue')
653 ConfigError: foo.invalid is not a valid integer ('somevalue')
656 """
654 """
657
655
658 return self.configwith(int, section, name, default, 'integer',
656 return self.configwith(int, section, name, default, 'integer',
659 untrusted)
657 untrusted)
660
658
661 def configbytes(self, section, name, default=_unset, untrusted=False):
659 def configbytes(self, section, name, default=_unset, untrusted=False):
662 """parse a configuration element as a quantity in bytes
660 """parse a configuration element as a quantity in bytes
663
661
664 Units can be specified as b (bytes), k or kb (kilobytes), m or
662 Units can be specified as b (bytes), k or kb (kilobytes), m or
665 mb (megabytes), g or gb (gigabytes).
663 mb (megabytes), g or gb (gigabytes).
666
664
667 >>> u = ui(); s = b'foo'
665 >>> u = ui(); s = b'foo'
668 >>> u.setconfig(s, b'val1', b'42')
666 >>> u.setconfig(s, b'val1', b'42')
669 >>> u.configbytes(s, b'val1')
667 >>> u.configbytes(s, b'val1')
670 42
668 42
671 >>> u.setconfig(s, b'val2', b'42.5 kb')
669 >>> u.setconfig(s, b'val2', b'42.5 kb')
672 >>> u.configbytes(s, b'val2')
670 >>> u.configbytes(s, b'val2')
673 43520
671 43520
674 >>> u.configbytes(s, b'unknown', b'7 MB')
672 >>> u.configbytes(s, b'unknown', b'7 MB')
675 7340032
673 7340032
676 >>> u.setconfig(s, b'invalid', b'somevalue')
674 >>> u.setconfig(s, b'invalid', b'somevalue')
677 >>> u.configbytes(s, b'invalid')
675 >>> u.configbytes(s, b'invalid')
678 Traceback (most recent call last):
676 Traceback (most recent call last):
679 ...
677 ...
680 ConfigError: foo.invalid is not a byte quantity ('somevalue')
678 ConfigError: foo.invalid is not a byte quantity ('somevalue')
681 """
679 """
682
680
683 value = self._config(section, name, default, untrusted)
681 value = self._config(section, name, default, untrusted)
684 if value is _unset:
682 if value is _unset:
685 if default is _unset:
683 if default is _unset:
686 default = 0
684 default = 0
687 value = default
685 value = default
688 if not isinstance(value, bytes):
686 if not isinstance(value, bytes):
689 return value
687 return value
690 try:
688 try:
691 return util.sizetoint(value)
689 return util.sizetoint(value)
692 except error.ParseError:
690 except error.ParseError:
693 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
691 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
694 % (section, name, value))
692 % (section, name, value))
695
693
696 def configlist(self, section, name, default=_unset, untrusted=False):
694 def configlist(self, section, name, default=_unset, untrusted=False):
697 """parse a configuration element as a list of comma/space separated
695 """parse a configuration element as a list of comma/space separated
698 strings
696 strings
699
697
700 >>> u = ui(); s = b'foo'
698 >>> u = ui(); s = b'foo'
701 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
699 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
702 >>> u.configlist(s, b'list1')
700 >>> u.configlist(s, b'list1')
703 ['this', 'is', 'a small', 'test']
701 ['this', 'is', 'a small', 'test']
704 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
702 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
705 >>> u.configlist(s, b'list2')
703 >>> u.configlist(s, b'list2')
706 ['this', 'is', 'a small', 'test']
704 ['this', 'is', 'a small', 'test']
707 """
705 """
708 # default is not always a list
706 # default is not always a list
709 v = self.configwith(config.parselist, section, name, default,
707 v = self.configwith(config.parselist, section, name, default,
710 'list', untrusted)
708 'list', untrusted)
711 if isinstance(v, bytes):
709 if isinstance(v, bytes):
712 return config.parselist(v)
710 return config.parselist(v)
713 elif v is None:
711 elif v is None:
714 return []
712 return []
715 return v
713 return v
716
714
717 def configdate(self, section, name, default=_unset, untrusted=False):
715 def configdate(self, section, name, default=_unset, untrusted=False):
718 """parse a configuration element as a tuple of ints
716 """parse a configuration element as a tuple of ints
719
717
720 >>> u = ui(); s = b'foo'
718 >>> u = ui(); s = b'foo'
721 >>> u.setconfig(s, b'date', b'0 0')
719 >>> u.setconfig(s, b'date', b'0 0')
722 >>> u.configdate(s, b'date')
720 >>> u.configdate(s, b'date')
723 (0, 0)
721 (0, 0)
724 """
722 """
725 if self.config(section, name, default, untrusted):
723 if self.config(section, name, default, untrusted):
726 return self.configwith(dateutil.parsedate, section, name, default,
724 return self.configwith(dateutil.parsedate, section, name, default,
727 'date', untrusted)
725 'date', untrusted)
728 if default is _unset:
726 if default is _unset:
729 return None
727 return None
730 return default
728 return default
731
729
732 def hasconfig(self, section, name, untrusted=False):
730 def hasconfig(self, section, name, untrusted=False):
733 return self._data(untrusted).hasitem(section, name)
731 return self._data(untrusted).hasitem(section, name)
734
732
735 def has_section(self, section, untrusted=False):
733 def has_section(self, section, untrusted=False):
736 '''tell whether section exists in config.'''
734 '''tell whether section exists in config.'''
737 return section in self._data(untrusted)
735 return section in self._data(untrusted)
738
736
739 def configitems(self, section, untrusted=False, ignoresub=False):
737 def configitems(self, section, untrusted=False, ignoresub=False):
740 items = self._data(untrusted).items(section)
738 items = self._data(untrusted).items(section)
741 if ignoresub:
739 if ignoresub:
742 items = [i for i in items if ':' not in i[0]]
740 items = [i for i in items if ':' not in i[0]]
743 if self.debugflag and not untrusted and self._reportuntrusted:
741 if self.debugflag and not untrusted and self._reportuntrusted:
744 for k, v in self._ucfg.items(section):
742 for k, v in self._ucfg.items(section):
745 if self._tcfg.get(section, k) != v:
743 if self._tcfg.get(section, k) != v:
746 self.debug("ignoring untrusted configuration option "
744 self.debug("ignoring untrusted configuration option "
747 "%s.%s = %s\n" % (section, k, v))
745 "%s.%s = %s\n" % (section, k, v))
748 return items
746 return items
749
747
750 def walkconfig(self, untrusted=False):
748 def walkconfig(self, untrusted=False):
751 cfg = self._data(untrusted)
749 cfg = self._data(untrusted)
752 for section in cfg.sections():
750 for section in cfg.sections():
753 for name, value in self.configitems(section, untrusted):
751 for name, value in self.configitems(section, untrusted):
754 yield section, name, value
752 yield section, name, value
755
753
756 def plain(self, feature=None):
754 def plain(self, feature=None):
757 '''is plain mode active?
755 '''is plain mode active?
758
756
759 Plain mode means that all configuration variables which affect
757 Plain mode means that all configuration variables which affect
760 the behavior and output of Mercurial should be
758 the behavior and output of Mercurial should be
761 ignored. Additionally, the output should be stable,
759 ignored. Additionally, the output should be stable,
762 reproducible and suitable for use in scripts or applications.
760 reproducible and suitable for use in scripts or applications.
763
761
764 The only way to trigger plain mode is by setting either the
762 The only way to trigger plain mode is by setting either the
765 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
763 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
766
764
767 The return value can either be
765 The return value can either be
768 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
766 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
769 - False if feature is disabled by default and not included in HGPLAIN
767 - False if feature is disabled by default and not included in HGPLAIN
770 - True otherwise
768 - True otherwise
771 '''
769 '''
772 if ('HGPLAIN' not in encoding.environ and
770 if ('HGPLAIN' not in encoding.environ and
773 'HGPLAINEXCEPT' not in encoding.environ):
771 'HGPLAINEXCEPT' not in encoding.environ):
774 return False
772 return False
775 exceptions = encoding.environ.get('HGPLAINEXCEPT',
773 exceptions = encoding.environ.get('HGPLAINEXCEPT',
776 '').strip().split(',')
774 '').strip().split(',')
777 # TODO: add support for HGPLAIN=+feature,-feature syntax
775 # TODO: add support for HGPLAIN=+feature,-feature syntax
778 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
776 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
779 exceptions.append('strictflags')
777 exceptions.append('strictflags')
780 if feature and exceptions:
778 if feature and exceptions:
781 return feature not in exceptions
779 return feature not in exceptions
782 return True
780 return True
783
781
784 def username(self, acceptempty=False):
782 def username(self, acceptempty=False):
785 """Return default username to be used in commits.
783 """Return default username to be used in commits.
786
784
787 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
785 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
788 and stop searching if one of these is set.
786 and stop searching if one of these is set.
789 If not found and acceptempty is True, returns None.
787 If not found and acceptempty is True, returns None.
790 If not found and ui.askusername is True, ask the user, else use
788 If not found and ui.askusername is True, ask the user, else use
791 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
789 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
792 If no username could be found, raise an Abort error.
790 If no username could be found, raise an Abort error.
793 """
791 """
794 user = encoding.environ.get("HGUSER")
792 user = encoding.environ.get("HGUSER")
795 if user is None:
793 if user is None:
796 user = self.config("ui", "username")
794 user = self.config("ui", "username")
797 if user is not None:
795 if user is not None:
798 user = os.path.expandvars(user)
796 user = os.path.expandvars(user)
799 if user is None:
797 if user is None:
800 user = encoding.environ.get("EMAIL")
798 user = encoding.environ.get("EMAIL")
801 if user is None and acceptempty:
799 if user is None and acceptempty:
802 return user
800 return user
803 if user is None and self.configbool("ui", "askusername"):
801 if user is None and self.configbool("ui", "askusername"):
804 user = self.prompt(_("enter a commit username:"), default=None)
802 user = self.prompt(_("enter a commit username:"), default=None)
805 if user is None and not self.interactive():
803 if user is None and not self.interactive():
806 try:
804 try:
807 user = '%s@%s' % (procutil.getuser(),
805 user = '%s@%s' % (procutil.getuser(),
808 encoding.strtolocal(socket.getfqdn()))
806 encoding.strtolocal(socket.getfqdn()))
809 self.warn(_("no username found, using '%s' instead\n") % user)
807 self.warn(_("no username found, using '%s' instead\n") % user)
810 except KeyError:
808 except KeyError:
811 pass
809 pass
812 if not user:
810 if not user:
813 raise error.Abort(_('no username supplied'),
811 raise error.Abort(_('no username supplied'),
814 hint=_("use 'hg config --edit' "
812 hint=_("use 'hg config --edit' "
815 'to set your username'))
813 'to set your username'))
816 if "\n" in user:
814 if "\n" in user:
817 raise error.Abort(_("username %r contains a newline\n")
815 raise error.Abort(_("username %r contains a newline\n")
818 % pycompat.bytestr(user))
816 % pycompat.bytestr(user))
819 return user
817 return user
820
818
821 def shortuser(self, user):
819 def shortuser(self, user):
822 """Return a short representation of a user name or email address."""
820 """Return a short representation of a user name or email address."""
823 if not self.verbose:
821 if not self.verbose:
824 user = stringutil.shortuser(user)
822 user = stringutil.shortuser(user)
825 return user
823 return user
826
824
827 def expandpath(self, loc, default=None):
825 def expandpath(self, loc, default=None):
828 """Return repository location relative to cwd or from [paths]"""
826 """Return repository location relative to cwd or from [paths]"""
829 try:
827 try:
830 p = self.paths.getpath(loc)
828 p = self.paths.getpath(loc)
831 if p:
829 if p:
832 return p.rawloc
830 return p.rawloc
833 except error.RepoError:
831 except error.RepoError:
834 pass
832 pass
835
833
836 if default:
834 if default:
837 try:
835 try:
838 p = self.paths.getpath(default)
836 p = self.paths.getpath(default)
839 if p:
837 if p:
840 return p.rawloc
838 return p.rawloc
841 except error.RepoError:
839 except error.RepoError:
842 pass
840 pass
843
841
844 return loc
842 return loc
845
843
846 @util.propertycache
844 @util.propertycache
847 def paths(self):
845 def paths(self):
848 return paths(self)
846 return paths(self)
849
847
850 def pushbuffer(self, error=False, subproc=False, labeled=False):
848 def pushbuffer(self, error=False, subproc=False, labeled=False):
851 """install a buffer to capture standard output of the ui object
849 """install a buffer to capture standard output of the ui object
852
850
853 If error is True, the error output will be captured too.
851 If error is True, the error output will be captured too.
854
852
855 If subproc is True, output from subprocesses (typically hooks) will be
853 If subproc is True, output from subprocesses (typically hooks) will be
856 captured too.
854 captured too.
857
855
858 If labeled is True, any labels associated with buffered
856 If labeled is True, any labels associated with buffered
859 output will be handled. By default, this has no effect
857 output will be handled. By default, this has no effect
860 on the output returned, but extensions and GUI tools may
858 on the output returned, but extensions and GUI tools may
861 handle this argument and returned styled output. If output
859 handle this argument and returned styled output. If output
862 is being buffered so it can be captured and parsed or
860 is being buffered so it can be captured and parsed or
863 processed, labeled should not be set to True.
861 processed, labeled should not be set to True.
864 """
862 """
865 self._buffers.append([])
863 self._buffers.append([])
866 self._bufferstates.append((error, subproc, labeled))
864 self._bufferstates.append((error, subproc, labeled))
867 self._bufferapplylabels = labeled
865 self._bufferapplylabels = labeled
868
866
869 def popbuffer(self):
867 def popbuffer(self):
870 '''pop the last buffer and return the buffered output'''
868 '''pop the last buffer and return the buffered output'''
871 self._bufferstates.pop()
869 self._bufferstates.pop()
872 if self._bufferstates:
870 if self._bufferstates:
873 self._bufferapplylabels = self._bufferstates[-1][2]
871 self._bufferapplylabels = self._bufferstates[-1][2]
874 else:
872 else:
875 self._bufferapplylabels = None
873 self._bufferapplylabels = None
876
874
877 return "".join(self._buffers.pop())
875 return "".join(self._buffers.pop())
878
876
879 def canwritewithoutlabels(self):
877 def canwritewithoutlabels(self):
880 '''check if write skips the label'''
878 '''check if write skips the label'''
881 if self._buffers and not self._bufferapplylabels:
879 if self._buffers and not self._bufferapplylabels:
882 return True
880 return True
883 return self._colormode is None
881 return self._colormode is None
884
882
885 def canbatchlabeledwrites(self):
883 def canbatchlabeledwrites(self):
886 '''check if write calls with labels are batchable'''
884 '''check if write calls with labels are batchable'''
887 # Windows color printing is special, see ``write``.
885 # Windows color printing is special, see ``write``.
888 return self._colormode != 'win32'
886 return self._colormode != 'win32'
889
887
890 def write(self, *args, **opts):
888 def write(self, *args, **opts):
891 '''write args to output
889 '''write args to output
892
890
893 By default, this method simply writes to the buffer or stdout.
891 By default, this method simply writes to the buffer or stdout.
894 Color mode can be set on the UI class to have the output decorated
892 Color mode can be set on the UI class to have the output decorated
895 with color modifier before being written to stdout.
893 with color modifier before being written to stdout.
896
894
897 The color used is controlled by an optional keyword argument, "label".
895 The color used is controlled by an optional keyword argument, "label".
898 This should be a string containing label names separated by space.
896 This should be a string containing label names separated by space.
899 Label names take the form of "topic.type". For example, ui.debug()
897 Label names take the form of "topic.type". For example, ui.debug()
900 issues a label of "ui.debug".
898 issues a label of "ui.debug".
901
899
902 When labeling output for a specific command, a label of
900 When labeling output for a specific command, a label of
903 "cmdname.type" is recommended. For example, status issues
901 "cmdname.type" is recommended. For example, status issues
904 a label of "status.modified" for modified files.
902 a label of "status.modified" for modified files.
905 '''
903 '''
906 if self._buffers:
904 if self._buffers:
907 if self._bufferapplylabels:
905 if self._bufferapplylabels:
908 label = opts.get(r'label', '')
906 label = opts.get(r'label', '')
909 self._buffers[-1].extend(self.label(a, label) for a in args)
907 self._buffers[-1].extend(self.label(a, label) for a in args)
910 else:
908 else:
911 self._buffers[-1].extend(args)
909 self._buffers[-1].extend(args)
912 else:
910 else:
913 self._writenobuf(*args, **opts)
911 self._writenobuf(*args, **opts)
914
912
915 def _writenobuf(self, *args, **opts):
913 def _writenobuf(self, *args, **opts):
916 if self._colormode == 'win32':
914 if self._colormode == 'win32':
917 # windows color printing is its own can of crab, defer to
915 # windows color printing is its own can of crab, defer to
918 # the color module and that is it.
916 # the color module and that is it.
919 color.win32print(self, self._write, *args, **opts)
917 color.win32print(self, self._write, *args, **opts)
920 else:
918 else:
921 msgs = args
919 msgs = args
922 if self._colormode is not None:
920 if self._colormode is not None:
923 label = opts.get(r'label', '')
921 label = opts.get(r'label', '')
924 msgs = [self.label(a, label) for a in args]
922 msgs = [self.label(a, label) for a in args]
925 self._write(*msgs, **opts)
923 self._write(*msgs, **opts)
926
924
927 def _write(self, *msgs, **opts):
925 def _write(self, *msgs, **opts):
928 self._progclear()
926 self._progclear()
929 # opencode timeblockedsection because this is a critical path
927 # opencode timeblockedsection because this is a critical path
930 starttime = util.timer()
928 starttime = util.timer()
931 try:
929 try:
932 self.fout.write(''.join(msgs))
930 self.fout.write(''.join(msgs))
933 except IOError as err:
931 except IOError as err:
934 raise error.StdioError(err)
932 raise error.StdioError(err)
935 finally:
933 finally:
936 self._blockedtimes['stdio_blocked'] += \
934 self._blockedtimes['stdio_blocked'] += \
937 (util.timer() - starttime) * 1000
935 (util.timer() - starttime) * 1000
938
936
939 def write_err(self, *args, **opts):
937 def write_err(self, *args, **opts):
940 self._progclear()
938 self._progclear()
941 if self._bufferstates and self._bufferstates[-1][0]:
939 if self._bufferstates and self._bufferstates[-1][0]:
942 self.write(*args, **opts)
940 self.write(*args, **opts)
943 elif self._colormode == 'win32':
941 elif self._colormode == 'win32':
944 # windows color printing is its own can of crab, defer to
942 # windows color printing is its own can of crab, defer to
945 # the color module and that is it.
943 # the color module and that is it.
946 color.win32print(self, self._write_err, *args, **opts)
944 color.win32print(self, self._write_err, *args, **opts)
947 else:
945 else:
948 msgs = args
946 msgs = args
949 if self._colormode is not None:
947 if self._colormode is not None:
950 label = opts.get(r'label', '')
948 label = opts.get(r'label', '')
951 msgs = [self.label(a, label) for a in args]
949 msgs = [self.label(a, label) for a in args]
952 self._write_err(*msgs, **opts)
950 self._write_err(*msgs, **opts)
953
951
954 def _write_err(self, *msgs, **opts):
952 def _write_err(self, *msgs, **opts):
955 try:
953 try:
956 with self.timeblockedsection('stdio'):
954 with self.timeblockedsection('stdio'):
957 if not getattr(self.fout, 'closed', False):
955 if not getattr(self.fout, 'closed', False):
958 self.fout.flush()
956 self.fout.flush()
959 for a in msgs:
957 for a in msgs:
960 self.ferr.write(a)
958 self.ferr.write(a)
961 # stderr may be buffered under win32 when redirected to files,
959 # stderr may be buffered under win32 when redirected to files,
962 # including stdout.
960 # including stdout.
963 if not getattr(self.ferr, 'closed', False):
961 if not getattr(self.ferr, 'closed', False):
964 self.ferr.flush()
962 self.ferr.flush()
965 except IOError as inst:
963 except IOError as inst:
966 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
964 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
967 raise error.StdioError(inst)
965 raise error.StdioError(inst)
968
966
969 def flush(self):
967 def flush(self):
970 # opencode timeblockedsection because this is a critical path
968 # opencode timeblockedsection because this is a critical path
971 starttime = util.timer()
969 starttime = util.timer()
972 try:
970 try:
973 try:
971 try:
974 self.fout.flush()
972 self.fout.flush()
975 except IOError as err:
973 except IOError as err:
976 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
974 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
977 raise error.StdioError(err)
975 raise error.StdioError(err)
978 finally:
976 finally:
979 try:
977 try:
980 self.ferr.flush()
978 self.ferr.flush()
981 except IOError as err:
979 except IOError as err:
982 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
980 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
983 raise error.StdioError(err)
981 raise error.StdioError(err)
984 finally:
982 finally:
985 self._blockedtimes['stdio_blocked'] += \
983 self._blockedtimes['stdio_blocked'] += \
986 (util.timer() - starttime) * 1000
984 (util.timer() - starttime) * 1000
987
985
988 def _isatty(self, fh):
986 def _isatty(self, fh):
989 if self.configbool('ui', 'nontty'):
987 if self.configbool('ui', 'nontty'):
990 return False
988 return False
991 return procutil.isatty(fh)
989 return procutil.isatty(fh)
992
990
993 def disablepager(self):
991 def disablepager(self):
994 self._disablepager = True
992 self._disablepager = True
995
993
996 def pager(self, command):
994 def pager(self, command):
997 """Start a pager for subsequent command output.
995 """Start a pager for subsequent command output.
998
996
999 Commands which produce a long stream of output should call
997 Commands which produce a long stream of output should call
1000 this function to activate the user's preferred pagination
998 this function to activate the user's preferred pagination
1001 mechanism (which may be no pager). Calling this function
999 mechanism (which may be no pager). Calling this function
1002 precludes any future use of interactive functionality, such as
1000 precludes any future use of interactive functionality, such as
1003 prompting the user or activating curses.
1001 prompting the user or activating curses.
1004
1002
1005 Args:
1003 Args:
1006 command: The full, non-aliased name of the command. That is, "log"
1004 command: The full, non-aliased name of the command. That is, "log"
1007 not "history, "summary" not "summ", etc.
1005 not "history, "summary" not "summ", etc.
1008 """
1006 """
1009 if (self._disablepager
1007 if (self._disablepager
1010 or self.pageractive):
1008 or self.pageractive):
1011 # how pager should do is already determined
1009 # how pager should do is already determined
1012 return
1010 return
1013
1011
1014 if not command.startswith('internal-always-') and (
1012 if not command.startswith('internal-always-') and (
1015 # explicit --pager=on (= 'internal-always-' prefix) should
1013 # explicit --pager=on (= 'internal-always-' prefix) should
1016 # take precedence over disabling factors below
1014 # take precedence over disabling factors below
1017 command in self.configlist('pager', 'ignore')
1015 command in self.configlist('pager', 'ignore')
1018 or not self.configbool('ui', 'paginate')
1016 or not self.configbool('ui', 'paginate')
1019 or not self.configbool('pager', 'attend-' + command, True)
1017 or not self.configbool('pager', 'attend-' + command, True)
1020 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1018 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1021 # formatted() will need some adjustment.
1019 # formatted() will need some adjustment.
1022 or not self.formatted()
1020 or not self.formatted()
1023 or self.plain()
1021 or self.plain()
1024 or self._buffers
1022 or self._buffers
1025 # TODO: expose debugger-enabled on the UI object
1023 # TODO: expose debugger-enabled on the UI object
1026 or '--debugger' in pycompat.sysargv):
1024 or '--debugger' in pycompat.sysargv):
1027 # We only want to paginate if the ui appears to be
1025 # We only want to paginate if the ui appears to be
1028 # interactive, the user didn't say HGPLAIN or
1026 # interactive, the user didn't say HGPLAIN or
1029 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1027 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1030 return
1028 return
1031
1029
1032 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1030 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1033 if not pagercmd:
1031 if not pagercmd:
1034 return
1032 return
1035
1033
1036 pagerenv = {}
1034 pagerenv = {}
1037 for name, value in rcutil.defaultpagerenv().items():
1035 for name, value in rcutil.defaultpagerenv().items():
1038 if name not in encoding.environ:
1036 if name not in encoding.environ:
1039 pagerenv[name] = value
1037 pagerenv[name] = value
1040
1038
1041 self.debug('starting pager for command %r\n' % command)
1039 self.debug('starting pager for command %r\n' % command)
1042 self.flush()
1040 self.flush()
1043
1041
1044 wasformatted = self.formatted()
1042 wasformatted = self.formatted()
1045 if util.safehasattr(signal, "SIGPIPE"):
1043 if util.safehasattr(signal, "SIGPIPE"):
1046 signal.signal(signal.SIGPIPE, _catchterm)
1044 signal.signal(signal.SIGPIPE, _catchterm)
1047 if self._runpager(pagercmd, pagerenv):
1045 if self._runpager(pagercmd, pagerenv):
1048 self.pageractive = True
1046 self.pageractive = True
1049 # Preserve the formatted-ness of the UI. This is important
1047 # Preserve the formatted-ness of the UI. This is important
1050 # because we mess with stdout, which might confuse
1048 # because we mess with stdout, which might confuse
1051 # auto-detection of things being formatted.
1049 # auto-detection of things being formatted.
1052 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1050 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1053 self.setconfig('ui', 'interactive', False, 'pager')
1051 self.setconfig('ui', 'interactive', False, 'pager')
1054
1052
1055 # If pagermode differs from color.mode, reconfigure color now that
1053 # If pagermode differs from color.mode, reconfigure color now that
1056 # pageractive is set.
1054 # pageractive is set.
1057 cm = self._colormode
1055 cm = self._colormode
1058 if cm != self.config('color', 'pagermode', cm):
1056 if cm != self.config('color', 'pagermode', cm):
1059 color.setup(self)
1057 color.setup(self)
1060 else:
1058 else:
1061 # If the pager can't be spawned in dispatch when --pager=on is
1059 # If the pager can't be spawned in dispatch when --pager=on is
1062 # given, don't try again when the command runs, to avoid a duplicate
1060 # given, don't try again when the command runs, to avoid a duplicate
1063 # warning about a missing pager command.
1061 # warning about a missing pager command.
1064 self.disablepager()
1062 self.disablepager()
1065
1063
1066 def _runpager(self, command, env=None):
1064 def _runpager(self, command, env=None):
1067 """Actually start the pager and set up file descriptors.
1065 """Actually start the pager and set up file descriptors.
1068
1066
1069 This is separate in part so that extensions (like chg) can
1067 This is separate in part so that extensions (like chg) can
1070 override how a pager is invoked.
1068 override how a pager is invoked.
1071 """
1069 """
1072 if command == 'cat':
1070 if command == 'cat':
1073 # Save ourselves some work.
1071 # Save ourselves some work.
1074 return False
1072 return False
1075 # If the command doesn't contain any of these characters, we
1073 # If the command doesn't contain any of these characters, we
1076 # assume it's a binary and exec it directly. This means for
1074 # assume it's a binary and exec it directly. This means for
1077 # simple pager command configurations, we can degrade
1075 # simple pager command configurations, we can degrade
1078 # gracefully and tell the user about their broken pager.
1076 # gracefully and tell the user about their broken pager.
1079 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1077 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1080
1078
1081 if pycompat.iswindows and not shell:
1079 if pycompat.iswindows and not shell:
1082 # Window's built-in `more` cannot be invoked with shell=False, but
1080 # Window's built-in `more` cannot be invoked with shell=False, but
1083 # its `more.com` can. Hide this implementation detail from the
1081 # its `more.com` can. Hide this implementation detail from the
1084 # user so we can also get sane bad PAGER behavior. MSYS has
1082 # user so we can also get sane bad PAGER behavior. MSYS has
1085 # `more.exe`, so do a cmd.exe style resolution of the executable to
1083 # `more.exe`, so do a cmd.exe style resolution of the executable to
1086 # determine which one to use.
1084 # determine which one to use.
1087 fullcmd = procutil.findexe(command)
1085 fullcmd = procutil.findexe(command)
1088 if not fullcmd:
1086 if not fullcmd:
1089 self.warn(_("missing pager command '%s', skipping pager\n")
1087 self.warn(_("missing pager command '%s', skipping pager\n")
1090 % command)
1088 % command)
1091 return False
1089 return False
1092
1090
1093 command = fullcmd
1091 command = fullcmd
1094
1092
1095 try:
1093 try:
1096 pager = subprocess.Popen(
1094 pager = subprocess.Popen(
1097 command, shell=shell, bufsize=-1,
1095 command, shell=shell, bufsize=-1,
1098 close_fds=procutil.closefds, stdin=subprocess.PIPE,
1096 close_fds=procutil.closefds, stdin=subprocess.PIPE,
1099 stdout=procutil.stdout, stderr=procutil.stderr,
1097 stdout=procutil.stdout, stderr=procutil.stderr,
1100 env=procutil.shellenviron(env))
1098 env=procutil.shellenviron(env))
1101 except OSError as e:
1099 except OSError as e:
1102 if e.errno == errno.ENOENT and not shell:
1100 if e.errno == errno.ENOENT and not shell:
1103 self.warn(_("missing pager command '%s', skipping pager\n")
1101 self.warn(_("missing pager command '%s', skipping pager\n")
1104 % command)
1102 % command)
1105 return False
1103 return False
1106 raise
1104 raise
1107
1105
1108 # back up original file descriptors
1106 # back up original file descriptors
1109 stdoutfd = os.dup(procutil.stdout.fileno())
1107 stdoutfd = os.dup(procutil.stdout.fileno())
1110 stderrfd = os.dup(procutil.stderr.fileno())
1108 stderrfd = os.dup(procutil.stderr.fileno())
1111
1109
1112 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1110 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1113 if self._isatty(procutil.stderr):
1111 if self._isatty(procutil.stderr):
1114 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1112 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1115
1113
1116 @self.atexit
1114 @self.atexit
1117 def killpager():
1115 def killpager():
1118 if util.safehasattr(signal, "SIGINT"):
1116 if util.safehasattr(signal, "SIGINT"):
1119 signal.signal(signal.SIGINT, signal.SIG_IGN)
1117 signal.signal(signal.SIGINT, signal.SIG_IGN)
1120 # restore original fds, closing pager.stdin copies in the process
1118 # restore original fds, closing pager.stdin copies in the process
1121 os.dup2(stdoutfd, procutil.stdout.fileno())
1119 os.dup2(stdoutfd, procutil.stdout.fileno())
1122 os.dup2(stderrfd, procutil.stderr.fileno())
1120 os.dup2(stderrfd, procutil.stderr.fileno())
1123 pager.stdin.close()
1121 pager.stdin.close()
1124 pager.wait()
1122 pager.wait()
1125
1123
1126 return True
1124 return True
1127
1125
1128 @property
1126 @property
1129 def _exithandlers(self):
1127 def _exithandlers(self):
1130 return _reqexithandlers
1128 return _reqexithandlers
1131
1129
1132 def atexit(self, func, *args, **kwargs):
1130 def atexit(self, func, *args, **kwargs):
1133 '''register a function to run after dispatching a request
1131 '''register a function to run after dispatching a request
1134
1132
1135 Handlers do not stay registered across request boundaries.'''
1133 Handlers do not stay registered across request boundaries.'''
1136 self._exithandlers.append((func, args, kwargs))
1134 self._exithandlers.append((func, args, kwargs))
1137 return func
1135 return func
1138
1136
1139 def interface(self, feature):
1137 def interface(self, feature):
1140 """what interface to use for interactive console features?
1138 """what interface to use for interactive console features?
1141
1139
1142 The interface is controlled by the value of `ui.interface` but also by
1140 The interface is controlled by the value of `ui.interface` but also by
1143 the value of feature-specific configuration. For example:
1141 the value of feature-specific configuration. For example:
1144
1142
1145 ui.interface.histedit = text
1143 ui.interface.histedit = text
1146 ui.interface.chunkselector = curses
1144 ui.interface.chunkselector = curses
1147
1145
1148 Here the features are "histedit" and "chunkselector".
1146 Here the features are "histedit" and "chunkselector".
1149
1147
1150 The configuration above means that the default interfaces for commands
1148 The configuration above means that the default interfaces for commands
1151 is curses, the interface for histedit is text and the interface for
1149 is curses, the interface for histedit is text and the interface for
1152 selecting chunk is crecord (the best curses interface available).
1150 selecting chunk is crecord (the best curses interface available).
1153
1151
1154 Consider the following example:
1152 Consider the following example:
1155 ui.interface = curses
1153 ui.interface = curses
1156 ui.interface.histedit = text
1154 ui.interface.histedit = text
1157
1155
1158 Then histedit will use the text interface and chunkselector will use
1156 Then histedit will use the text interface and chunkselector will use
1159 the default curses interface (crecord at the moment).
1157 the default curses interface (crecord at the moment).
1160 """
1158 """
1161 alldefaults = frozenset(["text", "curses"])
1159 alldefaults = frozenset(["text", "curses"])
1162
1160
1163 featureinterfaces = {
1161 featureinterfaces = {
1164 "chunkselector": [
1162 "chunkselector": [
1165 "text",
1163 "text",
1166 "curses",
1164 "curses",
1167 ]
1165 ]
1168 }
1166 }
1169
1167
1170 # Feature-specific interface
1168 # Feature-specific interface
1171 if feature not in featureinterfaces.keys():
1169 if feature not in featureinterfaces.keys():
1172 # Programming error, not user error
1170 # Programming error, not user error
1173 raise ValueError("Unknown feature requested %s" % feature)
1171 raise ValueError("Unknown feature requested %s" % feature)
1174
1172
1175 availableinterfaces = frozenset(featureinterfaces[feature])
1173 availableinterfaces = frozenset(featureinterfaces[feature])
1176 if alldefaults > availableinterfaces:
1174 if alldefaults > availableinterfaces:
1177 # Programming error, not user error. We need a use case to
1175 # Programming error, not user error. We need a use case to
1178 # define the right thing to do here.
1176 # define the right thing to do here.
1179 raise ValueError(
1177 raise ValueError(
1180 "Feature %s does not handle all default interfaces" %
1178 "Feature %s does not handle all default interfaces" %
1181 feature)
1179 feature)
1182
1180
1183 if self.plain():
1181 if self.plain():
1184 return "text"
1182 return "text"
1185
1183
1186 # Default interface for all the features
1184 # Default interface for all the features
1187 defaultinterface = "text"
1185 defaultinterface = "text"
1188 i = self.config("ui", "interface")
1186 i = self.config("ui", "interface")
1189 if i in alldefaults:
1187 if i in alldefaults:
1190 defaultinterface = i
1188 defaultinterface = i
1191
1189
1192 choseninterface = defaultinterface
1190 choseninterface = defaultinterface
1193 f = self.config("ui", "interface.%s" % feature)
1191 f = self.config("ui", "interface.%s" % feature)
1194 if f in availableinterfaces:
1192 if f in availableinterfaces:
1195 choseninterface = f
1193 choseninterface = f
1196
1194
1197 if i is not None and defaultinterface != i:
1195 if i is not None and defaultinterface != i:
1198 if f is not None:
1196 if f is not None:
1199 self.warn(_("invalid value for ui.interface: %s\n") %
1197 self.warn(_("invalid value for ui.interface: %s\n") %
1200 (i,))
1198 (i,))
1201 else:
1199 else:
1202 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1200 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1203 (i, choseninterface))
1201 (i, choseninterface))
1204 if f is not None and choseninterface != f:
1202 if f is not None and choseninterface != f:
1205 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1203 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1206 (feature, f, choseninterface))
1204 (feature, f, choseninterface))
1207
1205
1208 return choseninterface
1206 return choseninterface
1209
1207
1210 def interactive(self):
1208 def interactive(self):
1211 '''is interactive input allowed?
1209 '''is interactive input allowed?
1212
1210
1213 An interactive session is a session where input can be reasonably read
1211 An interactive session is a session where input can be reasonably read
1214 from `sys.stdin'. If this function returns false, any attempt to read
1212 from `sys.stdin'. If this function returns false, any attempt to read
1215 from stdin should fail with an error, unless a sensible default has been
1213 from stdin should fail with an error, unless a sensible default has been
1216 specified.
1214 specified.
1217
1215
1218 Interactiveness is triggered by the value of the `ui.interactive'
1216 Interactiveness is triggered by the value of the `ui.interactive'
1219 configuration variable or - if it is unset - when `sys.stdin' points
1217 configuration variable or - if it is unset - when `sys.stdin' points
1220 to a terminal device.
1218 to a terminal device.
1221
1219
1222 This function refers to input only; for output, see `ui.formatted()'.
1220 This function refers to input only; for output, see `ui.formatted()'.
1223 '''
1221 '''
1224 i = self.configbool("ui", "interactive")
1222 i = self.configbool("ui", "interactive")
1225 if i is None:
1223 if i is None:
1226 # some environments replace stdin without implementing isatty
1224 # some environments replace stdin without implementing isatty
1227 # usually those are non-interactive
1225 # usually those are non-interactive
1228 return self._isatty(self.fin)
1226 return self._isatty(self.fin)
1229
1227
1230 return i
1228 return i
1231
1229
1232 def termwidth(self):
1230 def termwidth(self):
1233 '''how wide is the terminal in columns?
1231 '''how wide is the terminal in columns?
1234 '''
1232 '''
1235 if 'COLUMNS' in encoding.environ:
1233 if 'COLUMNS' in encoding.environ:
1236 try:
1234 try:
1237 return int(encoding.environ['COLUMNS'])
1235 return int(encoding.environ['COLUMNS'])
1238 except ValueError:
1236 except ValueError:
1239 pass
1237 pass
1240 return scmutil.termsize(self)[0]
1238 return scmutil.termsize(self)[0]
1241
1239
1242 def formatted(self):
1240 def formatted(self):
1243 '''should formatted output be used?
1241 '''should formatted output be used?
1244
1242
1245 It is often desirable to format the output to suite the output medium.
1243 It is often desirable to format the output to suite the output medium.
1246 Examples of this are truncating long lines or colorizing messages.
1244 Examples of this are truncating long lines or colorizing messages.
1247 However, this is not often not desirable when piping output into other
1245 However, this is not often not desirable when piping output into other
1248 utilities, e.g. `grep'.
1246 utilities, e.g. `grep'.
1249
1247
1250 Formatted output is triggered by the value of the `ui.formatted'
1248 Formatted output is triggered by the value of the `ui.formatted'
1251 configuration variable or - if it is unset - when `sys.stdout' points
1249 configuration variable or - if it is unset - when `sys.stdout' points
1252 to a terminal device. Please note that `ui.formatted' should be
1250 to a terminal device. Please note that `ui.formatted' should be
1253 considered an implementation detail; it is not intended for use outside
1251 considered an implementation detail; it is not intended for use outside
1254 Mercurial or its extensions.
1252 Mercurial or its extensions.
1255
1253
1256 This function refers to output only; for input, see `ui.interactive()'.
1254 This function refers to output only; for input, see `ui.interactive()'.
1257 This function always returns false when in plain mode, see `ui.plain()'.
1255 This function always returns false when in plain mode, see `ui.plain()'.
1258 '''
1256 '''
1259 if self.plain():
1257 if self.plain():
1260 return False
1258 return False
1261
1259
1262 i = self.configbool("ui", "formatted")
1260 i = self.configbool("ui", "formatted")
1263 if i is None:
1261 if i is None:
1264 # some environments replace stdout without implementing isatty
1262 # some environments replace stdout without implementing isatty
1265 # usually those are non-interactive
1263 # usually those are non-interactive
1266 return self._isatty(self.fout)
1264 return self._isatty(self.fout)
1267
1265
1268 return i
1266 return i
1269
1267
1270 def _readline(self):
1268 def _readline(self):
1271 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1269 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1272 # because they have to be text streams with *no buffering*. Instead,
1270 # because they have to be text streams with *no buffering*. Instead,
1273 # we use rawinput() only if call_readline() will be invoked by
1271 # we use rawinput() only if call_readline() will be invoked by
1274 # PyOS_Readline(), so no I/O will be made at Python layer.
1272 # PyOS_Readline(), so no I/O will be made at Python layer.
1275 usereadline = (self._isatty(self.fin) and self._isatty(self.fout)
1273 usereadline = (self._isatty(self.fin) and self._isatty(self.fout)
1276 and procutil.isstdin(self.fin)
1274 and procutil.isstdin(self.fin)
1277 and procutil.isstdout(self.fout))
1275 and procutil.isstdout(self.fout))
1278 if usereadline:
1276 if usereadline:
1279 try:
1277 try:
1280 # magically add command line editing support, where
1278 # magically add command line editing support, where
1281 # available
1279 # available
1282 import readline
1280 import readline
1283 # force demandimport to really load the module
1281 # force demandimport to really load the module
1284 readline.read_history_file
1282 readline.read_history_file
1285 # windows sometimes raises something other than ImportError
1283 # windows sometimes raises something other than ImportError
1286 except Exception:
1284 except Exception:
1287 usereadline = False
1285 usereadline = False
1288
1286
1289 # prompt ' ' must exist; otherwise readline may delete entire line
1287 # prompt ' ' must exist; otherwise readline may delete entire line
1290 # - http://bugs.python.org/issue12833
1288 # - http://bugs.python.org/issue12833
1291 with self.timeblockedsection('stdio'):
1289 with self.timeblockedsection('stdio'):
1292 if usereadline:
1290 if usereadline:
1293 line = encoding.strtolocal(pycompat.rawinput(r' '))
1291 line = encoding.strtolocal(pycompat.rawinput(r' '))
1294 # When stdin is in binary mode on Windows, it can cause
1292 # When stdin is in binary mode on Windows, it can cause
1295 # raw_input() to emit an extra trailing carriage return
1293 # raw_input() to emit an extra trailing carriage return
1296 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1294 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1297 line = line[:-1]
1295 line = line[:-1]
1298 else:
1296 else:
1299 self.fout.write(b' ')
1297 self.fout.write(b' ')
1300 self.fout.flush()
1298 self.fout.flush()
1301 line = self.fin.readline()
1299 line = self.fin.readline()
1302 if not line:
1300 if not line:
1303 raise EOFError
1301 raise EOFError
1304 line = line.rstrip(pycompat.oslinesep)
1302 line = line.rstrip(pycompat.oslinesep)
1305
1303
1306 return line
1304 return line
1307
1305
1308 def prompt(self, msg, default="y"):
1306 def prompt(self, msg, default="y"):
1309 """Prompt user with msg, read response.
1307 """Prompt user with msg, read response.
1310 If ui is not interactive, the default is returned.
1308 If ui is not interactive, the default is returned.
1311 """
1309 """
1312 if not self.interactive():
1310 if not self.interactive():
1313 self.write(msg, ' ', default or '', "\n")
1311 self.write(msg, ' ', default or '', "\n")
1314 return default
1312 return default
1315 self._writenobuf(msg, label='ui.prompt')
1313 self._writenobuf(msg, label='ui.prompt')
1316 self.flush()
1314 self.flush()
1317 try:
1315 try:
1318 r = self._readline()
1316 r = self._readline()
1319 if not r:
1317 if not r:
1320 r = default
1318 r = default
1321 if self.configbool('ui', 'promptecho'):
1319 if self.configbool('ui', 'promptecho'):
1322 self.write(r, "\n")
1320 self.write(r, "\n")
1323 return r
1321 return r
1324 except EOFError:
1322 except EOFError:
1325 raise error.ResponseExpected()
1323 raise error.ResponseExpected()
1326
1324
1327 @staticmethod
1325 @staticmethod
1328 def extractchoices(prompt):
1326 def extractchoices(prompt):
1329 """Extract prompt message and list of choices from specified prompt.
1327 """Extract prompt message and list of choices from specified prompt.
1330
1328
1331 This returns tuple "(message, choices)", and "choices" is the
1329 This returns tuple "(message, choices)", and "choices" is the
1332 list of tuple "(response character, text without &)".
1330 list of tuple "(response character, text without &)".
1333
1331
1334 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1332 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1335 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1333 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1336 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1334 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1337 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1335 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1338 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1336 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1339 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1337 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1340 """
1338 """
1341
1339
1342 # Sadly, the prompt string may have been built with a filename
1340 # Sadly, the prompt string may have been built with a filename
1343 # containing "$$" so let's try to find the first valid-looking
1341 # containing "$$" so let's try to find the first valid-looking
1344 # prompt to start parsing. Sadly, we also can't rely on
1342 # prompt to start parsing. Sadly, we also can't rely on
1345 # choices containing spaces, ASCII, or basically anything
1343 # choices containing spaces, ASCII, or basically anything
1346 # except an ampersand followed by a character.
1344 # except an ampersand followed by a character.
1347 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1345 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1348 msg = m.group(1)
1346 msg = m.group(1)
1349 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1347 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1350 def choicetuple(s):
1348 def choicetuple(s):
1351 ampidx = s.index('&')
1349 ampidx = s.index('&')
1352 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1350 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1353 return (msg, [choicetuple(s) for s in choices])
1351 return (msg, [choicetuple(s) for s in choices])
1354
1352
1355 def promptchoice(self, prompt, default=0):
1353 def promptchoice(self, prompt, default=0):
1356 """Prompt user with a message, read response, and ensure it matches
1354 """Prompt user with a message, read response, and ensure it matches
1357 one of the provided choices. The prompt is formatted as follows:
1355 one of the provided choices. The prompt is formatted as follows:
1358
1356
1359 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1357 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1360
1358
1361 The index of the choice is returned. Responses are case
1359 The index of the choice is returned. Responses are case
1362 insensitive. If ui is not interactive, the default is
1360 insensitive. If ui is not interactive, the default is
1363 returned.
1361 returned.
1364 """
1362 """
1365
1363
1366 msg, choices = self.extractchoices(prompt)
1364 msg, choices = self.extractchoices(prompt)
1367 resps = [r for r, t in choices]
1365 resps = [r for r, t in choices]
1368 while True:
1366 while True:
1369 r = self.prompt(msg, resps[default])
1367 r = self.prompt(msg, resps[default])
1370 if r.lower() in resps:
1368 if r.lower() in resps:
1371 return resps.index(r.lower())
1369 return resps.index(r.lower())
1372 self.write(_("unrecognized response\n"))
1370 self.write(_("unrecognized response\n"))
1373
1371
1374 def getpass(self, prompt=None, default=None):
1372 def getpass(self, prompt=None, default=None):
1375 if not self.interactive():
1373 if not self.interactive():
1376 return default
1374 return default
1377 try:
1375 try:
1378 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1376 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1379 # disable getpass() only if explicitly specified. it's still valid
1377 # disable getpass() only if explicitly specified. it's still valid
1380 # to interact with tty even if fin is not a tty.
1378 # to interact with tty even if fin is not a tty.
1381 with self.timeblockedsection('stdio'):
1379 with self.timeblockedsection('stdio'):
1382 if self.configbool('ui', 'nontty'):
1380 if self.configbool('ui', 'nontty'):
1383 l = self.fin.readline()
1381 l = self.fin.readline()
1384 if not l:
1382 if not l:
1385 raise EOFError
1383 raise EOFError
1386 return l.rstrip('\n')
1384 return l.rstrip('\n')
1387 else:
1385 else:
1388 return getpass.getpass('')
1386 return getpass.getpass('')
1389 except EOFError:
1387 except EOFError:
1390 raise error.ResponseExpected()
1388 raise error.ResponseExpected()
1391 def status(self, *msg, **opts):
1389 def status(self, *msg, **opts):
1392 '''write status message to output (if ui.quiet is False)
1390 '''write status message to output (if ui.quiet is False)
1393
1391
1394 This adds an output label of "ui.status".
1392 This adds an output label of "ui.status".
1395 '''
1393 '''
1396 if not self.quiet:
1394 if not self.quiet:
1397 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1395 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1398 self.write(*msg, **opts)
1396 self.write(*msg, **opts)
1399 def warn(self, *msg, **opts):
1397 def warn(self, *msg, **opts):
1400 '''write warning message to output (stderr)
1398 '''write warning message to output (stderr)
1401
1399
1402 This adds an output label of "ui.warning".
1400 This adds an output label of "ui.warning".
1403 '''
1401 '''
1404 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1402 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1405 self.write_err(*msg, **opts)
1403 self.write_err(*msg, **opts)
1406 def note(self, *msg, **opts):
1404 def note(self, *msg, **opts):
1407 '''write note to output (if ui.verbose is True)
1405 '''write note to output (if ui.verbose is True)
1408
1406
1409 This adds an output label of "ui.note".
1407 This adds an output label of "ui.note".
1410 '''
1408 '''
1411 if self.verbose:
1409 if self.verbose:
1412 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1410 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1413 self.write(*msg, **opts)
1411 self.write(*msg, **opts)
1414 def debug(self, *msg, **opts):
1412 def debug(self, *msg, **opts):
1415 '''write debug message to output (if ui.debugflag is True)
1413 '''write debug message to output (if ui.debugflag is True)
1416
1414
1417 This adds an output label of "ui.debug".
1415 This adds an output label of "ui.debug".
1418 '''
1416 '''
1419 if self.debugflag:
1417 if self.debugflag:
1420 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1418 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1421 self.write(*msg, **opts)
1419 self.write(*msg, **opts)
1422
1420
1423 def edit(self, text, user, extra=None, editform=None, pending=None,
1421 def edit(self, text, user, extra=None, editform=None, pending=None,
1424 repopath=None, action=None):
1422 repopath=None, action=None):
1425 if action is None:
1423 if action is None:
1426 self.develwarn('action is None but will soon be a required '
1424 self.develwarn('action is None but will soon be a required '
1427 'parameter to ui.edit()')
1425 'parameter to ui.edit()')
1428 extra_defaults = {
1426 extra_defaults = {
1429 'prefix': 'editor',
1427 'prefix': 'editor',
1430 'suffix': '.txt',
1428 'suffix': '.txt',
1431 }
1429 }
1432 if extra is not None:
1430 if extra is not None:
1433 if extra.get('suffix') is not None:
1431 if extra.get('suffix') is not None:
1434 self.develwarn('extra.suffix is not None but will soon be '
1432 self.develwarn('extra.suffix is not None but will soon be '
1435 'ignored by ui.edit()')
1433 'ignored by ui.edit()')
1436 extra_defaults.update(extra)
1434 extra_defaults.update(extra)
1437 extra = extra_defaults
1435 extra = extra_defaults
1438
1436
1439 if action == 'diff':
1437 if action == 'diff':
1440 suffix = '.diff'
1438 suffix = '.diff'
1441 elif action:
1439 elif action:
1442 suffix = '.%s.hg.txt' % action
1440 suffix = '.%s.hg.txt' % action
1443 else:
1441 else:
1444 suffix = extra['suffix']
1442 suffix = extra['suffix']
1445
1443
1446 rdir = None
1444 rdir = None
1447 if self.configbool('experimental', 'editortmpinhg'):
1445 if self.configbool('experimental', 'editortmpinhg'):
1448 rdir = repopath
1446 rdir = repopath
1449 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1447 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1450 suffix=suffix,
1448 suffix=suffix,
1451 dir=rdir)
1449 dir=rdir)
1452 try:
1450 try:
1453 f = os.fdopen(fd, r'wb')
1451 f = os.fdopen(fd, r'wb')
1454 f.write(util.tonativeeol(text))
1452 f.write(util.tonativeeol(text))
1455 f.close()
1453 f.close()
1456
1454
1457 environ = {'HGUSER': user}
1455 environ = {'HGUSER': user}
1458 if 'transplant_source' in extra:
1456 if 'transplant_source' in extra:
1459 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1457 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1460 for label in ('intermediate-source', 'source', 'rebase_source'):
1458 for label in ('intermediate-source', 'source', 'rebase_source'):
1461 if label in extra:
1459 if label in extra:
1462 environ.update({'HGREVISION': extra[label]})
1460 environ.update({'HGREVISION': extra[label]})
1463 break
1461 break
1464 if editform:
1462 if editform:
1465 environ.update({'HGEDITFORM': editform})
1463 environ.update({'HGEDITFORM': editform})
1466 if pending:
1464 if pending:
1467 environ.update({'HG_PENDING': pending})
1465 environ.update({'HG_PENDING': pending})
1468
1466
1469 editor = self.geteditor()
1467 editor = self.geteditor()
1470
1468
1471 self.system("%s \"%s\"" % (editor, name),
1469 self.system("%s \"%s\"" % (editor, name),
1472 environ=environ,
1470 environ=environ,
1473 onerr=error.Abort, errprefix=_("edit failed"),
1471 onerr=error.Abort, errprefix=_("edit failed"),
1474 blockedtag='editor')
1472 blockedtag='editor')
1475
1473
1476 f = open(name, r'rb')
1474 f = open(name, r'rb')
1477 t = util.fromnativeeol(f.read())
1475 t = util.fromnativeeol(f.read())
1478 f.close()
1476 f.close()
1479 finally:
1477 finally:
1480 os.unlink(name)
1478 os.unlink(name)
1481
1479
1482 return t
1480 return t
1483
1481
1484 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1482 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1485 blockedtag=None):
1483 blockedtag=None):
1486 '''execute shell command with appropriate output stream. command
1484 '''execute shell command with appropriate output stream. command
1487 output will be redirected if fout is not stdout.
1485 output will be redirected if fout is not stdout.
1488
1486
1489 if command fails and onerr is None, return status, else raise onerr
1487 if command fails and onerr is None, return status, else raise onerr
1490 object as exception.
1488 object as exception.
1491 '''
1489 '''
1492 if blockedtag is None:
1490 if blockedtag is None:
1493 # Long cmds tend to be because of an absolute path on cmd. Keep
1491 # Long cmds tend to be because of an absolute path on cmd. Keep
1494 # the tail end instead
1492 # the tail end instead
1495 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1493 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1496 blockedtag = 'unknown_system_' + cmdsuffix
1494 blockedtag = 'unknown_system_' + cmdsuffix
1497 out = self.fout
1495 out = self.fout
1498 if any(s[1] for s in self._bufferstates):
1496 if any(s[1] for s in self._bufferstates):
1499 out = self
1497 out = self
1500 with self.timeblockedsection(blockedtag):
1498 with self.timeblockedsection(blockedtag):
1501 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1499 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1502 if rc and onerr:
1500 if rc and onerr:
1503 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1501 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1504 procutil.explainexit(rc))
1502 procutil.explainexit(rc))
1505 if errprefix:
1503 if errprefix:
1506 errmsg = '%s: %s' % (errprefix, errmsg)
1504 errmsg = '%s: %s' % (errprefix, errmsg)
1507 raise onerr(errmsg)
1505 raise onerr(errmsg)
1508 return rc
1506 return rc
1509
1507
1510 def _runsystem(self, cmd, environ, cwd, out):
1508 def _runsystem(self, cmd, environ, cwd, out):
1511 """actually execute the given shell command (can be overridden by
1509 """actually execute the given shell command (can be overridden by
1512 extensions like chg)"""
1510 extensions like chg)"""
1513 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1511 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1514
1512
1515 def traceback(self, exc=None, force=False):
1513 def traceback(self, exc=None, force=False):
1516 '''print exception traceback if traceback printing enabled or forced.
1514 '''print exception traceback if traceback printing enabled or forced.
1517 only to call in exception handler. returns true if traceback
1515 only to call in exception handler. returns true if traceback
1518 printed.'''
1516 printed.'''
1519 if self.tracebackflag or force:
1517 if self.tracebackflag or force:
1520 if exc is None:
1518 if exc is None:
1521 exc = sys.exc_info()
1519 exc = sys.exc_info()
1522 cause = getattr(exc[1], 'cause', None)
1520 cause = getattr(exc[1], 'cause', None)
1523
1521
1524 if cause is not None:
1522 if cause is not None:
1525 causetb = traceback.format_tb(cause[2])
1523 causetb = traceback.format_tb(cause[2])
1526 exctb = traceback.format_tb(exc[2])
1524 exctb = traceback.format_tb(exc[2])
1527 exconly = traceback.format_exception_only(cause[0], cause[1])
1525 exconly = traceback.format_exception_only(cause[0], cause[1])
1528
1526
1529 # exclude frame where 'exc' was chained and rethrown from exctb
1527 # exclude frame where 'exc' was chained and rethrown from exctb
1530 self.write_err('Traceback (most recent call last):\n',
1528 self.write_err('Traceback (most recent call last):\n',
1531 ''.join(exctb[:-1]),
1529 ''.join(exctb[:-1]),
1532 ''.join(causetb),
1530 ''.join(causetb),
1533 ''.join(exconly))
1531 ''.join(exconly))
1534 else:
1532 else:
1535 output = traceback.format_exception(exc[0], exc[1], exc[2])
1533 output = traceback.format_exception(exc[0], exc[1], exc[2])
1536 self.write_err(encoding.strtolocal(r''.join(output)))
1534 self.write_err(encoding.strtolocal(r''.join(output)))
1537 return self.tracebackflag or force
1535 return self.tracebackflag or force
1538
1536
1539 def geteditor(self):
1537 def geteditor(self):
1540 '''return editor to use'''
1538 '''return editor to use'''
1541 if pycompat.sysplatform == 'plan9':
1539 if pycompat.sysplatform == 'plan9':
1542 # vi is the MIPS instruction simulator on Plan 9. We
1540 # vi is the MIPS instruction simulator on Plan 9. We
1543 # instead default to E to plumb commit messages to
1541 # instead default to E to plumb commit messages to
1544 # avoid confusion.
1542 # avoid confusion.
1545 editor = 'E'
1543 editor = 'E'
1546 else:
1544 else:
1547 editor = 'vi'
1545 editor = 'vi'
1548 return (encoding.environ.get("HGEDITOR") or
1546 return (encoding.environ.get("HGEDITOR") or
1549 self.config("ui", "editor", editor))
1547 self.config("ui", "editor", editor))
1550
1548
1551 @util.propertycache
1549 @util.propertycache
1552 def _progbar(self):
1550 def _progbar(self):
1553 """setup the progbar singleton to the ui object"""
1551 """setup the progbar singleton to the ui object"""
1554 if (self.quiet or self.debugflag
1552 if (self.quiet or self.debugflag
1555 or self.configbool('progress', 'disable')
1553 or self.configbool('progress', 'disable')
1556 or not progress.shouldprint(self)):
1554 or not progress.shouldprint(self)):
1557 return None
1555 return None
1558 return getprogbar(self)
1556 return getprogbar(self)
1559
1557
1560 def _progclear(self):
1558 def _progclear(self):
1561 """clear progress bar output if any. use it before any output"""
1559 """clear progress bar output if any. use it before any output"""
1562 if not haveprogbar(): # nothing loaded yet
1560 if not haveprogbar(): # nothing loaded yet
1563 return
1561 return
1564 if self._progbar is not None and self._progbar.printed:
1562 if self._progbar is not None and self._progbar.printed:
1565 self._progbar.clear()
1563 self._progbar.clear()
1566
1564
1567 def progress(self, topic, pos, item="", unit="", total=None):
1565 def progress(self, topic, pos, item="", unit="", total=None):
1568 '''show a progress message
1566 '''show a progress message
1569
1567
1570 By default a textual progress bar will be displayed if an operation
1568 By default a textual progress bar will be displayed if an operation
1571 takes too long. 'topic' is the current operation, 'item' is a
1569 takes too long. 'topic' is the current operation, 'item' is a
1572 non-numeric marker of the current position (i.e. the currently
1570 non-numeric marker of the current position (i.e. the currently
1573 in-process file), 'pos' is the current numeric position (i.e.
1571 in-process file), 'pos' is the current numeric position (i.e.
1574 revision, bytes, etc.), unit is a corresponding unit label,
1572 revision, bytes, etc.), unit is a corresponding unit label,
1575 and total is the highest expected pos.
1573 and total is the highest expected pos.
1576
1574
1577 Multiple nested topics may be active at a time.
1575 Multiple nested topics may be active at a time.
1578
1576
1579 All topics should be marked closed by setting pos to None at
1577 All topics should be marked closed by setting pos to None at
1580 termination.
1578 termination.
1581 '''
1579 '''
1582 if self._progbar is not None:
1580 if self._progbar is not None:
1583 self._progbar.progress(topic, pos, item=item, unit=unit,
1581 self._progbar.progress(topic, pos, item=item, unit=unit,
1584 total=total)
1582 total=total)
1585 if pos is None or not self.configbool('progress', 'debug'):
1583 if pos is None or not self.configbool('progress', 'debug'):
1586 return
1584 return
1587
1585
1588 if unit:
1586 if unit:
1589 unit = ' ' + unit
1587 unit = ' ' + unit
1590 if item:
1588 if item:
1591 item = ' ' + item
1589 item = ' ' + item
1592
1590
1593 if total:
1591 if total:
1594 pct = 100.0 * pos / total
1592 pct = 100.0 * pos / total
1595 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1593 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1596 % (topic, item, pos, total, unit, pct))
1594 % (topic, item, pos, total, unit, pct))
1597 else:
1595 else:
1598 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1596 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1599
1597
1600 def log(self, service, *msg, **opts):
1598 def log(self, service, *msg, **opts):
1601 '''hook for logging facility extensions
1599 '''hook for logging facility extensions
1602
1600
1603 service should be a readily-identifiable subsystem, which will
1601 service should be a readily-identifiable subsystem, which will
1604 allow filtering.
1602 allow filtering.
1605
1603
1606 *msg should be a newline-terminated format string to log, and
1604 *msg should be a newline-terminated format string to log, and
1607 then any values to %-format into that format string.
1605 then any values to %-format into that format string.
1608
1606
1609 **opts currently has no defined meanings.
1607 **opts currently has no defined meanings.
1610 '''
1608 '''
1611
1609
1612 def label(self, msg, label):
1610 def label(self, msg, label):
1613 '''style msg based on supplied label
1611 '''style msg based on supplied label
1614
1612
1615 If some color mode is enabled, this will add the necessary control
1613 If some color mode is enabled, this will add the necessary control
1616 characters to apply such color. In addition, 'debug' color mode adds
1614 characters to apply such color. In addition, 'debug' color mode adds
1617 markup showing which label affects a piece of text.
1615 markup showing which label affects a piece of text.
1618
1616
1619 ui.write(s, 'label') is equivalent to
1617 ui.write(s, 'label') is equivalent to
1620 ui.write(ui.label(s, 'label')).
1618 ui.write(ui.label(s, 'label')).
1621 '''
1619 '''
1622 if self._colormode is not None:
1620 if self._colormode is not None:
1623 return color.colorlabel(self, msg, label)
1621 return color.colorlabel(self, msg, label)
1624 return msg
1622 return msg
1625
1623
1626 def develwarn(self, msg, stacklevel=1, config=None):
1624 def develwarn(self, msg, stacklevel=1, config=None):
1627 """issue a developer warning message
1625 """issue a developer warning message
1628
1626
1629 Use 'stacklevel' to report the offender some layers further up in the
1627 Use 'stacklevel' to report the offender some layers further up in the
1630 stack.
1628 stack.
1631 """
1629 """
1632 if not self.configbool('devel', 'all-warnings'):
1630 if not self.configbool('devel', 'all-warnings'):
1633 if config is None or not self.configbool('devel', config):
1631 if config is None or not self.configbool('devel', config):
1634 return
1632 return
1635 msg = 'devel-warn: ' + msg
1633 msg = 'devel-warn: ' + msg
1636 stacklevel += 1 # get in develwarn
1634 stacklevel += 1 # get in develwarn
1637 if self.tracebackflag:
1635 if self.tracebackflag:
1638 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1636 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1639 self.log('develwarn', '%s at:\n%s' %
1637 self.log('develwarn', '%s at:\n%s' %
1640 (msg, ''.join(util.getstackframes(stacklevel))))
1638 (msg, ''.join(util.getstackframes(stacklevel))))
1641 else:
1639 else:
1642 curframe = inspect.currentframe()
1640 curframe = inspect.currentframe()
1643 calframe = inspect.getouterframes(curframe, 2)
1641 calframe = inspect.getouterframes(curframe, 2)
1644 fname, lineno, fmsg = calframe[stacklevel][1:4]
1642 fname, lineno, fmsg = calframe[stacklevel][1:4]
1645 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
1643 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
1646 self.write_err('%s at: %s:%d (%s)\n'
1644 self.write_err('%s at: %s:%d (%s)\n'
1647 % (msg, fname, lineno, fmsg))
1645 % (msg, fname, lineno, fmsg))
1648 self.log('develwarn', '%s at: %s:%d (%s)\n',
1646 self.log('develwarn', '%s at: %s:%d (%s)\n',
1649 msg, fname, lineno, fmsg)
1647 msg, fname, lineno, fmsg)
1650 curframe = calframe = None # avoid cycles
1648 curframe = calframe = None # avoid cycles
1651
1649
1652 def deprecwarn(self, msg, version, stacklevel=2):
1650 def deprecwarn(self, msg, version, stacklevel=2):
1653 """issue a deprecation warning
1651 """issue a deprecation warning
1654
1652
1655 - msg: message explaining what is deprecated and how to upgrade,
1653 - msg: message explaining what is deprecated and how to upgrade,
1656 - version: last version where the API will be supported,
1654 - version: last version where the API will be supported,
1657 """
1655 """
1658 if not (self.configbool('devel', 'all-warnings')
1656 if not (self.configbool('devel', 'all-warnings')
1659 or self.configbool('devel', 'deprec-warn')):
1657 or self.configbool('devel', 'deprec-warn')):
1660 return
1658 return
1661 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1659 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1662 " update your code.)") % version
1660 " update your code.)") % version
1663 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1661 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1664
1662
1665 def exportableenviron(self):
1663 def exportableenviron(self):
1666 """The environment variables that are safe to export, e.g. through
1664 """The environment variables that are safe to export, e.g. through
1667 hgweb.
1665 hgweb.
1668 """
1666 """
1669 return self._exportableenviron
1667 return self._exportableenviron
1670
1668
1671 @contextlib.contextmanager
1669 @contextlib.contextmanager
1672 def configoverride(self, overrides, source=""):
1670 def configoverride(self, overrides, source=""):
1673 """Context manager for temporary config overrides
1671 """Context manager for temporary config overrides
1674 `overrides` must be a dict of the following structure:
1672 `overrides` must be a dict of the following structure:
1675 {(section, name) : value}"""
1673 {(section, name) : value}"""
1676 backups = {}
1674 backups = {}
1677 try:
1675 try:
1678 for (section, name), value in overrides.items():
1676 for (section, name), value in overrides.items():
1679 backups[(section, name)] = self.backupconfig(section, name)
1677 backups[(section, name)] = self.backupconfig(section, name)
1680 self.setconfig(section, name, value, source)
1678 self.setconfig(section, name, value, source)
1681 yield
1679 yield
1682 finally:
1680 finally:
1683 for __, backup in backups.items():
1681 for __, backup in backups.items():
1684 self.restoreconfig(backup)
1682 self.restoreconfig(backup)
1685 # just restoring ui.quiet config to the previous value is not enough
1683 # just restoring ui.quiet config to the previous value is not enough
1686 # as it does not update ui.quiet class member
1684 # as it does not update ui.quiet class member
1687 if ('ui', 'quiet') in overrides:
1685 if ('ui', 'quiet') in overrides:
1688 self.fixconfig(section='ui')
1686 self.fixconfig(section='ui')
1689
1687
1690 class paths(dict):
1688 class paths(dict):
1691 """Represents a collection of paths and their configs.
1689 """Represents a collection of paths and their configs.
1692
1690
1693 Data is initially derived from ui instances and the config files they have
1691 Data is initially derived from ui instances and the config files they have
1694 loaded.
1692 loaded.
1695 """
1693 """
1696 def __init__(self, ui):
1694 def __init__(self, ui):
1697 dict.__init__(self)
1695 dict.__init__(self)
1698
1696
1699 for name, loc in ui.configitems('paths', ignoresub=True):
1697 for name, loc in ui.configitems('paths', ignoresub=True):
1700 # No location is the same as not existing.
1698 # No location is the same as not existing.
1701 if not loc:
1699 if not loc:
1702 continue
1700 continue
1703 loc, sub = ui.configsuboptions('paths', name)
1701 loc, sub = ui.configsuboptions('paths', name)
1704 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1702 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1705
1703
1706 def getpath(self, name, default=None):
1704 def getpath(self, name, default=None):
1707 """Return a ``path`` from a string, falling back to default.
1705 """Return a ``path`` from a string, falling back to default.
1708
1706
1709 ``name`` can be a named path or locations. Locations are filesystem
1707 ``name`` can be a named path or locations. Locations are filesystem
1710 paths or URIs.
1708 paths or URIs.
1711
1709
1712 Returns None if ``name`` is not a registered path, a URI, or a local
1710 Returns None if ``name`` is not a registered path, a URI, or a local
1713 path to a repo.
1711 path to a repo.
1714 """
1712 """
1715 # Only fall back to default if no path was requested.
1713 # Only fall back to default if no path was requested.
1716 if name is None:
1714 if name is None:
1717 if not default:
1715 if not default:
1718 default = ()
1716 default = ()
1719 elif not isinstance(default, (tuple, list)):
1717 elif not isinstance(default, (tuple, list)):
1720 default = (default,)
1718 default = (default,)
1721 for k in default:
1719 for k in default:
1722 try:
1720 try:
1723 return self[k]
1721 return self[k]
1724 except KeyError:
1722 except KeyError:
1725 continue
1723 continue
1726 return None
1724 return None
1727
1725
1728 # Most likely empty string.
1726 # Most likely empty string.
1729 # This may need to raise in the future.
1727 # This may need to raise in the future.
1730 if not name:
1728 if not name:
1731 return None
1729 return None
1732
1730
1733 try:
1731 try:
1734 return self[name]
1732 return self[name]
1735 except KeyError:
1733 except KeyError:
1736 # Try to resolve as a local path or URI.
1734 # Try to resolve as a local path or URI.
1737 try:
1735 try:
1738 # We don't pass sub-options in, so no need to pass ui instance.
1736 # We don't pass sub-options in, so no need to pass ui instance.
1739 return path(None, None, rawloc=name)
1737 return path(None, None, rawloc=name)
1740 except ValueError:
1738 except ValueError:
1741 raise error.RepoError(_('repository %s does not exist') %
1739 raise error.RepoError(_('repository %s does not exist') %
1742 name)
1740 name)
1743
1741
1744 _pathsuboptions = {}
1742 _pathsuboptions = {}
1745
1743
1746 def pathsuboption(option, attr):
1744 def pathsuboption(option, attr):
1747 """Decorator used to declare a path sub-option.
1745 """Decorator used to declare a path sub-option.
1748
1746
1749 Arguments are the sub-option name and the attribute it should set on
1747 Arguments are the sub-option name and the attribute it should set on
1750 ``path`` instances.
1748 ``path`` instances.
1751
1749
1752 The decorated function will receive as arguments a ``ui`` instance,
1750 The decorated function will receive as arguments a ``ui`` instance,
1753 ``path`` instance, and the string value of this option from the config.
1751 ``path`` instance, and the string value of this option from the config.
1754 The function should return the value that will be set on the ``path``
1752 The function should return the value that will be set on the ``path``
1755 instance.
1753 instance.
1756
1754
1757 This decorator can be used to perform additional verification of
1755 This decorator can be used to perform additional verification of
1758 sub-options and to change the type of sub-options.
1756 sub-options and to change the type of sub-options.
1759 """
1757 """
1760 def register(func):
1758 def register(func):
1761 _pathsuboptions[option] = (attr, func)
1759 _pathsuboptions[option] = (attr, func)
1762 return func
1760 return func
1763 return register
1761 return register
1764
1762
1765 @pathsuboption('pushurl', 'pushloc')
1763 @pathsuboption('pushurl', 'pushloc')
1766 def pushurlpathoption(ui, path, value):
1764 def pushurlpathoption(ui, path, value):
1767 u = util.url(value)
1765 u = util.url(value)
1768 # Actually require a URL.
1766 # Actually require a URL.
1769 if not u.scheme:
1767 if not u.scheme:
1770 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1768 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1771 return None
1769 return None
1772
1770
1773 # Don't support the #foo syntax in the push URL to declare branch to
1771 # Don't support the #foo syntax in the push URL to declare branch to
1774 # push.
1772 # push.
1775 if u.fragment:
1773 if u.fragment:
1776 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1774 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1777 'ignoring)\n') % path.name)
1775 'ignoring)\n') % path.name)
1778 u.fragment = None
1776 u.fragment = None
1779
1777
1780 return bytes(u)
1778 return bytes(u)
1781
1779
1782 @pathsuboption('pushrev', 'pushrev')
1780 @pathsuboption('pushrev', 'pushrev')
1783 def pushrevpathoption(ui, path, value):
1781 def pushrevpathoption(ui, path, value):
1784 return value
1782 return value
1785
1783
1786 class path(object):
1784 class path(object):
1787 """Represents an individual path and its configuration."""
1785 """Represents an individual path and its configuration."""
1788
1786
1789 def __init__(self, ui, name, rawloc=None, suboptions=None):
1787 def __init__(self, ui, name, rawloc=None, suboptions=None):
1790 """Construct a path from its config options.
1788 """Construct a path from its config options.
1791
1789
1792 ``ui`` is the ``ui`` instance the path is coming from.
1790 ``ui`` is the ``ui`` instance the path is coming from.
1793 ``name`` is the symbolic name of the path.
1791 ``name`` is the symbolic name of the path.
1794 ``rawloc`` is the raw location, as defined in the config.
1792 ``rawloc`` is the raw location, as defined in the config.
1795 ``pushloc`` is the raw locations pushes should be made to.
1793 ``pushloc`` is the raw locations pushes should be made to.
1796
1794
1797 If ``name`` is not defined, we require that the location be a) a local
1795 If ``name`` is not defined, we require that the location be a) a local
1798 filesystem path with a .hg directory or b) a URL. If not,
1796 filesystem path with a .hg directory or b) a URL. If not,
1799 ``ValueError`` is raised.
1797 ``ValueError`` is raised.
1800 """
1798 """
1801 if not rawloc:
1799 if not rawloc:
1802 raise ValueError('rawloc must be defined')
1800 raise ValueError('rawloc must be defined')
1803
1801
1804 # Locations may define branches via syntax <base>#<branch>.
1802 # Locations may define branches via syntax <base>#<branch>.
1805 u = util.url(rawloc)
1803 u = util.url(rawloc)
1806 branch = None
1804 branch = None
1807 if u.fragment:
1805 if u.fragment:
1808 branch = u.fragment
1806 branch = u.fragment
1809 u.fragment = None
1807 u.fragment = None
1810
1808
1811 self.url = u
1809 self.url = u
1812 self.branch = branch
1810 self.branch = branch
1813
1811
1814 self.name = name
1812 self.name = name
1815 self.rawloc = rawloc
1813 self.rawloc = rawloc
1816 self.loc = '%s' % u
1814 self.loc = '%s' % u
1817
1815
1818 # When given a raw location but not a symbolic name, validate the
1816 # When given a raw location but not a symbolic name, validate the
1819 # location is valid.
1817 # location is valid.
1820 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1818 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1821 raise ValueError('location is not a URL or path to a local '
1819 raise ValueError('location is not a URL or path to a local '
1822 'repo: %s' % rawloc)
1820 'repo: %s' % rawloc)
1823
1821
1824 suboptions = suboptions or {}
1822 suboptions = suboptions or {}
1825
1823
1826 # Now process the sub-options. If a sub-option is registered, its
1824 # Now process the sub-options. If a sub-option is registered, its
1827 # attribute will always be present. The value will be None if there
1825 # attribute will always be present. The value will be None if there
1828 # was no valid sub-option.
1826 # was no valid sub-option.
1829 for suboption, (attr, func) in _pathsuboptions.iteritems():
1827 for suboption, (attr, func) in _pathsuboptions.iteritems():
1830 if suboption not in suboptions:
1828 if suboption not in suboptions:
1831 setattr(self, attr, None)
1829 setattr(self, attr, None)
1832 continue
1830 continue
1833
1831
1834 value = func(ui, self, suboptions[suboption])
1832 value = func(ui, self, suboptions[suboption])
1835 setattr(self, attr, value)
1833 setattr(self, attr, value)
1836
1834
1837 def _isvalidlocalpath(self, path):
1835 def _isvalidlocalpath(self, path):
1838 """Returns True if the given path is a potentially valid repository.
1836 """Returns True if the given path is a potentially valid repository.
1839 This is its own function so that extensions can change the definition of
1837 This is its own function so that extensions can change the definition of
1840 'valid' in this case (like when pulling from a git repo into a hg
1838 'valid' in this case (like when pulling from a git repo into a hg
1841 one)."""
1839 one)."""
1842 return os.path.isdir(os.path.join(path, '.hg'))
1840 return os.path.isdir(os.path.join(path, '.hg'))
1843
1841
1844 @property
1842 @property
1845 def suboptions(self):
1843 def suboptions(self):
1846 """Return sub-options and their values for this path.
1844 """Return sub-options and their values for this path.
1847
1845
1848 This is intended to be used for presentation purposes.
1846 This is intended to be used for presentation purposes.
1849 """
1847 """
1850 d = {}
1848 d = {}
1851 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1849 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1852 value = getattr(self, attr)
1850 value = getattr(self, attr)
1853 if value is not None:
1851 if value is not None:
1854 d[subopt] = value
1852 d[subopt] = value
1855 return d
1853 return d
1856
1854
1857 # we instantiate one globally shared progress bar to avoid
1855 # we instantiate one globally shared progress bar to avoid
1858 # competing progress bars when multiple UI objects get created
1856 # competing progress bars when multiple UI objects get created
1859 _progresssingleton = None
1857 _progresssingleton = None
1860
1858
1861 def getprogbar(ui):
1859 def getprogbar(ui):
1862 global _progresssingleton
1860 global _progresssingleton
1863 if _progresssingleton is None:
1861 if _progresssingleton is None:
1864 # passing 'ui' object to the singleton is fishy,
1862 # passing 'ui' object to the singleton is fishy,
1865 # this is how the extension used to work but feel free to rework it.
1863 # this is how the extension used to work but feel free to rework it.
1866 _progresssingleton = progress.progbar(ui)
1864 _progresssingleton = progress.progbar(ui)
1867 return _progresssingleton
1865 return _progresssingleton
1868
1866
1869 def haveprogbar():
1867 def haveprogbar():
1870 return _progresssingleton is not None
1868 return _progresssingleton is not None
General Comments 0
You need to be logged in to leave comments. Login now