The original condition was a bit harsh for extension authors since third-party
extensions need to preserve compatibility with older Mercurial versions, where
no defaults would be loaded from the configtable. So let's silence the warning
if the given default value matches, which should be harmless.
Since the ui objects can be created with the 'load' class method, it
is possible to lose the exit handlers information from the old ui instance. For
example, running 'test-bad-extension.t' leads to this situation where chg
creates a new ui instance which does not copy the exit handlers from the
earlier ui instance. For exit handlers, which are special cases anyways, it
probably makes sense to have a global state of the handlers. This would ensure
that the exit handlers registered once are definitely executed at the end of
the request.
Test Plan:
Ran all the tests without '--chg' option. This also fixes the
'test-bad-extension.t' with the '--chg' option.
Differential Revision: https://phab.mercurial-scm.org/D1166
Now that all known options are declared, we setup a warning to make sure it will
stay this way.
We disable the warning in two tests checking other behavior with random options.
This is the second-safest option we have to offer in `updatecheck`,
with `abort` being the safest and `linear` being the default. At the
sprint we discussed how much `none` and `linear` make us all
uncomfortable, and how we'd like to move the default behavior if we can.
I'm not sure we can get away with actually changing the out of the box
default behavior, but we can at *least* do this.
Differential Revision: https://phab.mercurial-scm.org/D1062
This password manager proxy is roughly the right-looking layer to
convert between strings and bytes. Many of these arguments can be
None, so we have a helper method to make the conversion preserve Nones
without exploding.
Differential Revision: https://phab.mercurial-scm.org/D886
We only ever call these functions in a single way, so let's just
actually specify them. We need to do some string/bytes encoding
dancing here for Python 3, so it'll help to know what arguments we
need to convert.
# no-check-commit because I'm modifying functions that check-commit
does not like.
Differential Revision: https://phab.mercurial-scm.org/D885
A combination of wrapping `ui` and progress bars interrupted by exceptions can
lead to the progress bar not being cleared when the exception error is printed.
This results in corrupted-looking output like this:
```
updating [===============================> ] 1/2u
nresolved conflicts (see hg resolve, then hg rebase --continue)
```
This is because in `ui._progclear`, we only check the local reference to the
progress bar, not whether or not there is an instance of the singleton. When a
progress bar is interrupted by an exception, the exception printing in
`scmutil.callcatch` uses the original instance of the `ui` object, not the
wrapped copy that has `_progbar` set.
When consider whether or not to clear the progress bar, check for the existence
of the singleton, rather than just whether or not we have a local reference to
it.
Differential Revision: https://phab.mercurial-scm.org/D743
45345e9870c3 and b30126fa95bc refactored ui methods to no longer
silently swallow some IOError instances. This is arguably the
correct thing to do. However, it had the unfortunate side-effect
of causing StdioError to bubble up to sensitive code like
transaction aborts, leading to an uncaught exceptions and failures
to e.g. roll back a transaction. This could occur when a remote
HTTP or SSH client connection dropped. The new behavior is
resulting in semi-frequent "abandonded transaction" errors on
multiple high-volume repositories at Mozilla.
This commit effectively reverts 45345e9870c3 and b30126fa95bc to
restore the old behavior.
I agree with the principle that I/O errors shouldn't be ignored.
That makes this change... unfortunate. However, our hands are tied
for what to do on stable. I think the proper solution is for the
ui's behavior to be configurable (possibly via a context manager).
During critical sections like transaction rollback and abort, it
should be possible to suppress errors. But this feature would not
be appropriate on stable.
Without this patch on Windows 'hg ci -i' hangs waiting for user input
and "examine changes to 'file'? [Ynesfdaq?]" is never displayed (at least
if the diff is sufficiently small). When Ctrl+C is pressed, this prompt
becomes visible, which suggests that the buffer just wasn't flushed.
I've never seen this happening on Linux, but this looks harmless enough
to not platform-gate it.
Before this patch, explicit --pager=on is unintentionally ignored by
any disabling factor, even if priority of it is less than --pager=on
(e.g. "[ui] paginate = off").
This is done by a script [2] using RedBaron [1], a tool designed for doing
code refactoring. All "default" values are decided by the script and are
strongly consistent with the existing code.
There are 2 changes done manually to fix tests:
[warn] mercurial/exchange.py: experimental.bundle2-output-capture: default needs manual removal
[warn] mercurial/localrepo.py: experimental.hook-track-tags: default needs manual removal
Since RedBaron is not confident about how to indent things [2].
[1]: https://github.com/PyCQA/redbaron
[2]: https://github.com/PyCQA/redbaron/issues/100
[3]:
#!/usr/bin/env python
# codemod_configitems.py - codemod tool to fill configitems
#
# Copyright 2017 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import, print_function
import os
import sys
import redbaron
def readpath(path):
with open(path) as f:
return f.read()
def writepath(path, content):
with open(path, 'w') as f:
f.write(content)
_configmethods = {'config', 'configbool', 'configint', 'configbytes',
'configlist', 'configdate'}
def extractstring(rnode):
"""get the string from a RedBaron string or call_argument node"""
while rnode.type != 'string':
rnode = rnode.value
return rnode.value[1:-1] # unquote, "'str'" -> "str"
def uiconfigitems(red):
"""match *.ui.config* pattern, yield (node, method, args, section, name)"""
for node in red.find_all('atomtrailers'):
entry = None
try:
obj = node[-3].value
method = node[-2].value
args = node[-1]
section = args[0].value
name = args[1].value
if (obj in ('ui', 'self') and method in _configmethods
and section.type == 'string' and name.type == 'string'):
entry = (node, method, args, extractstring(section),
extractstring(name))
except Exception:
pass
else:
if entry:
yield entry
def coreconfigitems(red):
"""match coreconfigitem(...) pattern, yield (node, args, section, name)"""
for node in red.find_all('atomtrailers'):
entry = None
try:
args = node[1]
section = args[0].value
name = args[1].value
if (node[0].value == 'coreconfigitem' and section.type == 'string'
and name.type == 'string'):
entry = (node, args, extractstring(section),
extractstring(name))
except Exception:
pass
else:
if entry:
yield entry
def registercoreconfig(cfgred, section, name, defaultrepr):
"""insert coreconfigitem to cfgred AST
section and name are plain string, defaultrepr is a string
"""
# find a place to insert the "coreconfigitem" item
entries = list(coreconfigitems(cfgred))
for node, args, nodesection, nodename in reversed(entries):
if (nodesection, nodename) < (section, name):
# insert after this entry
node.insert_after(
'coreconfigitem(%r, %r,\n'
' default=%s,\n'
')' % (section, name, defaultrepr))
return
def main(argv):
if not argv:
print('Usage: codemod_configitems.py FILES\n'
'For example, FILES could be "{hgext,mercurial}/*/**.py"')
dirname = os.path.dirname
reporoot = dirname(dirname(dirname(os.path.abspath(__file__))))
# register configitems to this destination
cfgpath = os.path.join(reporoot, 'mercurial', 'configitems.py')
cfgred = redbaron.RedBaron(readpath(cfgpath))
# state about what to do
registered = set((s, n) for n, a, s, n in coreconfigitems(cfgred))
toregister = {} # {(section, name): defaultrepr}
coreconfigs = set() # {(section, name)}, whether it's used in core
# first loop: scan all files before taking any action
for i, path in enumerate(argv):
print('(%d/%d) scanning %s' % (i + 1, len(argv), path))
iscore = ('mercurial' in path) and ('hgext' not in path)
red = redbaron.RedBaron(readpath(path))
# find all repo.ui.config* and ui.config* calls, and collect their
# section, name and default value information.
for node, method, args, section, name in uiconfigitems(red):
if section == 'web':
# [web] section has some weirdness, ignore them for now
continue
defaultrepr = None
key = (section, name)
if len(args) == 2:
if key in registered:
continue
if method == 'configlist':
defaultrepr = 'list'
elif method == 'configbool':
defaultrepr = 'False'
else:
defaultrepr = 'None'
elif len(args) >= 3 and (args[2].target is None or
args[2].target.value == 'default'):
# try to understand the "default" value
dnode = args[2].value
if dnode.type == 'name':
if dnode.value in {'None', 'True', 'False'}:
defaultrepr = dnode.value
elif dnode.type == 'string':
defaultrepr = repr(dnode.value[1:-1])
elif dnode.type in ('int', 'float'):
defaultrepr = dnode.value
# inconsistent default
if key in toregister and toregister[key] != defaultrepr:
defaultrepr = None
# interesting to rewrite
if key not in registered:
if defaultrepr is None:
print('[note] %s: %s.%s: unsupported default'
% (path, section, name))
registered.add(key) # skip checking it again
else:
toregister[key] = defaultrepr
if iscore:
coreconfigs.add(key)
# second loop: rewrite files given "toregister" result
for path in argv:
# reconstruct redbaron - trade CPU for memory
red = redbaron.RedBaron(readpath(path))
changed = False
for node, method, args, section, name in uiconfigitems(red):
key = (section, name)
defaultrepr = toregister.get(key)
if defaultrepr is None or key not in coreconfigs:
continue
if len(args) >= 3 and (args[2].target is None or
args[2].target.value == 'default'):
try:
del args[2]
changed = True
except Exception:
# redbaron fails to do the rewrite due to indentation
# see https://github.com/PyCQA/redbaron/issues/100
print('[warn] %s: %s.%s: default needs manual removal'
% (path, section, name))
if key not in registered:
print('registering %s.%s' % (section, name))
registercoreconfig(cfgred, section, name, defaultrepr)
registered.add(key)
if changed:
print('updating %s' % path)
writepath(path, red.dumps())
if toregister:
print('updating configitems.py')
writepath(cfgpath, cfgred.dumps())
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
In some case, the default of one value is derived from other value. We add a
way to register them anyway and an associated devel-warning.
The registration is very naive for the moment. We might be able to have a
better way for registering each of these cases but it could be done later.
Aliases define optional alternatives to existing options. For example the old
option ui.user was deprecated and replaced by ui.username. With this mechanism,
it's even possible to create an alias to an option in a different section.
Add ui.user as alias to ui.username as an example of this concept.
The old alternates principle in ui.config is removed as it was used only for
this option.
This is a follow-up to https://phab.mercurial-scm.org/D464 (02b917f3e672) that
introduced the new file extension behavior. It erroneously changed `.diff` to
`.diff.hg.txt`.
Test Plan:
Verified `make tests` passes, particularly `test-editor-filename.t`.
Differential Revision: https://phab.mercurial-scm.org/D607
Changes the API of `ui.edit()` to take an optional `action` argument,
which is used when constructing the suffix of the temp file.
Previously, it was possible to set the suffix by specifying a `suffix` to the
optional `extra` dict that was passed to `ui.edit()`, but the goal is to
drop support for `extra.suffix` and make `action` a required argument.
To this end, `ui.edit()` now yields a `develwarn()` if `action` is not set
or if `extra.suffix` is set.
I updated all calls to `ui.edit()` I could find in `hg-crew` to specify the
appropriate `action`. This means that when creating a commit, instead
of the path to the editor file being something like:
`/tmp/hg-editor-XXXXXX.txt`
it is now something like:
`/tmp/hg-editor-XXXXXX.commit.hg.txt`
Some editors (such as Atom) make it possible to statically define a [TextMate]
grammar for files with a particular suffix. For example, because Git reliably
uses `.git/COMMIT_EDITMSG` and `.git/MERGE_MSG` as the paths for commit-type
messages, it is trivial to define a grammar that is applied when files of
either name are opened in Atom:
https://github.com/atom/language-git/blob/v0.19.1/grammars/git%20commit%20message.cson#L4-L5
Because Hg historically used a generic `.txt` suffix, it was much harder to
disambiguate whether a file was an arbitrary text file as opposed to one
created for the specific purpose of authoring an Hg commit message.
This also makes it easier to add special support for `histedit`, as it has its own
suffix that is distinct from a commit:
`/tmp/hg-histedit-XXXXXX.histedit.hg.txt`
Test Plan:
Added an integration test: `test-editor-filename.t`.
Manually tested: ran `hg ci --amend` for this change and saw that it
used `/tmp/hg-editor-ZZjcz0.commit.hg.txt` as the path instead of
`/tmp/hg-editor-ZZjcz0.txt` as the path.
Verified `make tests` passes.
Differential Revision: https://phab.mercurial-scm.org/D464
The `pushbuffer`, `popbuffer` APIs are intended to capture internal output.
They will prevent `ui.write` from writing to the actual `ui.fout`. So a
pager won't receive the output and do the right thing. In general, it does
not make sense to start a pager if ui is in the "pushbuffer" mode.
Differential Revision: https://phab.mercurial-scm.org/D574
In order to make this work, we have to wrap the io streams in a
TextIOWrapper so that __builtins__.input() can do unicode IO on Python
3. We can't just restore the original (unicode) sys.std* because we
might be running a cmdserver, and if we blindly restore sys.* to the
original values then we end up breaking the cmdserver. Sadly,
TextIOWrapper tries to close the underlying stream during its __del__,
so we have to make a sublcass to prevent that.
If you see errors like:
TypeError: a bytes-like object is required, not 'str'
On an input() or print() call on Python 3, the substitution of
sys.std* is probably the root cause.
A previous version of this change tried to put the bytesinput() method
in pycompat - it turns out we need to do some encoding handling, so we
have to be in a higher layer that's allowed to use
mercurial.encoding.encoding. As a result, this is in util for now,
with the TextIOWrapper subclass hiding in encoding.py. I'm not sure of
a better place for the time being.
Differential Revision: https://phab.mercurial-scm.org/D299
Yuya pointed out that using mutable value as the default could be problematic.
To work around this we now support callable object as default value. This
allows for creating new mutable objects on demand when needed.
If the option is registered, there is already a default value available and
passing a new one is at best redundant. So we issue a deprecation warning in
this case.
(note: there will be case were the default value will not be as simple as what
is currently possible. We'll upgrade the configitems code to handle them in
time.)
We do not have any registered config yet, but we are now ready to use them.
For now we ignore this feature for config access with "alternates". On the long
run, we expect alternates to be handled as "aliases" by the config item
themself.
This should let 'configdate' delegate all special processing of the default
config value to the main 'config' method.
The default value for date (None) is still enforced in this method if no other
default were passed.
This should let 'configlist' delegate all special processing of the default
config value to the main 'config' method.
The default config value ([]) is still handled in this method.
This should let 'configwith' delegate all special processing of the default
config value to the main 'config' method.
This changeset introduce a small change in behavior since the default value is
run through the 'convert' function. This does not seems harmful and no actual
test break. This small change make the code simpler so I'm keeping it.
This should let 'configbool' delegate all special processing of the default
config value to the main 'config' method.
The default value for bool (False) is still enforced in this method if no other
default were passed.