Patchwork [1,of,3,RFC] chg: add a standalone entry point

login
register
mail settings
Submitter Jun Wu
Date Oct. 3, 2016, 6:11 a.m.
Message ID <7b19924c9fd57fbb7fa3.1475475078@x1c>
Download mbox | patch
Permalink /patch/16824/
State Changes Requested
Delegated to: Yuya Nishihara
Headers show

Comments

Jun Wu - Oct. 3, 2016, 6:11 a.m.
# HG changeset patch
# User Jun Wu <quark@fb.com>
# Date 1475458616 -3600
#      Mon Oct 03 02:36:56 2016 +0100
# Node ID 7b19924c9fd57fbb7fa39c103902d51d7ea9beb4
# Parent  cd7276f7ea8308df2c5d8874d335d73247d0f357
# Available At https://bitbucket.org/quark-zju/hg-draft
#              hg pull https://bitbucket.org/quark-zju/hg-draft -r 7b19924c9fd5
chg: add a standalone entry point

This is the beginning of a series that does a re-architect to chg mentioned
in [1], to achieve better compatibility.

The compatibility issues are mainly around "uisetup"s and "reposetup"s:

  - Developers are usually unaware that uisetup runs only once per chg
    process. We cannot reliably devel-warn them. The result is, potential
    broken code are written. For example, it's really hard for chg to deal
    with "experimental.evolution" changed from unset to manually set in
    config files because setconfig is used if that config option is not set.
  - An unnecessary "reposetup" caused by "hg serve" may have unwanted
    side effects. This can become troublesome if the repo requires things
    like remotefilelog or lz4revlog, and the user sets HGRCPATH to run
    tests.

The current chg implementation assumes that "loading" an extension is not
side effect free - if extension related config has changed, a restart is
needed. The new idea is, "loading" = "importing" + "run ui/extsetup", the
"importing" part can be side-effect free for some extensions. And benchmark
shows "import" takes most of the time consumed, while "uisetup" is usually
very fast. We can afford running "uisetup"s per request.

To be able to (pre-)"import" extensions without running any "uisetup"s, a
different entry point is needed. Otherwise as long as we go through the
normal dispatch / runcommand ("hg serve") flow, "uisetup"s cannot be
avoided.

Aside from better compatibility, we can also remove some hacks:

  - chg client: no longer needs to extract sensitive argv
  - chg server: confighash can be changed to only hash environment variables
    (reduce the number of server processes)
  - chg server: srcui.walkconfig hack is no longer necessary

This patch adds a new script "chgserve" as the new entry point. Currently,
it is just a minimal implementation that makes "CHGHG=chgserve chg ..."
work, without doing any pre-importing. The change could also be done in the
"hg" script. But since chg is still experimental, let's keep "hg" untouched
for now.

[1]: www.mercurial-scm.org/pipermail/mercurial-devel/2016-July/085965.html

Patch

diff --git a/contrib/chg/chgserve b/contrib/chg/chgserve
new file mode 100755
--- /dev/null
+++ b/contrib/chg/chgserve
@@ -0,0 +1,46 @@ 
+#!/usr/bin/env python
+
+from __future__ import absolute_import
+
+import os
+import sys
+
+from mercurial import (
+    cmdutil,
+    commands,
+    commandserver,
+    dispatch,
+    fancyopts,
+    ui as uimod,
+    util,
+)
+
+from hgext import chgserver
+
+def _confighash(ui):
+    envitems = [(k, v) for k, v in os.environ.iteritems()
+                if chgserver._envre.match(k)]
+    envhash = chgserver._hashlist(sorted(envitems))
+    return envhash[:12]
+
+def _startchgservice():
+    # patch chgserver's confighash to only hash environment variables
+    chgserver._confighash = _confighash
+
+    ui = uimod.ui()
+    repo = None
+    args = sys.argv[2:]
+    dispatch._earlygetopt(['--config'], args)
+    opts = {}
+    fancyopts.fancyopts(args, commands.table['^serve'][1], opts)
+
+    service = chgserver.chgunixservice(ui, repo, opts)
+    cmdutil.service(opts, initfn=service.init, runfn=service.run)
+
+def _setbinary():
+    for fp in (sys.stdin, sys.stdout, sys.stderr):
+        util.setbinary(fp)
+
+if __name__ == '__main__':
+    _setbinary()
+    sys.exit(_startchgservice())