##// END OF EJS Templates
pycompat: provide bytes os.linesep
Yuya Nishihara -
r31775:8181f378 default
parent child Browse files
Show More
@@ -1,392 +1,393 b''
1 """automatically manage newlines in repository files
1 """automatically manage newlines in repository files
2
2
3 This extension allows you to manage the type of line endings (CRLF or
3 This extension allows you to manage the type of line endings (CRLF or
4 LF) that are used in the repository and in the local working
4 LF) that are used in the repository and in the local working
5 directory. That way you can get CRLF line endings on Windows and LF on
5 directory. That way you can get CRLF line endings on Windows and LF on
6 Unix/Mac, thereby letting everybody use their OS native line endings.
6 Unix/Mac, thereby letting everybody use their OS native line endings.
7
7
8 The extension reads its configuration from a versioned ``.hgeol``
8 The extension reads its configuration from a versioned ``.hgeol``
9 configuration file found in the root of the working directory. The
9 configuration file found in the root of the working directory. The
10 ``.hgeol`` file use the same syntax as all other Mercurial
10 ``.hgeol`` file use the same syntax as all other Mercurial
11 configuration files. It uses two sections, ``[patterns]`` and
11 configuration files. It uses two sections, ``[patterns]`` and
12 ``[repository]``.
12 ``[repository]``.
13
13
14 The ``[patterns]`` section specifies how line endings should be
14 The ``[patterns]`` section specifies how line endings should be
15 converted between the working directory and the repository. The format is
15 converted between the working directory and the repository. The format is
16 specified by a file pattern. The first match is used, so put more
16 specified by a file pattern. The first match is used, so put more
17 specific patterns first. The available line endings are ``LF``,
17 specific patterns first. The available line endings are ``LF``,
18 ``CRLF``, and ``BIN``.
18 ``CRLF``, and ``BIN``.
19
19
20 Files with the declared format of ``CRLF`` or ``LF`` are always
20 Files with the declared format of ``CRLF`` or ``LF`` are always
21 checked out and stored in the repository in that format and files
21 checked out and stored in the repository in that format and files
22 declared to be binary (``BIN``) are left unchanged. Additionally,
22 declared to be binary (``BIN``) are left unchanged. Additionally,
23 ``native`` is an alias for checking out in the platform's default line
23 ``native`` is an alias for checking out in the platform's default line
24 ending: ``LF`` on Unix (including Mac OS X) and ``CRLF`` on
24 ending: ``LF`` on Unix (including Mac OS X) and ``CRLF`` on
25 Windows. Note that ``BIN`` (do nothing to line endings) is Mercurial's
25 Windows. Note that ``BIN`` (do nothing to line endings) is Mercurial's
26 default behavior; it is only needed if you need to override a later,
26 default behavior; it is only needed if you need to override a later,
27 more general pattern.
27 more general pattern.
28
28
29 The optional ``[repository]`` section specifies the line endings to
29 The optional ``[repository]`` section specifies the line endings to
30 use for files stored in the repository. It has a single setting,
30 use for files stored in the repository. It has a single setting,
31 ``native``, which determines the storage line endings for files
31 ``native``, which determines the storage line endings for files
32 declared as ``native`` in the ``[patterns]`` section. It can be set to
32 declared as ``native`` in the ``[patterns]`` section. It can be set to
33 ``LF`` or ``CRLF``. The default is ``LF``. For example, this means
33 ``LF`` or ``CRLF``. The default is ``LF``. For example, this means
34 that on Windows, files configured as ``native`` (``CRLF`` by default)
34 that on Windows, files configured as ``native`` (``CRLF`` by default)
35 will be converted to ``LF`` when stored in the repository. Files
35 will be converted to ``LF`` when stored in the repository. Files
36 declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section
36 declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section
37 are always stored as-is in the repository.
37 are always stored as-is in the repository.
38
38
39 Example versioned ``.hgeol`` file::
39 Example versioned ``.hgeol`` file::
40
40
41 [patterns]
41 [patterns]
42 **.py = native
42 **.py = native
43 **.vcproj = CRLF
43 **.vcproj = CRLF
44 **.txt = native
44 **.txt = native
45 Makefile = LF
45 Makefile = LF
46 **.jpg = BIN
46 **.jpg = BIN
47
47
48 [repository]
48 [repository]
49 native = LF
49 native = LF
50
50
51 .. note::
51 .. note::
52
52
53 The rules will first apply when files are touched in the working
53 The rules will first apply when files are touched in the working
54 directory, e.g. by updating to null and back to tip to touch all files.
54 directory, e.g. by updating to null and back to tip to touch all files.
55
55
56 The extension uses an optional ``[eol]`` section read from both the
56 The extension uses an optional ``[eol]`` section read from both the
57 normal Mercurial configuration files and the ``.hgeol`` file, with the
57 normal Mercurial configuration files and the ``.hgeol`` file, with the
58 latter overriding the former. You can use that section to control the
58 latter overriding the former. You can use that section to control the
59 overall behavior. There are three settings:
59 overall behavior. There are three settings:
60
60
61 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
61 - ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
62 ``CRLF`` to override the default interpretation of ``native`` for
62 ``CRLF`` to override the default interpretation of ``native`` for
63 checkout. This can be used with :hg:`archive` on Unix, say, to
63 checkout. This can be used with :hg:`archive` on Unix, say, to
64 generate an archive where files have line endings for Windows.
64 generate an archive where files have line endings for Windows.
65
65
66 - ``eol.only-consistent`` (default True) can be set to False to make
66 - ``eol.only-consistent`` (default True) can be set to False to make
67 the extension convert files with inconsistent EOLs. Inconsistent
67 the extension convert files with inconsistent EOLs. Inconsistent
68 means that there is both ``CRLF`` and ``LF`` present in the file.
68 means that there is both ``CRLF`` and ``LF`` present in the file.
69 Such files are normally not touched under the assumption that they
69 Such files are normally not touched under the assumption that they
70 have mixed EOLs on purpose.
70 have mixed EOLs on purpose.
71
71
72 - ``eol.fix-trailing-newline`` (default False) can be set to True to
72 - ``eol.fix-trailing-newline`` (default False) can be set to True to
73 ensure that converted files end with a EOL character (either ``\\n``
73 ensure that converted files end with a EOL character (either ``\\n``
74 or ``\\r\\n`` as per the configured patterns).
74 or ``\\r\\n`` as per the configured patterns).
75
75
76 The extension provides ``cleverencode:`` and ``cleverdecode:`` filters
76 The extension provides ``cleverencode:`` and ``cleverdecode:`` filters
77 like the deprecated win32text extension does. This means that you can
77 like the deprecated win32text extension does. This means that you can
78 disable win32text and enable eol and your filters will still work. You
78 disable win32text and enable eol and your filters will still work. You
79 only need to these filters until you have prepared a ``.hgeol`` file.
79 only need to these filters until you have prepared a ``.hgeol`` file.
80
80
81 The ``win32text.forbid*`` hooks provided by the win32text extension
81 The ``win32text.forbid*`` hooks provided by the win32text extension
82 have been unified into a single hook named ``eol.checkheadshook``. The
82 have been unified into a single hook named ``eol.checkheadshook``. The
83 hook will lookup the expected line endings from the ``.hgeol`` file,
83 hook will lookup the expected line endings from the ``.hgeol`` file,
84 which means you must migrate to a ``.hgeol`` file first before using
84 which means you must migrate to a ``.hgeol`` file first before using
85 the hook. ``eol.checkheadshook`` only checks heads, intermediate
85 the hook. ``eol.checkheadshook`` only checks heads, intermediate
86 invalid revisions will be pushed. To forbid them completely, use the
86 invalid revisions will be pushed. To forbid them completely, use the
87 ``eol.checkallhook`` hook. These hooks are best used as
87 ``eol.checkallhook`` hook. These hooks are best used as
88 ``pretxnchangegroup`` hooks.
88 ``pretxnchangegroup`` hooks.
89
89
90 See :hg:`help patterns` for more information about the glob patterns
90 See :hg:`help patterns` for more information about the glob patterns
91 used.
91 used.
92 """
92 """
93
93
94 from __future__ import absolute_import
94 from __future__ import absolute_import
95
95
96 import os
96 import os
97 import re
97 import re
98 from mercurial.i18n import _
98 from mercurial.i18n import _
99 from mercurial import (
99 from mercurial import (
100 config,
100 config,
101 error,
101 error,
102 extensions,
102 extensions,
103 match,
103 match,
104 pycompat,
104 util,
105 util,
105 )
106 )
106
107
107 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
108 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
108 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
109 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
109 # be specifying the version(s) of Mercurial they are tested with, or
110 # be specifying the version(s) of Mercurial they are tested with, or
110 # leave the attribute unspecified.
111 # leave the attribute unspecified.
111 testedwith = 'ships-with-hg-core'
112 testedwith = 'ships-with-hg-core'
112
113
113 # Matches a lone LF, i.e., one that is not part of CRLF.
114 # Matches a lone LF, i.e., one that is not part of CRLF.
114 singlelf = re.compile('(^|[^\r])\n')
115 singlelf = re.compile('(^|[^\r])\n')
115 # Matches a single EOL which can either be a CRLF where repeated CR
116 # Matches a single EOL which can either be a CRLF where repeated CR
116 # are removed or a LF. We do not care about old Macintosh files, so a
117 # are removed or a LF. We do not care about old Macintosh files, so a
117 # stray CR is an error.
118 # stray CR is an error.
118 eolre = re.compile('\r*\n')
119 eolre = re.compile('\r*\n')
119
120
120
121
121 def inconsistenteol(data):
122 def inconsistenteol(data):
122 return '\r\n' in data and singlelf.search(data)
123 return '\r\n' in data and singlelf.search(data)
123
124
124 def tolf(s, params, ui, **kwargs):
125 def tolf(s, params, ui, **kwargs):
125 """Filter to convert to LF EOLs."""
126 """Filter to convert to LF EOLs."""
126 if util.binary(s):
127 if util.binary(s):
127 return s
128 return s
128 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
129 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
129 return s
130 return s
130 if (ui.configbool('eol', 'fix-trailing-newline', False)
131 if (ui.configbool('eol', 'fix-trailing-newline', False)
131 and s and s[-1] != '\n'):
132 and s and s[-1] != '\n'):
132 s = s + '\n'
133 s = s + '\n'
133 return eolre.sub('\n', s)
134 return eolre.sub('\n', s)
134
135
135 def tocrlf(s, params, ui, **kwargs):
136 def tocrlf(s, params, ui, **kwargs):
136 """Filter to convert to CRLF EOLs."""
137 """Filter to convert to CRLF EOLs."""
137 if util.binary(s):
138 if util.binary(s):
138 return s
139 return s
139 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
140 if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
140 return s
141 return s
141 if (ui.configbool('eol', 'fix-trailing-newline', False)
142 if (ui.configbool('eol', 'fix-trailing-newline', False)
142 and s and s[-1] != '\n'):
143 and s and s[-1] != '\n'):
143 s = s + '\n'
144 s = s + '\n'
144 return eolre.sub('\r\n', s)
145 return eolre.sub('\r\n', s)
145
146
146 def isbinary(s, params):
147 def isbinary(s, params):
147 """Filter to do nothing with the file."""
148 """Filter to do nothing with the file."""
148 return s
149 return s
149
150
150 filters = {
151 filters = {
151 'to-lf': tolf,
152 'to-lf': tolf,
152 'to-crlf': tocrlf,
153 'to-crlf': tocrlf,
153 'is-binary': isbinary,
154 'is-binary': isbinary,
154 # The following provide backwards compatibility with win32text
155 # The following provide backwards compatibility with win32text
155 'cleverencode:': tolf,
156 'cleverencode:': tolf,
156 'cleverdecode:': tocrlf
157 'cleverdecode:': tocrlf
157 }
158 }
158
159
159 class eolfile(object):
160 class eolfile(object):
160 def __init__(self, ui, root, data):
161 def __init__(self, ui, root, data):
161 self._decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
162 self._decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
162 self._encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
163 self._encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
163
164
164 self.cfg = config.config()
165 self.cfg = config.config()
165 # Our files should not be touched. The pattern must be
166 # Our files should not be touched. The pattern must be
166 # inserted first override a '** = native' pattern.
167 # inserted first override a '** = native' pattern.
167 self.cfg.set('patterns', '.hg*', 'BIN', 'eol')
168 self.cfg.set('patterns', '.hg*', 'BIN', 'eol')
168 # We can then parse the user's patterns.
169 # We can then parse the user's patterns.
169 self.cfg.parse('.hgeol', data)
170 self.cfg.parse('.hgeol', data)
170
171
171 isrepolf = self.cfg.get('repository', 'native') != 'CRLF'
172 isrepolf = self.cfg.get('repository', 'native') != 'CRLF'
172 self._encode['NATIVE'] = isrepolf and 'to-lf' or 'to-crlf'
173 self._encode['NATIVE'] = isrepolf and 'to-lf' or 'to-crlf'
173 iswdlf = ui.config('eol', 'native', os.linesep) in ('LF', '\n')
174 iswdlf = ui.config('eol', 'native', pycompat.oslinesep) in ('LF', '\n')
174 self._decode['NATIVE'] = iswdlf and 'to-lf' or 'to-crlf'
175 self._decode['NATIVE'] = iswdlf and 'to-lf' or 'to-crlf'
175
176
176 include = []
177 include = []
177 exclude = []
178 exclude = []
178 self.patterns = []
179 self.patterns = []
179 for pattern, style in self.cfg.items('patterns'):
180 for pattern, style in self.cfg.items('patterns'):
180 key = style.upper()
181 key = style.upper()
181 if key == 'BIN':
182 if key == 'BIN':
182 exclude.append(pattern)
183 exclude.append(pattern)
183 else:
184 else:
184 include.append(pattern)
185 include.append(pattern)
185 m = match.match(root, '', [pattern])
186 m = match.match(root, '', [pattern])
186 self.patterns.append((pattern, key, m))
187 self.patterns.append((pattern, key, m))
187 # This will match the files for which we need to care
188 # This will match the files for which we need to care
188 # about inconsistent newlines.
189 # about inconsistent newlines.
189 self.match = match.match(root, '', [], include, exclude)
190 self.match = match.match(root, '', [], include, exclude)
190
191
191 def copytoui(self, ui):
192 def copytoui(self, ui):
192 for pattern, key, m in self.patterns:
193 for pattern, key, m in self.patterns:
193 try:
194 try:
194 ui.setconfig('decode', pattern, self._decode[key], 'eol')
195 ui.setconfig('decode', pattern, self._decode[key], 'eol')
195 ui.setconfig('encode', pattern, self._encode[key], 'eol')
196 ui.setconfig('encode', pattern, self._encode[key], 'eol')
196 except KeyError:
197 except KeyError:
197 ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
198 ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
198 % (key, self.cfg.source('patterns', pattern)))
199 % (key, self.cfg.source('patterns', pattern)))
199 # eol.only-consistent can be specified in ~/.hgrc or .hgeol
200 # eol.only-consistent can be specified in ~/.hgrc or .hgeol
200 for k, v in self.cfg.items('eol'):
201 for k, v in self.cfg.items('eol'):
201 ui.setconfig('eol', k, v, 'eol')
202 ui.setconfig('eol', k, v, 'eol')
202
203
203 def checkrev(self, repo, ctx, files):
204 def checkrev(self, repo, ctx, files):
204 failed = []
205 failed = []
205 for f in (files or ctx.files()):
206 for f in (files or ctx.files()):
206 if f not in ctx:
207 if f not in ctx:
207 continue
208 continue
208 for pattern, key, m in self.patterns:
209 for pattern, key, m in self.patterns:
209 if not m(f):
210 if not m(f):
210 continue
211 continue
211 target = self._encode[key]
212 target = self._encode[key]
212 data = ctx[f].data()
213 data = ctx[f].data()
213 if (target == "to-lf" and "\r\n" in data
214 if (target == "to-lf" and "\r\n" in data
214 or target == "to-crlf" and singlelf.search(data)):
215 or target == "to-crlf" and singlelf.search(data)):
215 failed.append((f, target, str(ctx)))
216 failed.append((f, target, str(ctx)))
216 break
217 break
217 return failed
218 return failed
218
219
219 def parseeol(ui, repo, nodes):
220 def parseeol(ui, repo, nodes):
220 try:
221 try:
221 for node in nodes:
222 for node in nodes:
222 try:
223 try:
223 if node is None:
224 if node is None:
224 # Cannot use workingctx.data() since it would load
225 # Cannot use workingctx.data() since it would load
225 # and cache the filters before we configure them.
226 # and cache the filters before we configure them.
226 data = repo.wvfs('.hgeol').read()
227 data = repo.wvfs('.hgeol').read()
227 else:
228 else:
228 data = repo[node]['.hgeol'].data()
229 data = repo[node]['.hgeol'].data()
229 return eolfile(ui, repo.root, data)
230 return eolfile(ui, repo.root, data)
230 except (IOError, LookupError):
231 except (IOError, LookupError):
231 pass
232 pass
232 except error.ParseError as inst:
233 except error.ParseError as inst:
233 ui.warn(_("warning: ignoring .hgeol file due to parse error "
234 ui.warn(_("warning: ignoring .hgeol file due to parse error "
234 "at %s: %s\n") % (inst.args[1], inst.args[0]))
235 "at %s: %s\n") % (inst.args[1], inst.args[0]))
235 return None
236 return None
236
237
237 def _checkhook(ui, repo, node, headsonly):
238 def _checkhook(ui, repo, node, headsonly):
238 # Get revisions to check and touched files at the same time
239 # Get revisions to check and touched files at the same time
239 files = set()
240 files = set()
240 revs = set()
241 revs = set()
241 for rev in xrange(repo[node].rev(), len(repo)):
242 for rev in xrange(repo[node].rev(), len(repo)):
242 revs.add(rev)
243 revs.add(rev)
243 if headsonly:
244 if headsonly:
244 ctx = repo[rev]
245 ctx = repo[rev]
245 files.update(ctx.files())
246 files.update(ctx.files())
246 for pctx in ctx.parents():
247 for pctx in ctx.parents():
247 revs.discard(pctx.rev())
248 revs.discard(pctx.rev())
248 failed = []
249 failed = []
249 for rev in revs:
250 for rev in revs:
250 ctx = repo[rev]
251 ctx = repo[rev]
251 eol = parseeol(ui, repo, [ctx.node()])
252 eol = parseeol(ui, repo, [ctx.node()])
252 if eol:
253 if eol:
253 failed.extend(eol.checkrev(repo, ctx, files))
254 failed.extend(eol.checkrev(repo, ctx, files))
254
255
255 if failed:
256 if failed:
256 eols = {'to-lf': 'CRLF', 'to-crlf': 'LF'}
257 eols = {'to-lf': 'CRLF', 'to-crlf': 'LF'}
257 msgs = []
258 msgs = []
258 for f, target, node in sorted(failed):
259 for f, target, node in sorted(failed):
259 msgs.append(_(" %s in %s should not have %s line endings") %
260 msgs.append(_(" %s in %s should not have %s line endings") %
260 (f, node, eols[target]))
261 (f, node, eols[target]))
261 raise error.Abort(_("end-of-line check failed:\n") + "\n".join(msgs))
262 raise error.Abort(_("end-of-line check failed:\n") + "\n".join(msgs))
262
263
263 def checkallhook(ui, repo, node, hooktype, **kwargs):
264 def checkallhook(ui, repo, node, hooktype, **kwargs):
264 """verify that files have expected EOLs"""
265 """verify that files have expected EOLs"""
265 _checkhook(ui, repo, node, False)
266 _checkhook(ui, repo, node, False)
266
267
267 def checkheadshook(ui, repo, node, hooktype, **kwargs):
268 def checkheadshook(ui, repo, node, hooktype, **kwargs):
268 """verify that files have expected EOLs"""
269 """verify that files have expected EOLs"""
269 _checkhook(ui, repo, node, True)
270 _checkhook(ui, repo, node, True)
270
271
271 # "checkheadshook" used to be called "hook"
272 # "checkheadshook" used to be called "hook"
272 hook = checkheadshook
273 hook = checkheadshook
273
274
274 def preupdate(ui, repo, hooktype, parent1, parent2):
275 def preupdate(ui, repo, hooktype, parent1, parent2):
275 repo.loadeol([parent1])
276 repo.loadeol([parent1])
276 return False
277 return False
277
278
278 def uisetup(ui):
279 def uisetup(ui):
279 ui.setconfig('hooks', 'preupdate.eol', preupdate, 'eol')
280 ui.setconfig('hooks', 'preupdate.eol', preupdate, 'eol')
280
281
281 def extsetup(ui):
282 def extsetup(ui):
282 try:
283 try:
283 extensions.find('win32text')
284 extensions.find('win32text')
284 ui.warn(_("the eol extension is incompatible with the "
285 ui.warn(_("the eol extension is incompatible with the "
285 "win32text extension\n"))
286 "win32text extension\n"))
286 except KeyError:
287 except KeyError:
287 pass
288 pass
288
289
289
290
290 def reposetup(ui, repo):
291 def reposetup(ui, repo):
291 uisetup(repo.ui)
292 uisetup(repo.ui)
292
293
293 if not repo.local():
294 if not repo.local():
294 return
295 return
295 for name, fn in filters.iteritems():
296 for name, fn in filters.iteritems():
296 repo.adddatafilter(name, fn)
297 repo.adddatafilter(name, fn)
297
298
298 ui.setconfig('patch', 'eol', 'auto', 'eol')
299 ui.setconfig('patch', 'eol', 'auto', 'eol')
299
300
300 class eolrepo(repo.__class__):
301 class eolrepo(repo.__class__):
301
302
302 def loadeol(self, nodes):
303 def loadeol(self, nodes):
303 eol = parseeol(self.ui, self, nodes)
304 eol = parseeol(self.ui, self, nodes)
304 if eol is None:
305 if eol is None:
305 return None
306 return None
306 eol.copytoui(self.ui)
307 eol.copytoui(self.ui)
307 return eol.match
308 return eol.match
308
309
309 def _hgcleardirstate(self):
310 def _hgcleardirstate(self):
310 self._eolmatch = self.loadeol([None, 'tip'])
311 self._eolmatch = self.loadeol([None, 'tip'])
311 if not self._eolmatch:
312 if not self._eolmatch:
312 self._eolmatch = util.never
313 self._eolmatch = util.never
313 return
314 return
314
315
315 oldeol = None
316 oldeol = None
316 try:
317 try:
317 cachemtime = os.path.getmtime(self.vfs.join("eol.cache"))
318 cachemtime = os.path.getmtime(self.vfs.join("eol.cache"))
318 except OSError:
319 except OSError:
319 cachemtime = 0
320 cachemtime = 0
320 else:
321 else:
321 olddata = self.vfs.read("eol.cache")
322 olddata = self.vfs.read("eol.cache")
322 if olddata:
323 if olddata:
323 oldeol = eolfile(self.ui, self.root, olddata)
324 oldeol = eolfile(self.ui, self.root, olddata)
324
325
325 try:
326 try:
326 eolmtime = os.path.getmtime(self.wjoin(".hgeol"))
327 eolmtime = os.path.getmtime(self.wjoin(".hgeol"))
327 except OSError:
328 except OSError:
328 eolmtime = 0
329 eolmtime = 0
329
330
330 if eolmtime > cachemtime:
331 if eolmtime > cachemtime:
331 self.ui.debug("eol: detected change in .hgeol\n")
332 self.ui.debug("eol: detected change in .hgeol\n")
332
333
333 hgeoldata = self.wvfs.read('.hgeol')
334 hgeoldata = self.wvfs.read('.hgeol')
334 neweol = eolfile(self.ui, self.root, hgeoldata)
335 neweol = eolfile(self.ui, self.root, hgeoldata)
335
336
336 wlock = None
337 wlock = None
337 try:
338 try:
338 wlock = self.wlock()
339 wlock = self.wlock()
339 for f in self.dirstate:
340 for f in self.dirstate:
340 if self.dirstate[f] != 'n':
341 if self.dirstate[f] != 'n':
341 continue
342 continue
342 if oldeol is not None:
343 if oldeol is not None:
343 if not oldeol.match(f) and not neweol.match(f):
344 if not oldeol.match(f) and not neweol.match(f):
344 continue
345 continue
345 oldkey = None
346 oldkey = None
346 for pattern, key, m in oldeol.patterns:
347 for pattern, key, m in oldeol.patterns:
347 if m(f):
348 if m(f):
348 oldkey = key
349 oldkey = key
349 break
350 break
350 newkey = None
351 newkey = None
351 for pattern, key, m in neweol.patterns:
352 for pattern, key, m in neweol.patterns:
352 if m(f):
353 if m(f):
353 newkey = key
354 newkey = key
354 break
355 break
355 if oldkey == newkey:
356 if oldkey == newkey:
356 continue
357 continue
357 # all normal files need to be looked at again since
358 # all normal files need to be looked at again since
358 # the new .hgeol file specify a different filter
359 # the new .hgeol file specify a different filter
359 self.dirstate.normallookup(f)
360 self.dirstate.normallookup(f)
360 # Write the cache to update mtime and cache .hgeol
361 # Write the cache to update mtime and cache .hgeol
361 with self.vfs("eol.cache", "w") as f:
362 with self.vfs("eol.cache", "w") as f:
362 f.write(hgeoldata)
363 f.write(hgeoldata)
363 except error.LockUnavailable:
364 except error.LockUnavailable:
364 # If we cannot lock the repository and clear the
365 # If we cannot lock the repository and clear the
365 # dirstate, then a commit might not see all files
366 # dirstate, then a commit might not see all files
366 # as modified. But if we cannot lock the
367 # as modified. But if we cannot lock the
367 # repository, then we can also not make a commit,
368 # repository, then we can also not make a commit,
368 # so ignore the error.
369 # so ignore the error.
369 pass
370 pass
370 finally:
371 finally:
371 if wlock is not None:
372 if wlock is not None:
372 wlock.release()
373 wlock.release()
373
374
374 def commitctx(self, ctx, haserror=False):
375 def commitctx(self, ctx, haserror=False):
375 for f in sorted(ctx.added() + ctx.modified()):
376 for f in sorted(ctx.added() + ctx.modified()):
376 if not self._eolmatch(f):
377 if not self._eolmatch(f):
377 continue
378 continue
378 fctx = ctx[f]
379 fctx = ctx[f]
379 if fctx is None:
380 if fctx is None:
380 continue
381 continue
381 data = fctx.data()
382 data = fctx.data()
382 if util.binary(data):
383 if util.binary(data):
383 # We should not abort here, since the user should
384 # We should not abort here, since the user should
384 # be able to say "** = native" to automatically
385 # be able to say "** = native" to automatically
385 # have all non-binary files taken care of.
386 # have all non-binary files taken care of.
386 continue
387 continue
387 if inconsistenteol(data):
388 if inconsistenteol(data):
388 raise error.Abort(_("inconsistent newline style "
389 raise error.Abort(_("inconsistent newline style "
389 "in %s\n") % f)
390 "in %s\n") % f)
390 return super(eolrepo, self).commitctx(ctx, haserror)
391 return super(eolrepo, self).commitctx(ctx, haserror)
391 repo.__class__ = eolrepo
392 repo.__class__ = eolrepo
392 repo._hgcleardirstate()
393 repo._hgcleardirstate()
@@ -1,391 +1,393 b''
1 # pycompat.py - portability shim for python 3
1 # pycompat.py - portability shim for python 3
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 """Mercurial portability shim for python 3.
6 """Mercurial portability shim for python 3.
7
7
8 This contains aliases to hide python version-specific details from the core.
8 This contains aliases to hide python version-specific details from the core.
9 """
9 """
10
10
11 from __future__ import absolute_import
11 from __future__ import absolute_import
12
12
13 import getopt
13 import getopt
14 import os
14 import os
15 import shlex
15 import shlex
16 import sys
16 import sys
17
17
18 ispy3 = (sys.version_info[0] >= 3)
18 ispy3 = (sys.version_info[0] >= 3)
19
19
20 if not ispy3:
20 if not ispy3:
21 import cPickle as pickle
21 import cPickle as pickle
22 import httplib
22 import httplib
23 import Queue as _queue
23 import Queue as _queue
24 import SocketServer as socketserver
24 import SocketServer as socketserver
25 import xmlrpclib
25 import xmlrpclib
26 else:
26 else:
27 import http.client as httplib
27 import http.client as httplib
28 import pickle
28 import pickle
29 import queue as _queue
29 import queue as _queue
30 import socketserver
30 import socketserver
31 import xmlrpc.client as xmlrpclib
31 import xmlrpc.client as xmlrpclib
32
32
33 def identity(a):
33 def identity(a):
34 return a
34 return a
35
35
36 if ispy3:
36 if ispy3:
37 import builtins
37 import builtins
38 import functools
38 import functools
39 import io
39 import io
40 import struct
40 import struct
41
41
42 fsencode = os.fsencode
42 fsencode = os.fsencode
43 fsdecode = os.fsdecode
43 fsdecode = os.fsdecode
44 # A bytes version of os.name.
44 # A bytes version of os.name.
45 oslinesep = os.linesep.encode('ascii')
45 osname = os.name.encode('ascii')
46 osname = os.name.encode('ascii')
46 ospathsep = os.pathsep.encode('ascii')
47 ospathsep = os.pathsep.encode('ascii')
47 ossep = os.sep.encode('ascii')
48 ossep = os.sep.encode('ascii')
48 osaltsep = os.altsep
49 osaltsep = os.altsep
49 if osaltsep:
50 if osaltsep:
50 osaltsep = osaltsep.encode('ascii')
51 osaltsep = osaltsep.encode('ascii')
51 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
52 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
52 # returns bytes.
53 # returns bytes.
53 getcwd = os.getcwdb
54 getcwd = os.getcwdb
54 sysplatform = sys.platform.encode('ascii')
55 sysplatform = sys.platform.encode('ascii')
55 sysexecutable = sys.executable
56 sysexecutable = sys.executable
56 if sysexecutable:
57 if sysexecutable:
57 sysexecutable = os.fsencode(sysexecutable)
58 sysexecutable = os.fsencode(sysexecutable)
58 stringio = io.BytesIO
59 stringio = io.BytesIO
59 maplist = lambda *args: list(map(*args))
60 maplist = lambda *args: list(map(*args))
60
61
61 # TODO: .buffer might not exist if std streams were replaced; we'll need
62 # TODO: .buffer might not exist if std streams were replaced; we'll need
62 # a silly wrapper to make a bytes stream backed by a unicode one.
63 # a silly wrapper to make a bytes stream backed by a unicode one.
63 stdin = sys.stdin.buffer
64 stdin = sys.stdin.buffer
64 stdout = sys.stdout.buffer
65 stdout = sys.stdout.buffer
65 stderr = sys.stderr.buffer
66 stderr = sys.stderr.buffer
66
67
67 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
68 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
68 # we can use os.fsencode() to get back bytes argv.
69 # we can use os.fsencode() to get back bytes argv.
69 #
70 #
70 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
71 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
71 #
72 #
72 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
73 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
73 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
74 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
74 if getattr(sys, 'argv', None) is not None:
75 if getattr(sys, 'argv', None) is not None:
75 sysargv = list(map(os.fsencode, sys.argv))
76 sysargv = list(map(os.fsencode, sys.argv))
76
77
77 bytechr = struct.Struct('>B').pack
78 bytechr = struct.Struct('>B').pack
78
79
79 class bytestr(bytes):
80 class bytestr(bytes):
80 """A bytes which mostly acts as a Python 2 str
81 """A bytes which mostly acts as a Python 2 str
81
82
82 >>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1)
83 >>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1)
83 (b'', b'foo', b'ascii', b'1')
84 (b'', b'foo', b'ascii', b'1')
84 >>> s = bytestr(b'foo')
85 >>> s = bytestr(b'foo')
85 >>> assert s is bytestr(s)
86 >>> assert s is bytestr(s)
86
87
87 There's no implicit conversion from non-ascii str as its encoding is
88 There's no implicit conversion from non-ascii str as its encoding is
88 unknown:
89 unknown:
89
90
90 >>> bytestr(chr(0x80)) # doctest: +ELLIPSIS
91 >>> bytestr(chr(0x80)) # doctest: +ELLIPSIS
91 Traceback (most recent call last):
92 Traceback (most recent call last):
92 ...
93 ...
93 UnicodeEncodeError: ...
94 UnicodeEncodeError: ...
94
95
95 Comparison between bytestr and bytes should work:
96 Comparison between bytestr and bytes should work:
96
97
97 >>> assert bytestr(b'foo') == b'foo'
98 >>> assert bytestr(b'foo') == b'foo'
98 >>> assert b'foo' == bytestr(b'foo')
99 >>> assert b'foo' == bytestr(b'foo')
99 >>> assert b'f' in bytestr(b'foo')
100 >>> assert b'f' in bytestr(b'foo')
100 >>> assert bytestr(b'f') in b'foo'
101 >>> assert bytestr(b'f') in b'foo'
101
102
102 Sliced elements should be bytes, not integer:
103 Sliced elements should be bytes, not integer:
103
104
104 >>> s[1], s[:2]
105 >>> s[1], s[:2]
105 (b'o', b'fo')
106 (b'o', b'fo')
106 >>> list(s), list(reversed(s))
107 >>> list(s), list(reversed(s))
107 ([b'f', b'o', b'o'], [b'o', b'o', b'f'])
108 ([b'f', b'o', b'o'], [b'o', b'o', b'f'])
108
109
109 As bytestr type isn't propagated across operations, you need to cast
110 As bytestr type isn't propagated across operations, you need to cast
110 bytes to bytestr explicitly:
111 bytes to bytestr explicitly:
111
112
112 >>> s = bytestr(b'foo').upper()
113 >>> s = bytestr(b'foo').upper()
113 >>> t = bytestr(s)
114 >>> t = bytestr(s)
114 >>> s[0], t[0]
115 >>> s[0], t[0]
115 (70, b'F')
116 (70, b'F')
116
117
117 Be careful to not pass a bytestr object to a function which expects
118 Be careful to not pass a bytestr object to a function which expects
118 bytearray-like behavior.
119 bytearray-like behavior.
119
120
120 >>> t = bytes(t) # cast to bytes
121 >>> t = bytes(t) # cast to bytes
121 >>> assert type(t) is bytes
122 >>> assert type(t) is bytes
122 """
123 """
123
124
124 def __new__(cls, s=b''):
125 def __new__(cls, s=b''):
125 if isinstance(s, bytestr):
126 if isinstance(s, bytestr):
126 return s
127 return s
127 if not isinstance(s, (bytes, bytearray)):
128 if not isinstance(s, (bytes, bytearray)):
128 s = str(s).encode(u'ascii')
129 s = str(s).encode(u'ascii')
129 return bytes.__new__(cls, s)
130 return bytes.__new__(cls, s)
130
131
131 def __getitem__(self, key):
132 def __getitem__(self, key):
132 s = bytes.__getitem__(self, key)
133 s = bytes.__getitem__(self, key)
133 if not isinstance(s, bytes):
134 if not isinstance(s, bytes):
134 s = bytechr(s)
135 s = bytechr(s)
135 return s
136 return s
136
137
137 def __iter__(self):
138 def __iter__(self):
138 return iterbytestr(bytes.__iter__(self))
139 return iterbytestr(bytes.__iter__(self))
139
140
140 def iterbytestr(s):
141 def iterbytestr(s):
141 """Iterate bytes as if it were a str object of Python 2"""
142 """Iterate bytes as if it were a str object of Python 2"""
142 return map(bytechr, s)
143 return map(bytechr, s)
143
144
144 def sysstr(s):
145 def sysstr(s):
145 """Return a keyword str to be passed to Python functions such as
146 """Return a keyword str to be passed to Python functions such as
146 getattr() and str.encode()
147 getattr() and str.encode()
147
148
148 This never raises UnicodeDecodeError. Non-ascii characters are
149 This never raises UnicodeDecodeError. Non-ascii characters are
149 considered invalid and mapped to arbitrary but unique code points
150 considered invalid and mapped to arbitrary but unique code points
150 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
151 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
151 """
152 """
152 if isinstance(s, builtins.str):
153 if isinstance(s, builtins.str):
153 return s
154 return s
154 return s.decode(u'latin-1')
155 return s.decode(u'latin-1')
155
156
156 def _wrapattrfunc(f):
157 def _wrapattrfunc(f):
157 @functools.wraps(f)
158 @functools.wraps(f)
158 def w(object, name, *args):
159 def w(object, name, *args):
159 return f(object, sysstr(name), *args)
160 return f(object, sysstr(name), *args)
160 return w
161 return w
161
162
162 # these wrappers are automagically imported by hgloader
163 # these wrappers are automagically imported by hgloader
163 delattr = _wrapattrfunc(builtins.delattr)
164 delattr = _wrapattrfunc(builtins.delattr)
164 getattr = _wrapattrfunc(builtins.getattr)
165 getattr = _wrapattrfunc(builtins.getattr)
165 hasattr = _wrapattrfunc(builtins.hasattr)
166 hasattr = _wrapattrfunc(builtins.hasattr)
166 setattr = _wrapattrfunc(builtins.setattr)
167 setattr = _wrapattrfunc(builtins.setattr)
167 xrange = builtins.range
168 xrange = builtins.range
168
169
169 def open(name, mode='r', buffering=-1):
170 def open(name, mode='r', buffering=-1):
170 return builtins.open(name, sysstr(mode), buffering)
171 return builtins.open(name, sysstr(mode), buffering)
171
172
172 # getopt.getopt() on Python 3 deals with unicodes internally so we cannot
173 # getopt.getopt() on Python 3 deals with unicodes internally so we cannot
173 # pass bytes there. Passing unicodes will result in unicodes as return
174 # pass bytes there. Passing unicodes will result in unicodes as return
174 # values which we need to convert again to bytes.
175 # values which we need to convert again to bytes.
175 def getoptb(args, shortlist, namelist):
176 def getoptb(args, shortlist, namelist):
176 args = [a.decode('latin-1') for a in args]
177 args = [a.decode('latin-1') for a in args]
177 shortlist = shortlist.decode('latin-1')
178 shortlist = shortlist.decode('latin-1')
178 namelist = [a.decode('latin-1') for a in namelist]
179 namelist = [a.decode('latin-1') for a in namelist]
179 opts, args = getopt.getopt(args, shortlist, namelist)
180 opts, args = getopt.getopt(args, shortlist, namelist)
180 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
181 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
181 for a in opts]
182 for a in opts]
182 args = [a.encode('latin-1') for a in args]
183 args = [a.encode('latin-1') for a in args]
183 return opts, args
184 return opts, args
184
185
185 # keys of keyword arguments in Python need to be strings which are unicodes
186 # keys of keyword arguments in Python need to be strings which are unicodes
186 # Python 3. This function takes keyword arguments, convert the keys to str.
187 # Python 3. This function takes keyword arguments, convert the keys to str.
187 def strkwargs(dic):
188 def strkwargs(dic):
188 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
189 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
189 return dic
190 return dic
190
191
191 # keys of keyword arguments need to be unicode while passing into
192 # keys of keyword arguments need to be unicode while passing into
192 # a function. This function helps us to convert those keys back to bytes
193 # a function. This function helps us to convert those keys back to bytes
193 # again as we need to deal with bytes.
194 # again as we need to deal with bytes.
194 def byteskwargs(dic):
195 def byteskwargs(dic):
195 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
196 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
196 return dic
197 return dic
197
198
198 # shlex.split() accepts unicodes on Python 3. This function takes bytes
199 # shlex.split() accepts unicodes on Python 3. This function takes bytes
199 # argument, convert it into unicodes, pass into shlex.split(), convert the
200 # argument, convert it into unicodes, pass into shlex.split(), convert the
200 # returned value to bytes and return that.
201 # returned value to bytes and return that.
201 # TODO: handle shlex.shlex().
202 # TODO: handle shlex.shlex().
202 def shlexsplit(s):
203 def shlexsplit(s):
203 ret = shlex.split(s.decode('latin-1'))
204 ret = shlex.split(s.decode('latin-1'))
204 return [a.encode('latin-1') for a in ret]
205 return [a.encode('latin-1') for a in ret]
205
206
206 else:
207 else:
207 import cStringIO
208 import cStringIO
208
209
209 bytechr = chr
210 bytechr = chr
210 bytestr = str
211 bytestr = str
211 iterbytestr = iter
212 iterbytestr = iter
212 sysstr = identity
213 sysstr = identity
213
214
214 # Partial backport from os.py in Python 3, which only accepts bytes.
215 # Partial backport from os.py in Python 3, which only accepts bytes.
215 # In Python 2, our paths should only ever be bytes, a unicode path
216 # In Python 2, our paths should only ever be bytes, a unicode path
216 # indicates a bug.
217 # indicates a bug.
217 def fsencode(filename):
218 def fsencode(filename):
218 if isinstance(filename, str):
219 if isinstance(filename, str):
219 return filename
220 return filename
220 else:
221 else:
221 raise TypeError(
222 raise TypeError(
222 "expect str, not %s" % type(filename).__name__)
223 "expect str, not %s" % type(filename).__name__)
223
224
224 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
225 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
225 # better not to touch Python 2 part as it's already working fine.
226 # better not to touch Python 2 part as it's already working fine.
226 fsdecode = identity
227 fsdecode = identity
227
228
228 def getoptb(args, shortlist, namelist):
229 def getoptb(args, shortlist, namelist):
229 return getopt.getopt(args, shortlist, namelist)
230 return getopt.getopt(args, shortlist, namelist)
230
231
231 strkwargs = identity
232 strkwargs = identity
232 byteskwargs = identity
233 byteskwargs = identity
233
234
235 oslinesep = os.linesep
234 osname = os.name
236 osname = os.name
235 ospathsep = os.pathsep
237 ospathsep = os.pathsep
236 ossep = os.sep
238 ossep = os.sep
237 osaltsep = os.altsep
239 osaltsep = os.altsep
238 stdin = sys.stdin
240 stdin = sys.stdin
239 stdout = sys.stdout
241 stdout = sys.stdout
240 stderr = sys.stderr
242 stderr = sys.stderr
241 if getattr(sys, 'argv', None) is not None:
243 if getattr(sys, 'argv', None) is not None:
242 sysargv = sys.argv
244 sysargv = sys.argv
243 sysplatform = sys.platform
245 sysplatform = sys.platform
244 getcwd = os.getcwd
246 getcwd = os.getcwd
245 sysexecutable = sys.executable
247 sysexecutable = sys.executable
246 shlexsplit = shlex.split
248 shlexsplit = shlex.split
247 stringio = cStringIO.StringIO
249 stringio = cStringIO.StringIO
248 maplist = map
250 maplist = map
249
251
250 empty = _queue.Empty
252 empty = _queue.Empty
251 queue = _queue.Queue
253 queue = _queue.Queue
252
254
253 class _pycompatstub(object):
255 class _pycompatstub(object):
254 def __init__(self):
256 def __init__(self):
255 self._aliases = {}
257 self._aliases = {}
256
258
257 def _registeraliases(self, origin, items):
259 def _registeraliases(self, origin, items):
258 """Add items that will be populated at the first access"""
260 """Add items that will be populated at the first access"""
259 items = map(sysstr, items)
261 items = map(sysstr, items)
260 self._aliases.update(
262 self._aliases.update(
261 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
263 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
262 for item in items)
264 for item in items)
263
265
264 def _registeralias(self, origin, attr, name):
266 def _registeralias(self, origin, attr, name):
265 """Alias ``origin``.``attr`` as ``name``"""
267 """Alias ``origin``.``attr`` as ``name``"""
266 self._aliases[sysstr(name)] = (origin, sysstr(attr))
268 self._aliases[sysstr(name)] = (origin, sysstr(attr))
267
269
268 def __getattr__(self, name):
270 def __getattr__(self, name):
269 try:
271 try:
270 origin, item = self._aliases[name]
272 origin, item = self._aliases[name]
271 except KeyError:
273 except KeyError:
272 raise AttributeError(name)
274 raise AttributeError(name)
273 self.__dict__[name] = obj = getattr(origin, item)
275 self.__dict__[name] = obj = getattr(origin, item)
274 return obj
276 return obj
275
277
276 httpserver = _pycompatstub()
278 httpserver = _pycompatstub()
277 urlreq = _pycompatstub()
279 urlreq = _pycompatstub()
278 urlerr = _pycompatstub()
280 urlerr = _pycompatstub()
279 if not ispy3:
281 if not ispy3:
280 import BaseHTTPServer
282 import BaseHTTPServer
281 import CGIHTTPServer
283 import CGIHTTPServer
282 import SimpleHTTPServer
284 import SimpleHTTPServer
283 import urllib2
285 import urllib2
284 import urllib
286 import urllib
285 import urlparse
287 import urlparse
286 urlreq._registeraliases(urllib, (
288 urlreq._registeraliases(urllib, (
287 "addclosehook",
289 "addclosehook",
288 "addinfourl",
290 "addinfourl",
289 "ftpwrapper",
291 "ftpwrapper",
290 "pathname2url",
292 "pathname2url",
291 "quote",
293 "quote",
292 "splitattr",
294 "splitattr",
293 "splitpasswd",
295 "splitpasswd",
294 "splitport",
296 "splitport",
295 "splituser",
297 "splituser",
296 "unquote",
298 "unquote",
297 "url2pathname",
299 "url2pathname",
298 "urlencode",
300 "urlencode",
299 ))
301 ))
300 urlreq._registeraliases(urllib2, (
302 urlreq._registeraliases(urllib2, (
301 "AbstractHTTPHandler",
303 "AbstractHTTPHandler",
302 "BaseHandler",
304 "BaseHandler",
303 "build_opener",
305 "build_opener",
304 "FileHandler",
306 "FileHandler",
305 "FTPHandler",
307 "FTPHandler",
306 "HTTPBasicAuthHandler",
308 "HTTPBasicAuthHandler",
307 "HTTPDigestAuthHandler",
309 "HTTPDigestAuthHandler",
308 "HTTPHandler",
310 "HTTPHandler",
309 "HTTPPasswordMgrWithDefaultRealm",
311 "HTTPPasswordMgrWithDefaultRealm",
310 "HTTPSHandler",
312 "HTTPSHandler",
311 "install_opener",
313 "install_opener",
312 "ProxyHandler",
314 "ProxyHandler",
313 "Request",
315 "Request",
314 "urlopen",
316 "urlopen",
315 ))
317 ))
316 urlreq._registeraliases(urlparse, (
318 urlreq._registeraliases(urlparse, (
317 "urlparse",
319 "urlparse",
318 "urlunparse",
320 "urlunparse",
319 ))
321 ))
320 urlerr._registeraliases(urllib2, (
322 urlerr._registeraliases(urllib2, (
321 "HTTPError",
323 "HTTPError",
322 "URLError",
324 "URLError",
323 ))
325 ))
324 httpserver._registeraliases(BaseHTTPServer, (
326 httpserver._registeraliases(BaseHTTPServer, (
325 "HTTPServer",
327 "HTTPServer",
326 "BaseHTTPRequestHandler",
328 "BaseHTTPRequestHandler",
327 ))
329 ))
328 httpserver._registeraliases(SimpleHTTPServer, (
330 httpserver._registeraliases(SimpleHTTPServer, (
329 "SimpleHTTPRequestHandler",
331 "SimpleHTTPRequestHandler",
330 ))
332 ))
331 httpserver._registeraliases(CGIHTTPServer, (
333 httpserver._registeraliases(CGIHTTPServer, (
332 "CGIHTTPRequestHandler",
334 "CGIHTTPRequestHandler",
333 ))
335 ))
334
336
335 else:
337 else:
336 import urllib.parse
338 import urllib.parse
337 urlreq._registeraliases(urllib.parse, (
339 urlreq._registeraliases(urllib.parse, (
338 "splitattr",
340 "splitattr",
339 "splitpasswd",
341 "splitpasswd",
340 "splitport",
342 "splitport",
341 "splituser",
343 "splituser",
342 "urlparse",
344 "urlparse",
343 "urlunparse",
345 "urlunparse",
344 ))
346 ))
345 urlreq._registeralias(urllib.parse, "unquote_to_bytes", "unquote")
347 urlreq._registeralias(urllib.parse, "unquote_to_bytes", "unquote")
346 import urllib.request
348 import urllib.request
347 urlreq._registeraliases(urllib.request, (
349 urlreq._registeraliases(urllib.request, (
348 "AbstractHTTPHandler",
350 "AbstractHTTPHandler",
349 "BaseHandler",
351 "BaseHandler",
350 "build_opener",
352 "build_opener",
351 "FileHandler",
353 "FileHandler",
352 "FTPHandler",
354 "FTPHandler",
353 "ftpwrapper",
355 "ftpwrapper",
354 "HTTPHandler",
356 "HTTPHandler",
355 "HTTPSHandler",
357 "HTTPSHandler",
356 "install_opener",
358 "install_opener",
357 "pathname2url",
359 "pathname2url",
358 "HTTPBasicAuthHandler",
360 "HTTPBasicAuthHandler",
359 "HTTPDigestAuthHandler",
361 "HTTPDigestAuthHandler",
360 "HTTPPasswordMgrWithDefaultRealm",
362 "HTTPPasswordMgrWithDefaultRealm",
361 "ProxyHandler",
363 "ProxyHandler",
362 "Request",
364 "Request",
363 "url2pathname",
365 "url2pathname",
364 "urlopen",
366 "urlopen",
365 ))
367 ))
366 import urllib.response
368 import urllib.response
367 urlreq._registeraliases(urllib.response, (
369 urlreq._registeraliases(urllib.response, (
368 "addclosehook",
370 "addclosehook",
369 "addinfourl",
371 "addinfourl",
370 ))
372 ))
371 import urllib.error
373 import urllib.error
372 urlerr._registeraliases(urllib.error, (
374 urlerr._registeraliases(urllib.error, (
373 "HTTPError",
375 "HTTPError",
374 "URLError",
376 "URLError",
375 ))
377 ))
376 import http.server
378 import http.server
377 httpserver._registeraliases(http.server, (
379 httpserver._registeraliases(http.server, (
378 "HTTPServer",
380 "HTTPServer",
379 "BaseHTTPRequestHandler",
381 "BaseHTTPRequestHandler",
380 "SimpleHTTPRequestHandler",
382 "SimpleHTTPRequestHandler",
381 "CGIHTTPRequestHandler",
383 "CGIHTTPRequestHandler",
382 ))
384 ))
383
385
384 # urllib.parse.quote() accepts both str and bytes, decodes bytes
386 # urllib.parse.quote() accepts both str and bytes, decodes bytes
385 # (if necessary), and returns str. This is wonky. We provide a custom
387 # (if necessary), and returns str. This is wonky. We provide a custom
386 # implementation that only accepts bytes and emits bytes.
388 # implementation that only accepts bytes and emits bytes.
387 def quote(s, safe=r'/'):
389 def quote(s, safe=r'/'):
388 s = urllib.parse.quote_from_bytes(s, safe=safe)
390 s = urllib.parse.quote_from_bytes(s, safe=safe)
389 return s.encode('ascii', 'strict')
391 return s.encode('ascii', 'strict')
390
392
391 urlreq.quote = quote
393 urlreq.quote = quote
@@ -1,1654 +1,1654 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 atexit
10 import atexit
11 import collections
11 import collections
12 import contextlib
12 import contextlib
13 import errno
13 import errno
14 import getpass
14 import getpass
15 import inspect
15 import inspect
16 import os
16 import os
17 import re
17 import re
18 import signal
18 import signal
19 import socket
19 import socket
20 import subprocess
20 import subprocess
21 import sys
21 import sys
22 import tempfile
22 import tempfile
23 import traceback
23 import traceback
24
24
25 from .i18n import _
25 from .i18n import _
26 from .node import hex
26 from .node import hex
27
27
28 from . import (
28 from . import (
29 color,
29 color,
30 config,
30 config,
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
40
41 urlreq = util.urlreq
41 urlreq = util.urlreq
42
42
43 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
43 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
44 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
44 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
45 if not c.isalnum())
45 if not c.isalnum())
46
46
47 samplehgrcs = {
47 samplehgrcs = {
48 'user':
48 'user':
49 """# example user config (see 'hg help config' for more info)
49 """# example user config (see 'hg help config' for more info)
50 [ui]
50 [ui]
51 # name and email, e.g.
51 # name and email, e.g.
52 # username = Jane Doe <jdoe@example.com>
52 # username = Jane Doe <jdoe@example.com>
53 username =
53 username =
54
54
55 # uncomment to colorize command output
55 # uncomment to colorize command output
56 # color = auto
56 # color = auto
57
57
58 [extensions]
58 [extensions]
59 # uncomment these lines to enable some popular extensions
59 # uncomment these lines to enable some popular extensions
60 # (see 'hg help extensions' for more info)
60 # (see 'hg help extensions' for more info)
61 #
61 #
62 # pager =""",
62 # pager =""",
63
63
64 'cloned':
64 'cloned':
65 """# example repository config (see 'hg help config' for more info)
65 """# example repository config (see 'hg help config' for more info)
66 [paths]
66 [paths]
67 default = %s
67 default = %s
68
68
69 # path aliases to other clones of this repo in URLs or filesystem paths
69 # path aliases to other clones of this repo in URLs or filesystem paths
70 # (see 'hg help config.paths' for more info)
70 # (see 'hg help config.paths' for more info)
71 #
71 #
72 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
72 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
73 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
73 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
74 # my-clone = /home/jdoe/jdoes-clone
74 # my-clone = /home/jdoe/jdoes-clone
75
75
76 [ui]
76 [ui]
77 # name and email (local to this repository, optional), e.g.
77 # name and email (local to this repository, optional), e.g.
78 # username = Jane Doe <jdoe@example.com>
78 # username = Jane Doe <jdoe@example.com>
79 """,
79 """,
80
80
81 'local':
81 'local':
82 """# example repository config (see 'hg help config' for more info)
82 """# example repository config (see 'hg help config' for more info)
83 [paths]
83 [paths]
84 # path aliases to other clones of this repo in URLs or filesystem paths
84 # path aliases to other clones of this repo in URLs or filesystem paths
85 # (see 'hg help config.paths' for more info)
85 # (see 'hg help config.paths' for more info)
86 #
86 #
87 # default = http://example.com/hg/example-repo
87 # default = http://example.com/hg/example-repo
88 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
88 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
89 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
89 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
90 # my-clone = /home/jdoe/jdoes-clone
90 # my-clone = /home/jdoe/jdoes-clone
91
91
92 [ui]
92 [ui]
93 # name and email (local to this repository, optional), e.g.
93 # name and email (local to this repository, optional), e.g.
94 # username = Jane Doe <jdoe@example.com>
94 # username = Jane Doe <jdoe@example.com>
95 """,
95 """,
96
96
97 'global':
97 'global':
98 """# example system-wide hg config (see 'hg help config' for more info)
98 """# example system-wide hg config (see 'hg help config' for more info)
99
99
100 [ui]
100 [ui]
101 # uncomment to colorize command output
101 # uncomment to colorize command output
102 # color = auto
102 # color = auto
103
103
104 [extensions]
104 [extensions]
105 # uncomment these lines to enable some popular extensions
105 # uncomment these lines to enable some popular extensions
106 # (see 'hg help extensions' for more info)
106 # (see 'hg help extensions' for more info)
107 #
107 #
108 # blackbox =
108 # blackbox =
109 # pager =""",
109 # pager =""",
110 }
110 }
111
111
112
112
113 class httppasswordmgrdbproxy(object):
113 class httppasswordmgrdbproxy(object):
114 """Delays loading urllib2 until it's needed."""
114 """Delays loading urllib2 until it's needed."""
115 def __init__(self):
115 def __init__(self):
116 self._mgr = None
116 self._mgr = None
117
117
118 def _get_mgr(self):
118 def _get_mgr(self):
119 if self._mgr is None:
119 if self._mgr is None:
120 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
120 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
121 return self._mgr
121 return self._mgr
122
122
123 def add_password(self, *args, **kwargs):
123 def add_password(self, *args, **kwargs):
124 return self._get_mgr().add_password(*args, **kwargs)
124 return self._get_mgr().add_password(*args, **kwargs)
125
125
126 def find_user_password(self, *args, **kwargs):
126 def find_user_password(self, *args, **kwargs):
127 return self._get_mgr().find_user_password(*args, **kwargs)
127 return self._get_mgr().find_user_password(*args, **kwargs)
128
128
129 def _catchterm(*args):
129 def _catchterm(*args):
130 raise error.SignalInterrupt
130 raise error.SignalInterrupt
131
131
132 class ui(object):
132 class ui(object):
133 def __init__(self, src=None):
133 def __init__(self, src=None):
134 """Create a fresh new ui object if no src given
134 """Create a fresh new ui object if no src given
135
135
136 Use uimod.ui.load() to create a ui which knows global and user configs.
136 Use uimod.ui.load() to create a ui which knows global and user configs.
137 In most cases, you should use ui.copy() to create a copy of an existing
137 In most cases, you should use ui.copy() to create a copy of an existing
138 ui object.
138 ui object.
139 """
139 """
140 # _buffers: used for temporary capture of output
140 # _buffers: used for temporary capture of output
141 self._buffers = []
141 self._buffers = []
142 # 3-tuple describing how each buffer in the stack behaves.
142 # 3-tuple describing how each buffer in the stack behaves.
143 # Values are (capture stderr, capture subprocesses, apply labels).
143 # Values are (capture stderr, capture subprocesses, apply labels).
144 self._bufferstates = []
144 self._bufferstates = []
145 # When a buffer is active, defines whether we are expanding labels.
145 # When a buffer is active, defines whether we are expanding labels.
146 # This exists to prevent an extra list lookup.
146 # This exists to prevent an extra list lookup.
147 self._bufferapplylabels = None
147 self._bufferapplylabels = None
148 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
148 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
149 self._reportuntrusted = True
149 self._reportuntrusted = True
150 self._ocfg = config.config() # overlay
150 self._ocfg = config.config() # overlay
151 self._tcfg = config.config() # trusted
151 self._tcfg = config.config() # trusted
152 self._ucfg = config.config() # untrusted
152 self._ucfg = config.config() # untrusted
153 self._trustusers = set()
153 self._trustusers = set()
154 self._trustgroups = set()
154 self._trustgroups = set()
155 self.callhooks = True
155 self.callhooks = True
156 # Insecure server connections requested.
156 # Insecure server connections requested.
157 self.insecureconnections = False
157 self.insecureconnections = False
158 # Blocked time
158 # Blocked time
159 self.logblockedtimes = False
159 self.logblockedtimes = False
160 # color mode: see mercurial/color.py for possible value
160 # color mode: see mercurial/color.py for possible value
161 self._colormode = None
161 self._colormode = None
162 self._terminfoparams = {}
162 self._terminfoparams = {}
163 self._styles = {}
163 self._styles = {}
164
164
165 if src:
165 if src:
166 self.fout = src.fout
166 self.fout = src.fout
167 self.ferr = src.ferr
167 self.ferr = src.ferr
168 self.fin = src.fin
168 self.fin = src.fin
169 self.pageractive = src.pageractive
169 self.pageractive = src.pageractive
170 self._disablepager = src._disablepager
170 self._disablepager = src._disablepager
171
171
172 self._tcfg = src._tcfg.copy()
172 self._tcfg = src._tcfg.copy()
173 self._ucfg = src._ucfg.copy()
173 self._ucfg = src._ucfg.copy()
174 self._ocfg = src._ocfg.copy()
174 self._ocfg = src._ocfg.copy()
175 self._trustusers = src._trustusers.copy()
175 self._trustusers = src._trustusers.copy()
176 self._trustgroups = src._trustgroups.copy()
176 self._trustgroups = src._trustgroups.copy()
177 self.environ = src.environ
177 self.environ = src.environ
178 self.callhooks = src.callhooks
178 self.callhooks = src.callhooks
179 self.insecureconnections = src.insecureconnections
179 self.insecureconnections = src.insecureconnections
180 self._colormode = src._colormode
180 self._colormode = src._colormode
181 self._terminfoparams = src._terminfoparams.copy()
181 self._terminfoparams = src._terminfoparams.copy()
182 self._styles = src._styles.copy()
182 self._styles = src._styles.copy()
183
183
184 self.fixconfig()
184 self.fixconfig()
185
185
186 self.httppasswordmgrdb = src.httppasswordmgrdb
186 self.httppasswordmgrdb = src.httppasswordmgrdb
187 self._blockedtimes = src._blockedtimes
187 self._blockedtimes = src._blockedtimes
188 else:
188 else:
189 self.fout = util.stdout
189 self.fout = util.stdout
190 self.ferr = util.stderr
190 self.ferr = util.stderr
191 self.fin = util.stdin
191 self.fin = util.stdin
192 self.pageractive = False
192 self.pageractive = False
193 self._disablepager = False
193 self._disablepager = False
194
194
195 # shared read-only environment
195 # shared read-only environment
196 self.environ = encoding.environ
196 self.environ = encoding.environ
197
197
198 self.httppasswordmgrdb = httppasswordmgrdbproxy()
198 self.httppasswordmgrdb = httppasswordmgrdbproxy()
199 self._blockedtimes = collections.defaultdict(int)
199 self._blockedtimes = collections.defaultdict(int)
200
200
201 allowed = self.configlist('experimental', 'exportableenviron')
201 allowed = self.configlist('experimental', 'exportableenviron')
202 if '*' in allowed:
202 if '*' in allowed:
203 self._exportableenviron = self.environ
203 self._exportableenviron = self.environ
204 else:
204 else:
205 self._exportableenviron = {}
205 self._exportableenviron = {}
206 for k in allowed:
206 for k in allowed:
207 if k in self.environ:
207 if k in self.environ:
208 self._exportableenviron[k] = self.environ[k]
208 self._exportableenviron[k] = self.environ[k]
209
209
210 @classmethod
210 @classmethod
211 def load(cls):
211 def load(cls):
212 """Create a ui and load global and user configs"""
212 """Create a ui and load global and user configs"""
213 u = cls()
213 u = cls()
214 # we always trust global config files and environment variables
214 # we always trust global config files and environment variables
215 for t, f in rcutil.rccomponents():
215 for t, f in rcutil.rccomponents():
216 if t == 'path':
216 if t == 'path':
217 u.readconfig(f, trust=True)
217 u.readconfig(f, trust=True)
218 elif t == 'items':
218 elif t == 'items':
219 sections = set()
219 sections = set()
220 for section, name, value, source in f:
220 for section, name, value, source in f:
221 # do not set u._ocfg
221 # do not set u._ocfg
222 # XXX clean this up once immutable config object is a thing
222 # XXX clean this up once immutable config object is a thing
223 u._tcfg.set(section, name, value, source)
223 u._tcfg.set(section, name, value, source)
224 u._ucfg.set(section, name, value, source)
224 u._ucfg.set(section, name, value, source)
225 sections.add(section)
225 sections.add(section)
226 for section in sections:
226 for section in sections:
227 u.fixconfig(section=section)
227 u.fixconfig(section=section)
228 else:
228 else:
229 raise error.ProgrammingError('unknown rctype: %s' % t)
229 raise error.ProgrammingError('unknown rctype: %s' % t)
230 return u
230 return u
231
231
232 def copy(self):
232 def copy(self):
233 return self.__class__(self)
233 return self.__class__(self)
234
234
235 def resetstate(self):
235 def resetstate(self):
236 """Clear internal state that shouldn't persist across commands"""
236 """Clear internal state that shouldn't persist across commands"""
237 if self._progbar:
237 if self._progbar:
238 self._progbar.resetstate() # reset last-print time of progress bar
238 self._progbar.resetstate() # reset last-print time of progress bar
239 self.httppasswordmgrdb = httppasswordmgrdbproxy()
239 self.httppasswordmgrdb = httppasswordmgrdbproxy()
240
240
241 @contextlib.contextmanager
241 @contextlib.contextmanager
242 def timeblockedsection(self, key):
242 def timeblockedsection(self, key):
243 # this is open-coded below - search for timeblockedsection to find them
243 # this is open-coded below - search for timeblockedsection to find them
244 starttime = util.timer()
244 starttime = util.timer()
245 try:
245 try:
246 yield
246 yield
247 finally:
247 finally:
248 self._blockedtimes[key + '_blocked'] += \
248 self._blockedtimes[key + '_blocked'] += \
249 (util.timer() - starttime) * 1000
249 (util.timer() - starttime) * 1000
250
250
251 def formatter(self, topic, opts):
251 def formatter(self, topic, opts):
252 return formatter.formatter(self, topic, opts)
252 return formatter.formatter(self, topic, opts)
253
253
254 def _trusted(self, fp, f):
254 def _trusted(self, fp, f):
255 st = util.fstat(fp)
255 st = util.fstat(fp)
256 if util.isowner(st):
256 if util.isowner(st):
257 return True
257 return True
258
258
259 tusers, tgroups = self._trustusers, self._trustgroups
259 tusers, tgroups = self._trustusers, self._trustgroups
260 if '*' in tusers or '*' in tgroups:
260 if '*' in tusers or '*' in tgroups:
261 return True
261 return True
262
262
263 user = util.username(st.st_uid)
263 user = util.username(st.st_uid)
264 group = util.groupname(st.st_gid)
264 group = util.groupname(st.st_gid)
265 if user in tusers or group in tgroups or user == util.username():
265 if user in tusers or group in tgroups or user == util.username():
266 return True
266 return True
267
267
268 if self._reportuntrusted:
268 if self._reportuntrusted:
269 self.warn(_('not trusting file %s from untrusted '
269 self.warn(_('not trusting file %s from untrusted '
270 'user %s, group %s\n') % (f, user, group))
270 'user %s, group %s\n') % (f, user, group))
271 return False
271 return False
272
272
273 def readconfig(self, filename, root=None, trust=False,
273 def readconfig(self, filename, root=None, trust=False,
274 sections=None, remap=None):
274 sections=None, remap=None):
275 try:
275 try:
276 fp = open(filename, u'rb')
276 fp = open(filename, u'rb')
277 except IOError:
277 except IOError:
278 if not sections: # ignore unless we were looking for something
278 if not sections: # ignore unless we were looking for something
279 return
279 return
280 raise
280 raise
281
281
282 cfg = config.config()
282 cfg = config.config()
283 trusted = sections or trust or self._trusted(fp, filename)
283 trusted = sections or trust or self._trusted(fp, filename)
284
284
285 try:
285 try:
286 cfg.read(filename, fp, sections=sections, remap=remap)
286 cfg.read(filename, fp, sections=sections, remap=remap)
287 fp.close()
287 fp.close()
288 except error.ConfigError as inst:
288 except error.ConfigError as inst:
289 if trusted:
289 if trusted:
290 raise
290 raise
291 self.warn(_("ignored: %s\n") % str(inst))
291 self.warn(_("ignored: %s\n") % str(inst))
292
292
293 if self.plain():
293 if self.plain():
294 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
294 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
295 'logtemplate', 'statuscopies', 'style',
295 'logtemplate', 'statuscopies', 'style',
296 'traceback', 'verbose'):
296 'traceback', 'verbose'):
297 if k in cfg['ui']:
297 if k in cfg['ui']:
298 del cfg['ui'][k]
298 del cfg['ui'][k]
299 for k, v in cfg.items('defaults'):
299 for k, v in cfg.items('defaults'):
300 del cfg['defaults'][k]
300 del cfg['defaults'][k]
301 for k, v in cfg.items('commands'):
301 for k, v in cfg.items('commands'):
302 del cfg['commands'][k]
302 del cfg['commands'][k]
303 # Don't remove aliases from the configuration if in the exceptionlist
303 # Don't remove aliases from the configuration if in the exceptionlist
304 if self.plain('alias'):
304 if self.plain('alias'):
305 for k, v in cfg.items('alias'):
305 for k, v in cfg.items('alias'):
306 del cfg['alias'][k]
306 del cfg['alias'][k]
307 if self.plain('revsetalias'):
307 if self.plain('revsetalias'):
308 for k, v in cfg.items('revsetalias'):
308 for k, v in cfg.items('revsetalias'):
309 del cfg['revsetalias'][k]
309 del cfg['revsetalias'][k]
310 if self.plain('templatealias'):
310 if self.plain('templatealias'):
311 for k, v in cfg.items('templatealias'):
311 for k, v in cfg.items('templatealias'):
312 del cfg['templatealias'][k]
312 del cfg['templatealias'][k]
313
313
314 if trusted:
314 if trusted:
315 self._tcfg.update(cfg)
315 self._tcfg.update(cfg)
316 self._tcfg.update(self._ocfg)
316 self._tcfg.update(self._ocfg)
317 self._ucfg.update(cfg)
317 self._ucfg.update(cfg)
318 self._ucfg.update(self._ocfg)
318 self._ucfg.update(self._ocfg)
319
319
320 if root is None:
320 if root is None:
321 root = os.path.expanduser('~')
321 root = os.path.expanduser('~')
322 self.fixconfig(root=root)
322 self.fixconfig(root=root)
323
323
324 def fixconfig(self, root=None, section=None):
324 def fixconfig(self, root=None, section=None):
325 if section in (None, 'paths'):
325 if section in (None, 'paths'):
326 # expand vars and ~
326 # expand vars and ~
327 # translate paths relative to root (or home) into absolute paths
327 # translate paths relative to root (or home) into absolute paths
328 root = root or pycompat.getcwd()
328 root = root or pycompat.getcwd()
329 for c in self._tcfg, self._ucfg, self._ocfg:
329 for c in self._tcfg, self._ucfg, self._ocfg:
330 for n, p in c.items('paths'):
330 for n, p in c.items('paths'):
331 # Ignore sub-options.
331 # Ignore sub-options.
332 if ':' in n:
332 if ':' in n:
333 continue
333 continue
334 if not p:
334 if not p:
335 continue
335 continue
336 if '%%' in p:
336 if '%%' in p:
337 s = self.configsource('paths', n) or 'none'
337 s = self.configsource('paths', n) or 'none'
338 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
338 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
339 % (n, p, s))
339 % (n, p, s))
340 p = p.replace('%%', '%')
340 p = p.replace('%%', '%')
341 p = util.expandpath(p)
341 p = util.expandpath(p)
342 if not util.hasscheme(p) and not os.path.isabs(p):
342 if not util.hasscheme(p) and not os.path.isabs(p):
343 p = os.path.normpath(os.path.join(root, p))
343 p = os.path.normpath(os.path.join(root, p))
344 c.set("paths", n, p)
344 c.set("paths", n, p)
345
345
346 if section in (None, 'ui'):
346 if section in (None, 'ui'):
347 # update ui options
347 # update ui options
348 self.debugflag = self.configbool('ui', 'debug')
348 self.debugflag = self.configbool('ui', 'debug')
349 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
349 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
350 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
350 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
351 if self.verbose and self.quiet:
351 if self.verbose and self.quiet:
352 self.quiet = self.verbose = False
352 self.quiet = self.verbose = False
353 self._reportuntrusted = self.debugflag or self.configbool("ui",
353 self._reportuntrusted = self.debugflag or self.configbool("ui",
354 "report_untrusted", True)
354 "report_untrusted", True)
355 self.tracebackflag = self.configbool('ui', 'traceback', False)
355 self.tracebackflag = self.configbool('ui', 'traceback', False)
356 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
356 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
357
357
358 if section in (None, 'trusted'):
358 if section in (None, 'trusted'):
359 # update trust information
359 # update trust information
360 self._trustusers.update(self.configlist('trusted', 'users'))
360 self._trustusers.update(self.configlist('trusted', 'users'))
361 self._trustgroups.update(self.configlist('trusted', 'groups'))
361 self._trustgroups.update(self.configlist('trusted', 'groups'))
362
362
363 def backupconfig(self, section, item):
363 def backupconfig(self, section, item):
364 return (self._ocfg.backup(section, item),
364 return (self._ocfg.backup(section, item),
365 self._tcfg.backup(section, item),
365 self._tcfg.backup(section, item),
366 self._ucfg.backup(section, item),)
366 self._ucfg.backup(section, item),)
367 def restoreconfig(self, data):
367 def restoreconfig(self, data):
368 self._ocfg.restore(data[0])
368 self._ocfg.restore(data[0])
369 self._tcfg.restore(data[1])
369 self._tcfg.restore(data[1])
370 self._ucfg.restore(data[2])
370 self._ucfg.restore(data[2])
371
371
372 def setconfig(self, section, name, value, source=''):
372 def setconfig(self, section, name, value, source=''):
373 for cfg in (self._ocfg, self._tcfg, self._ucfg):
373 for cfg in (self._ocfg, self._tcfg, self._ucfg):
374 cfg.set(section, name, value, source)
374 cfg.set(section, name, value, source)
375 self.fixconfig(section=section)
375 self.fixconfig(section=section)
376
376
377 def _data(self, untrusted):
377 def _data(self, untrusted):
378 return untrusted and self._ucfg or self._tcfg
378 return untrusted and self._ucfg or self._tcfg
379
379
380 def configsource(self, section, name, untrusted=False):
380 def configsource(self, section, name, untrusted=False):
381 return self._data(untrusted).source(section, name)
381 return self._data(untrusted).source(section, name)
382
382
383 def config(self, section, name, default=None, untrusted=False):
383 def config(self, section, name, default=None, untrusted=False):
384 if isinstance(name, list):
384 if isinstance(name, list):
385 alternates = name
385 alternates = name
386 else:
386 else:
387 alternates = [name]
387 alternates = [name]
388
388
389 for n in alternates:
389 for n in alternates:
390 value = self._data(untrusted).get(section, n, None)
390 value = self._data(untrusted).get(section, n, None)
391 if value is not None:
391 if value is not None:
392 name = n
392 name = n
393 break
393 break
394 else:
394 else:
395 value = default
395 value = default
396
396
397 if self.debugflag and not untrusted and self._reportuntrusted:
397 if self.debugflag and not untrusted and self._reportuntrusted:
398 for n in alternates:
398 for n in alternates:
399 uvalue = self._ucfg.get(section, n)
399 uvalue = self._ucfg.get(section, n)
400 if uvalue is not None and uvalue != value:
400 if uvalue is not None and uvalue != value:
401 self.debug("ignoring untrusted configuration option "
401 self.debug("ignoring untrusted configuration option "
402 "%s.%s = %s\n" % (section, n, uvalue))
402 "%s.%s = %s\n" % (section, n, uvalue))
403 return value
403 return value
404
404
405 def configsuboptions(self, section, name, default=None, untrusted=False):
405 def configsuboptions(self, section, name, default=None, untrusted=False):
406 """Get a config option and all sub-options.
406 """Get a config option and all sub-options.
407
407
408 Some config options have sub-options that are declared with the
408 Some config options have sub-options that are declared with the
409 format "key:opt = value". This method is used to return the main
409 format "key:opt = value". This method is used to return the main
410 option and all its declared sub-options.
410 option and all its declared sub-options.
411
411
412 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
412 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
413 is a dict of defined sub-options where keys and values are strings.
413 is a dict of defined sub-options where keys and values are strings.
414 """
414 """
415 data = self._data(untrusted)
415 data = self._data(untrusted)
416 main = data.get(section, name, default)
416 main = data.get(section, name, default)
417 if self.debugflag and not untrusted and self._reportuntrusted:
417 if self.debugflag and not untrusted and self._reportuntrusted:
418 uvalue = self._ucfg.get(section, name)
418 uvalue = self._ucfg.get(section, name)
419 if uvalue is not None and uvalue != main:
419 if uvalue is not None and uvalue != main:
420 self.debug('ignoring untrusted configuration option '
420 self.debug('ignoring untrusted configuration option '
421 '%s.%s = %s\n' % (section, name, uvalue))
421 '%s.%s = %s\n' % (section, name, uvalue))
422
422
423 sub = {}
423 sub = {}
424 prefix = '%s:' % name
424 prefix = '%s:' % name
425 for k, v in data.items(section):
425 for k, v in data.items(section):
426 if k.startswith(prefix):
426 if k.startswith(prefix):
427 sub[k[len(prefix):]] = v
427 sub[k[len(prefix):]] = v
428
428
429 if self.debugflag and not untrusted and self._reportuntrusted:
429 if self.debugflag and not untrusted and self._reportuntrusted:
430 for k, v in sub.items():
430 for k, v in sub.items():
431 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
431 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
432 if uvalue is not None and uvalue != v:
432 if uvalue is not None and uvalue != v:
433 self.debug('ignoring untrusted configuration option '
433 self.debug('ignoring untrusted configuration option '
434 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
434 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
435
435
436 return main, sub
436 return main, sub
437
437
438 def configpath(self, section, name, default=None, untrusted=False):
438 def configpath(self, section, name, default=None, untrusted=False):
439 'get a path config item, expanded relative to repo root or config file'
439 'get a path config item, expanded relative to repo root or config file'
440 v = self.config(section, name, default, untrusted)
440 v = self.config(section, name, default, untrusted)
441 if v is None:
441 if v is None:
442 return None
442 return None
443 if not os.path.isabs(v) or "://" not in v:
443 if not os.path.isabs(v) or "://" not in v:
444 src = self.configsource(section, name, untrusted)
444 src = self.configsource(section, name, untrusted)
445 if ':' in src:
445 if ':' in src:
446 base = os.path.dirname(src.rsplit(':')[0])
446 base = os.path.dirname(src.rsplit(':')[0])
447 v = os.path.join(base, os.path.expanduser(v))
447 v = os.path.join(base, os.path.expanduser(v))
448 return v
448 return v
449
449
450 def configbool(self, section, name, default=False, untrusted=False):
450 def configbool(self, section, name, default=False, untrusted=False):
451 """parse a configuration element as a boolean
451 """parse a configuration element as a boolean
452
452
453 >>> u = ui(); s = 'foo'
453 >>> u = ui(); s = 'foo'
454 >>> u.setconfig(s, 'true', 'yes')
454 >>> u.setconfig(s, 'true', 'yes')
455 >>> u.configbool(s, 'true')
455 >>> u.configbool(s, 'true')
456 True
456 True
457 >>> u.setconfig(s, 'false', 'no')
457 >>> u.setconfig(s, 'false', 'no')
458 >>> u.configbool(s, 'false')
458 >>> u.configbool(s, 'false')
459 False
459 False
460 >>> u.configbool(s, 'unknown')
460 >>> u.configbool(s, 'unknown')
461 False
461 False
462 >>> u.configbool(s, 'unknown', True)
462 >>> u.configbool(s, 'unknown', True)
463 True
463 True
464 >>> u.setconfig(s, 'invalid', 'somevalue')
464 >>> u.setconfig(s, 'invalid', 'somevalue')
465 >>> u.configbool(s, 'invalid')
465 >>> u.configbool(s, 'invalid')
466 Traceback (most recent call last):
466 Traceback (most recent call last):
467 ...
467 ...
468 ConfigError: foo.invalid is not a boolean ('somevalue')
468 ConfigError: foo.invalid is not a boolean ('somevalue')
469 """
469 """
470
470
471 v = self.config(section, name, None, untrusted)
471 v = self.config(section, name, None, untrusted)
472 if v is None:
472 if v is None:
473 return default
473 return default
474 if isinstance(v, bool):
474 if isinstance(v, bool):
475 return v
475 return v
476 b = util.parsebool(v)
476 b = util.parsebool(v)
477 if b is None:
477 if b is None:
478 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
478 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
479 % (section, name, v))
479 % (section, name, v))
480 return b
480 return b
481
481
482 def configwith(self, convert, section, name, default=None,
482 def configwith(self, convert, section, name, default=None,
483 desc=None, untrusted=False):
483 desc=None, untrusted=False):
484 """parse a configuration element with a conversion function
484 """parse a configuration element with a conversion function
485
485
486 >>> u = ui(); s = 'foo'
486 >>> u = ui(); s = 'foo'
487 >>> u.setconfig(s, 'float1', '42')
487 >>> u.setconfig(s, 'float1', '42')
488 >>> u.configwith(float, s, 'float1')
488 >>> u.configwith(float, s, 'float1')
489 42.0
489 42.0
490 >>> u.setconfig(s, 'float2', '-4.25')
490 >>> u.setconfig(s, 'float2', '-4.25')
491 >>> u.configwith(float, s, 'float2')
491 >>> u.configwith(float, s, 'float2')
492 -4.25
492 -4.25
493 >>> u.configwith(float, s, 'unknown', 7)
493 >>> u.configwith(float, s, 'unknown', 7)
494 7
494 7
495 >>> u.setconfig(s, 'invalid', 'somevalue')
495 >>> u.setconfig(s, 'invalid', 'somevalue')
496 >>> u.configwith(float, s, 'invalid')
496 >>> u.configwith(float, s, 'invalid')
497 Traceback (most recent call last):
497 Traceback (most recent call last):
498 ...
498 ...
499 ConfigError: foo.invalid is not a valid float ('somevalue')
499 ConfigError: foo.invalid is not a valid float ('somevalue')
500 >>> u.configwith(float, s, 'invalid', desc='womble')
500 >>> u.configwith(float, s, 'invalid', desc='womble')
501 Traceback (most recent call last):
501 Traceback (most recent call last):
502 ...
502 ...
503 ConfigError: foo.invalid is not a valid womble ('somevalue')
503 ConfigError: foo.invalid is not a valid womble ('somevalue')
504 """
504 """
505
505
506 v = self.config(section, name, None, untrusted)
506 v = self.config(section, name, None, untrusted)
507 if v is None:
507 if v is None:
508 return default
508 return default
509 try:
509 try:
510 return convert(v)
510 return convert(v)
511 except ValueError:
511 except ValueError:
512 if desc is None:
512 if desc is None:
513 desc = convert.__name__
513 desc = convert.__name__
514 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
514 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
515 % (section, name, desc, v))
515 % (section, name, desc, v))
516
516
517 def configint(self, section, name, default=None, untrusted=False):
517 def configint(self, section, name, default=None, untrusted=False):
518 """parse a configuration element as an integer
518 """parse a configuration element as an integer
519
519
520 >>> u = ui(); s = 'foo'
520 >>> u = ui(); s = 'foo'
521 >>> u.setconfig(s, 'int1', '42')
521 >>> u.setconfig(s, 'int1', '42')
522 >>> u.configint(s, 'int1')
522 >>> u.configint(s, 'int1')
523 42
523 42
524 >>> u.setconfig(s, 'int2', '-42')
524 >>> u.setconfig(s, 'int2', '-42')
525 >>> u.configint(s, 'int2')
525 >>> u.configint(s, 'int2')
526 -42
526 -42
527 >>> u.configint(s, 'unknown', 7)
527 >>> u.configint(s, 'unknown', 7)
528 7
528 7
529 >>> u.setconfig(s, 'invalid', 'somevalue')
529 >>> u.setconfig(s, 'invalid', 'somevalue')
530 >>> u.configint(s, 'invalid')
530 >>> u.configint(s, 'invalid')
531 Traceback (most recent call last):
531 Traceback (most recent call last):
532 ...
532 ...
533 ConfigError: foo.invalid is not a valid integer ('somevalue')
533 ConfigError: foo.invalid is not a valid integer ('somevalue')
534 """
534 """
535
535
536 return self.configwith(int, section, name, default, 'integer',
536 return self.configwith(int, section, name, default, 'integer',
537 untrusted)
537 untrusted)
538
538
539 def configbytes(self, section, name, default=0, untrusted=False):
539 def configbytes(self, section, name, default=0, untrusted=False):
540 """parse a configuration element as a quantity in bytes
540 """parse a configuration element as a quantity in bytes
541
541
542 Units can be specified as b (bytes), k or kb (kilobytes), m or
542 Units can be specified as b (bytes), k or kb (kilobytes), m or
543 mb (megabytes), g or gb (gigabytes).
543 mb (megabytes), g or gb (gigabytes).
544
544
545 >>> u = ui(); s = 'foo'
545 >>> u = ui(); s = 'foo'
546 >>> u.setconfig(s, 'val1', '42')
546 >>> u.setconfig(s, 'val1', '42')
547 >>> u.configbytes(s, 'val1')
547 >>> u.configbytes(s, 'val1')
548 42
548 42
549 >>> u.setconfig(s, 'val2', '42.5 kb')
549 >>> u.setconfig(s, 'val2', '42.5 kb')
550 >>> u.configbytes(s, 'val2')
550 >>> u.configbytes(s, 'val2')
551 43520
551 43520
552 >>> u.configbytes(s, 'unknown', '7 MB')
552 >>> u.configbytes(s, 'unknown', '7 MB')
553 7340032
553 7340032
554 >>> u.setconfig(s, 'invalid', 'somevalue')
554 >>> u.setconfig(s, 'invalid', 'somevalue')
555 >>> u.configbytes(s, 'invalid')
555 >>> u.configbytes(s, 'invalid')
556 Traceback (most recent call last):
556 Traceback (most recent call last):
557 ...
557 ...
558 ConfigError: foo.invalid is not a byte quantity ('somevalue')
558 ConfigError: foo.invalid is not a byte quantity ('somevalue')
559 """
559 """
560
560
561 value = self.config(section, name, None, untrusted)
561 value = self.config(section, name, None, untrusted)
562 if value is None:
562 if value is None:
563 if not isinstance(default, str):
563 if not isinstance(default, str):
564 return default
564 return default
565 value = default
565 value = default
566 try:
566 try:
567 return util.sizetoint(value)
567 return util.sizetoint(value)
568 except error.ParseError:
568 except error.ParseError:
569 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
569 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
570 % (section, name, value))
570 % (section, name, value))
571
571
572 def configlist(self, section, name, default=None, untrusted=False):
572 def configlist(self, section, name, default=None, untrusted=False):
573 """parse a configuration element as a list of comma/space separated
573 """parse a configuration element as a list of comma/space separated
574 strings
574 strings
575
575
576 >>> u = ui(); s = 'foo'
576 >>> u = ui(); s = 'foo'
577 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
577 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
578 >>> u.configlist(s, 'list1')
578 >>> u.configlist(s, 'list1')
579 ['this', 'is', 'a small', 'test']
579 ['this', 'is', 'a small', 'test']
580 """
580 """
581 # default is not always a list
581 # default is not always a list
582 if isinstance(default, bytes):
582 if isinstance(default, bytes):
583 default = config.parselist(default)
583 default = config.parselist(default)
584 return self.configwith(config.parselist, section, name, default or [],
584 return self.configwith(config.parselist, section, name, default or [],
585 'list', untrusted)
585 'list', untrusted)
586
586
587 def hasconfig(self, section, name, untrusted=False):
587 def hasconfig(self, section, name, untrusted=False):
588 return self._data(untrusted).hasitem(section, name)
588 return self._data(untrusted).hasitem(section, name)
589
589
590 def has_section(self, section, untrusted=False):
590 def has_section(self, section, untrusted=False):
591 '''tell whether section exists in config.'''
591 '''tell whether section exists in config.'''
592 return section in self._data(untrusted)
592 return section in self._data(untrusted)
593
593
594 def configitems(self, section, untrusted=False, ignoresub=False):
594 def configitems(self, section, untrusted=False, ignoresub=False):
595 items = self._data(untrusted).items(section)
595 items = self._data(untrusted).items(section)
596 if ignoresub:
596 if ignoresub:
597 newitems = {}
597 newitems = {}
598 for k, v in items:
598 for k, v in items:
599 if ':' not in k:
599 if ':' not in k:
600 newitems[k] = v
600 newitems[k] = v
601 items = newitems.items()
601 items = newitems.items()
602 if self.debugflag and not untrusted and self._reportuntrusted:
602 if self.debugflag and not untrusted and self._reportuntrusted:
603 for k, v in self._ucfg.items(section):
603 for k, v in self._ucfg.items(section):
604 if self._tcfg.get(section, k) != v:
604 if self._tcfg.get(section, k) != v:
605 self.debug("ignoring untrusted configuration option "
605 self.debug("ignoring untrusted configuration option "
606 "%s.%s = %s\n" % (section, k, v))
606 "%s.%s = %s\n" % (section, k, v))
607 return items
607 return items
608
608
609 def walkconfig(self, untrusted=False):
609 def walkconfig(self, untrusted=False):
610 cfg = self._data(untrusted)
610 cfg = self._data(untrusted)
611 for section in cfg.sections():
611 for section in cfg.sections():
612 for name, value in self.configitems(section, untrusted):
612 for name, value in self.configitems(section, untrusted):
613 yield section, name, value
613 yield section, name, value
614
614
615 def plain(self, feature=None):
615 def plain(self, feature=None):
616 '''is plain mode active?
616 '''is plain mode active?
617
617
618 Plain mode means that all configuration variables which affect
618 Plain mode means that all configuration variables which affect
619 the behavior and output of Mercurial should be
619 the behavior and output of Mercurial should be
620 ignored. Additionally, the output should be stable,
620 ignored. Additionally, the output should be stable,
621 reproducible and suitable for use in scripts or applications.
621 reproducible and suitable for use in scripts or applications.
622
622
623 The only way to trigger plain mode is by setting either the
623 The only way to trigger plain mode is by setting either the
624 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
624 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
625
625
626 The return value can either be
626 The return value can either be
627 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
627 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
628 - True otherwise
628 - True otherwise
629 '''
629 '''
630 if ('HGPLAIN' not in encoding.environ and
630 if ('HGPLAIN' not in encoding.environ and
631 'HGPLAINEXCEPT' not in encoding.environ):
631 'HGPLAINEXCEPT' not in encoding.environ):
632 return False
632 return False
633 exceptions = encoding.environ.get('HGPLAINEXCEPT',
633 exceptions = encoding.environ.get('HGPLAINEXCEPT',
634 '').strip().split(',')
634 '').strip().split(',')
635 if feature and exceptions:
635 if feature and exceptions:
636 return feature not in exceptions
636 return feature not in exceptions
637 return True
637 return True
638
638
639 def username(self):
639 def username(self):
640 """Return default username to be used in commits.
640 """Return default username to be used in commits.
641
641
642 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
642 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
643 and stop searching if one of these is set.
643 and stop searching if one of these is set.
644 If not found and ui.askusername is True, ask the user, else use
644 If not found and ui.askusername is True, ask the user, else use
645 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
645 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
646 """
646 """
647 user = encoding.environ.get("HGUSER")
647 user = encoding.environ.get("HGUSER")
648 if user is None:
648 if user is None:
649 user = self.config("ui", ["username", "user"])
649 user = self.config("ui", ["username", "user"])
650 if user is not None:
650 if user is not None:
651 user = os.path.expandvars(user)
651 user = os.path.expandvars(user)
652 if user is None:
652 if user is None:
653 user = encoding.environ.get("EMAIL")
653 user = encoding.environ.get("EMAIL")
654 if user is None and self.configbool("ui", "askusername"):
654 if user is None and self.configbool("ui", "askusername"):
655 user = self.prompt(_("enter a commit username:"), default=None)
655 user = self.prompt(_("enter a commit username:"), default=None)
656 if user is None and not self.interactive():
656 if user is None and not self.interactive():
657 try:
657 try:
658 user = '%s@%s' % (util.getuser(), socket.getfqdn())
658 user = '%s@%s' % (util.getuser(), socket.getfqdn())
659 self.warn(_("no username found, using '%s' instead\n") % user)
659 self.warn(_("no username found, using '%s' instead\n") % user)
660 except KeyError:
660 except KeyError:
661 pass
661 pass
662 if not user:
662 if not user:
663 raise error.Abort(_('no username supplied'),
663 raise error.Abort(_('no username supplied'),
664 hint=_("use 'hg config --edit' "
664 hint=_("use 'hg config --edit' "
665 'to set your username'))
665 'to set your username'))
666 if "\n" in user:
666 if "\n" in user:
667 raise error.Abort(_("username %s contains a newline\n")
667 raise error.Abort(_("username %s contains a newline\n")
668 % repr(user))
668 % repr(user))
669 return user
669 return user
670
670
671 def shortuser(self, user):
671 def shortuser(self, user):
672 """Return a short representation of a user name or email address."""
672 """Return a short representation of a user name or email address."""
673 if not self.verbose:
673 if not self.verbose:
674 user = util.shortuser(user)
674 user = util.shortuser(user)
675 return user
675 return user
676
676
677 def expandpath(self, loc, default=None):
677 def expandpath(self, loc, default=None):
678 """Return repository location relative to cwd or from [paths]"""
678 """Return repository location relative to cwd or from [paths]"""
679 try:
679 try:
680 p = self.paths.getpath(loc)
680 p = self.paths.getpath(loc)
681 if p:
681 if p:
682 return p.rawloc
682 return p.rawloc
683 except error.RepoError:
683 except error.RepoError:
684 pass
684 pass
685
685
686 if default:
686 if default:
687 try:
687 try:
688 p = self.paths.getpath(default)
688 p = self.paths.getpath(default)
689 if p:
689 if p:
690 return p.rawloc
690 return p.rawloc
691 except error.RepoError:
691 except error.RepoError:
692 pass
692 pass
693
693
694 return loc
694 return loc
695
695
696 @util.propertycache
696 @util.propertycache
697 def paths(self):
697 def paths(self):
698 return paths(self)
698 return paths(self)
699
699
700 def pushbuffer(self, error=False, subproc=False, labeled=False):
700 def pushbuffer(self, error=False, subproc=False, labeled=False):
701 """install a buffer to capture standard output of the ui object
701 """install a buffer to capture standard output of the ui object
702
702
703 If error is True, the error output will be captured too.
703 If error is True, the error output will be captured too.
704
704
705 If subproc is True, output from subprocesses (typically hooks) will be
705 If subproc is True, output from subprocesses (typically hooks) will be
706 captured too.
706 captured too.
707
707
708 If labeled is True, any labels associated with buffered
708 If labeled is True, any labels associated with buffered
709 output will be handled. By default, this has no effect
709 output will be handled. By default, this has no effect
710 on the output returned, but extensions and GUI tools may
710 on the output returned, but extensions and GUI tools may
711 handle this argument and returned styled output. If output
711 handle this argument and returned styled output. If output
712 is being buffered so it can be captured and parsed or
712 is being buffered so it can be captured and parsed or
713 processed, labeled should not be set to True.
713 processed, labeled should not be set to True.
714 """
714 """
715 self._buffers.append([])
715 self._buffers.append([])
716 self._bufferstates.append((error, subproc, labeled))
716 self._bufferstates.append((error, subproc, labeled))
717 self._bufferapplylabels = labeled
717 self._bufferapplylabels = labeled
718
718
719 def popbuffer(self):
719 def popbuffer(self):
720 '''pop the last buffer and return the buffered output'''
720 '''pop the last buffer and return the buffered output'''
721 self._bufferstates.pop()
721 self._bufferstates.pop()
722 if self._bufferstates:
722 if self._bufferstates:
723 self._bufferapplylabels = self._bufferstates[-1][2]
723 self._bufferapplylabels = self._bufferstates[-1][2]
724 else:
724 else:
725 self._bufferapplylabels = None
725 self._bufferapplylabels = None
726
726
727 return "".join(self._buffers.pop())
727 return "".join(self._buffers.pop())
728
728
729 def write(self, *args, **opts):
729 def write(self, *args, **opts):
730 '''write args to output
730 '''write args to output
731
731
732 By default, this method simply writes to the buffer or stdout.
732 By default, this method simply writes to the buffer or stdout.
733 Color mode can be set on the UI class to have the output decorated
733 Color mode can be set on the UI class to have the output decorated
734 with color modifier before being written to stdout.
734 with color modifier before being written to stdout.
735
735
736 The color used is controlled by an optional keyword argument, "label".
736 The color used is controlled by an optional keyword argument, "label".
737 This should be a string containing label names separated by space.
737 This should be a string containing label names separated by space.
738 Label names take the form of "topic.type". For example, ui.debug()
738 Label names take the form of "topic.type". For example, ui.debug()
739 issues a label of "ui.debug".
739 issues a label of "ui.debug".
740
740
741 When labeling output for a specific command, a label of
741 When labeling output for a specific command, a label of
742 "cmdname.type" is recommended. For example, status issues
742 "cmdname.type" is recommended. For example, status issues
743 a label of "status.modified" for modified files.
743 a label of "status.modified" for modified files.
744 '''
744 '''
745 if self._buffers and not opts.get('prompt', False):
745 if self._buffers and not opts.get('prompt', False):
746 if self._bufferapplylabels:
746 if self._bufferapplylabels:
747 label = opts.get('label', '')
747 label = opts.get('label', '')
748 self._buffers[-1].extend(self.label(a, label) for a in args)
748 self._buffers[-1].extend(self.label(a, label) for a in args)
749 else:
749 else:
750 self._buffers[-1].extend(args)
750 self._buffers[-1].extend(args)
751 elif self._colormode == 'win32':
751 elif self._colormode == 'win32':
752 # windows color printing is its own can of crab, defer to
752 # windows color printing is its own can of crab, defer to
753 # the color module and that is it.
753 # the color module and that is it.
754 color.win32print(self, self._write, *args, **opts)
754 color.win32print(self, self._write, *args, **opts)
755 else:
755 else:
756 msgs = args
756 msgs = args
757 if self._colormode is not None:
757 if self._colormode is not None:
758 label = opts.get('label', '')
758 label = opts.get('label', '')
759 msgs = [self.label(a, label) for a in args]
759 msgs = [self.label(a, label) for a in args]
760 self._write(*msgs, **opts)
760 self._write(*msgs, **opts)
761
761
762 def _write(self, *msgs, **opts):
762 def _write(self, *msgs, **opts):
763 self._progclear()
763 self._progclear()
764 # opencode timeblockedsection because this is a critical path
764 # opencode timeblockedsection because this is a critical path
765 starttime = util.timer()
765 starttime = util.timer()
766 try:
766 try:
767 for a in msgs:
767 for a in msgs:
768 self.fout.write(a)
768 self.fout.write(a)
769 finally:
769 finally:
770 self._blockedtimes['stdio_blocked'] += \
770 self._blockedtimes['stdio_blocked'] += \
771 (util.timer() - starttime) * 1000
771 (util.timer() - starttime) * 1000
772
772
773 def write_err(self, *args, **opts):
773 def write_err(self, *args, **opts):
774 self._progclear()
774 self._progclear()
775 if self._bufferstates and self._bufferstates[-1][0]:
775 if self._bufferstates and self._bufferstates[-1][0]:
776 self.write(*args, **opts)
776 self.write(*args, **opts)
777 elif self._colormode == 'win32':
777 elif self._colormode == 'win32':
778 # windows color printing is its own can of crab, defer to
778 # windows color printing is its own can of crab, defer to
779 # the color module and that is it.
779 # the color module and that is it.
780 color.win32print(self, self._write_err, *args, **opts)
780 color.win32print(self, self._write_err, *args, **opts)
781 else:
781 else:
782 msgs = args
782 msgs = args
783 if self._colormode is not None:
783 if self._colormode is not None:
784 label = opts.get('label', '')
784 label = opts.get('label', '')
785 msgs = [self.label(a, label) for a in args]
785 msgs = [self.label(a, label) for a in args]
786 self._write_err(*msgs, **opts)
786 self._write_err(*msgs, **opts)
787
787
788 def _write_err(self, *msgs, **opts):
788 def _write_err(self, *msgs, **opts):
789 try:
789 try:
790 with self.timeblockedsection('stdio'):
790 with self.timeblockedsection('stdio'):
791 if not getattr(self.fout, 'closed', False):
791 if not getattr(self.fout, 'closed', False):
792 self.fout.flush()
792 self.fout.flush()
793 for a in msgs:
793 for a in msgs:
794 self.ferr.write(a)
794 self.ferr.write(a)
795 # stderr may be buffered under win32 when redirected to files,
795 # stderr may be buffered under win32 when redirected to files,
796 # including stdout.
796 # including stdout.
797 if not getattr(self.ferr, 'closed', False):
797 if not getattr(self.ferr, 'closed', False):
798 self.ferr.flush()
798 self.ferr.flush()
799 except IOError as inst:
799 except IOError as inst:
800 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
800 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
801 raise
801 raise
802
802
803 def flush(self):
803 def flush(self):
804 # opencode timeblockedsection because this is a critical path
804 # opencode timeblockedsection because this is a critical path
805 starttime = util.timer()
805 starttime = util.timer()
806 try:
806 try:
807 try: self.fout.flush()
807 try: self.fout.flush()
808 except (IOError, ValueError): pass
808 except (IOError, ValueError): pass
809 try: self.ferr.flush()
809 try: self.ferr.flush()
810 except (IOError, ValueError): pass
810 except (IOError, ValueError): pass
811 finally:
811 finally:
812 self._blockedtimes['stdio_blocked'] += \
812 self._blockedtimes['stdio_blocked'] += \
813 (util.timer() - starttime) * 1000
813 (util.timer() - starttime) * 1000
814
814
815 def _isatty(self, fh):
815 def _isatty(self, fh):
816 if self.configbool('ui', 'nontty', False):
816 if self.configbool('ui', 'nontty', False):
817 return False
817 return False
818 return util.isatty(fh)
818 return util.isatty(fh)
819
819
820 def disablepager(self):
820 def disablepager(self):
821 self._disablepager = True
821 self._disablepager = True
822
822
823 def pager(self, command):
823 def pager(self, command):
824 """Start a pager for subsequent command output.
824 """Start a pager for subsequent command output.
825
825
826 Commands which produce a long stream of output should call
826 Commands which produce a long stream of output should call
827 this function to activate the user's preferred pagination
827 this function to activate the user's preferred pagination
828 mechanism (which may be no pager). Calling this function
828 mechanism (which may be no pager). Calling this function
829 precludes any future use of interactive functionality, such as
829 precludes any future use of interactive functionality, such as
830 prompting the user or activating curses.
830 prompting the user or activating curses.
831
831
832 Args:
832 Args:
833 command: The full, non-aliased name of the command. That is, "log"
833 command: The full, non-aliased name of the command. That is, "log"
834 not "history, "summary" not "summ", etc.
834 not "history, "summary" not "summ", etc.
835 """
835 """
836 if (self._disablepager
836 if (self._disablepager
837 or self.pageractive
837 or self.pageractive
838 or command in self.configlist('pager', 'ignore')
838 or command in self.configlist('pager', 'ignore')
839 or not self.configbool('pager', 'enable', True)
839 or not self.configbool('pager', 'enable', True)
840 or not self.configbool('pager', 'attend-' + command, True)
840 or not self.configbool('pager', 'attend-' + command, True)
841 # TODO: if we want to allow HGPLAINEXCEPT=pager,
841 # TODO: if we want to allow HGPLAINEXCEPT=pager,
842 # formatted() will need some adjustment.
842 # formatted() will need some adjustment.
843 or not self.formatted()
843 or not self.formatted()
844 or self.plain()
844 or self.plain()
845 # TODO: expose debugger-enabled on the UI object
845 # TODO: expose debugger-enabled on the UI object
846 or '--debugger' in pycompat.sysargv):
846 or '--debugger' in pycompat.sysargv):
847 # We only want to paginate if the ui appears to be
847 # We only want to paginate if the ui appears to be
848 # interactive, the user didn't say HGPLAIN or
848 # interactive, the user didn't say HGPLAIN or
849 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
849 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
850 return
850 return
851
851
852 fallbackpager = 'more'
852 fallbackpager = 'more'
853 pagercmd = self.config('pager', 'pager', fallbackpager)
853 pagercmd = self.config('pager', 'pager', fallbackpager)
854 if not pagercmd:
854 if not pagercmd:
855 return
855 return
856
856
857 self.debug('starting pager for command %r\n' % command)
857 self.debug('starting pager for command %r\n' % command)
858 self.flush()
858 self.flush()
859
859
860 wasformatted = self.formatted()
860 wasformatted = self.formatted()
861 if util.safehasattr(signal, "SIGPIPE"):
861 if util.safehasattr(signal, "SIGPIPE"):
862 signal.signal(signal.SIGPIPE, _catchterm)
862 signal.signal(signal.SIGPIPE, _catchterm)
863 if self._runpager(pagercmd):
863 if self._runpager(pagercmd):
864 self.pageractive = True
864 self.pageractive = True
865 # Preserve the formatted-ness of the UI. This is important
865 # Preserve the formatted-ness of the UI. This is important
866 # because we mess with stdout, which might confuse
866 # because we mess with stdout, which might confuse
867 # auto-detection of things being formatted.
867 # auto-detection of things being formatted.
868 self.setconfig('ui', 'formatted', wasformatted, 'pager')
868 self.setconfig('ui', 'formatted', wasformatted, 'pager')
869 self.setconfig('ui', 'interactive', False, 'pager')
869 self.setconfig('ui', 'interactive', False, 'pager')
870
870
871 # If pagermode differs from color.mode, reconfigure color now that
871 # If pagermode differs from color.mode, reconfigure color now that
872 # pageractive is set.
872 # pageractive is set.
873 cm = self._colormode
873 cm = self._colormode
874 if cm != self.config('color', 'pagermode', cm):
874 if cm != self.config('color', 'pagermode', cm):
875 color.setup(self)
875 color.setup(self)
876 else:
876 else:
877 # If the pager can't be spawned in dispatch when --pager=on is
877 # If the pager can't be spawned in dispatch when --pager=on is
878 # given, don't try again when the command runs, to avoid a duplicate
878 # given, don't try again when the command runs, to avoid a duplicate
879 # warning about a missing pager command.
879 # warning about a missing pager command.
880 self.disablepager()
880 self.disablepager()
881
881
882 def _runpager(self, command):
882 def _runpager(self, command):
883 """Actually start the pager and set up file descriptors.
883 """Actually start the pager and set up file descriptors.
884
884
885 This is separate in part so that extensions (like chg) can
885 This is separate in part so that extensions (like chg) can
886 override how a pager is invoked.
886 override how a pager is invoked.
887 """
887 """
888 if command == 'cat':
888 if command == 'cat':
889 # Save ourselves some work.
889 # Save ourselves some work.
890 return False
890 return False
891 # If the command doesn't contain any of these characters, we
891 # If the command doesn't contain any of these characters, we
892 # assume it's a binary and exec it directly. This means for
892 # assume it's a binary and exec it directly. This means for
893 # simple pager command configurations, we can degrade
893 # simple pager command configurations, we can degrade
894 # gracefully and tell the user about their broken pager.
894 # gracefully and tell the user about their broken pager.
895 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
895 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
896
896
897 if pycompat.osname == 'nt' and not shell:
897 if pycompat.osname == 'nt' and not shell:
898 # Window's built-in `more` cannot be invoked with shell=False, but
898 # Window's built-in `more` cannot be invoked with shell=False, but
899 # its `more.com` can. Hide this implementation detail from the
899 # its `more.com` can. Hide this implementation detail from the
900 # user so we can also get sane bad PAGER behavior. MSYS has
900 # user so we can also get sane bad PAGER behavior. MSYS has
901 # `more.exe`, so do a cmd.exe style resolution of the executable to
901 # `more.exe`, so do a cmd.exe style resolution of the executable to
902 # determine which one to use.
902 # determine which one to use.
903 fullcmd = util.findexe(command)
903 fullcmd = util.findexe(command)
904 if not fullcmd:
904 if not fullcmd:
905 self.warn(_("missing pager command '%s', skipping pager\n")
905 self.warn(_("missing pager command '%s', skipping pager\n")
906 % command)
906 % command)
907 return False
907 return False
908
908
909 command = fullcmd
909 command = fullcmd
910
910
911 try:
911 try:
912 pager = subprocess.Popen(
912 pager = subprocess.Popen(
913 command, shell=shell, bufsize=-1,
913 command, shell=shell, bufsize=-1,
914 close_fds=util.closefds, stdin=subprocess.PIPE,
914 close_fds=util.closefds, stdin=subprocess.PIPE,
915 stdout=util.stdout, stderr=util.stderr)
915 stdout=util.stdout, stderr=util.stderr)
916 except OSError as e:
916 except OSError as e:
917 if e.errno == errno.ENOENT and not shell:
917 if e.errno == errno.ENOENT and not shell:
918 self.warn(_("missing pager command '%s', skipping pager\n")
918 self.warn(_("missing pager command '%s', skipping pager\n")
919 % command)
919 % command)
920 return False
920 return False
921 raise
921 raise
922
922
923 # back up original file descriptors
923 # back up original file descriptors
924 stdoutfd = os.dup(util.stdout.fileno())
924 stdoutfd = os.dup(util.stdout.fileno())
925 stderrfd = os.dup(util.stderr.fileno())
925 stderrfd = os.dup(util.stderr.fileno())
926
926
927 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
927 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
928 if self._isatty(util.stderr):
928 if self._isatty(util.stderr):
929 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
929 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
930
930
931 @atexit.register
931 @atexit.register
932 def killpager():
932 def killpager():
933 if util.safehasattr(signal, "SIGINT"):
933 if util.safehasattr(signal, "SIGINT"):
934 signal.signal(signal.SIGINT, signal.SIG_IGN)
934 signal.signal(signal.SIGINT, signal.SIG_IGN)
935 # restore original fds, closing pager.stdin copies in the process
935 # restore original fds, closing pager.stdin copies in the process
936 os.dup2(stdoutfd, util.stdout.fileno())
936 os.dup2(stdoutfd, util.stdout.fileno())
937 os.dup2(stderrfd, util.stderr.fileno())
937 os.dup2(stderrfd, util.stderr.fileno())
938 pager.stdin.close()
938 pager.stdin.close()
939 pager.wait()
939 pager.wait()
940
940
941 return True
941 return True
942
942
943 def interface(self, feature):
943 def interface(self, feature):
944 """what interface to use for interactive console features?
944 """what interface to use for interactive console features?
945
945
946 The interface is controlled by the value of `ui.interface` but also by
946 The interface is controlled by the value of `ui.interface` but also by
947 the value of feature-specific configuration. For example:
947 the value of feature-specific configuration. For example:
948
948
949 ui.interface.histedit = text
949 ui.interface.histedit = text
950 ui.interface.chunkselector = curses
950 ui.interface.chunkselector = curses
951
951
952 Here the features are "histedit" and "chunkselector".
952 Here the features are "histedit" and "chunkselector".
953
953
954 The configuration above means that the default interfaces for commands
954 The configuration above means that the default interfaces for commands
955 is curses, the interface for histedit is text and the interface for
955 is curses, the interface for histedit is text and the interface for
956 selecting chunk is crecord (the best curses interface available).
956 selecting chunk is crecord (the best curses interface available).
957
957
958 Consider the following example:
958 Consider the following example:
959 ui.interface = curses
959 ui.interface = curses
960 ui.interface.histedit = text
960 ui.interface.histedit = text
961
961
962 Then histedit will use the text interface and chunkselector will use
962 Then histedit will use the text interface and chunkselector will use
963 the default curses interface (crecord at the moment).
963 the default curses interface (crecord at the moment).
964 """
964 """
965 alldefaults = frozenset(["text", "curses"])
965 alldefaults = frozenset(["text", "curses"])
966
966
967 featureinterfaces = {
967 featureinterfaces = {
968 "chunkselector": [
968 "chunkselector": [
969 "text",
969 "text",
970 "curses",
970 "curses",
971 ]
971 ]
972 }
972 }
973
973
974 # Feature-specific interface
974 # Feature-specific interface
975 if feature not in featureinterfaces.keys():
975 if feature not in featureinterfaces.keys():
976 # Programming error, not user error
976 # Programming error, not user error
977 raise ValueError("Unknown feature requested %s" % feature)
977 raise ValueError("Unknown feature requested %s" % feature)
978
978
979 availableinterfaces = frozenset(featureinterfaces[feature])
979 availableinterfaces = frozenset(featureinterfaces[feature])
980 if alldefaults > availableinterfaces:
980 if alldefaults > availableinterfaces:
981 # Programming error, not user error. We need a use case to
981 # Programming error, not user error. We need a use case to
982 # define the right thing to do here.
982 # define the right thing to do here.
983 raise ValueError(
983 raise ValueError(
984 "Feature %s does not handle all default interfaces" %
984 "Feature %s does not handle all default interfaces" %
985 feature)
985 feature)
986
986
987 if self.plain():
987 if self.plain():
988 return "text"
988 return "text"
989
989
990 # Default interface for all the features
990 # Default interface for all the features
991 defaultinterface = "text"
991 defaultinterface = "text"
992 i = self.config("ui", "interface", None)
992 i = self.config("ui", "interface", None)
993 if i in alldefaults:
993 if i in alldefaults:
994 defaultinterface = i
994 defaultinterface = i
995
995
996 choseninterface = defaultinterface
996 choseninterface = defaultinterface
997 f = self.config("ui", "interface.%s" % feature, None)
997 f = self.config("ui", "interface.%s" % feature, None)
998 if f in availableinterfaces:
998 if f in availableinterfaces:
999 choseninterface = f
999 choseninterface = f
1000
1000
1001 if i is not None and defaultinterface != i:
1001 if i is not None and defaultinterface != i:
1002 if f is not None:
1002 if f is not None:
1003 self.warn(_("invalid value for ui.interface: %s\n") %
1003 self.warn(_("invalid value for ui.interface: %s\n") %
1004 (i,))
1004 (i,))
1005 else:
1005 else:
1006 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1006 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1007 (i, choseninterface))
1007 (i, choseninterface))
1008 if f is not None and choseninterface != f:
1008 if f is not None and choseninterface != f:
1009 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1009 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1010 (feature, f, choseninterface))
1010 (feature, f, choseninterface))
1011
1011
1012 return choseninterface
1012 return choseninterface
1013
1013
1014 def interactive(self):
1014 def interactive(self):
1015 '''is interactive input allowed?
1015 '''is interactive input allowed?
1016
1016
1017 An interactive session is a session where input can be reasonably read
1017 An interactive session is a session where input can be reasonably read
1018 from `sys.stdin'. If this function returns false, any attempt to read
1018 from `sys.stdin'. If this function returns false, any attempt to read
1019 from stdin should fail with an error, unless a sensible default has been
1019 from stdin should fail with an error, unless a sensible default has been
1020 specified.
1020 specified.
1021
1021
1022 Interactiveness is triggered by the value of the `ui.interactive'
1022 Interactiveness is triggered by the value of the `ui.interactive'
1023 configuration variable or - if it is unset - when `sys.stdin' points
1023 configuration variable or - if it is unset - when `sys.stdin' points
1024 to a terminal device.
1024 to a terminal device.
1025
1025
1026 This function refers to input only; for output, see `ui.formatted()'.
1026 This function refers to input only; for output, see `ui.formatted()'.
1027 '''
1027 '''
1028 i = self.configbool("ui", "interactive", None)
1028 i = self.configbool("ui", "interactive", None)
1029 if i is None:
1029 if i is None:
1030 # some environments replace stdin without implementing isatty
1030 # some environments replace stdin without implementing isatty
1031 # usually those are non-interactive
1031 # usually those are non-interactive
1032 return self._isatty(self.fin)
1032 return self._isatty(self.fin)
1033
1033
1034 return i
1034 return i
1035
1035
1036 def termwidth(self):
1036 def termwidth(self):
1037 '''how wide is the terminal in columns?
1037 '''how wide is the terminal in columns?
1038 '''
1038 '''
1039 if 'COLUMNS' in encoding.environ:
1039 if 'COLUMNS' in encoding.environ:
1040 try:
1040 try:
1041 return int(encoding.environ['COLUMNS'])
1041 return int(encoding.environ['COLUMNS'])
1042 except ValueError:
1042 except ValueError:
1043 pass
1043 pass
1044 return scmutil.termsize(self)[0]
1044 return scmutil.termsize(self)[0]
1045
1045
1046 def formatted(self):
1046 def formatted(self):
1047 '''should formatted output be used?
1047 '''should formatted output be used?
1048
1048
1049 It is often desirable to format the output to suite the output medium.
1049 It is often desirable to format the output to suite the output medium.
1050 Examples of this are truncating long lines or colorizing messages.
1050 Examples of this are truncating long lines or colorizing messages.
1051 However, this is not often not desirable when piping output into other
1051 However, this is not often not desirable when piping output into other
1052 utilities, e.g. `grep'.
1052 utilities, e.g. `grep'.
1053
1053
1054 Formatted output is triggered by the value of the `ui.formatted'
1054 Formatted output is triggered by the value of the `ui.formatted'
1055 configuration variable or - if it is unset - when `sys.stdout' points
1055 configuration variable or - if it is unset - when `sys.stdout' points
1056 to a terminal device. Please note that `ui.formatted' should be
1056 to a terminal device. Please note that `ui.formatted' should be
1057 considered an implementation detail; it is not intended for use outside
1057 considered an implementation detail; it is not intended for use outside
1058 Mercurial or its extensions.
1058 Mercurial or its extensions.
1059
1059
1060 This function refers to output only; for input, see `ui.interactive()'.
1060 This function refers to output only; for input, see `ui.interactive()'.
1061 This function always returns false when in plain mode, see `ui.plain()'.
1061 This function always returns false when in plain mode, see `ui.plain()'.
1062 '''
1062 '''
1063 if self.plain():
1063 if self.plain():
1064 return False
1064 return False
1065
1065
1066 i = self.configbool("ui", "formatted", None)
1066 i = self.configbool("ui", "formatted", None)
1067 if i is None:
1067 if i is None:
1068 # some environments replace stdout without implementing isatty
1068 # some environments replace stdout without implementing isatty
1069 # usually those are non-interactive
1069 # usually those are non-interactive
1070 return self._isatty(self.fout)
1070 return self._isatty(self.fout)
1071
1071
1072 return i
1072 return i
1073
1073
1074 def _readline(self, prompt=''):
1074 def _readline(self, prompt=''):
1075 if self._isatty(self.fin):
1075 if self._isatty(self.fin):
1076 try:
1076 try:
1077 # magically add command line editing support, where
1077 # magically add command line editing support, where
1078 # available
1078 # available
1079 import readline
1079 import readline
1080 # force demandimport to really load the module
1080 # force demandimport to really load the module
1081 readline.read_history_file
1081 readline.read_history_file
1082 # windows sometimes raises something other than ImportError
1082 # windows sometimes raises something other than ImportError
1083 except Exception:
1083 except Exception:
1084 pass
1084 pass
1085
1085
1086 # call write() so output goes through subclassed implementation
1086 # call write() so output goes through subclassed implementation
1087 # e.g. color extension on Windows
1087 # e.g. color extension on Windows
1088 self.write(prompt, prompt=True)
1088 self.write(prompt, prompt=True)
1089
1089
1090 # instead of trying to emulate raw_input, swap (self.fin,
1090 # instead of trying to emulate raw_input, swap (self.fin,
1091 # self.fout) with (sys.stdin, sys.stdout)
1091 # self.fout) with (sys.stdin, sys.stdout)
1092 oldin = sys.stdin
1092 oldin = sys.stdin
1093 oldout = sys.stdout
1093 oldout = sys.stdout
1094 sys.stdin = self.fin
1094 sys.stdin = self.fin
1095 sys.stdout = self.fout
1095 sys.stdout = self.fout
1096 # prompt ' ' must exist; otherwise readline may delete entire line
1096 # prompt ' ' must exist; otherwise readline may delete entire line
1097 # - http://bugs.python.org/issue12833
1097 # - http://bugs.python.org/issue12833
1098 with self.timeblockedsection('stdio'):
1098 with self.timeblockedsection('stdio'):
1099 line = raw_input(' ')
1099 line = raw_input(' ')
1100 sys.stdin = oldin
1100 sys.stdin = oldin
1101 sys.stdout = oldout
1101 sys.stdout = oldout
1102
1102
1103 # When stdin is in binary mode on Windows, it can cause
1103 # When stdin is in binary mode on Windows, it can cause
1104 # raw_input() to emit an extra trailing carriage return
1104 # raw_input() to emit an extra trailing carriage return
1105 if os.linesep == '\r\n' and line and line[-1] == '\r':
1105 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1106 line = line[:-1]
1106 line = line[:-1]
1107 return line
1107 return line
1108
1108
1109 def prompt(self, msg, default="y"):
1109 def prompt(self, msg, default="y"):
1110 """Prompt user with msg, read response.
1110 """Prompt user with msg, read response.
1111 If ui is not interactive, the default is returned.
1111 If ui is not interactive, the default is returned.
1112 """
1112 """
1113 if not self.interactive():
1113 if not self.interactive():
1114 self.write(msg, ' ', default or '', "\n")
1114 self.write(msg, ' ', default or '', "\n")
1115 return default
1115 return default
1116 try:
1116 try:
1117 r = self._readline(self.label(msg, 'ui.prompt'))
1117 r = self._readline(self.label(msg, 'ui.prompt'))
1118 if not r:
1118 if not r:
1119 r = default
1119 r = default
1120 if self.configbool('ui', 'promptecho'):
1120 if self.configbool('ui', 'promptecho'):
1121 self.write(r, "\n")
1121 self.write(r, "\n")
1122 return r
1122 return r
1123 except EOFError:
1123 except EOFError:
1124 raise error.ResponseExpected()
1124 raise error.ResponseExpected()
1125
1125
1126 @staticmethod
1126 @staticmethod
1127 def extractchoices(prompt):
1127 def extractchoices(prompt):
1128 """Extract prompt message and list of choices from specified prompt.
1128 """Extract prompt message and list of choices from specified prompt.
1129
1129
1130 This returns tuple "(message, choices)", and "choices" is the
1130 This returns tuple "(message, choices)", and "choices" is the
1131 list of tuple "(response character, text without &)".
1131 list of tuple "(response character, text without &)".
1132
1132
1133 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
1133 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
1134 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1134 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1135 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
1135 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
1136 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1136 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1137 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
1137 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
1138 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1138 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1139 """
1139 """
1140
1140
1141 # Sadly, the prompt string may have been built with a filename
1141 # Sadly, the prompt string may have been built with a filename
1142 # containing "$$" so let's try to find the first valid-looking
1142 # containing "$$" so let's try to find the first valid-looking
1143 # prompt to start parsing. Sadly, we also can't rely on
1143 # prompt to start parsing. Sadly, we also can't rely on
1144 # choices containing spaces, ASCII, or basically anything
1144 # choices containing spaces, ASCII, or basically anything
1145 # except an ampersand followed by a character.
1145 # except an ampersand followed by a character.
1146 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1146 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1147 msg = m.group(1)
1147 msg = m.group(1)
1148 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1148 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1149 return (msg,
1149 return (msg,
1150 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
1150 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
1151 for s in choices])
1151 for s in choices])
1152
1152
1153 def promptchoice(self, prompt, default=0):
1153 def promptchoice(self, prompt, default=0):
1154 """Prompt user with a message, read response, and ensure it matches
1154 """Prompt user with a message, read response, and ensure it matches
1155 one of the provided choices. The prompt is formatted as follows:
1155 one of the provided choices. The prompt is formatted as follows:
1156
1156
1157 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1157 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1158
1158
1159 The index of the choice is returned. Responses are case
1159 The index of the choice is returned. Responses are case
1160 insensitive. If ui is not interactive, the default is
1160 insensitive. If ui is not interactive, the default is
1161 returned.
1161 returned.
1162 """
1162 """
1163
1163
1164 msg, choices = self.extractchoices(prompt)
1164 msg, choices = self.extractchoices(prompt)
1165 resps = [r for r, t in choices]
1165 resps = [r for r, t in choices]
1166 while True:
1166 while True:
1167 r = self.prompt(msg, resps[default])
1167 r = self.prompt(msg, resps[default])
1168 if r.lower() in resps:
1168 if r.lower() in resps:
1169 return resps.index(r.lower())
1169 return resps.index(r.lower())
1170 self.write(_("unrecognized response\n"))
1170 self.write(_("unrecognized response\n"))
1171
1171
1172 def getpass(self, prompt=None, default=None):
1172 def getpass(self, prompt=None, default=None):
1173 if not self.interactive():
1173 if not self.interactive():
1174 return default
1174 return default
1175 try:
1175 try:
1176 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1176 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1177 # disable getpass() only if explicitly specified. it's still valid
1177 # disable getpass() only if explicitly specified. it's still valid
1178 # to interact with tty even if fin is not a tty.
1178 # to interact with tty even if fin is not a tty.
1179 with self.timeblockedsection('stdio'):
1179 with self.timeblockedsection('stdio'):
1180 if self.configbool('ui', 'nontty'):
1180 if self.configbool('ui', 'nontty'):
1181 l = self.fin.readline()
1181 l = self.fin.readline()
1182 if not l:
1182 if not l:
1183 raise EOFError
1183 raise EOFError
1184 return l.rstrip('\n')
1184 return l.rstrip('\n')
1185 else:
1185 else:
1186 return getpass.getpass('')
1186 return getpass.getpass('')
1187 except EOFError:
1187 except EOFError:
1188 raise error.ResponseExpected()
1188 raise error.ResponseExpected()
1189 def status(self, *msg, **opts):
1189 def status(self, *msg, **opts):
1190 '''write status message to output (if ui.quiet is False)
1190 '''write status message to output (if ui.quiet is False)
1191
1191
1192 This adds an output label of "ui.status".
1192 This adds an output label of "ui.status".
1193 '''
1193 '''
1194 if not self.quiet:
1194 if not self.quiet:
1195 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1195 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1196 self.write(*msg, **opts)
1196 self.write(*msg, **opts)
1197 def warn(self, *msg, **opts):
1197 def warn(self, *msg, **opts):
1198 '''write warning message to output (stderr)
1198 '''write warning message to output (stderr)
1199
1199
1200 This adds an output label of "ui.warning".
1200 This adds an output label of "ui.warning".
1201 '''
1201 '''
1202 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1202 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1203 self.write_err(*msg, **opts)
1203 self.write_err(*msg, **opts)
1204 def note(self, *msg, **opts):
1204 def note(self, *msg, **opts):
1205 '''write note to output (if ui.verbose is True)
1205 '''write note to output (if ui.verbose is True)
1206
1206
1207 This adds an output label of "ui.note".
1207 This adds an output label of "ui.note".
1208 '''
1208 '''
1209 if self.verbose:
1209 if self.verbose:
1210 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1210 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1211 self.write(*msg, **opts)
1211 self.write(*msg, **opts)
1212 def debug(self, *msg, **opts):
1212 def debug(self, *msg, **opts):
1213 '''write debug message to output (if ui.debugflag is True)
1213 '''write debug message to output (if ui.debugflag is True)
1214
1214
1215 This adds an output label of "ui.debug".
1215 This adds an output label of "ui.debug".
1216 '''
1216 '''
1217 if self.debugflag:
1217 if self.debugflag:
1218 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1218 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1219 self.write(*msg, **opts)
1219 self.write(*msg, **opts)
1220
1220
1221 def edit(self, text, user, extra=None, editform=None, pending=None,
1221 def edit(self, text, user, extra=None, editform=None, pending=None,
1222 repopath=None):
1222 repopath=None):
1223 extra_defaults = {
1223 extra_defaults = {
1224 'prefix': 'editor',
1224 'prefix': 'editor',
1225 'suffix': '.txt',
1225 'suffix': '.txt',
1226 }
1226 }
1227 if extra is not None:
1227 if extra is not None:
1228 extra_defaults.update(extra)
1228 extra_defaults.update(extra)
1229 extra = extra_defaults
1229 extra = extra_defaults
1230
1230
1231 rdir = None
1231 rdir = None
1232 if self.configbool('experimental', 'editortmpinhg'):
1232 if self.configbool('experimental', 'editortmpinhg'):
1233 rdir = repopath
1233 rdir = repopath
1234 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1234 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1235 suffix=extra['suffix'], text=True,
1235 suffix=extra['suffix'], text=True,
1236 dir=rdir)
1236 dir=rdir)
1237 try:
1237 try:
1238 f = os.fdopen(fd, pycompat.sysstr("w"))
1238 f = os.fdopen(fd, pycompat.sysstr("w"))
1239 f.write(encoding.strfromlocal(text))
1239 f.write(encoding.strfromlocal(text))
1240 f.close()
1240 f.close()
1241
1241
1242 environ = {'HGUSER': user}
1242 environ = {'HGUSER': user}
1243 if 'transplant_source' in extra:
1243 if 'transplant_source' in extra:
1244 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1244 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1245 for label in ('intermediate-source', 'source', 'rebase_source'):
1245 for label in ('intermediate-source', 'source', 'rebase_source'):
1246 if label in extra:
1246 if label in extra:
1247 environ.update({'HGREVISION': extra[label]})
1247 environ.update({'HGREVISION': extra[label]})
1248 break
1248 break
1249 if editform:
1249 if editform:
1250 environ.update({'HGEDITFORM': editform})
1250 environ.update({'HGEDITFORM': editform})
1251 if pending:
1251 if pending:
1252 environ.update({'HG_PENDING': pending})
1252 environ.update({'HG_PENDING': pending})
1253
1253
1254 editor = self.geteditor()
1254 editor = self.geteditor()
1255
1255
1256 self.system("%s \"%s\"" % (editor, name),
1256 self.system("%s \"%s\"" % (editor, name),
1257 environ=environ,
1257 environ=environ,
1258 onerr=error.Abort, errprefix=_("edit failed"),
1258 onerr=error.Abort, errprefix=_("edit failed"),
1259 blockedtag='editor')
1259 blockedtag='editor')
1260
1260
1261 f = open(name)
1261 f = open(name)
1262 t = encoding.strtolocal(f.read())
1262 t = encoding.strtolocal(f.read())
1263 f.close()
1263 f.close()
1264 finally:
1264 finally:
1265 os.unlink(name)
1265 os.unlink(name)
1266
1266
1267 return t
1267 return t
1268
1268
1269 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1269 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1270 blockedtag=None):
1270 blockedtag=None):
1271 '''execute shell command with appropriate output stream. command
1271 '''execute shell command with appropriate output stream. command
1272 output will be redirected if fout is not stdout.
1272 output will be redirected if fout is not stdout.
1273
1273
1274 if command fails and onerr is None, return status, else raise onerr
1274 if command fails and onerr is None, return status, else raise onerr
1275 object as exception.
1275 object as exception.
1276 '''
1276 '''
1277 if blockedtag is None:
1277 if blockedtag is None:
1278 # Long cmds tend to be because of an absolute path on cmd. Keep
1278 # Long cmds tend to be because of an absolute path on cmd. Keep
1279 # the tail end instead
1279 # the tail end instead
1280 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1280 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1281 blockedtag = 'unknown_system_' + cmdsuffix
1281 blockedtag = 'unknown_system_' + cmdsuffix
1282 out = self.fout
1282 out = self.fout
1283 if any(s[1] for s in self._bufferstates):
1283 if any(s[1] for s in self._bufferstates):
1284 out = self
1284 out = self
1285 with self.timeblockedsection(blockedtag):
1285 with self.timeblockedsection(blockedtag):
1286 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1286 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1287 if rc and onerr:
1287 if rc and onerr:
1288 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1288 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1289 util.explainexit(rc)[0])
1289 util.explainexit(rc)[0])
1290 if errprefix:
1290 if errprefix:
1291 errmsg = '%s: %s' % (errprefix, errmsg)
1291 errmsg = '%s: %s' % (errprefix, errmsg)
1292 raise onerr(errmsg)
1292 raise onerr(errmsg)
1293 return rc
1293 return rc
1294
1294
1295 def _runsystem(self, cmd, environ, cwd, out):
1295 def _runsystem(self, cmd, environ, cwd, out):
1296 """actually execute the given shell command (can be overridden by
1296 """actually execute the given shell command (can be overridden by
1297 extensions like chg)"""
1297 extensions like chg)"""
1298 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1298 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1299
1299
1300 def traceback(self, exc=None, force=False):
1300 def traceback(self, exc=None, force=False):
1301 '''print exception traceback if traceback printing enabled or forced.
1301 '''print exception traceback if traceback printing enabled or forced.
1302 only to call in exception handler. returns true if traceback
1302 only to call in exception handler. returns true if traceback
1303 printed.'''
1303 printed.'''
1304 if self.tracebackflag or force:
1304 if self.tracebackflag or force:
1305 if exc is None:
1305 if exc is None:
1306 exc = sys.exc_info()
1306 exc = sys.exc_info()
1307 cause = getattr(exc[1], 'cause', None)
1307 cause = getattr(exc[1], 'cause', None)
1308
1308
1309 if cause is not None:
1309 if cause is not None:
1310 causetb = traceback.format_tb(cause[2])
1310 causetb = traceback.format_tb(cause[2])
1311 exctb = traceback.format_tb(exc[2])
1311 exctb = traceback.format_tb(exc[2])
1312 exconly = traceback.format_exception_only(cause[0], cause[1])
1312 exconly = traceback.format_exception_only(cause[0], cause[1])
1313
1313
1314 # exclude frame where 'exc' was chained and rethrown from exctb
1314 # exclude frame where 'exc' was chained and rethrown from exctb
1315 self.write_err('Traceback (most recent call last):\n',
1315 self.write_err('Traceback (most recent call last):\n',
1316 ''.join(exctb[:-1]),
1316 ''.join(exctb[:-1]),
1317 ''.join(causetb),
1317 ''.join(causetb),
1318 ''.join(exconly))
1318 ''.join(exconly))
1319 else:
1319 else:
1320 output = traceback.format_exception(exc[0], exc[1], exc[2])
1320 output = traceback.format_exception(exc[0], exc[1], exc[2])
1321 data = r''.join(output)
1321 data = r''.join(output)
1322 if pycompat.ispy3:
1322 if pycompat.ispy3:
1323 enc = pycompat.sysstr(encoding.encoding)
1323 enc = pycompat.sysstr(encoding.encoding)
1324 data = data.encode(enc, errors=r'replace')
1324 data = data.encode(enc, errors=r'replace')
1325 self.write_err(data)
1325 self.write_err(data)
1326 return self.tracebackflag or force
1326 return self.tracebackflag or force
1327
1327
1328 def geteditor(self):
1328 def geteditor(self):
1329 '''return editor to use'''
1329 '''return editor to use'''
1330 if pycompat.sysplatform == 'plan9':
1330 if pycompat.sysplatform == 'plan9':
1331 # vi is the MIPS instruction simulator on Plan 9. We
1331 # vi is the MIPS instruction simulator on Plan 9. We
1332 # instead default to E to plumb commit messages to
1332 # instead default to E to plumb commit messages to
1333 # avoid confusion.
1333 # avoid confusion.
1334 editor = 'E'
1334 editor = 'E'
1335 else:
1335 else:
1336 editor = 'vi'
1336 editor = 'vi'
1337 return (encoding.environ.get("HGEDITOR") or
1337 return (encoding.environ.get("HGEDITOR") or
1338 self.config("ui", "editor", editor))
1338 self.config("ui", "editor", editor))
1339
1339
1340 @util.propertycache
1340 @util.propertycache
1341 def _progbar(self):
1341 def _progbar(self):
1342 """setup the progbar singleton to the ui object"""
1342 """setup the progbar singleton to the ui object"""
1343 if (self.quiet or self.debugflag
1343 if (self.quiet or self.debugflag
1344 or self.configbool('progress', 'disable', False)
1344 or self.configbool('progress', 'disable', False)
1345 or not progress.shouldprint(self)):
1345 or not progress.shouldprint(self)):
1346 return None
1346 return None
1347 return getprogbar(self)
1347 return getprogbar(self)
1348
1348
1349 def _progclear(self):
1349 def _progclear(self):
1350 """clear progress bar output if any. use it before any output"""
1350 """clear progress bar output if any. use it before any output"""
1351 if '_progbar' not in vars(self): # nothing loaded yet
1351 if '_progbar' not in vars(self): # nothing loaded yet
1352 return
1352 return
1353 if self._progbar is not None and self._progbar.printed:
1353 if self._progbar is not None and self._progbar.printed:
1354 self._progbar.clear()
1354 self._progbar.clear()
1355
1355
1356 def progress(self, topic, pos, item="", unit="", total=None):
1356 def progress(self, topic, pos, item="", unit="", total=None):
1357 '''show a progress message
1357 '''show a progress message
1358
1358
1359 By default a textual progress bar will be displayed if an operation
1359 By default a textual progress bar will be displayed if an operation
1360 takes too long. 'topic' is the current operation, 'item' is a
1360 takes too long. 'topic' is the current operation, 'item' is a
1361 non-numeric marker of the current position (i.e. the currently
1361 non-numeric marker of the current position (i.e. the currently
1362 in-process file), 'pos' is the current numeric position (i.e.
1362 in-process file), 'pos' is the current numeric position (i.e.
1363 revision, bytes, etc.), unit is a corresponding unit label,
1363 revision, bytes, etc.), unit is a corresponding unit label,
1364 and total is the highest expected pos.
1364 and total is the highest expected pos.
1365
1365
1366 Multiple nested topics may be active at a time.
1366 Multiple nested topics may be active at a time.
1367
1367
1368 All topics should be marked closed by setting pos to None at
1368 All topics should be marked closed by setting pos to None at
1369 termination.
1369 termination.
1370 '''
1370 '''
1371 if self._progbar is not None:
1371 if self._progbar is not None:
1372 self._progbar.progress(topic, pos, item=item, unit=unit,
1372 self._progbar.progress(topic, pos, item=item, unit=unit,
1373 total=total)
1373 total=total)
1374 if pos is None or not self.configbool('progress', 'debug'):
1374 if pos is None or not self.configbool('progress', 'debug'):
1375 return
1375 return
1376
1376
1377 if unit:
1377 if unit:
1378 unit = ' ' + unit
1378 unit = ' ' + unit
1379 if item:
1379 if item:
1380 item = ' ' + item
1380 item = ' ' + item
1381
1381
1382 if total:
1382 if total:
1383 pct = 100.0 * pos / total
1383 pct = 100.0 * pos / total
1384 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1384 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1385 % (topic, item, pos, total, unit, pct))
1385 % (topic, item, pos, total, unit, pct))
1386 else:
1386 else:
1387 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1387 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1388
1388
1389 def log(self, service, *msg, **opts):
1389 def log(self, service, *msg, **opts):
1390 '''hook for logging facility extensions
1390 '''hook for logging facility extensions
1391
1391
1392 service should be a readily-identifiable subsystem, which will
1392 service should be a readily-identifiable subsystem, which will
1393 allow filtering.
1393 allow filtering.
1394
1394
1395 *msg should be a newline-terminated format string to log, and
1395 *msg should be a newline-terminated format string to log, and
1396 then any values to %-format into that format string.
1396 then any values to %-format into that format string.
1397
1397
1398 **opts currently has no defined meanings.
1398 **opts currently has no defined meanings.
1399 '''
1399 '''
1400
1400
1401 def label(self, msg, label):
1401 def label(self, msg, label):
1402 '''style msg based on supplied label
1402 '''style msg based on supplied label
1403
1403
1404 If some color mode is enabled, this will add the necessary control
1404 If some color mode is enabled, this will add the necessary control
1405 characters to apply such color. In addition, 'debug' color mode adds
1405 characters to apply such color. In addition, 'debug' color mode adds
1406 markup showing which label affects a piece of text.
1406 markup showing which label affects a piece of text.
1407
1407
1408 ui.write(s, 'label') is equivalent to
1408 ui.write(s, 'label') is equivalent to
1409 ui.write(ui.label(s, 'label')).
1409 ui.write(ui.label(s, 'label')).
1410 '''
1410 '''
1411 if self._colormode is not None:
1411 if self._colormode is not None:
1412 return color.colorlabel(self, msg, label)
1412 return color.colorlabel(self, msg, label)
1413 return msg
1413 return msg
1414
1414
1415 def develwarn(self, msg, stacklevel=1, config=None):
1415 def develwarn(self, msg, stacklevel=1, config=None):
1416 """issue a developer warning message
1416 """issue a developer warning message
1417
1417
1418 Use 'stacklevel' to report the offender some layers further up in the
1418 Use 'stacklevel' to report the offender some layers further up in the
1419 stack.
1419 stack.
1420 """
1420 """
1421 if not self.configbool('devel', 'all-warnings'):
1421 if not self.configbool('devel', 'all-warnings'):
1422 if config is not None and not self.configbool('devel', config):
1422 if config is not None and not self.configbool('devel', config):
1423 return
1423 return
1424 msg = 'devel-warn: ' + msg
1424 msg = 'devel-warn: ' + msg
1425 stacklevel += 1 # get in develwarn
1425 stacklevel += 1 # get in develwarn
1426 if self.tracebackflag:
1426 if self.tracebackflag:
1427 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1427 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1428 self.log('develwarn', '%s at:\n%s' %
1428 self.log('develwarn', '%s at:\n%s' %
1429 (msg, ''.join(util.getstackframes(stacklevel))))
1429 (msg, ''.join(util.getstackframes(stacklevel))))
1430 else:
1430 else:
1431 curframe = inspect.currentframe()
1431 curframe = inspect.currentframe()
1432 calframe = inspect.getouterframes(curframe, 2)
1432 calframe = inspect.getouterframes(curframe, 2)
1433 self.write_err('%s at: %s:%s (%s)\n'
1433 self.write_err('%s at: %s:%s (%s)\n'
1434 % ((msg,) + calframe[stacklevel][1:4]))
1434 % ((msg,) + calframe[stacklevel][1:4]))
1435 self.log('develwarn', '%s at: %s:%s (%s)\n',
1435 self.log('develwarn', '%s at: %s:%s (%s)\n',
1436 msg, *calframe[stacklevel][1:4])
1436 msg, *calframe[stacklevel][1:4])
1437 curframe = calframe = None # avoid cycles
1437 curframe = calframe = None # avoid cycles
1438
1438
1439 def deprecwarn(self, msg, version):
1439 def deprecwarn(self, msg, version):
1440 """issue a deprecation warning
1440 """issue a deprecation warning
1441
1441
1442 - msg: message explaining what is deprecated and how to upgrade,
1442 - msg: message explaining what is deprecated and how to upgrade,
1443 - version: last version where the API will be supported,
1443 - version: last version where the API will be supported,
1444 """
1444 """
1445 if not (self.configbool('devel', 'all-warnings')
1445 if not (self.configbool('devel', 'all-warnings')
1446 or self.configbool('devel', 'deprec-warn')):
1446 or self.configbool('devel', 'deprec-warn')):
1447 return
1447 return
1448 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1448 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1449 " update your code.)") % version
1449 " update your code.)") % version
1450 self.develwarn(msg, stacklevel=2, config='deprec-warn')
1450 self.develwarn(msg, stacklevel=2, config='deprec-warn')
1451
1451
1452 def exportableenviron(self):
1452 def exportableenviron(self):
1453 """The environment variables that are safe to export, e.g. through
1453 """The environment variables that are safe to export, e.g. through
1454 hgweb.
1454 hgweb.
1455 """
1455 """
1456 return self._exportableenviron
1456 return self._exportableenviron
1457
1457
1458 @contextlib.contextmanager
1458 @contextlib.contextmanager
1459 def configoverride(self, overrides, source=""):
1459 def configoverride(self, overrides, source=""):
1460 """Context manager for temporary config overrides
1460 """Context manager for temporary config overrides
1461 `overrides` must be a dict of the following structure:
1461 `overrides` must be a dict of the following structure:
1462 {(section, name) : value}"""
1462 {(section, name) : value}"""
1463 backups = {}
1463 backups = {}
1464 try:
1464 try:
1465 for (section, name), value in overrides.items():
1465 for (section, name), value in overrides.items():
1466 backups[(section, name)] = self.backupconfig(section, name)
1466 backups[(section, name)] = self.backupconfig(section, name)
1467 self.setconfig(section, name, value, source)
1467 self.setconfig(section, name, value, source)
1468 yield
1468 yield
1469 finally:
1469 finally:
1470 for __, backup in backups.items():
1470 for __, backup in backups.items():
1471 self.restoreconfig(backup)
1471 self.restoreconfig(backup)
1472 # just restoring ui.quiet config to the previous value is not enough
1472 # just restoring ui.quiet config to the previous value is not enough
1473 # as it does not update ui.quiet class member
1473 # as it does not update ui.quiet class member
1474 if ('ui', 'quiet') in overrides:
1474 if ('ui', 'quiet') in overrides:
1475 self.fixconfig(section='ui')
1475 self.fixconfig(section='ui')
1476
1476
1477 class paths(dict):
1477 class paths(dict):
1478 """Represents a collection of paths and their configs.
1478 """Represents a collection of paths and their configs.
1479
1479
1480 Data is initially derived from ui instances and the config files they have
1480 Data is initially derived from ui instances and the config files they have
1481 loaded.
1481 loaded.
1482 """
1482 """
1483 def __init__(self, ui):
1483 def __init__(self, ui):
1484 dict.__init__(self)
1484 dict.__init__(self)
1485
1485
1486 for name, loc in ui.configitems('paths', ignoresub=True):
1486 for name, loc in ui.configitems('paths', ignoresub=True):
1487 # No location is the same as not existing.
1487 # No location is the same as not existing.
1488 if not loc:
1488 if not loc:
1489 continue
1489 continue
1490 loc, sub = ui.configsuboptions('paths', name)
1490 loc, sub = ui.configsuboptions('paths', name)
1491 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1491 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1492
1492
1493 def getpath(self, name, default=None):
1493 def getpath(self, name, default=None):
1494 """Return a ``path`` from a string, falling back to default.
1494 """Return a ``path`` from a string, falling back to default.
1495
1495
1496 ``name`` can be a named path or locations. Locations are filesystem
1496 ``name`` can be a named path or locations. Locations are filesystem
1497 paths or URIs.
1497 paths or URIs.
1498
1498
1499 Returns None if ``name`` is not a registered path, a URI, or a local
1499 Returns None if ``name`` is not a registered path, a URI, or a local
1500 path to a repo.
1500 path to a repo.
1501 """
1501 """
1502 # Only fall back to default if no path was requested.
1502 # Only fall back to default if no path was requested.
1503 if name is None:
1503 if name is None:
1504 if not default:
1504 if not default:
1505 default = ()
1505 default = ()
1506 elif not isinstance(default, (tuple, list)):
1506 elif not isinstance(default, (tuple, list)):
1507 default = (default,)
1507 default = (default,)
1508 for k in default:
1508 for k in default:
1509 try:
1509 try:
1510 return self[k]
1510 return self[k]
1511 except KeyError:
1511 except KeyError:
1512 continue
1512 continue
1513 return None
1513 return None
1514
1514
1515 # Most likely empty string.
1515 # Most likely empty string.
1516 # This may need to raise in the future.
1516 # This may need to raise in the future.
1517 if not name:
1517 if not name:
1518 return None
1518 return None
1519
1519
1520 try:
1520 try:
1521 return self[name]
1521 return self[name]
1522 except KeyError:
1522 except KeyError:
1523 # Try to resolve as a local path or URI.
1523 # Try to resolve as a local path or URI.
1524 try:
1524 try:
1525 # We don't pass sub-options in, so no need to pass ui instance.
1525 # We don't pass sub-options in, so no need to pass ui instance.
1526 return path(None, None, rawloc=name)
1526 return path(None, None, rawloc=name)
1527 except ValueError:
1527 except ValueError:
1528 raise error.RepoError(_('repository %s does not exist') %
1528 raise error.RepoError(_('repository %s does not exist') %
1529 name)
1529 name)
1530
1530
1531 _pathsuboptions = {}
1531 _pathsuboptions = {}
1532
1532
1533 def pathsuboption(option, attr):
1533 def pathsuboption(option, attr):
1534 """Decorator used to declare a path sub-option.
1534 """Decorator used to declare a path sub-option.
1535
1535
1536 Arguments are the sub-option name and the attribute it should set on
1536 Arguments are the sub-option name and the attribute it should set on
1537 ``path`` instances.
1537 ``path`` instances.
1538
1538
1539 The decorated function will receive as arguments a ``ui`` instance,
1539 The decorated function will receive as arguments a ``ui`` instance,
1540 ``path`` instance, and the string value of this option from the config.
1540 ``path`` instance, and the string value of this option from the config.
1541 The function should return the value that will be set on the ``path``
1541 The function should return the value that will be set on the ``path``
1542 instance.
1542 instance.
1543
1543
1544 This decorator can be used to perform additional verification of
1544 This decorator can be used to perform additional verification of
1545 sub-options and to change the type of sub-options.
1545 sub-options and to change the type of sub-options.
1546 """
1546 """
1547 def register(func):
1547 def register(func):
1548 _pathsuboptions[option] = (attr, func)
1548 _pathsuboptions[option] = (attr, func)
1549 return func
1549 return func
1550 return register
1550 return register
1551
1551
1552 @pathsuboption('pushurl', 'pushloc')
1552 @pathsuboption('pushurl', 'pushloc')
1553 def pushurlpathoption(ui, path, value):
1553 def pushurlpathoption(ui, path, value):
1554 u = util.url(value)
1554 u = util.url(value)
1555 # Actually require a URL.
1555 # Actually require a URL.
1556 if not u.scheme:
1556 if not u.scheme:
1557 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1557 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1558 return None
1558 return None
1559
1559
1560 # Don't support the #foo syntax in the push URL to declare branch to
1560 # Don't support the #foo syntax in the push URL to declare branch to
1561 # push.
1561 # push.
1562 if u.fragment:
1562 if u.fragment:
1563 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1563 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1564 'ignoring)\n') % path.name)
1564 'ignoring)\n') % path.name)
1565 u.fragment = None
1565 u.fragment = None
1566
1566
1567 return str(u)
1567 return str(u)
1568
1568
1569 @pathsuboption('pushrev', 'pushrev')
1569 @pathsuboption('pushrev', 'pushrev')
1570 def pushrevpathoption(ui, path, value):
1570 def pushrevpathoption(ui, path, value):
1571 return value
1571 return value
1572
1572
1573 class path(object):
1573 class path(object):
1574 """Represents an individual path and its configuration."""
1574 """Represents an individual path and its configuration."""
1575
1575
1576 def __init__(self, ui, name, rawloc=None, suboptions=None):
1576 def __init__(self, ui, name, rawloc=None, suboptions=None):
1577 """Construct a path from its config options.
1577 """Construct a path from its config options.
1578
1578
1579 ``ui`` is the ``ui`` instance the path is coming from.
1579 ``ui`` is the ``ui`` instance the path is coming from.
1580 ``name`` is the symbolic name of the path.
1580 ``name`` is the symbolic name of the path.
1581 ``rawloc`` is the raw location, as defined in the config.
1581 ``rawloc`` is the raw location, as defined in the config.
1582 ``pushloc`` is the raw locations pushes should be made to.
1582 ``pushloc`` is the raw locations pushes should be made to.
1583
1583
1584 If ``name`` is not defined, we require that the location be a) a local
1584 If ``name`` is not defined, we require that the location be a) a local
1585 filesystem path with a .hg directory or b) a URL. If not,
1585 filesystem path with a .hg directory or b) a URL. If not,
1586 ``ValueError`` is raised.
1586 ``ValueError`` is raised.
1587 """
1587 """
1588 if not rawloc:
1588 if not rawloc:
1589 raise ValueError('rawloc must be defined')
1589 raise ValueError('rawloc must be defined')
1590
1590
1591 # Locations may define branches via syntax <base>#<branch>.
1591 # Locations may define branches via syntax <base>#<branch>.
1592 u = util.url(rawloc)
1592 u = util.url(rawloc)
1593 branch = None
1593 branch = None
1594 if u.fragment:
1594 if u.fragment:
1595 branch = u.fragment
1595 branch = u.fragment
1596 u.fragment = None
1596 u.fragment = None
1597
1597
1598 self.url = u
1598 self.url = u
1599 self.branch = branch
1599 self.branch = branch
1600
1600
1601 self.name = name
1601 self.name = name
1602 self.rawloc = rawloc
1602 self.rawloc = rawloc
1603 self.loc = '%s' % u
1603 self.loc = '%s' % u
1604
1604
1605 # When given a raw location but not a symbolic name, validate the
1605 # When given a raw location but not a symbolic name, validate the
1606 # location is valid.
1606 # location is valid.
1607 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1607 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1608 raise ValueError('location is not a URL or path to a local '
1608 raise ValueError('location is not a URL or path to a local '
1609 'repo: %s' % rawloc)
1609 'repo: %s' % rawloc)
1610
1610
1611 suboptions = suboptions or {}
1611 suboptions = suboptions or {}
1612
1612
1613 # Now process the sub-options. If a sub-option is registered, its
1613 # Now process the sub-options. If a sub-option is registered, its
1614 # attribute will always be present. The value will be None if there
1614 # attribute will always be present. The value will be None if there
1615 # was no valid sub-option.
1615 # was no valid sub-option.
1616 for suboption, (attr, func) in _pathsuboptions.iteritems():
1616 for suboption, (attr, func) in _pathsuboptions.iteritems():
1617 if suboption not in suboptions:
1617 if suboption not in suboptions:
1618 setattr(self, attr, None)
1618 setattr(self, attr, None)
1619 continue
1619 continue
1620
1620
1621 value = func(ui, self, suboptions[suboption])
1621 value = func(ui, self, suboptions[suboption])
1622 setattr(self, attr, value)
1622 setattr(self, attr, value)
1623
1623
1624 def _isvalidlocalpath(self, path):
1624 def _isvalidlocalpath(self, path):
1625 """Returns True if the given path is a potentially valid repository.
1625 """Returns True if the given path is a potentially valid repository.
1626 This is its own function so that extensions can change the definition of
1626 This is its own function so that extensions can change the definition of
1627 'valid' in this case (like when pulling from a git repo into a hg
1627 'valid' in this case (like when pulling from a git repo into a hg
1628 one)."""
1628 one)."""
1629 return os.path.isdir(os.path.join(path, '.hg'))
1629 return os.path.isdir(os.path.join(path, '.hg'))
1630
1630
1631 @property
1631 @property
1632 def suboptions(self):
1632 def suboptions(self):
1633 """Return sub-options and their values for this path.
1633 """Return sub-options and their values for this path.
1634
1634
1635 This is intended to be used for presentation purposes.
1635 This is intended to be used for presentation purposes.
1636 """
1636 """
1637 d = {}
1637 d = {}
1638 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1638 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1639 value = getattr(self, attr)
1639 value = getattr(self, attr)
1640 if value is not None:
1640 if value is not None:
1641 d[subopt] = value
1641 d[subopt] = value
1642 return d
1642 return d
1643
1643
1644 # we instantiate one globally shared progress bar to avoid
1644 # we instantiate one globally shared progress bar to avoid
1645 # competing progress bars when multiple UI objects get created
1645 # competing progress bars when multiple UI objects get created
1646 _progresssingleton = None
1646 _progresssingleton = None
1647
1647
1648 def getprogbar(ui):
1648 def getprogbar(ui):
1649 global _progresssingleton
1649 global _progresssingleton
1650 if _progresssingleton is None:
1650 if _progresssingleton is None:
1651 # passing 'ui' object to the singleton is fishy,
1651 # passing 'ui' object to the singleton is fishy,
1652 # this is how the extension used to work but feel free to rework it.
1652 # this is how the extension used to work but feel free to rework it.
1653 _progresssingleton = progress.progbar(ui)
1653 _progresssingleton = progress.progbar(ui)
1654 return _progresssingleton
1654 return _progresssingleton
General Comments 0
You need to be logged in to leave comments. Login now