Quantcast

[PATCH 1 of 9 pager] pager: move pager-initiating code into core

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH 1 of 9 pager] pager: move pager-initiating code into core

Augie Fackler-2
# HG changeset patch
# User Augie Fackler <[hidden email]>
# Date 1487198871 18000
#      Wed Feb 15 17:47:51 2017 -0500
# Node ID 675643abfdb6adbdfd8bddfbc263701c9caca539
# Parent  e5363cb96233861fc99f7e9b85d7884d3121558c
pager: move pager-initiating code into core

No functionality change.

A previous version of this API had a category argument on
ui.pager(). As I migrated the commands in core, I couldn't come up
with good enough consistency in any categorization scheme so I just
scrapped the whole idea. It may be worth revisiting in the future.

diff --git a/hgext/pager.py b/hgext/pager.py
--- a/hgext/pager.py
+++ b/hgext/pager.py
@@ -60,19 +60,11 @@ you can use --pager=<value>::
 '''
 from __future__ import absolute_import
 
-import atexit
-import os
-import signal
-import subprocess
-import sys
-
 from mercurial.i18n import _
 from mercurial import (
     cmdutil,
     commands,
     dispatch,
-    encoding,
-    error,
     extensions,
     util,
     )
@@ -83,48 +75,14 @@ from mercurial import (
 # leave the attribute unspecified.
 testedwith = 'ships-with-hg-core'
 
-def _runpager(ui, p):
-    pager = subprocess.Popen(p, shell=True, bufsize=-1,
-                             close_fds=util.closefds, stdin=subprocess.PIPE,
-                             stdout=util.stdout, stderr=util.stderr)
-
-    # back up original file descriptors
-    stdoutfd = os.dup(util.stdout.fileno())
-    stderrfd = os.dup(util.stderr.fileno())
-
-    os.dup2(pager.stdin.fileno(), util.stdout.fileno())
-    if ui._isatty(util.stderr):
-        os.dup2(pager.stdin.fileno(), util.stderr.fileno())
-
-    @atexit.register
-    def killpager():
-        if util.safehasattr(signal, "SIGINT"):
-            signal.signal(signal.SIGINT, signal.SIG_IGN)
-        # restore original fds, closing pager.stdin copies in the process
-        os.dup2(stdoutfd, util.stdout.fileno())
-        os.dup2(stderrfd, util.stderr.fileno())
-        pager.stdin.close()
-        pager.wait()
-
-def catchterm(*args):
-    raise error.SignalInterrupt
-
 def uisetup(ui):
-    class pagerui(ui.__class__):
-        def _runpager(self, pagercmd):
-            _runpager(self, pagercmd)
-
-    ui.__class__ = pagerui
 
     def pagecmd(orig, ui, options, cmd, cmdfunc):
-        p = ui.config("pager", "pager", encoding.environ.get("PAGER"))
         usepager = False
         always = util.parsebool(options['pager'])
         auto = options['pager'] == 'auto'
 
-        if not p or '--debugger' in sys.argv or not ui.formatted():
-            pass
-        elif always:
+        if always:
             usepager = True
         elif not auto:
             usepager = False
@@ -143,14 +101,8 @@ def uisetup(ui):
                     usepager = True
                     break
 
-        setattr(ui, 'pageractive', usepager)
-
         if usepager:
-            ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
-            ui.setconfig('ui', 'interactive', False, 'pager')
-            if util.safehasattr(signal, "SIGPIPE"):
-                signal.signal(signal.SIGPIPE, catchterm)
-            ui._runpager(p)
+            ui.pager('extension-via-attend-' + cmd)
         return orig(ui, options, cmd, cmdfunc)
 
     # Wrap dispatch._runcommand after color is loaded so color can see
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -7,13 +7,16 @@
 
 from __future__ import absolute_import
 
+import atexit
 import contextlib
 import errno
 import getpass
 import inspect
 import os
 import re
+import signal
 import socket
+import subprocess
 import sys
 import tempfile
 import traceback
@@ -143,6 +146,7 @@ class ui(object):
             self.fout = src.fout
             self.ferr = src.ferr
             self.fin = src.fin
+            self.pageractive = src.pageractive
 
             self._tcfg = src._tcfg.copy()
             self._ucfg = src._ucfg.copy()
@@ -159,6 +163,7 @@ class ui(object):
             self.fout = util.stdout
             self.ferr = util.stderr
             self.fin = util.stdin
+            self.pageractive = False
 
             # shared read-only environment
             self.environ = encoding.environ
@@ -792,6 +797,75 @@ class ui(object):
             return False
         return util.isatty(fh)
 
+    def pager(self, command):
+        """Start a pager for subsequent command output.
+
+        Commands which produce a long stream of output should call
+        this function to activate the user's preferred pagination
+        mechanism (which may be no pager). Calling this function
+        precludes any future use of interactive functionality, such as
+        prompting the user or activating curses.
+
+        Args:
+          command: The full, non-aliased name of the command. That is, "log"
+                   not "history, "summary" not "summ", etc.
+        """
+        if (self.pageractive
+            # TODO: if we want to allow HGPLAINEXCEPT=pager,
+            # formatted() will need some adjustment.
+            or not self.formatted()
+            or self.plain()
+            # TODO: expose debugger-enabled on the UI object
+            or '--debugger' in sys.argv):
+            # We only want to paginate if the ui appears to be
+            # interactive, the user didn't say HGPLAIN or
+            # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
+            return
+
+        # TODO: add a "system defaults" config section so this default
+        # of more(1) can be easily replaced with a global
+        # configuration file. For example, on OS X the sane default is
+        # less(1), not more(1), and on debian it's
+        # sensible-pager(1). We should probably also give the system
+        # default editor command similar treatment.
+        envpager = encoding.environ.get('PAGER', 'more')
+        pagercmd = self.config('pager', 'pager', envpager)
+        self.pageractive = True
+        # Preserve the formatted-ness of the UI. This is important
+        # because we mess with stdout, which might confuse
+        # auto-detection of things being formatted.
+        self.setconfig('ui', 'formatted', self.formatted(), 'pager')
+        self.setconfig('ui', 'interactive', False, 'pager')
+        self._runpager(pagercmd)
+
+    def _runpager(self, command):
+        """Actually start the pager and set up file descriptors.
+
+        This is separate in part so that extensions (like chg) can
+        override how a pager is invoked.
+        """
+        pager = subprocess.Popen(command, shell=True, bufsize=-1,
+                                 close_fds=util.closefds, stdin=subprocess.PIPE,
+                                 stdout=util.stdout, stderr=util.stderr)
+
+        # back up original file descriptors
+        stdoutfd = os.dup(util.stdout.fileno())
+        stderrfd = os.dup(util.stderr.fileno())
+
+        os.dup2(pager.stdin.fileno(), util.stdout.fileno())
+        if self._isatty(util.stderr):
+            os.dup2(pager.stdin.fileno(), util.stderr.fileno())
+
+        @atexit.register
+        def killpager():
+            if util.safehasattr(signal, "SIGINT"):
+                signal.signal(signal.SIGINT, signal.SIG_IGN)
+            # restore original fds, closing pager.stdin copies in the process
+            os.dup2(stdoutfd, util.stdout.fileno())
+            os.dup2(stderrfd, util.stderr.fileno())
+            pager.stdin.close()
+            pager.wait()
+
     def interface(self, feature):
         """what interface to use for interactive console features?
 
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH 2 of 9 pager] pager: move more behavior into core

Augie Fackler-2
# HG changeset patch
# User Augie Fackler <[hidden email]>
# Date 1487198877 18000
#      Wed Feb 15 17:47:57 2017 -0500
# Node ID cafe46e7d79c59567edc185979cf6568367def93
# Parent  675643abfdb6adbdfd8bddfbc263701c9caca539
pager: move more behavior into core

This moves the global flag and the --pager=yes logic into core. Only
functionality change is that users now always get a --pager flag and
can enable the pager via the flag without the extension active.

Moving the flag into core exposes a defect in the ro localization,
which will have to be corrected later.

diff --git a/hgext/pager.py b/hgext/pager.py
--- a/hgext/pager.py
+++ b/hgext/pager.py
@@ -60,13 +60,11 @@ you can use --pager=<value>::
 '''
 from __future__ import absolute_import
 
