[PATCH RFC] shellext: automatically add shell scripts as new commands

Martin Geisler mg at aragost.com
Sat Jan 7 11:56:59 CST 2012


# HG changeset patch
# User Martin Geisler <mg at aragost.com>
# Date 1325959011 -3600
# Node ID 8c34fa2adf476a1e07512207fb18866e7908cf58
# Parent  3fe39d6d2bd8331932df063d272fff8af327881c
shellext: automatically add shell scripts as new commands

People sometimes note that Mercurial extensions must be written in
Python, whereas extensions for Git can be written in any language.
Some people see this as an advantage for Git. This extension lets you
do the same for Mercurial.

The only required change to core Mercurial is loading the extensions
before checking for shell aliases -- this is so that the extension has
a chance to register the new shell aliases.

diff --git a/hgext/shellext.py b/hgext/shellext.py
new file mode 100644
--- /dev/null
+++ b/hgext/shellext.py
@@ -0,0 +1,50 @@
+# Copyright 2012  Martin Geisler <mg at aragost.com>
+#
+# This software may be used and distributed according to the terms of
+# the GNU General Public License version 2 or any later version.
+
+"""automatically register executables in ~/.hgext as new commands
+
+Enable this extension and you'll get a new command for any executable
+script found in your ``~/.hgext`` folder. A script named ``foo.sh``
+will give you a new ``hg foo`` command. If you invoke it as::
+
+  hg foo -x bar -y baz
+
+then ``foo.sh`` will be invoked as
+
+  foo.sh -x bar -y baz
+
+The above directory is the default and you can change it by setting
+``shellext.extdir`` to another value.
+"""
+
+import os, sys
+from mercurial import util, dispatch
+
+_earlyopts = ["--config", "--cwd", "-R", "--repository", "--repo"]
+
+def uisetup(ui):
+    args = sys.argv[1:]
+    # remove the earlyopts that dispatch has already acted upon.
+    dispatch._earlygetopt(_earlyopts, args)
+    try:
+        # remove one layer of "quoting" so that it's possible to pass
+        # global opts to a shell command by putting them after '--'.
+        args.remove('--')
+    except ValueError:
+        pass
+
+    aliasargs = ' '.join(map(util.shellquote, args[1:]))
+
+    extdir = ui.configpath('shellext', 'extdir', os.path.expanduser('~/.hgext'))
+    if not os.path.isdir(extdir):
+        ui.debug('shellext: %s is not a directory\n' % extdir)
+        return
+
+    for script in os.listdir(extdir):
+        path = os.path.join(extdir, script)
+        alias = os.path.splitext(script)[0]
+        if not os.access(path, os.X_OK):
+            continue
+        ui.setconfig('alias', alias, '!%s %s' % (path, aliasargs))
diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
--- a/mercurial/dispatch.py
+++ b/mercurial/dispatch.py
@@ -540,12 +540,6 @@
     rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
     path, lui = _getlocal(ui, rpath)
 
-    # Now that we're operating in the right directory/repository with
-    # the right config settings, check for shell aliases
-    shellaliasfn = _checkshellalias(lui, ui, args)
-    if shellaliasfn:
-        return shellaliasfn()
-
     # Configure extensions in phases: uisetup, extsetup, cmdtable, and
     # reposetup. Programs like TortoiseHg will call _dispatch several
     # times so we keep track of configured extensions in _loaded.
@@ -556,6 +550,12 @@
 
     # (uisetup and extsetup are handled in extensions.loadall)
 
+    # Now that we're operating in the right directory/repository with
+    # the right config settings, check for shell aliases
+    shellaliasfn = _checkshellalias(lui, ui, args)
+    if shellaliasfn:
+        return shellaliasfn()
+
     for name, module in exts:
         cmdtable = getattr(module, 'cmdtable', {})
         overrides = [cmd for cmd in cmdtable if cmd in commands.table]


More information about the Mercurial-devel mailing list