[PATCH stable] pager: fork and exec pager as parent process

Brodie Rao dackze at gmail.com
Mon May 3 14:06:22 CDT 2010


# HG changeset patch
# User Brodie Rao <brodie at bitheap.org>
# Date 1272913234 18000
# Branch stable
# Node ID d7625733fa0f33b913f7432941ba436f4645cea9
# Parent  b8d0b4721affa90241f304272e0836ba566d0b3a
pager: fork and exec pager as parent process

With the pager as the child process instead of the parent process, the
termination of the parent Mercurial process can cause the terminal to return
before the pager exits. Inverting the relationship prevents that issue.

Platforms without fork() will continue to use util.popen().

diff --git a/hgext/color.py b/hgext/color.py
--- a/hgext/color.py
+++ b/hgext/color.py
@@ -333,11 +333,14 @@ def extsetup(ui):
 
 def _setupcmd(ui, cmd, table, func, effectsmap):
     '''patch in command to command table and load effect map'''
+    # check isatty() before anything else changes it (like pager)
+    isatty = sys.__stdout__.isatty()
+
     def nocolor(orig, *args, **opts):
 
         if (opts['no_color'] or opts['color'] == 'never' or
             (opts['color'] == 'auto' and (os.environ.get('TERM') == 'dumb'
-                                          or not sys.__stdout__.isatty()))):
+                                          or not isatty))):
             del opts['no_color']
             del opts['color']
             return orig(*args, **opts)
diff --git a/hgext/pager.py b/hgext/pager.py
--- a/hgext/pager.py
+++ b/hgext/pager.py
@@ -49,9 +49,27 @@ To ignore global commands like "hg versi
 specify them in the global .hgrc
 '''
 
-import sys, os, signal
+import sys, os, signal, shlex
 from mercurial import dispatch, util, extensions
 
+def _runpager(p):
+    if not hasattr(os, 'fork'):
+        sys.stderr = sys.stdout = util.popen(p, 'wb')
+        return
+    fdin, fdout = os.pipe()
+    pid = os.fork()
+    if pid == 0:
+        os.close(fdin)
+        os.dup2(fdout, sys.stdout.fileno())
+        os.dup2(fdout, sys.stderr.fileno())
+        os.close(fdout)
+        return
+    os.dup2(fdin, sys.stdin.fileno())
+    os.close(fdin)
+    os.close(fdout)
+    args = shlex.split(p)
+    os.execvp(args[0], args)
+
 def uisetup(ui):
     def pagecmd(orig, ui, options, cmd, cmdfunc):
         p = ui.config("pager", "pager", os.environ.get("PAGER"))
@@ -60,7 +78,7 @@ def uisetup(ui):
             if (cmd in attend or
                 (cmd not in ui.configlist('pager', 'ignore') and not attend)):
                 ui.setconfig('ui', 'interactive', False)
-                sys.stderr = sys.stdout = util.popen(p, "wb")
+                _runpager(p)
                 if ui.configbool('pager', 'quiet'):
                     signal.signal(signal.SIGPIPE, signal.SIG_DFL)
         return orig(ui, options, cmd, cmdfunc)


More information about the Mercurial-devel mailing list