ui: add support for a tweakdefaults knob
authorAugie Fackler <augie@google.com>
Wed, 14 Jun 2017 20:56:34 -0400
changeset 32872 9fcb6df413c9
parent 32871 6fa245f80b6f
child 32873 2ecce24dfcd3
ui: add support for a tweakdefaults knob We've been talking for years about a one-stop config knob to opt in to better behavior. There have been a lot of ideas thrown around, but they all seem to be too complicated to get anyone to actually do the work.. As such, this patch is the stupidest thing that can possibly work in the name of getting a good feature to users. Right now it's just three config settings that I think are generally uncontroversial, but I expect to add more soon. That will likely include adding new config knobs for the express purpose of adding them to tweakdefaults.
mercurial/help/config.txt
mercurial/ui.py
tests/test-status.t
--- a/mercurial/help/config.txt	Thu Jun 15 15:13:18 2017 -0700
+++ b/mercurial/help/config.txt	Wed Jun 14 20:56:34 2017 -0400
@@ -2093,6 +2093,15 @@
     on all exceptions, even those recognized by Mercurial (such as
     IOError or MemoryError). (default: False)
 
+``tweakdefaults``
+
+    By default Mercurial's behavior changes very little from release
+    to release, but over time the recommended config settings
+    shift. Enable this config to opt in to get automatic tweaks to
+    Mercurial's behavior over time. This config setting will have no
+    effet if ``HGPLAIN` is set or ``HGPLAINEXCEPT`` is set and does
+    not include ``tweakdefaults``. (default: False)
+
 ``username``
     The committer of a changeset created when running "commit".
     Typically a person's name and email address, e.g. ``Fred Widget
--- a/mercurial/ui.py	Thu Jun 15 15:13:18 2017 -0700
+++ b/mercurial/ui.py	Wed Jun 14 20:56:34 2017 -0400
@@ -43,6 +43,20 @@
 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
                      if not c.isalnum())
 
+# The config knobs that will be altered (if unset) by ui.tweakdefaults.
+tweakrc = """
+[ui]
+# The rollback command is dangerous. As a rule, don't use it.
+rollback = False
+
+[commands]
+# Make `hg status` emit cwd-relative paths by default.
+status.relative = yes
+
+[diff]
+git = 1
+"""
+
 samplehgrcs = {
     'user':
 """# example user config (see 'hg help config' for more info)
@@ -182,6 +196,7 @@
             self.fin = src.fin
             self.pageractive = src.pageractive
             self._disablepager = src._disablepager
+            self._tweaked = src._tweaked
 
             self._tcfg = src._tcfg.copy()
             self._ucfg = src._ucfg.copy()
@@ -205,6 +220,7 @@
             self.fin = util.stdin
             self.pageractive = False
             self._disablepager = False
+            self._tweaked = False
 
             # shared read-only environment
             self.environ = encoding.environ
@@ -241,8 +257,29 @@
                     u.fixconfig(section=section)
             else:
                 raise error.ProgrammingError('unknown rctype: %s' % t)
+        u._maybetweakdefaults()
         return u
 
+    def _maybetweakdefaults(self):
+        if not self.configbool('ui', 'tweakdefaults'):
+            return
+        if self._tweaked or self.plain('tweakdefaults'):
+            return
+
+        # Note: it is SUPER IMPORTANT that you set self._tweaked to
+        # True *before* any calls to setconfig(), otherwise you'll get
+        # infinite recursion between setconfig and this method.
+        #
+        # TODO: We should extract an inner method in setconfig() to
+        # avoid this weirdness.
+        self._tweaked = True
+        tmpcfg = config.config()
+        tmpcfg.parse('<tweakdefaults>', tweakrc)
+        for section in tmpcfg:
+            for name, value in tmpcfg.items(section):
+                if not self.hasconfig(section, name):
+                    self.setconfig(section, name, value, "<tweakdefaults>")
+
     def copy(self):
         return self.__class__(self)
 
@@ -387,6 +424,7 @@
         for cfg in (self._ocfg, self._tcfg, self._ucfg):
             cfg.set(section, name, value, source)
         self.fixconfig(section=section)
+        self._maybetweakdefaults()
 
     def _data(self, untrusted):
         return untrusted and self._ucfg or self._tcfg
--- a/tests/test-status.t	Thu Jun 15 15:13:18 2017 -0700
+++ b/tests/test-status.t	Wed Jun 14 20:56:34 2017 -0400
@@ -107,6 +107,29 @@
   ? a/in_a
   ? b/in_b
 
+tweaking defaults works
+  $ hg status --cwd a --config ui.tweakdefaults=yes
+  ? 1/in_a_1
+  ? in_a
+  ? ../b/1/in_b_1
+  ? ../b/2/in_b_2
+  ? ../b/in_b
+  ? ../in_root
+  $ HGPLAIN=1 hg status --cwd a --config ui.tweakdefaults=yes
+  ? a/1/in_a_1 (glob)
+  ? a/in_a (glob)
+  ? b/1/in_b_1 (glob)
+  ? b/2/in_b_2 (glob)
+  ? b/in_b (glob)
+  ? in_root
+  $ HGPLAINEXCEPT=tweakdefaults hg status --cwd a --config ui.tweakdefaults=yes
+  ? 1/in_a_1
+  ? in_a
+  ? ../b/1/in_b_1
+  ? ../b/2/in_b_2
+  ? ../b/in_b
+  ? ../in_root
+
 relative paths can be requested
 
   $ cat >> $HGRCPATH <<EOF
@@ -128,6 +151,19 @@
   ? b/in_b (glob)
   ? in_root
 
+if relative paths are explicitly off, tweakdefaults doesn't change it
+  $ cat >> $HGRCPATH <<EOF
+  > [commands]
+  > status.relative = False
+  > EOF
+  $ hg status --cwd a --config ui.tweakdefaults=yes
+  ? a/1/in_a_1 (glob)
+  ? a/in_a (glob)
+  ? b/1/in_b_1 (glob)
+  ? b/2/in_b_2 (glob)
+  ? b/in_b (glob)
+  ? in_root
+
   $ cd ..
 
   $ hg init repo2