-from mercurial.i18n import _
 from mercurial import (
     cmdutil,
     commands,
     dispatch,
     extensions,
-    util,
     )
 
 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
@@ -78,15 +76,9 @@ testedwith = 'ships-with-hg-core'
 def uisetup(ui):
 
     def pagecmd(orig, ui, options, cmd, cmdfunc):
-        usepager = False
-        always = util.parsebool(options['pager'])
         auto = options['pager'] == 'auto'
-
-        if always:
-            usepager = True
-        elif not auto:
+        if auto and not ui.pageractive:
             usepager = False
-        else:
             attend = ui.configlist('pager', 'attend', attended)
             ignore = ui.configlist('pager', 'ignore')
             cmds, _ = cmdutil.findcmd(cmd, commands.table)
@@ -101,8 +93,8 @@ def uisetup(ui):
                     usepager = True
                     break
 
-        if usepager:
-            ui.pager('extension-via-attend-' + cmd)
+            if usepager:
+                ui.pager('extension-via-attend-' + cmd)
         return orig(ui, options, cmd, cmdfunc)
 
     # Wrap dispatch._runcommand after color is loaded so color can see
@@ -112,10 +104,4 @@ def uisetup(ui):
         extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
     extensions.afterloaded('color', afterloaded)
 
-def extsetup(ui):
-    commands.globalopts.append(
-        ('', 'pager', 'auto',
-         _("when to paginate (boolean, always, auto, or never)"),
-         _('TYPE')))
-
 attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -93,6 +93,8 @@ globalopts = [
     ('', 'version', None, _('output version information and exit')),
     ('h', 'help', None, _('display help and exit')),
     ('', 'hidden', False, _('consider hidden changesets')),
+    ('', 'pager', 'auto',
+     _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
 ]
 
 dryrunopts = [('n', 'dry-run', None,
diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
--- a/mercurial/dispatch.py
+++ b/mercurial/dispatch.py
@@ -816,6 +816,8 @@ def _dispatch(req):
 
 def _runcommand(ui, options, cmd, cmdfunc):
     """Run a command function, possibly with profiling enabled."""
+    if util.parsebool(options['pager']):
+        ui.pager('internal-always-' + cmd)
     try:
         return cmdfunc()
     except error.SignatureError:
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -138,6 +138,7 @@ Show the global options
   --help
   --hidden
   --noninteractive
+  --pager
   --profile
   --quiet
   --repository
@@ -171,6 +172,7 @@ Show the options for the "serve" command
   --ipv6
   --name
   --noninteractive
+  --pager
   --pid-file
   --port
   --prefix
diff --git a/tests/test-extension.t b/tests/test-extension.t
--- a/tests/test-extension.t
+++ b/tests/test-extension.t
@@ -543,6 +543,8 @@ hide outer repo
       --version           output version information and exit
    -h --help              display help and exit
       --hidden            consider hidden changesets
+      --pager TYPE        when to paginate (boolean, always, auto, or never)
+                          (default: auto)
 
 
 
@@ -578,6 +580,8 @@ hide outer repo
       --version           output version information and exit
    -h --help              display help and exit
       --hidden            consider hidden changesets
+      --pager TYPE        when to paginate (boolean, always, auto, or never)
+                          (default: auto)
 
 
 
@@ -856,6 +860,8 @@ extension help itself
       --version           output version information and exit
    -h --help              display help and exit
       --hidden            consider hidden changesets
+      --pager TYPE        when to paginate (boolean, always, auto, or never)
+                          (default: auto)
 
 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
   $ hg help -v dodo
@@ -889,6 +895,8 @@ Make sure that single '-v' option shows
       --version           output version information and exit
    -h --help              display help and exit
       --hidden            consider hidden changesets
+      --pager TYPE        when to paginate (boolean, always, auto, or never)
+                          (default: auto)
 
 In case when extension name doesn't match any of its commands,
 help message should ask for '-v' to get list of built-in aliases
@@ -960,6 +968,8 @@ help options '-v' and '-v -e' should be
       --version           output version information and exit
    -h --help              display help and exit
       --hidden            consider hidden changesets
+      --pager TYPE        when to paginate (boolean, always, auto, or never)
+                          (default: auto)
 
   $ hg help -v -e dudu
   dudu extension -
@@ -992,6 +1002,8 @@ help options '-v' and '-v -e' should be
       --version           output version information and exit
    -h --help              display help and exit
       --hidden            consider hidden changesets
+      --pager TYPE        when to paginate (boolean, always, auto, or never)
+                          (default: auto)
 
 Disabled extension commands:
 
diff --git a/tests/test-gendoc-ro.t b/tests/test-gendoc-ro.t
--- a/tests/test-gendoc-ro.t
+++ b/tests/test-gendoc-ro.t
@@ -1,4 +1,9 @@
 #require docutils gettext
 
+Error: the current ro localization has some rst defects exposed by
+moving pager to core. These two warnings about references are expected
+until the localization is corrected.
   $ $TESTDIR/check-gendoc ro
   checking for parse errors
+  gendoc.txt:55: (WARNING/2) Inline interpreted text or phrase reference start-string without end-string.
+  gendoc.txt:55: (WARNING/2) Inline interpreted text or phrase reference start-string without end-string.
diff --git a/tests/test-help.t b/tests/test-help.t
--- a/tests/test-help.t
+++ b/tests/test-help.t
@@ -326,6 +326,8 @@ Test short command list with verbose opt
       --version           output version information and exit
    -h --help              display help and exit
       --hidden            consider hidden changesets
+      --pager TYPE        when to paginate (boolean, always, auto, or never)
+                          (default: auto)
   
   (use 'hg help' for the full list of commands)
 
@@ -422,6 +424,8 @@ Verbose help for add
       --version           output version information and exit
    -h --help              display help and exit
       --hidden            consider hidden changesets
+      --pager TYPE        when to paginate (boolean, always, auto, or never)
+                          (default: auto)
 
 Test the textwidth config option
 
@@ -2523,6 +2527,9 @@ Dish up an empty repo; serve it cold.
   <tr><td></td>
   <td>--hidden</td>
   <td>consider hidden changesets</td></tr>
+  <tr><td></td>
+  <td>--pager TYPE</td>
+  <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
   </table>
   
   </div>
@@ -2718,6 +2725,9 @@ Dish up an empty repo; serve it cold.
   <tr><td></td>
   <td>--hidden</td>
   <td>consider hidden changesets</td></tr>
+  <tr><td></td>
+  <td>--pager TYPE</td>
+  <td>when to paginate (boolean, always, auto, or never) (default: auto)</td></tr>
   </table>
   
   </div>
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH 3 of 9 pager] ui: introduce neverpager() call

Augie Fackler-2
In reply to this post by Augie Fackler-2
# HG changeset patch
# User Augie Fackler <[hidden email]>
# Date 1487198883 18000
#      Wed Feb 15 17:48:03 2017 -0500
# Node ID 8695f290ba7c1a7f6c1b93ba344fbbe1193ab1a5
# Parent  cafe46e7d79c59567edc185979cf6568367def93
ui: introduce neverpager() call

I'm about to add direct paging support to some commands, and as a
result we need a way to communicate from the higher layers of dispatch
that paging is explicitly disabled.

diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
--- a/mercurial/dispatch.py
+++ b/mercurial/dispatch.py
@@ -746,6 +746,9 @@ def _dispatch(req):
             for ui_ in uis:
                 ui_.setconfig('ui', 'interactive', 'off', '-y')
 
+        if options['pager'] != 'auto' and not util.parsebool(options['pager']):
+            ui.neverpager()
+
         if cmdoptions.get('insecure', False):
             for ui_ in uis:
                 ui_.insecureconnections = True
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -147,6 +147,7 @@ class ui(object):
             self.ferr = src.ferr
             self.fin = src.fin
             self.pageractive = src.pageractive
+            self._neverpager = src._neverpager
 
             self._tcfg = src._tcfg.copy()
             self._ucfg = src._ucfg.copy()
@@ -164,6 +165,7 @@ class ui(object):
             self.ferr = util.stderr
             self.fin = util.stdin
             self.pageractive = False
+            self._neverpager = False
 
             # shared read-only environment
             self.environ = encoding.environ
@@ -797,6 +799,9 @@ class ui(object):
             return False
         return util.isatty(fh)
 
+    def neverpager(self):
+        self._neverpager = True
+
     def pager(self, command):
         """Start a pager for subsequent command output.
 
@@ -810,7 +815,8 @@ class ui(object):
           command: The full, non-aliased name of the command. That is, "log"
                    not "history, "summary" not "summ", etc.
         """
-        if (self.pageractive
+        if (self._neverpager
+            or self.pageractive
             # TODO: if we want to allow HGPLAINEXCEPT=pager,
             # formatted() will need some adjustment.
             or not self.formatted()
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH 4 of 9 pager] ui: add ignore-single-command functionality

Augie Fackler-2
In reply to this post by Augie Fackler-2
# HG changeset patch
# User Augie Fackler <[hidden email]>
# Date 1486441305 18000
#      Mon Feb 06 23:21:45 2017 -0500
# Node ID 1a671cd4d4aff343040e7764fcc4402725fb5a0d
# Parent  8695f290ba7c1a7f6c1b93ba344fbbe1193ab1a5
ui: add ignore-single-command functionality

This closes the last feature gap other than the attend list from the
extension. For now, I'm leaving the attend list in the extension,
because I'm unsure it has merit in a world where commands have been
updated to take advantage of the modern API.

diff --git a/hgext/pager.py b/hgext/pager.py
--- a/hgext/pager.py
+++ b/hgext/pager.py
@@ -94,6 +94,12 @@ def uisetup(ui):
                     break
 
             if usepager:
+                # Slight hack: the attend list is supposed to override
+                # the ignore list for the pager extension, but the
+                # core code doesn't know about attend, so we have to
+                # lobotomize the ignore list so that the extension's
+                # behavior is preserved.
+                ui.setconfig('pager', 'ignore', '', 'pager')
                 ui.pager('extension-via-attend-' + cmd)
         return orig(ui, options, cmd, cmdfunc)
 
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -817,6 +817,7 @@ class ui(object):
         """
         if (self._neverpager
             or self.pageractive
+            or command in self.configlist('pager', 'ignore')
             # TODO: if we want to allow HGPLAINEXCEPT=pager,
             # formatted() will need some adjustment.
             or not self.formatted()
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH 5 of 9 pager] tests: clean up a bunch of pager testing that is about to be invalidated

Augie Fackler-2
In reply to this post by Augie Fackler-2
# HG changeset patch
# User Augie Fackler <[hidden email]>
# Date 1486442730 18000
#      Mon Feb 06 23:45:30 2017 -0500
# Node ID 78410f8a924623287609eacf70bd4912b17a01bf
# Parent  1a671cd4d4aff343040e7764fcc4402725fb5a0d
tests: clean up a bunch of pager testing that is about to be invalidated

All this attend logic and potential bugs just no longer make sense to test.

diff --git a/tests/test-pager.t b/tests/test-pager.t
--- a/tests/test-pager.t
+++ b/tests/test-pager.t
@@ -58,7 +58,8 @@ We can enable the pager on id:
   $ hg --config pager.attend-id=yes id
   paged! '46106edeeb38 tip\n'
 
-If we completely change the attend list that's respected:
+Setting attend-$COMMAND to a false value works, even with pager in
+core:
 
   $ hg --config pager.attend-diff=no diff -c 2
   diff -r f4be7687d414 -r bce265549556 a
@@ -69,15 +70,6 @@ If we completely change the attend list
    a 1
   +a 2
 
-  $ hg --config pager.attend=summary diff -c 2
-  diff -r f4be7687d414 -r bce265549556 a
-  --- a/a Thu Jan 01 00:00:00 1970 +0000
-  +++ b/a Thu Jan 01 00:00:00 1970 +0000
-  @@ -1,2 +1,3 @@
-   a
-   a 1
-  +a 2
-
 If 'log' is in attend, then 'history' should also be paged:
   $ hg history --limit 2 --config pager.attend=log
   paged! 'changeset:   10:46106edeeb38\n'
@@ -92,56 +84,6 @@ If 'log' is in attend, then 'history' sh
   paged! 'summary:     modify a 9\n'
   paged! '\n'
 
-Possible bug: history is explicitly ignored in pager config, but
-because log is in the attend list it still gets pager treatment.
-
-  $ hg history --limit 2 --config pager.attend=log \
-  >   --config pager.ignore=history
-  paged! 'changeset:   10:46106edeeb38\n'
-  paged! 'tag:         tip\n'
-  paged! 'user:        test\n'
-  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
-  paged! 'summary:     modify a 10\n'
-  paged! '\n'
-  paged! 'changeset:   9:6dd8ea7dd621\n'
-  paged! 'user:        test\n'
-  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
-  paged! 'summary:     modify a 9\n'
-  paged! '\n'
-
-Possible bug: history is explicitly marked as attend-history=no, but
-it doesn't fail to get paged because log is still in the attend list.
-
-  $ hg history --limit 2 --config pager.attend-history=no
-  paged! 'changeset:   10:46106edeeb38\n'
-  paged! 'tag:         tip\n'
-  paged! 'user:        test\n'
-  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
-  paged! 'summary:     modify a 10\n'
-  paged! '\n'
-  paged! 'changeset:   9:6dd8ea7dd621\n'
-  paged! 'user:        test\n'
-  paged! 'date:        Thu Jan 01 00:00:00 1970 +0000\n'
-  paged! 'summary:     modify a 9\n'
-  paged! '\n'
-
-Possible bug: disabling pager for log but enabling it for history
-doesn't result in history being paged.
-
-  $ hg history --limit 2 --config pager.attend-log=no \
-  > --config pager.attend-history=yes
-  changeset:   10:46106edeeb38
-  tag:         tip
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     modify a 10
-  
-  changeset:   9:6dd8ea7dd621
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     modify a 9
-  
-
 Pager should not start if stdout is not a tty.
 
   $ hg log -l1 -q --config ui.formatted=False
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH 6 of 9 pager] ui: respect historic pager.attend-$COMMAND=no

Augie Fackler-2
In reply to this post by Augie Fackler-2
# HG changeset patch
# User Augie Fackler <[hidden email]>
# Date 1486442524 18000
#      Mon Feb 06 23:42:04 2017 -0500
# Node ID cfa569ed77883c41aecb3eaaa42876a5b0b7c52a
# Parent  78410f8a924623287609eacf70bd4912b17a01bf
ui: respect historic pager.attend-$COMMAND=no

I'm on the fence about this behavior, but the user's intent was pretty
specific and it's not expensive to support this case.

diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -818,6 +818,7 @@ class ui(object):
         if (self._neverpager
             or self.pageractive
             or command in self.configlist('pager', 'ignore')
+            or not self.configbool('pager', 'attend-' + command, True)
             # TODO: if we want to allow HGPLAINEXCEPT=pager,
             # formatted() will need some adjustment.
             or not self.formatted()
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH 7 of 9 pager] ui: add a debug print right before we start the pager

Augie Fackler-2
In reply to this post by Augie Fackler-2
# HG changeset patch
# User Augie Fackler <[hidden email]>
# Date 1486443354 18000
#      Mon Feb 06 23:55:54 2017 -0500
# Node ID 277dab1428ca0e37b649b9cd980b1467cdfc85d0
# Parent  cfa569ed77883c41aecb3eaaa42876a5b0b7c52a
ui: add a debug print right before we start the pager

This makes it easier to figure out why a command is getting paginated.

diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -829,6 +829,7 @@ class ui(object):
             # interactive, the user didn't say HGPLAIN or
             # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
             return
+        self.debug('starting pager for command %r\n' % command)
 
         # TODO: add a "system defaults" config section so this default
         # of more(1) can be easily replaced with a global
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH 8 of 9 pager] annotate: migrate to modern pager API

Augie Fackler-2
In reply to this post by Augie Fackler-2
# HG changeset patch
# User Augie Fackler <[hidden email]>
# Date 1486439567 18000
#      Mon Feb 06 22:52:47 2017 -0500
# Node ID 074b3f1ed66b41062e809eeabd4eb05b5554c680
# Parent  277dab1428ca0e37b649b9cd980b1467cdfc85d0
annotate: migrate to modern pager API

diff --git a/hgext/pager.py b/hgext/pager.py
--- a/hgext/pager.py
+++ b/hgext/pager.py
@@ -110,4 +110,4 @@ def uisetup(ui):
         extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
     extensions.afterloaded('color', afterloaded)
 
-attended = ['annotate', 'cat', 'diff', 'export', 'glog', 'log', 'qdiff']
+attended = ['cat', 'diff', 'export', 'glog', 'log', 'qdiff']
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -361,6 +361,7 @@ def annotate(ui, repo, *pats, **opts):
 
     Returns 0 on success.
     """
+    ui.pager('annotate')
     if not pats:
         raise error.Abort(_('at least one filename or pattern is required'))
 
diff --git a/tests/test-pager.t b/tests/test-pager.t
--- a/tests/test-pager.t
+++ b/tests/test-pager.t
@@ -164,3 +164,43 @@ Pager should not override the exit code
   $ hg fortytwo --pager=on
   paged! '42\n'
   [42]
+
+A command that asks for paging using ui.pager() directly works:
+  $ hg blame a
+  paged! ' 0: a\n'
+  paged! ' 1: a 1\n'
+  paged! ' 2: a 2\n'
+  paged! ' 3: a 3\n'
+  paged! ' 4: a 4\n'
+  paged! ' 5: a 5\n'
+  paged! ' 6: a 6\n'
+  paged! ' 7: a 7\n'
+  paged! ' 8: a 8\n'
+  paged! ' 9: a 9\n'
+  paged! '10: a 10\n'
+but not with HGPLAIN
+  $ HGPLAIN=1 hg blame a
+   0: a
+   1: a 1
+   2: a 2
+   3: a 3
+   4: a 4
+   5: a 5
+   6: a 6
+   7: a 7
+   8: a 8
+   9: a 9
+  10: a 10
+explicit flags work too:
+  $ hg blame --pager=no a
+   0: a
+   1: a 1
+   2: a 2
+   3: a 3
+   4: a 4
+   5: a 5
+   6: a 6
+   7: a 7
+   8: a 8
+   9: a 9
+  10: a 10
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[PATCH 9 of 9 pager] tests: prove that ignore works

Augie Fackler-2
In reply to this post by Augie Fackler-2
# HG changeset patch
# User Augie Fackler <[hidden email]>
# Date 1486441324 18000
#      Mon Feb 06 23:22:04 2017 -0500
# Node ID bce80a4789de7a273102a5e61a0ad6b13e8cbef8
# Parent  074b3f1ed66b41062e809eeabd4eb05b5554c680
tests: prove that ignore works

diff --git a/tests/test-pager.t b/tests/test-pager.t
--- a/tests/test-pager.t
+++ b/tests/test-pager.t
@@ -204,3 +204,21 @@ explicit flags work too:
    8: a 8
    9: a 9
   10: a 10
+
+Put annotate in the ignore list for pager:
+  $ cat >> $HGRCPATH <<EOF
+  > [pager]
+  > ignore = annotate
+  > EOF
+  $ hg blame a
+   0: a
+   1: a 1
+   2: a 2
+   3: a 3
+   4: a 4
+   5: a 5
+   6: a 6
+   7: a 7
+   8: a 8
+   9: a 9
+  10: a 10
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH 1 of 9 pager] pager: move pager-initiating code into core

Augie Fackler-2
In reply to this post by Augie Fackler-2

> On Feb 15, 2017, at 9:12 PM, Augie Fackler <[hidden email]> wrote:
>
> # HG changeset patch
> # User Augie Fackler <[hidden email]>
> # Date 1487198871 18000
> #      Wed Feb 15 17:47:51 2017 -0500
> # Node ID 675643abfdb6adbdfd8bddfbc263701c9caca539
> # Parent  e5363cb96233861fc99f7e9b85d7884d3121558c
> pager: move pager-initiating code into core

These 9 patches represent most of the functionally interesting bits of my pager-in-core series. The rest of the stack is visible at https://hg.durin42.com/hg-wip/graph/pager - the majority of the patches beyond this point are one-line “turn on pager” commits. I expect to send the remaining 21 patches in two batches: one wave of 19 trivial “turn it on” patches, and then 2 patches that rearrange some docs and mark the pager extension as deprecated.

>
> No functionality change.
>
> A previous version of this API had a category argument on
> ui.pager(). As I migrated the commands in core, I couldn't come up
> with good enough consistency in any categorization scheme so I just
> scrapped the whole idea. It may be worth revisiting in the future.
>
> diff --git a/hgext/pager.py b/hgext/pager.py
> --- a/hgext/pager.py
> +++ b/hgext/pager.py
> @@ -60,19 +60,11 @@ you can use --pager=<value>::
> '''
> from __future__ import absolute_import
>
> -import atexit
> -import os
> -import signal
> -import subprocess
> -import sys
> -
> from mercurial.i18n import _
> from mercurial import (
>     cmdutil,
>     commands,
>     dispatch,
> -    encoding,
> -    error,
>     extensions,
>     util,
>     )
> @@ -83,48 +75,14 @@ from mercurial import (
> # leave the attribute unspecified.
> testedwith = 'ships-with-hg-core'
>
> -def _runpager(ui, p):
> -    pager = subprocess.Popen(p, shell=True, bufsize=-1,
> -                             close_fds=util.closefds, stdin=subprocess.PIPE,
> -                             stdout=util.stdout, stderr=util.stderr)
> -
> -    # back up original file descriptors
> -    stdoutfd = os.dup(util.stdout.fileno())
> -    stderrfd = os.dup(util.stderr.fileno())
> -
> -    os.dup2(pager.stdin.fileno(), util.stdout.fileno())
> -    if ui._isatty(util.stderr):
> -        os.dup2(pager.stdin.fileno(), util.stderr.fileno())
> -
> -    @atexit.register
> -    def killpager():
> -        if util.safehasattr(signal, "SIGINT"):
> -            signal.signal(signal.SIGINT, signal.SIG_IGN)
> -        # restore original fds, closing pager.stdin copies in the process
> -        os.dup2(stdoutfd, util.stdout.fileno())
> -        os.dup2(stderrfd, util.stderr.fileno())
> -        pager.stdin.close()
> -        pager.wait()
> -
> -def catchterm(*args):
> -    raise error.SignalInterrupt
> -
> def uisetup(ui):
> -    class pagerui(ui.__class__):
> -        def _runpager(self, pagercmd):
> -            _runpager(self, pagercmd)
> -
> -    ui.__class__ = pagerui
>
>     def pagecmd(orig, ui, options, cmd, cmdfunc):
> -        p = ui.config("pager", "pager", encoding.environ.get("PAGER"))
>         usepager = False
>         always = util.parsebool(options['pager'])
>         auto = options['pager'] == 'auto'
>
> -        if not p or '--debugger' in sys.argv or not ui.formatted():
> -            pass
> -        elif always:
> +        if always:
>             usepager = True
>         elif not auto:
>             usepager = False
> @@ -143,14 +101,8 @@ def uisetup(ui):
>                     usepager = True
>                     break
>
> -        setattr(ui, 'pageractive', usepager)
> -
>         if usepager:
> -            ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
> -            ui.setconfig('ui', 'interactive', False, 'pager')
> -            if util.safehasattr(signal, "SIGPIPE"):
> -                signal.signal(signal.SIGPIPE, catchterm)
> -            ui._runpager(p)
> +            ui.pager('extension-via-attend-' + cmd)
>         return orig(ui, options, cmd, cmdfunc)
>
>     # Wrap dispatch._runcommand after color is loaded so color can see
> diff --git a/mercurial/ui.py b/mercurial/ui.py
> --- a/mercurial/ui.py
> +++ b/mercurial/ui.py
> @@ -7,13 +7,16 @@
>
> from __future__ import absolute_import
>
> +import atexit
> import contextlib
> import errno
> import getpass
> import inspect
> import os
> import re
> +import signal
> import socket
> +import subprocess
> import sys
> import tempfile
> import traceback
> @@ -143,6 +146,7 @@ class ui(object):
>             self.fout = src.fout
>             self.ferr = src.ferr
>             self.fin = src.fin
> +            self.pageractive = src.pageractive
>
>             self._tcfg = src._tcfg.copy()
>             self._ucfg = src._ucfg.copy()
> @@ -159,6 +163,7 @@ class ui(object):
>             self.fout = util.stdout
>             self.ferr = util.stderr
>             self.fin = util.stdin
> +            self.pageractive = False
>
>             # shared read-only environment
>             self.environ = encoding.environ
> @@ -792,6 +797,75 @@ class ui(object):
>             return False
>         return util.isatty(fh)
>
> +    def pager(self, command):
> +        """Start a pager for subsequent command output.
> +
> +        Commands which produce a long stream of output should call
> +        this function to activate the user's preferred pagination
> +        mechanism (which may be no pager). Calling this function
> +        precludes any future use of interactive functionality, such as
> +        prompting the user or activating curses.
> +
> +        Args:
> +          command: The full, non-aliased name of the command. That is, "log"
> +                   not "history, "summary" not "summ", etc.
> +        """
> +        if (self.pageractive
> +            # TODO: if we want to allow HGPLAINEXCEPT=pager,
> +            # formatted() will need some adjustment.
> +            or not self.formatted()
> +            or self.plain()
> +            # TODO: expose debugger-enabled on the UI object
> +            or '--debugger' in sys.argv):
> +            # We only want to paginate if the ui appears to be
> +            # interactive, the user didn't say HGPLAIN or
> +            # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
> +            return
> +
> +        # TODO: add a "system defaults" config section so this default
> +        # of more(1) can be easily replaced with a global
> +        # configuration file. For example, on OS X the sane default is
> +        # less(1), not more(1), and on debian it's
> +        # sensible-pager(1). We should probably also give the system
> +        # default editor command similar treatment.
> +        envpager = encoding.environ.get('PAGER', 'more')
> +        pagercmd = self.config('pager', 'pager', envpager)
> +        self.pageractive = True
> +        # Preserve the formatted-ness of the UI. This is important
> +        # because we mess with stdout, which might confuse
> +        # auto-detection of things being formatted.
> +        self.setconfig('ui', 'formatted', self.formatted(), 'pager')
> +        self.setconfig('ui', 'interactive', False, 'pager')
> +        self._runpager(pagercmd)
> +
> +    def _runpager(self, command):
> +        """Actually start the pager and set up file descriptors.
> +
> +        This is separate in part so that extensions (like chg) can
> +        override how a pager is invoked.
> +        """
> +        pager = subprocess.Popen(command, shell=True, bufsize=-1,
> +                                 close_fds=util.closefds, stdin=subprocess.PIPE,
> +                                 stdout=util.stdout, stderr=util.stderr)
> +
> +        # back up original file descriptors
> +        stdoutfd = os.dup(util.stdout.fileno())
> +        stderrfd = os.dup(util.stderr.fileno())
> +
> +        os.dup2(pager.stdin.fileno(), util.stdout.fileno())
> +        if self._isatty(util.stderr):
> +            os.dup2(pager.stdin.fileno(), util.stderr.fileno())
> +
> +        @atexit.register
> +        def killpager():
> +            if util.safehasattr(signal, "SIGINT"):
> +                signal.signal(signal.SIGINT, signal.SIG_IGN)
> +            # restore original fds, closing pager.stdin copies in the process
> +            os.dup2(stdoutfd, util.stdout.fileno())
> +            os.dup2(stderrfd, util.stderr.fileno())
> +            pager.stdin.close()
> +            pager.wait()
> +
>     def interface(self, feature):
>         """what interface to use for interactive console features?
>
> _______________________________________________
> Mercurial-devel mailing list
> [hidden email]
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: [PATCH 1 of 9 pager] pager: move pager-initiating code into core

Simon Farnsworth
In reply to this post by Augie Fackler-2
On 16/02/2017 02:12, Augie Fackler wrote:

> # HG changeset patch
> # User Augie Fackler <[hidden email]>
> # Date 1487198871 18000
> #      Wed Feb 15 17:47:51 2017 -0500
> # Node ID 675643abfdb6adbdfd8bddfbc263701c9caca539
> # Parent  e5363cb96233861fc99f7e9b85d7884d3121558c
> pager: move pager-initiating code into core
>
> No functionality change.
>
> A previous version of this API had a category argument on
> ui.pager(). As I migrated the commands in core, I couldn't come up
> with good enough consistency in any categorization scheme so I just
> scrapped the whole idea. It may be worth revisiting in the future.
>
> diff --git a/hgext/pager.py b/hgext/pager.py
> --- a/hgext/pager.py
> +++ b/hgext/pager.py
> @@ -60,19 +60,11 @@ you can use --pager=<value>::
>  '''
>  from __future__ import absolute_import
>
> -import atexit
> -import os
> -import signal
> -import subprocess
> -import sys
> -
>  from mercurial.i18n import _
>  from mercurial import (
>      cmdutil,
>      commands,
>      dispatch,
> -    encoding,
> -    error,
>      extensions,
>      util,
>      )
> @@ -83,48 +75,14 @@ from mercurial import (
>  # leave the attribute unspecified.
>  testedwith = 'ships-with-hg-core'
>
> -def _runpager(ui, p):
> -    pager = subprocess.Popen(p, shell=True, bufsize=-1,
> -                             close_fds=util.closefds, stdin=subprocess.PIPE,
> -                             stdout=util.stdout, stderr=util.stderr)
> -
> -    # back up original file descriptors
> -    stdoutfd = os.dup(util.stdout.fileno())
> -    stderrfd = os.dup(util.stderr.fileno())
> -
> -    os.dup2(pager.stdin.fileno(), util.stdout.fileno())
> -    if ui._isatty(util.stderr):
> -        os.dup2(pager.stdin.fileno(), util.stderr.fileno())
> -
> -    @atexit.register
> -    def killpager():
> -        if util.safehasattr(signal, "SIGINT"):
> -            signal.signal(signal.SIGINT, signal.SIG_IGN)
> -        # restore original fds, closing pager.stdin copies in the process
> -        os.dup2(stdoutfd, util.stdout.fileno())
> -        os.dup2(stderrfd, util.stderr.fileno())
> -        pager.stdin.close()
> -        pager.wait()
> -
> -def catchterm(*args):
> -    raise error.SignalInterrupt
> -

This little helper...

>  def uisetup(ui):
> -    class pagerui(ui.__class__):
> -        def _runpager(self, pagercmd):
> -            _runpager(self, pagercmd)
> -
> -    ui.__class__ = pagerui
>
>      def pagecmd(orig, ui, options, cmd, cmdfunc):
> -        p = ui.config("pager", "pager", encoding.environ.get("PAGER"))
>          usepager = False
>          always = util.parsebool(options['pager'])
>          auto = options['pager'] == 'auto'
>
> -        if not p or '--debugger' in sys.argv or not ui.formatted():
> -            pass
> -        elif always:
> +        if always:
>              usepager = True
>          elif not auto:
>              usepager = False
> @@ -143,14 +101,8 @@ def uisetup(ui):
>                      usepager = True
>                      break
>
> -        setattr(ui, 'pageractive', usepager)
> -
>          if usepager:
> -            ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
> -            ui.setconfig('ui', 'interactive', False, 'pager')
> -            if util.safehasattr(signal, "SIGPIPE"):
> -                signal.signal(signal.SIGPIPE, catchterm)

..and this change to signal handling are important. Without them, if you
do `hg log -r 'all()'` on a large repository, then quit the pager,
Mercurial will continue to crunch away in the background, generating
stdout that goes to the (now-deceased) pager.

> -            ui._runpager(p)
> +            ui.pager('extension-via-attend-' + cmd)
>          return orig(ui, options, cmd, cmdfunc)
>
>      # Wrap dispatch._runcommand after color is loaded so color can see
> diff --git a/mercurial/ui.py b/mercurial/ui.py
> --- a/mercurial/ui.py
> +++ b/mercurial/ui.py
> @@ -7,13 +7,16 @@
>
>  from __future__ import absolute_import
>
> +import atexit
>  import contextlib
>  import errno
>  import getpass
>  import inspect
>  import os
>  import re
> +import signal
>  import socket
> +import subprocess
>  import sys
>  import tempfile
>  import traceback
> @@ -143,6 +146,7 @@ class ui(object):
>              self.fout = src.fout
>              self.ferr = src.ferr
>              self.fin = src.fin
> +            self.pageractive = src.pageractive
>
>              self._tcfg = src._tcfg.copy()
>              self._ucfg = src._ucfg.copy()
> @@ -159,6 +163,7 @@ class ui(object):
>              self.fout = util.stdout
>              self.ferr = util.stderr
>              self.fin = util.stdin
> +            self.pageractive = False
>
>              # shared read-only environment
>              self.environ = encoding.environ
> @@ -792,6 +797,75 @@ class ui(object):
>              return False
>          return util.isatty(fh)
>
> +    def pager(self, command):
> +        """Start a pager for subsequent command output.
> +
> +        Commands which produce a long stream of output should call
> +        this function to activate the user's preferred pagination
> +        mechanism (which may be no pager). Calling this function
> +        precludes any future use of interactive functionality, such as
> +        prompting the user or activating curses.
> +
> +        Args:
> +          command: The full, non-aliased name of the command. That is, "log"
> +                   not "history, "summary" not "summ", etc.
> +        """
> +        if (self.pageractive
> +            # TODO: if we want to allow HGPLAINEXCEPT=pager,
> +            # formatted() will need some adjustment.
> +            or not self.formatted()
> +            or self.plain()
> +            # TODO: expose debugger-enabled on the UI object
> +            or '--debugger' in sys.argv):
> +            # We only want to paginate if the ui appears to be
> +            # interactive, the user didn't say HGPLAIN or
> +            # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
> +            return
> +
> +        # TODO: add a "system defaults" config section so this default
> +        # of more(1) can be easily replaced with a global
> +        # configuration file. For example, on OS X the sane default is
> +        # less(1), not more(1), and on debian it's
> +        # sensible-pager(1). We should probably also give the system
> +        # default editor command similar treatment.
> +        envpager = encoding.environ.get('PAGER', 'more')
> +        pagercmd = self.config('pager', 'pager', envpager)
> +        self.pageractive = True
> +        # Preserve the formatted-ness of the UI. This is important
> +        # because we mess with stdout, which might confuse
> +        # auto-detection of things being formatted.
> +        self.setconfig('ui', 'formatted', self.formatted(), 'pager')
> +        self.setconfig('ui', 'interactive', False, 'pager')

The equivalent signal handling change belongs here.

> +        self._runpager(pagercmd)
> +
> +    def _runpager(self, command):
> +        """Actually start the pager and set up file descriptors.
> +
> +        This is separate in part so that extensions (like chg) can
> +        override how a pager is invoked.
> +        """
> +        pager = subprocess.Popen(command, shell=True, bufsize=-1,
> +                                 close_fds=util.closefds, stdin=subprocess.PIPE,
> +                                 stdout=util.stdout, stderr=util.stderr)
> +
> +        # back up original file descriptors
> +        stdoutfd = os.dup(util.stdout.fileno())
> +        stderrfd = os.dup(util.stderr.fileno())
> +
> +        os.dup2(pager.stdin.fileno(), util.stdout.fileno())
> +        if self._isatty(util.stderr):
> +            os.dup2(pager.stdin.fileno(), util.stderr.fileno())
> +
> +        @atexit.register
> +        def killpager():
> +            if util.safehasattr(signal, "SIGINT"):
> +                signal.signal(signal.SIGINT, signal.SIG_IGN)
> +            # restore original fds, closing pager.stdin copies in the process
> +            os.dup2(stdoutfd, util.stdout.fileno())
> +            os.dup2(stderrfd, util.stderr.fileno())
> +            pager.stdin.close()
> +            pager.wait()
> +
>      def interface(self, feature):
>          """what interface to use for interactive console features?
>
> _______________________________________________
> Mercurial-devel mailing list
> [hidden email]
> https://urldefense.proofpoint.com/v2/url?u=https-3A__www.mercurial-2Dscm.org_mailman_listinfo_mercurial-2Ddevel&d=DwIGaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=mEgSWILcY4c4W3zjApBQLA&m=ZiiKlZHpcE6FnMZhBU6-nzVLClXOgHOARg0HpwT43yU&s=7H11BAlHtU6CGYWE4DAym4-8Q81xpiKmHHJ2JBpLKko&e=
>

--
Simon Farnsworth
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Loading...