{"thread":{"id":"15611","subject":"[StGit] kha/{safe,experimental} updated","startedAt":"2008-09-21T15:17:42Z","lastAt":"2008-09-25T07:33:49Z","messageCount":4,"participants":["Karl Hasselström","Catalin Marinas"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"91230","messageId":"20080921151725.11942.27640.stgit@yoghurt","threadId":"15611","inReplyTo":"20080921153757.GA9098@diana.vm.bytemark.co.uk","subject":"[StGit PATCH] Automatic bash completion","fromName":"Karl Hasselström","fromEmail":"kha@treskal.com","sentAt":"2008-09-21T15:17:42Z","receivedAt":"2008-09-21T15:17:42Z","isPatch":true,"sender":{"key":"kha@treskal.com","avatar":"https://gravatar.com/avatar/f0120c734b5279b345075a28521e1ac66acb20c9913ffe9bf6ae97e53f7f3f13?d=mp&s=160"},"body":"Teach the build process to create a bash tab completion script. This\nhas three benefits:\n\n  1. The tab completion gets faster, since it no longer has to run stg\n     to figure out the set of available flags for each command.\n\n  2. The tab completion script used to encode the list of stg\n     subcommands, and the kind of arguments each subcommand expected.\n     This information now lives in just one place: the subcommand's\n     module.\n\n  3. The tab completion script now knows what kind of argument each\n     flag wants, and can tab complete those as well. So \"stg refresh\n     <tab>\" will complete dirty files, but \"stg refresh -p <tab>\" will\n     complete patch names.\n\nSigned-off-by: Karl Hasselström <kha@treskal.com>\n\n---\n\n .gitignore                    |    1 \n Makefile                      |    5 +\n contrib/stgit-completion.bash |  270 -----------------------------------------\n setup.py                      |    4 -\n stg-build                     |    6 +\n stgit/argparse.py             |   92 ++++++++++++--\n stgit/commands/branch.py      |    3 \n stgit/commands/clean.py       |    1 \n stgit/commands/clone.py       |    3 \n stgit/commands/coalesce.py    |    2 \n stgit/commands/commit.py      |    3 \n stgit/commands/delete.py      |    5 +\n stgit/commands/diff.py        |    4 +\n stgit/commands/edit.py        |    2 \n stgit/commands/export.py      |    9 +\n stgit/commands/files.py       |    2 \n stgit/commands/float.py       |    4 -\n stgit/commands/fold.py        |    5 -\n stgit/commands/goto.py        |    2 \n stgit/commands/hide.py        |    6 +\n stgit/commands/id.py          |    3 \n stgit/commands/imprt.py       |    3 \n stgit/commands/init.py        |    1 \n stgit/commands/log.py         |    7 +\n stgit/commands/mail.py        |    5 +\n stgit/commands/new.py         |    1 \n stgit/commands/patches.py     |    5 -\n stgit/commands/pick.py        |    9 +\n stgit/commands/pop.py         |    3 \n stgit/commands/pull.py        |    3 \n stgit/commands/push.py        |    3 \n stgit/commands/rebase.py      |    3 \n stgit/commands/redo.py        |    1 \n stgit/commands/refresh.py     |    4 -\n stgit/commands/rename.py      |    6 +\n stgit/commands/repair.py      |    1 \n stgit/commands/reset.py       |    4 +\n stgit/commands/resolved.py    |    4 -\n stgit/commands/series.py      |    8 +\n stgit/commands/show.py        |    5 +\n stgit/commands/sink.py        |    6 +\n stgit/commands/status.py      |    3 \n stgit/commands/sync.py        |    8 +\n stgit/commands/top.py         |    4 -\n stgit/commands/uncommit.py    |    6 +\n stgit/commands/undo.py        |    1 \n stgit/commands/unhide.py      |    5 -\n stgit/completion.py           |  140 +++++++++++++++++++++\n 48 files changed, 354 insertions(+), 327 deletions(-)\n delete mode 100644 contrib/stgit-completion.bash\n create mode 100644 stgit/completion.py\n\n\ndiff --git a/.gitignore b/.gitignore\nindex 91dbad2..e7fffb0 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -6,3 +6,4 @@ patches-*\n release.sh\n setup.cfg.rpm\n snapshot.sh\n+stgit-completion.bash\ndiff --git a/Makefile b/Makefile\nindex 288622a..7183670 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -7,13 +7,16 @@ TEST_PATCHES ?= ..\n all: build\n \t$(PYTHON) setup.py build\n \n-build: stgit/commands/cmdlist.py\n+build: stgit/commands/cmdlist.py stgit-completion.bash\n \n ALL_PY = $(shell find stgit -name '*.py')\n \n stgit/commands/cmdlist.py: $(ALL_PY)\n \t$(PYTHON) stg-build --py-cmd-list > $@\n \n+stgit-completion.bash: $(ALL_PY)\n+\t$(PYTHON) stg-build --bash-completion > $@\n+\n install: build\n \t$(PYTHON) setup.py install --prefix=$(prefix) --root=$(DESTDIR) --force\n \ndiff --git a/contrib/stgit-completion.bash b/contrib/stgit-completion.bash\ndeleted file mode 100644\nindex 1467c28..0000000\n--- a/contrib/stgit-completion.bash\n+++ /dev/null\n@@ -1,270 +0,0 @@\n-# bash completion support for StGIT                        -*- shell-script -*-\n-#\n-# Copyright (C) 2006, Karl Hasselström <kha@treskal.com>\n-# Based on git-completion.sh\n-#\n-# To use these routines:\n-#\n-#    1. Copy this file to somewhere (e.g. ~/.stgit-completion.bash).\n-#\n-#    2. Add the following line to your .bashrc:\n-#         . ~/.stgit-completion.bash\n-\n-_stg_commands=\"\n-    branch\n-    delete\n-    diff\n-    clean\n-    clone\n-    coalesce\n-    commit\n-    edit\n-    export\n-    files\n-    float\n-    fold\n-    goto\n-    hide\n-    id\n-    import\n-    init\n-    log\n-    mail\n-    new\n-    patches\n-    pick\n-    pop\n-    pull\n-    push\n-    rebase\n-    refresh\n-    rename\n-    repair\n-    resolved\n-    series\n-    show\n-    sink\n-    status\n-    sync\n-    top\n-    uncommit\n-    unhide\n-\"\n-\n-# The path to .git, or empty if we're not in a repository.\n-_gitdir ()\n-{\n-    echo \"$(git rev-parse --git-dir 2>/dev/null)\"\n-}\n-\n-# Name of the current branch, or empty if there isn't one.\n-_current_branch ()\n-{\n-    local b=$(git symbolic-ref HEAD 2>/dev/null)\n-    echo ${b#refs/heads/}\n-}\n-\n-# List of all applied patches.\n-_applied_patches ()\n-{\n-    local g=$(_gitdir)\n-    [ \"$g\" ] && cat \"$g/patches/$(_current_branch)/applied\"\n-}\n-\n-# List of all unapplied patches.\n-_unapplied_patches ()\n-{\n-    local g=$(_gitdir)\n-    [ \"$g\" ] && cat \"$g/patches/$(_current_branch)/unapplied\"\n-}\n-\n-# List of all applied patches.\n-_hidden_patches ()\n-{\n-    local g=$(_gitdir)\n-    [ \"$g\" ] && cat \"$g/patches/$(_current_branch)/hidden\"\n-}\n-\n-# List of all patches.\n-_all_patches ()\n-{\n-    local b=$(_current_branch)\n-    local g=$(_gitdir)\n-    [ \"$g\" ] && cat \"$g/patches/$b/applied\" \"$g/patches/$b/unapplied\"\n-}\n-\n-# List of all patches except the current patch.\n-_all_other_patches ()\n-{\n-    local b=$(_current_branch)\n-    local g=$(_gitdir)\n-    [ \"$g\" ] && cat \"$g/patches/$b/applied\" \"$g/patches/$b/unapplied\" \\\n-        | grep -v \"^$(cat $g/patches/$b/current 2> /dev/null)$\"\n-}\n-\n-_all_branches ()\n-{\n-    local g=$(_gitdir)\n-    [ \"$g\" ] && (cd $g/patches/ && echo *)\n-}\n-\n-_conflicting_files ()\n-{\n-    local g=$(_gitdir)\n-    [ \"$g\" ] && stg status --conflict\n-}\n-\n-_dirty_files ()\n-{\n-    local g=$(_gitdir)\n-    [ \"$g\" ] && stg status --modified --new --deleted\n-}\n-\n-_unknown_files ()\n-{\n-    local g=$(_gitdir)\n-    [ \"$g\" ] && stg status --unknown\n-}\n-\n-_known_files ()\n-{\n-    local g=$(_gitdir)\n-    [ \"$g\" ] && git ls-files\n-}\n-\n-# List the command options\n-_cmd_options ()\n-{\n-    stg $1 --help 2>/dev/null | grep -e \" --[A-Za-z]\" | sed -e \"s/.*\\(--[^ =]\\+\\).*/\\1/\"\n-}\n-\n-# Generate completions for patches and patch ranges from the given\n-# patch list function, and options from the given list.\n-_complete_patch_range ()\n-{\n-    local patchlist=\"$1\" options=\"$2\"\n-    local pfx cur=\"${COMP_WORDS[COMP_CWORD]}\"\n-    case \"$cur\" in\n-        *..*)\n-            pfx=\"${cur%..*}..\"\n-            cur=\"${cur#*..}\"\n-            COMPREPLY=($(compgen -P \"$pfx\" -W \"$($patchlist)\" -- \"$cur\"))\n-            ;;\n-        *)\n-            COMPREPLY=($(compgen -W \"$options $($patchlist)\" -- \"$cur\"))\n-            ;;\n-    esac\n-}\n-\n-_complete_patch_range_options ()\n-{\n-    local patchlist=\"$1\" options=\"$2\" patch_options=\"$3\"\n-    local prev=\"${COMP_WORDS[COMP_CWORD-1]}\"\n-    local cur=\"${COMP_WORDS[COMP_CWORD]}\"\n-    local popt\n-    for popt in $patch_options; do\n-        if [ $prev == $popt ]; then\n-            _complete_patch_range $patchlist\n-            return\n-        fi\n-    done\n-    COMPREPLY=($(compgen -W \"$options\" -- \"$cur\"))\n-}\n-\n-_complete_branch ()\n-{\n-     COMPREPLY=($(compgen -W \"$(_cmd_options $1) $($2)\" -- \"${COMP_WORDS[COMP_CWORD]}\"))\n-}\n-\n-# Generate completions for options from the given list.\n-_complete_options ()\n-{\n-    local options=\"$1\"\n-    COMPREPLY=($(compgen -W \"$options\" -- \"${COMP_WORDS[COMP_CWORD]}\"))\n-}\n-\n-_complete_files ()\n-{\n-    COMPREPLY=($(compgen -W \"$(_cmd_options $1) $2\" -- \"${COMP_WORDS[COMP_CWORD]}\"))\n-}\n-\n-_stg_common ()\n-{\n-    _complete_options \"$(_cmd_options $1)\"\n-}\n-\n-_stg_patches ()\n-{\n-    _complete_patch_range \"$2\" \"$(_cmd_options $1)\"\n-}\n-\n-_stg_patches_options ()\n-{\n-    _complete_patch_range_options \"$2\" \"$(_cmd_options $1)\" \"$3\"\n-}\n-\n-_stg_help ()\n-{\n-    _complete_options \"$_stg_commands\"\n-}\n-\n-_stg ()\n-{\n-    local i c=1 command\n-\n-    while [ $c -lt $COMP_CWORD ]; do\n-        if [ $c == 1 ]; then\n-            command=\"${COMP_WORDS[c]}\"\n-        fi\n-        c=$((++c))\n-    done\n-\n-    # Complete name of subcommand.\n-    if [ $c -eq $COMP_CWORD -a -z \"$command\" ]; then\n-        COMPREPLY=($(compgen \\\n-            -W \"--help --version copyright help $_stg_commands\" \\\n-            -- \"${COMP_WORDS[COMP_CWORD]}\"))\n-        return;\n-    fi\n-\n-    # Complete arguments to subcommands.\n-    case \"$command\" in\n-        # generic commands\n-        help)   _stg_help ;;\n-        # repository commands\n-        id)     _stg_patches $command _all_patches ;;\n-        # stack commands\n-        coalesce) _stg_patches $command _applied_patches ;;\n-        float)  _stg_patches $command _all_patches ;;\n-        goto)   _stg_patches $command _all_other_patches ;;\n-        hide)   _stg_patches $command _unapplied_patches ;;\n-        pop)    _stg_patches $command _applied_patches ;;\n-        push)   _stg_patches $command _unapplied_patches ;;\n-        series) _stg_patches $command _all_patches ;;\n-        sink)   _stg_patches $command _all_patches ;;\n-        unhide) _stg_patches $command _hidden_patches ;;\n-        # patch commands\n-        delete) _stg_patches $command _all_patches ;;\n-        edit)   _stg_patches $command _applied_patches ;;\n-        export) _stg_patches $command _all_patches ;;\n-        files)  _stg_patches $command _all_patches ;;\n-        log)    _stg_patches $command _all_patches ;;\n-        mail)   _stg_patches $command _all_patches ;;\n-        pick)   _stg_patches $command _unapplied_patches ;;\n-#\trefresh)_stg_patches_options $command _applied_patches \"-p --patch\" ;;\n-        refresh) _complete_files $command \"$(_dirty_files)\" ;;\n-        rename) _stg_patches $command _all_patches ;;\n-        show)   _stg_patches $command _all_patches ;;\n-        sync)   _stg_patches $command _applied_patches ;;\n-        # working-copy commands\n-        diff)   _stg_patches_options $command _applied_patches \"-r --range\" ;;\n-\tresolved) _complete_files $command \"$(_conflicting_files)\" ;;\n-\t# commands that usually raher accept branches\n-\tbranch) _complete_branch $command _all_branches ;;\n-\trebase) _complete_branch $command _all_branches ;;\n-        # all the other commands\n-        *)      _stg_common $command ;;\n-    esac\n-}\n-\n-complete -o default -F _stg stg\ndiff --git a/setup.py b/setup.py\nindex 81854d3..fb67958 100755\n--- a/setup.py\n+++ b/setup.py\n@@ -59,8 +59,8 @@ def __run_setup():\n             ('share/stgit/examples', glob.glob('examples/*.tmpl')),\n             ('share/stgit/examples', ['examples/gitconfig']),\n             ('share/stgit/contrib', ['contrib/diffcol.sh',\n-                                     'contrib/stgbashprompt.sh',\n-                                     'contrib/stgit-completion.bash']),\n+                                     'contrib/stgbashprompt.sh']),\n+            ('share/stgit/completion', ['stgit-completion.bash'])\n             ])\n \n # Check the minimum versions required\ndiff --git a/stg-build b/stg-build\nindex 3c9dbfa..2af6523 100755\n--- a/stg-build\n+++ b/stg-build\n@@ -2,7 +2,7 @@\n # -*- python -*-\n import optparse, sys\n import stgit.main\n-from stgit import argparse, commands\n+from stgit import argparse, commands, completion\n \n def main():\n     op = optparse.OptionParser()\n@@ -14,6 +14,8 @@ def main():\n                   help = 'Print asciidoc command list')\n     op.add_option('--py-cmd-list', action = 'store_true',\n                   help = 'Write Python command list')\n+    op.add_option('--bash-completion', action = 'store_true',\n+                  help = 'Write bash completion code')\n     options, args = op.parse_args()\n     if args:\n         op.error('Wrong number of arguments')\n@@ -30,6 +32,8 @@ def main():\n     elif options.py_cmd_list:\n         commands.py_commands(commands.get_commands(allow_cached = False),\n                              sys.stdout)\n+    elif options.bash_completion:\n+        completion.write_completion(sys.stdout)\n     else:\n         op.error('No command')\n \ndiff --git a/stgit/argparse.py b/stgit/argparse.py\nindex bd71817..406ada3 100644\n--- a/stgit/argparse.py\n+++ b/stgit/argparse.py\n@@ -26,28 +26,27 @@ def _paragraphs(s):\n \n class opt(object):\n     \"\"\"Represents a command-line flag.\"\"\"\n-    def __init__(self, *args, **kwargs):\n-        self.args = args\n+    def __init__(self, *pargs, **kwargs):\n+        self.pargs = pargs\n         self.kwargs = kwargs\n     def get_option(self):\n         kwargs = dict(self.kwargs)\n         kwargs['help'] = kwargs['short']\n-        del kwargs['short']\n-        if 'long' in kwargs:\n-            del kwargs['long']\n-        return optparse.make_option(*self.args, **kwargs)\n+        for k in ['short', 'long', 'args']:\n+            kwargs.pop(k, None)\n+        return optparse.make_option(*self.pargs, **kwargs)\n     def metavar(self):\n         o = self.get_option()\n         if not o.nargs:\n             return None\n         if o.metavar:\n             return o.metavar\n-        for flag in self.args:\n+        for flag in self.pargs:\n             if flag.startswith('--'):\n                 return utils.strip_prefix('--', flag).upper()\n         raise Exception('Cannot determine metavar')\n     def write_asciidoc(self, f):\n-        for flag in self.args:\n+        for flag in self.pargs:\n             f.write(flag)\n             m = self.metavar()\n             if m:\n@@ -60,6 +59,16 @@ class opt(object):\n             f.write('+\\n')\n             for line in para:\n                 f.write(line + '\\n')\n+    @property\n+    def flags(self):\n+        return self.pargs\n+    @property\n+    def args(self):\n+        if self.kwargs.get('action', None) in ['store_true', 'store_false']:\n+            default = []\n+        else:\n+            default = [files]\n+        return self.kwargs.get('args', default)\n \n def _cmd_name(cmd_mod):\n     return getattr(cmd_mod, 'name', cmd_mod.__name__.split('.')[-1])\n@@ -103,11 +112,11 @@ def sign_options():\n                 '--ack and --sign were both specified')\n         parser.values.sign_str = sign_str\n     return [\n-        opt('--sign', action = 'callback', dest = 'sign_str',\n+        opt('--sign', action = 'callback', dest = 'sign_str', args = [],\n             callback = callback, callback_args = ('Signed-off-by',),\n             short = 'Add \"Signed-off-by:\" line', long = \"\"\"\n             Add a \"Signed-off-by:\" to the end of the patch.\"\"\"),\n-        opt('--ack', action = 'callback', dest = 'sign_str',\n+        opt('--ack', action = 'callback', dest = 'sign_str', args = [],\n             callback = callback, callback_args = ('Acked-by',),\n             short = 'Add \"Acked-by:\" line', long = \"\"\"\n             Add an \"Acked-by:\" line to the end of the patch.\"\"\")]\n@@ -151,7 +160,7 @@ def message_options(save_template):\n             callback = msg_callback, dest = 'message', type = 'string',\n             short = 'Use MESSAGE instead of invoking the editor'),\n         opt('-f', '--file', action = 'callback', callback = file_callback,\n-            dest = 'message', type = 'string',\n+            dest = 'message', type = 'string', args = [files],\n             short = 'Use FILE instead of invoking the editor', long = \"\"\"\n             Use the contents of FILE instead of invoking the editor.\n             (If FILE is \"-\", write to stdout.)\"\"\")]\n@@ -181,6 +190,7 @@ def diff_opts_option():\n             default = (config.get('stgit.diff-opts') or '').split(),\n             action = 'callback', callback = diff_opts_callback,\n             type = 'string', metavar = 'OPTIONS',\n+            args = [strings('-M', '-C')],\n             short = 'Extra options to pass to \"git diff\"')]\n \n def _person_opts(person, short):\n@@ -212,3 +222,63 @@ def author_options():\n \n def author_committer_options():\n     return _person_opts('author', 'auth') + _person_opts('committer', 'comm')\n+\n+class CompgenBase(object):\n+    def actions(self, var): return set()\n+    def words(self, var): return set()\n+    def command(self, var):\n+        cmd = ['compgen']\n+        for act in self.actions(var):\n+            cmd += ['-A', act]\n+        words = self.words(var)\n+        if words:\n+            cmd += ['-W', '\"%s\"' % ' '.join(words)]\n+        cmd += ['--', '\"%s\"' % var]\n+        return ' '.join(cmd)\n+\n+class CompgenJoin(CompgenBase):\n+    def __init__(self, a, b):\n+        assert isinstance(a, CompgenBase)\n+        assert isinstance(b, CompgenBase)\n+        self.__a = a\n+        self.__b = b\n+    def words(self, var): return self.__a.words(var) | self.__b.words(var)\n+    def actions(self, var): return self.__a.actions(var) | self.__b.actions(var)\n+\n+class Compgen(CompgenBase):\n+    def __init__(self, words = frozenset(), actions = frozenset()):\n+        self.__words = set(words)\n+        self.__actions = set(actions)\n+    def actions(self, var): return self.__actions\n+    def words(self, var): return self.__words\n+\n+def compjoin(compgens):\n+    comp = Compgen()\n+    for c in compgens:\n+        comp = CompgenJoin(comp, c)\n+    return comp\n+\n+all_branches = Compgen(['$(_all_branches)'])\n+stg_branches = Compgen(['$(_stg_branches)'])\n+applied_patches = Compgen(['$(_applied_patches)'])\n+other_applied_patches = Compgen(['$(_other_applied_patches)'])\n+unapplied_patches = Compgen(['$(_unapplied_patches)'])\n+hidden_patches = Compgen(['$(_hidden_patches)'])\n+commit = Compgen(['$(_all_branches) $(_tags) $(_remotes)'])\n+conflicting_files = Compgen(['$(_conflicting_files)'])\n+dirty_files = Compgen(['$(_dirty_files)'])\n+unknown_files = Compgen(['$(_unknown_files)'])\n+known_files = Compgen(['$(_known_files)'])\n+repo = Compgen(actions = ['directory'])\n+dir = Compgen(actions = ['directory'])\n+files = Compgen(actions = ['file'])\n+def strings(*ss): return Compgen(ss)\n+class patch_range(CompgenBase):\n+    def __init__(self, *endpoints):\n+        self.__endpoints = endpoints\n+    def words(self, var):\n+        words = set()\n+        for e in self.__endpoints:\n+            assert not e.actions(var)\n+            words |= e.words(var)\n+        return set(['$(_patch_range \"%s\" \"%s\")' % (' '.join(words), var)])\ndiff --git a/stgit/commands/branch.py b/stgit/commands/branch.py\nindex ef71547..3d912fc 100644\n--- a/stgit/commands/branch.py\n+++ b/stgit/commands/branch.py\n@@ -20,7 +20,7 @@ from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n from stgit.out import *\n-from stgit import stack, git, basedir\n+from stgit import argparse, stack, git, basedir\n from stgit.lib import log\n \n help = 'Branch operations: switch, list, create, rename, delete, ...'\n@@ -45,6 +45,7 @@ within a git repository.\n 'stg branch' <branch>::\n         Switch to the given branch.\"\"\"\n \n+args = [argparse.all_branches]\n options = [\n     opt('-l', '--list', action = 'store_true',\n         short = 'List the branches contained in this repository', long = \"\"\"\ndiff --git a/stgit/commands/clean.py b/stgit/commands/clean.py\nindex 27a7716..9b48e7b 100644\n--- a/stgit/commands/clean.py\n+++ b/stgit/commands/clean.py\n@@ -28,6 +28,7 @@ Delete the empty patches in the whole series or only those applied or\n unapplied. A patch is considered empty if the two commit objects\n representing its boundaries refer to the same tree object.\"\"\"\n \n+args = []\n options = [\n     opt('-a', '--applied', action = 'store_true',\n         short = 'Delete the empty applied patches'),\ndiff --git a/stgit/commands/clone.py b/stgit/commands/clone.py\nindex 659712d..efb7198 100644\n--- a/stgit/commands/clone.py\n+++ b/stgit/commands/clone.py\n@@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n import sys, os\n from stgit.commands.common import *\n from stgit.utils import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Make a local clone of a remote repository'\n kind = 'repo'\n@@ -35,6 +35,7 @@ commands of stglink:branch[].\n The target directory <dir> will be created by this command, and must\n not already exist.\"\"\"\n \n+args = [argparse.repo, argparse.dir]\n options = []\n \n directory = DirectoryAnywhere(needs_current_series = False, log = False)\ndiff --git a/stgit/commands/coalesce.py b/stgit/commands/coalesce.py\nindex ef8e912..4b5c00a 100644\n--- a/stgit/commands/coalesce.py\n+++ b/stgit/commands/coalesce.py\n@@ -34,6 +34,8 @@ If there are conflicts when reordering the patches to match the order\n you specify, you will have to resolve them manually just as if you had\n done a sequence of pushes and pops yourself.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches)]\n options = [opt('-n', '--name', short = 'Name of coalesced patch')\n            ] + argparse.message_options(save_template = True)\n \ndiff --git a/stgit/commands/commit.py b/stgit/commands/commit.py\nindex 99b7b5d..dd8d6e6 100644\n--- a/stgit/commands/commit.py\n+++ b/stgit/commands/commit.py\n@@ -19,6 +19,7 @@ from stgit.argparse import opt\n from stgit.commands import common\n from stgit.lib import transaction\n from stgit.out import *\n+from stgit import argparse\n \n help = 'Permanently store the applied patches into the stack base'\n kind = 'stack'\n@@ -40,6 +41,8 @@ The -n/--number option specifies the number of applied patches to\n commit (counting from the bottom of the stack). If -a/--all is given,\n all applied patches are committed.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches)]\n options = [\n     opt('-n', '--number', type = 'int',\n         short = 'Commit the specified number of patches'),\ndiff --git a/stgit/commands/delete.py b/stgit/commands/delete.py\nindex b92a039..40cef3f 100644\n--- a/stgit/commands/delete.py\n+++ b/stgit/commands/delete.py\n@@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n from stgit.argparse import opt\n from stgit.commands import common\n from stgit.lib import transaction\n+from stgit import argparse\n \n help = 'Delete patches'\n kind = 'patch'\n@@ -28,8 +29,10 @@ Delete the patches passed as arguments.\n \n Note that the 'delete' operation is irreversible.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches)]\n options = [\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'Use BRANCH instead of the default branch')]\n \n directory = common.DirectoryHasRepositoryLib()\ndiff --git a/stgit/commands/diff.py b/stgit/commands/diff.py\nindex 05f4f4c..7d2f719 100644\n--- a/stgit/commands/diff.py\n+++ b/stgit/commands/diff.py\n@@ -38,8 +38,12 @@ representing the bottom of the current stack.\n \n rev = '[branch:](<patch>|{base}) | <tree-ish>'\"\"\"\n \n+args = [argparse.known_files, argparse.dirty_files]\n options = [\n     opt('-r', '--range', metavar = 'rev1[..[rev2]]', dest = 'revs',\n+        args = [argparse.patch_range(argparse.applied_patches,\n+                                     argparse.unapplied_patches,\n+                                     argparse.hidden_patches)],\n         short = 'Show the diff between revisions'),\n     opt('-s', '--stat', action = 'store_true',\n         short = 'Show the stat instead of the diff'),\ndiff --git a/stgit/commands/edit.py b/stgit/commands/edit.py\nindex b370f5c..4904f68 100644\n--- a/stgit/commands/edit.py\n+++ b/stgit/commands/edit.py\n@@ -54,6 +54,8 @@ If the patch diff is edited but does not apply, no changes are made to\n the patch at all. The edited patch is saved to a file which you can\n feed to \"stg edit --file\", once you have made sure it does apply.\"\"\"\n \n+args = [argparse.applied_patches, argparse.unapplied_patches,\n+        argparse.hidden_patches]\n options = [\n     opt('-d', '--diff', action = 'store_true',\n         short = 'Edit the patch diff'),\ndiff --git a/stgit/commands/export.py b/stgit/commands/export.py\nindex c7ed802..dfdcea1 100644\n--- a/stgit/commands/export.py\n+++ b/stgit/commands/export.py\n@@ -47,8 +47,11 @@ file:\n   %(commname)s    - committer's name\n   %(commemail)s   - committer's e-mail\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches,\n+                             argparse.hidden_patches)]\n options = [\n-    opt('-d', '--dir',\n+    opt('-d', '--dir', args = [argparse.dir],\n         short = 'Export patches to DIR instead of the default'),\n     opt('-p', '--patch', action = 'store_true',\n         short = 'Append .patch to the patch names'),\n@@ -56,9 +59,9 @@ options = [\n         short = 'Append .EXTENSION to the patch names'),\n     opt('-n', '--numbered', action = 'store_true',\n         short = 'Prefix the patch names with order numbers'),\n-    opt('-t', '--template', metavar = 'FILE',\n+    opt('-t', '--template', metavar = 'FILE', args = [argparse.files],\n         short = 'Use FILE as a template'),\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'Use BRANCH instead of the default branch'),\n     opt('-s', '--stdout', action = 'store_true',\n         short = 'Dump the patches to the standard output'),\ndiff --git a/stgit/commands/files.py b/stgit/commands/files.py\nindex d63a33e..46d43c1 100644\n--- a/stgit/commands/files.py\n+++ b/stgit/commands/files.py\n@@ -34,6 +34,8 @@ given patch. Note that this command doesn't show the files modified in\n the working tree and not yet included in the patch by a 'refresh'\n command. Use the 'diff' or 'status' commands for these files.\"\"\"\n \n+args = [argparse.applied_patches, argparse.unapplied_patches,\n+        argparse.hidden_patches]\n options = [\n     opt('-s', '--stat', action = 'store_true',\n         short = 'Show the diffstat'),\ndiff --git a/stgit/commands/float.py b/stgit/commands/float.py\nindex 93bb69b..7c3dcdf 100644\n--- a/stgit/commands/float.py\n+++ b/stgit/commands/float.py\n@@ -20,7 +20,7 @@ import sys, os\n from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Push patches to the top, even if applied'\n kind = 'stack'\n@@ -32,6 +32,8 @@ necessary pop and push operations will be performed to accomplish\n this. The '--series' option can be used to rearrange the (top) patches\n as specified by the given series file (or the standard input).\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches)]\n options = [\n     opt('-s', '--series', action = 'store_true',\n         short = 'Rearrange according to a series file')]\ndiff --git a/stgit/commands/fold.py b/stgit/commands/fold.py\nindex 165ff52..66a2dd9 100644\n--- a/stgit/commands/fold.py\n+++ b/stgit/commands/fold.py\n@@ -20,7 +20,7 @@ from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n from stgit.out import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Integrate a GNU diff patch into the current patch'\n kind = 'patch'\n@@ -33,10 +33,11 @@ performed with the current top. With the --base option, the patch is\n applied onto the specified base and a three-way merged is performed\n with the current top.\"\"\"\n \n+args = [argparse.files]\n options = [\n     opt('-t', '--threeway', action = 'store_true',\n         short = 'Perform a three-way merge with the current patch'),\n-    opt('-b', '--base',\n+    opt('-b', '--base', args = [argparse.commit],\n         short = 'Use BASE instead of HEAD applying the patch')]\n \n directory = DirectoryHasRepository(log = True)\ndiff --git a/stgit/commands/goto.py b/stgit/commands/goto.py\nindex 0d4cd29..60a917e 100644\n--- a/stgit/commands/goto.py\n+++ b/stgit/commands/goto.py\n@@ -17,6 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n \n from stgit.commands import common\n from stgit.lib import transaction\n+from stgit import argparse\n \n help = 'Push or pop patches to the given one'\n kind = 'stack'\n@@ -25,6 +26,7 @@ description = \"\"\"\n Push/pop patches to/from the stack until the one given on the command\n line becomes current.\"\"\"\n \n+args = [argparse.other_applied_patches, argparse.unapplied_patches]\n options = []\n \n directory = common.DirectoryHasRepositoryLib()\ndiff --git a/stgit/commands/hide.py b/stgit/commands/hide.py\nindex 1bcb5f1..014febb 100644\n--- a/stgit/commands/hide.py\n+++ b/stgit/commands/hide.py\n@@ -20,7 +20,7 @@ from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n from stgit.out import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Hide a patch in the series'\n kind = 'stack'\n@@ -29,8 +29,10 @@ description = \"\"\"\n Hide a range of unapplied patches so that they are no longer shown in\n the plain 'series' command output.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches)]\n options = [\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'Use BRANCH instead of the default branch')]\n \n directory = DirectoryHasRepository(log = True)\ndiff --git a/stgit/commands/id.py b/stgit/commands/id.py\nindex 857ec33..566edcc 100644\n--- a/stgit/commands/id.py\n+++ b/stgit/commands/id.py\n@@ -18,6 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n from stgit.out import out\n from stgit.commands import common\n from stgit.lib import stack\n+from stgit import argparse\n \n help = 'Print the git hash value of a StGit reference'\n kind = 'repo'\n@@ -30,6 +31,8 @@ or the base of the stack. If no branch is specified, it defaults to the\n current one. The bottom of a patch is accessible with the\n '[<branch>:]<patch>^' format.\"\"\"\n \n+args = [argparse.applied_patches, argparse.unapplied_patches,\n+        argparse.hidden_patches]\n options = []\n \n directory = common.DirectoryHasRepositoryLib()\ndiff --git a/stgit/commands/imprt.py b/stgit/commands/imprt.py\nindex 9f2df05..de5e9a5 100644\n--- a/stgit/commands/imprt.py\n+++ b/stgit/commands/imprt.py\n@@ -44,6 +44,7 @@ stack.\n The patch description has to be separated from the data with a '---'\n line.\"\"\"\n \n+args = [argparse.files]\n options = [\n     opt('-m', '--mail', action = 'store_true',\n         short = 'Import the patch from a standard e-mail file'),\n@@ -61,7 +62,7 @@ options = [\n         short = 'Ignore the applied patches in the series'),\n     opt('--replace', action = 'store_true',\n         short = 'Replace the unapplied patches in the series'),\n-    opt('-b', '--base',\n+    opt('-b', '--base', args = [argparse.commit],\n         short = 'Use BASE instead of HEAD for file importing'),\n     opt('-e', '--edit', action = 'store_true',\n         short = 'Invoke an editor for the patch description'),\ndiff --git a/stgit/commands/init.py b/stgit/commands/init.py\nindex 67d20d1..6ffb93e 100644\n--- a/stgit/commands/init.py\n+++ b/stgit/commands/init.py\n@@ -27,6 +27,7 @@ Initialise the current git branch to be used as an StGIT stack. The\n branch (and the git repository it is in) must already exist and\n contain at least one commit.\"\"\"\n \n+args = []\n options = []\n \n directory = common.DirectoryHasRepositoryLib()\ndiff --git a/stgit/commands/log.py b/stgit/commands/log.py\nindex 1f63ef5..39cdfe7 100644\n--- a/stgit/commands/log.py\n+++ b/stgit/commands/log.py\n@@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n \n import os.path\n from optparse import make_option\n-from stgit import run\n+from stgit import argparse, run\n from stgit.argparse import opt\n from stgit.commands import common\n from stgit.lib import log\n@@ -37,8 +37,11 @@ the named patches.\n \"stg undo\" and \"stg redo\" let you step back and forth in the patch\n stack. \"stg reset\" lets you go directly to any state.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches,\n+                             argparse.hidden_patches)]\n options = [\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'Use BRANCH instead of the default one'),\n     opt('-p', '--patch', action = 'store_true',\n         short = 'Show the refresh diffs'),\ndiff --git a/stgit/commands/mail.py b/stgit/commands/mail.py\nindex e0a5521..0b3157e 100644\n--- a/stgit/commands/mail.py\n+++ b/stgit/commands/mail.py\n@@ -93,6 +93,9 @@ the following:\n   %(prefix)s       - 'prefix ' string passed on the command line\n   %(shortdescr)s   - the first line of the patch description\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches,\n+                             argparse.hidden_patches)]\n options = [\n     opt('-a', '--all', action = 'store_true',\n         short = 'E-mail all the applied patches'),\n@@ -134,7 +137,7 @@ options = [\n         short = 'Password for SMTP authentication'),\n     opt('-T', '--smtp-tls', action = 'store_true',\n         short = 'Use SMTP with TLS encryption'),\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'Use BRANCH instead of the default branch'),\n     opt('-m', '--mbox', action = 'store_true',\n         short = 'Generate an mbox file instead of sending')\ndiff --git a/stgit/commands/new.py b/stgit/commands/new.py\nindex 4117e4e..067882a 100644\n--- a/stgit/commands/new.py\n+++ b/stgit/commands/new.py\n@@ -39,6 +39,7 @@ the patch, unless the '--message' flag already specified one. The\n 'patchdescr.tmpl' template file (if available) is used to pre-fill the\n editor.\"\"\"\n \n+args = []\n options = (argparse.author_committer_options()\n            + argparse.message_options(save_template = True)\n            + argparse.sign_options())\ndiff --git a/stgit/commands/patches.py b/stgit/commands/patches.py\nindex e877171..54fac21 100644\n--- a/stgit/commands/patches.py\n+++ b/stgit/commands/patches.py\n@@ -21,7 +21,7 @@ from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n from stgit.out import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Show the applied patches modifying a file'\n kind = 'stack'\n@@ -32,10 +32,11 @@ it shows the patches affected by the local tree modifications. The\n '--diff' option also lists the patch log and the diff for the given\n files.\"\"\"\n \n+args = [argparse.known_files]\n options = [\n     opt('-d', '--diff', action = 'store_true',\n         short = 'Show the diff for the given files'),\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'Use BRANCH instead of the default branch')]\n \n directory = DirectoryHasRepository(log = False)\ndiff --git a/stgit/commands/pick.py b/stgit/commands/pick.py\nindex e1c531d..760918b 100644\n--- a/stgit/commands/pick.py\n+++ b/stgit/commands/pick.py\n@@ -20,7 +20,7 @@ from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n from stgit.out import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n from stgit.stack import Series\n \n help = 'Import a patch from a different branch or a commit object'\n@@ -34,14 +34,17 @@ used as the name of the current patch. It can be overridden with the\n option. The log and author information are those of the commit\n object.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches,\n+                             argparse.hidden_patches)]\n options = [\n     opt('-n', '--name',\n         short = 'Use NAME as the patch name'),\n-    opt('-B', '--ref-branch',\n+    opt('-B', '--ref-branch', args = [argparse.stg_branches],\n         short = 'Pick patches from BRANCH'),\n     opt('-r', '--reverse', action = 'store_true',\n         short = 'Reverse the commit object before importing'),\n-    opt('-p', '--parent', metavar = 'COMMITID',\n+    opt('-p', '--parent', metavar = 'COMMITID', args = [argparse.commit],\n         short = 'Use COMMITID as parent'),\n     opt('-x', '--expose', action = 'store_true',\n         short = 'Append the imported commit id to the patch log'),\ndiff --git a/stgit/commands/pop.py b/stgit/commands/pop.py\nindex 855dc09..2c78ac2 100644\n--- a/stgit/commands/pop.py\n+++ b/stgit/commands/pop.py\n@@ -20,7 +20,7 @@ import sys, os\n from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Pop one or more patches from the stack'\n kind = 'stack'\n@@ -35,6 +35,7 @@ patches passed on the command line are popped from the stack. Some of\n the push operations may fail because of conflicts (\"stg undo\" would\n revert the last push operation).\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches)]\n options = [\n     opt('-a', '--all', action = 'store_true',\n         short = 'Pop all the applied patches'),\ndiff --git a/stgit/commands/pull.py b/stgit/commands/pull.py\nindex 82035c6..f6d1398 100644\n--- a/stgit/commands/pull.py\n+++ b/stgit/commands/pull.py\n@@ -21,7 +21,7 @@ from stgit.commands.common import *\n from stgit.utils import *\n from stgit.out import *\n from stgit.config import GitConfigException\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Pull changes from a remote repository'\n kind = 'stack'\n@@ -37,6 +37,7 @@ resolved and the patch pushed again.\n \n Check the 'git fetch' documentation for the <repository> format.\"\"\"\n \n+args = [argparse.repo]\n options = [\n     opt('-n', '--nopush', action = 'store_true',\n         short = 'Do not push the patches back after pulling'),\ndiff --git a/stgit/commands/push.py b/stgit/commands/push.py\nindex a7c7578..818e02d 100644\n--- a/stgit/commands/push.py\n+++ b/stgit/commands/push.py\n@@ -21,7 +21,7 @@ from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n from stgit.out import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Push one or more patches onto the stack'\n kind = 'stack'\n@@ -38,6 +38,7 @@ conflicting push with 'stg undo').\n The command also notifies when the patch becomes empty (fully merged\n upstream) or is modified (three-way merged) by the 'push' operation.\"\"\"\n \n+args = [argparse.patch_range(argparse.unapplied_patches)]\n options = [\n     opt('-a', '--all', action = 'store_true',\n         short = 'Push all the unapplied patches'),\ndiff --git a/stgit/commands/rebase.py b/stgit/commands/rebase.py\nindex 60168ab..a4bc6e7 100644\n--- a/stgit/commands/rebase.py\n+++ b/stgit/commands/rebase.py\n@@ -19,7 +19,7 @@ import sys, os\n from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Move the stack base to another point in history'\n kind = 'stack'\n@@ -40,6 +40,7 @@ Or if you want to skip that patch:\n         $ stg undo --hard\n         $ stg push next-patch..top-patch\"\"\"\n \n+args = [argparse.commit]\n options = [\n     opt('-n', '--nopush', action = 'store_true',\n         short = 'Do not push the patches back after rebasing'),\ndiff --git a/stgit/commands/redo.py b/stgit/commands/redo.py\nindex 8e62a1d..eb8b20f 100644\n--- a/stgit/commands/redo.py\n+++ b/stgit/commands/redo.py\n@@ -32,6 +32,7 @@ the effects of consecutive invocations of \"stg undo\".\n It is an error to run \"stg redo\" if the last command was not an\n undo.\"\"\"\n \n+args = []\n options = [\n     opt('-n', '--number', type = 'int', metavar = 'N', default = 1,\n         short = 'Undo the last N undos'),\ndiff --git a/stgit/commands/refresh.py b/stgit/commands/refresh.py\nindex 3c82906..27cccc5 100644\n--- a/stgit/commands/refresh.py\n+++ b/stgit/commands/refresh.py\n@@ -48,6 +48,7 @@ the patch stack log; this means that one undo step will undo the merge\n between the other patch and the temp patch, and two undo steps will\n additionally get rid of the temp patch.\"\"\"\n \n+args = [argparse.dirty_files]\n options = [\n     opt('-u', '--update', action = 'store_true',\n         short = 'Only update the current patch files'),\n@@ -55,7 +56,8 @@ options = [\n         short = 'Refresh from index instead of worktree', long = \"\"\"\n         Instead of setting the patch top to the current contents of\n         the worktree, set it to the current contents of the index.\"\"\"),\n-    opt('-p', '--patch',\n+    opt('-p', '--patch', args = [argparse.other_applied_patches,\n+                                 argparse.unapplied_patches],\n         short = 'Refresh (applied) PATCH instead of the top patch'),\n     opt('-e', '--edit', action = 'store_true',\n         short = 'Invoke an editor for the patch description'),\ndiff --git a/stgit/commands/rename.py b/stgit/commands/rename.py\nindex 7e0fbf5..8a593ac 100644\n--- a/stgit/commands/rename.py\n+++ b/stgit/commands/rename.py\n@@ -20,7 +20,7 @@ from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n from stgit.out import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Rename a patch'\n kind = 'patch'\n@@ -29,8 +29,10 @@ description = \"\"\"\n Rename <oldpatch> into <newpatch> in a series. If <oldpatch> is not\n given, the top-most patch will be renamed.\"\"\"\n \n+args = [argparse.applied_patches, argparse.unapplied_patches,\n+        argparse.hidden_patches]\n options = [\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'use BRANCH instead of the default one')]\n \n directory = DirectoryHasRepository(log = True)\ndiff --git a/stgit/commands/repair.py b/stgit/commands/repair.py\nindex ff9bb61..37c4bab 100644\n--- a/stgit/commands/repair.py\n+++ b/stgit/commands/repair.py\n@@ -69,6 +69,7 @@ NOTE: If using git commands on the stack was a mistake, running \"stg\n repair\" is _not_ what you want. In that case, what you want is option\n (1) above.\"\"\"\n \n+args = []\n options = []\n \n directory = DirectoryGotoToplevel(log = True)\ndiff --git a/stgit/commands/reset.py b/stgit/commands/reset.py\nindex 3ccbf1b..7dfd4a0 100644\n--- a/stgit/commands/reset.py\n+++ b/stgit/commands/reset.py\n@@ -21,6 +21,7 @@ from stgit.argparse import opt\n from stgit.commands import common\n from stgit.lib import git, log, transaction\n from stgit.out import out\n+from stgit import argparse\n \n help = 'Reset the patch stack to an earlier state'\n kind = 'stack'\n@@ -33,6 +34,9 @@ a commit id from a stack log; \"stg log\" lets you view this log, and\n If one or more patch names are given, reset only those patches, and\n leave the rest alone.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches,\n+                             argparse.hidden_patches)]\n options = [\n     opt('--hard', action = 'store_true',\n         short = 'Discard changes in your index/worktree')]\ndiff --git a/stgit/commands/resolved.py b/stgit/commands/resolved.py\nindex ce8630d..2ce7ec3 100644\n--- a/stgit/commands/resolved.py\n+++ b/stgit/commands/resolved.py\n@@ -20,7 +20,7 @@ import sys, os\n from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n-from stgit import stack, git, basedir\n+from stgit import argparse, stack, git, basedir\n from stgit.config import config, file_extensions\n from stgit.gitmergeonefile import interactive_merge\n \n@@ -32,10 +32,12 @@ Mark a merge conflict as resolved. The conflicts can be seen with the\n 'status' command, the corresponding files being prefixed with a\n 'C'.\"\"\"\n \n+args = [argparse.conflicting_files]\n options = [\n     opt('-a', '--all', action = 'store_true',\n         short = 'Mark all conflicts as solved'),\n     opt('-r', '--reset', metavar = '(ancestor|current|patched)',\n+        args = [argparse.strings('ancestor', 'current', 'patched')],\n         short = 'Reset the file(s) to the given state'),\n     opt('-i', '--interactive', action = 'store_true',\n         short = 'Run the interactive merging tool')]\ndiff --git a/stgit/commands/series.py b/stgit/commands/series.py\nindex e9d148a..95196d3 100644\n--- a/stgit/commands/series.py\n+++ b/stgit/commands/series.py\n@@ -21,6 +21,7 @@ from stgit.commands import common\n from stgit.commands.common import parse_patches\n from stgit.out import out\n from stgit.config import config\n+from stgit import argparse\n \n help = 'Print the patch series'\n kind = 'stack'\n@@ -31,8 +32,11 @@ range. The applied patches are prefixed with a '+', the unapplied ones\n with a '-' and the hidden ones with a '!'. The current patch is\n prefixed with a '>'. Empty patches are prefixed with a '0'.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches,\n+                             argparse.hidden_patches)]\n options = [\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'Use BRANCH instead of the default branch'),\n     opt('-a', '--all', action = 'store_true',\n         short = 'Show all patches, including the hidden ones'),\n@@ -42,7 +46,7 @@ options = [\n         short = 'Show the unapplied patches only'),\n     opt('-H', '--hidden', action = 'store_true',\n         short = 'Show the hidden patches only'),\n-    opt('-m', '--missing', metavar = 'BRANCH',\n+    opt('-m', '--missing', metavar = 'BRANCH',  args = [argparse.stg_branches],\n         short = 'Show patches in BRANCH missing in current'),\n     opt('-c', '--count', action = 'store_true',\n         short = 'Print the number of patches in the series'),\ndiff --git a/stgit/commands/show.py b/stgit/commands/show.py\nindex e08551b..9a1f48b 100644\n--- a/stgit/commands/show.py\n+++ b/stgit/commands/show.py\n@@ -28,8 +28,11 @@ description = \"\"\"\n Show the commit log and the diff corresponding to the given patches.\n The output is similar to that generated by 'git show'.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches,\n+                             argparse.hidden_patches)]\n options = [\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'Use BRANCH instead of the default branch'),\n     opt('-a', '--applied', action = 'store_true',\n         short = 'Show the applied patches'),\ndiff --git a/stgit/commands/sink.py b/stgit/commands/sink.py\nindex 34f81c9..d4561ed 100644\n--- a/stgit/commands/sink.py\n+++ b/stgit/commands/sink.py\n@@ -20,7 +20,7 @@ import sys, os\n from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Send patches deeper down the stack'\n kind = 'stack'\n@@ -41,12 +41,14 @@ including <target patch>), then pushing the patches to sink, and then\n (unless '--nopush' is also given) pushing back into place the\n formerly-applied patches.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches)]\n options = [\n     opt('-n', '--nopush', action = 'store_true',\n         short = 'Do not push the patches back after sinking', long = \"\"\"\n         Do not push back on the stack the formerly-applied patches.\n         Only the patches to sink are pushed.\"\"\"),\n-    opt('-t', '--to', metavar = 'TARGET',\n+    opt('-t', '--to', metavar = 'TARGET', args = [argparse.applied_patches],\n         short = 'Sink patches below the TARGET patch', long = \"\"\"\n         Specify a target patch to place the patches below, instead of\n         sinking them to the bottom of the stack.\"\"\")]\ndiff --git a/stgit/commands/status.py b/stgit/commands/status.py\nindex c78bc1b..730b47c 100644\n--- a/stgit/commands/status.py\n+++ b/stgit/commands/status.py\n@@ -20,7 +20,7 @@ import sys, os\n from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Show the tree status'\n kind = 'wc'\n@@ -39,6 +39,7 @@ under revision control. The files are prefixed as follows:\n An 'stg refresh' command clears the status of the modified, new and\n deleted files.\"\"\"\n \n+args = [argparse.files]\n options = [\n     opt('-m', '--modified', action = 'store_true',\n         short = 'Show modified files only'),\ndiff --git a/stgit/commands/sync.py b/stgit/commands/sync.py\nindex 966ac55..ea949d6 100644\n--- a/stgit/commands/sync.py\n+++ b/stgit/commands/sync.py\n@@ -21,7 +21,7 @@ from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n from stgit.out import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Synchronise patches with a branch or a series'\n kind = 'patch'\n@@ -33,12 +33,14 @@ for keeping patches on several branches in sync. Note that the\n operation may fail for some patches because of conflicts. The patches\n in the series must apply cleanly.\"\"\"\n \n+args = [argparse.patch_range(argparse.applied_patches,\n+                             argparse.unapplied_patches)]\n options = [\n     opt('-a', '--all', action = 'store_true',\n         short = 'Synchronise all the applied patches'),\n-    opt('-B', '--ref-branch',\n+    opt('-B', '--ref-branch', args = [argparse.stg_branches],\n         short = 'Syncronise patches with BRANCH'),\n-    opt('-s', '--series',\n+    opt('-s', '--series', args = [argparse.files],\n         short = 'Syncronise patches with SERIES')]\n \n directory = DirectoryGotoToplevel(log = True)\ndiff --git a/stgit/commands/top.py b/stgit/commands/top.py\nindex 523afa4..4ec37b4 100644\n--- a/stgit/commands/top.py\n+++ b/stgit/commands/top.py\n@@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n from stgit.argparse import opt\n from stgit.commands import common\n from stgit.out import out\n+from stgit import argparse\n \n help = 'Print the name of the top patch'\n kind = 'stack'\n@@ -26,8 +27,9 @@ usage = ['']\n description = \"\"\"\n Print the name of the current (topmost) patch.\"\"\"\n \n+args = []\n options = [\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'Use BRANCH instead of the default branch')]\n \n directory = common.DirectoryHasRepositoryLib()\ndiff --git a/stgit/commands/uncommit.py b/stgit/commands/uncommit.py\nindex b9950ca..bcc8bac 100644\n--- a/stgit/commands/uncommit.py\n+++ b/stgit/commands/uncommit.py\n@@ -21,7 +21,7 @@ from stgit.argparse import opt\n from stgit.commands import common\n from stgit.lib import transaction\n from stgit.out import *\n-from stgit import utils\n+from stgit import argparse, utils\n \n help = 'Turn regular git commits into StGit patches'\n kind = 'stack'\n@@ -49,10 +49,12 @@ given commit should be uncommitted.\n Only commits with exactly one parent can be uncommitted; in other\n words, you can't uncommit a merge.\"\"\"\n \n+args = []\n options = [\n     opt('-n', '--number', type = 'int',\n         short = 'Uncommit the specified number of commits'),\n-    opt('-t', '--to', short = 'Uncommit to the specified commit'),\n+    opt('-t', '--to', args = [argparse.commit],\n+        short = 'Uncommit to the specified commit'),\n     opt('-x', '--exclusive', action = 'store_true',\n         short = 'Exclude the commit specified by the --to option')]\n \ndiff --git a/stgit/commands/undo.py b/stgit/commands/undo.py\nindex b7b1b73..6a04363 100644\n--- a/stgit/commands/undo.py\n+++ b/stgit/commands/undo.py\n@@ -29,6 +29,7 @@ description = \"\"\"\n Reset the patch stack to the previous state. Consecutive invocations\n of \"stg undo\" will take you ever further into the past.\"\"\"\n \n+args = []\n options = [\n     opt('-n', '--number', type = 'int', metavar = 'N', default = 1,\n         short = 'Undo the last N commands'),\ndiff --git a/stgit/commands/unhide.py b/stgit/commands/unhide.py\nindex acfef29..0c0832a 100644\n--- a/stgit/commands/unhide.py\n+++ b/stgit/commands/unhide.py\n@@ -20,7 +20,7 @@ from stgit.argparse import opt\n from stgit.commands.common import *\n from stgit.utils import *\n from stgit.out import *\n-from stgit import stack, git\n+from stgit import argparse, stack, git\n \n help = 'Unhide a hidden patch'\n kind = 'stack'\n@@ -29,8 +29,9 @@ description = \"\"\"\n Unhide a hidden range of patches so that they are shown in the plain\n 'stg series' command output.\"\"\"\n \n+args = [argparse.patch_range(argparse.hidden_patches)]\n options = [\n-    opt('-b', '--branch',\n+    opt('-b', '--branch', args = [argparse.stg_branches],\n         short = 'Use BRANCH instead of the default branch')]\n \n directory = DirectoryHasRepository(log = True)\ndiff --git a/stgit/completion.py b/stgit/completion.py\nnew file mode 100644\nindex 0000000..e461e3b\n--- /dev/null\n+++ b/stgit/completion.py\n@@ -0,0 +1,140 @@\n+import textwrap\n+import stgit.commands\n+from stgit import argparse\n+\n+def fun(name, *body):\n+    return ['%s ()' % name, '{', list(body), '}']\n+\n+def fun_desc(name, desc, *body):\n+    return ['# %s' % desc] + fun(name, *body)\n+\n+def flatten(stuff, sep):\n+    r = stuff[0]\n+    for s in stuff[1:]:\n+        r.append(sep)\n+        r.extend(s)\n+    return r\n+\n+def write(f, stuff, indent = 0):\n+    for s in stuff:\n+        if isinstance(s, str):\n+            f.write((' '*4*indent + s).rstrip() + '\\n')\n+        else:\n+            write(f, s, indent + 1)\n+\n+def patch_list_fun(type):\n+    return fun('_%s_patches' % type, 'local g=$(_gitdir)',\n+               'test \"$g\" && cat \"$g/patches/$(_current_branch)/%s\"' % type)\n+\n+def file_list_fun(name, cmd):\n+    return fun('_%s_files' % name, 'local g=$(_gitdir)',\n+               'test \"$g\" && %s' % cmd)\n+\n+def ref_list_fun(name, prefix):\n+    return fun(name, 'local g=$(_gitdir)',\n+               (\"test \\\"$g\\\" && git show-ref | grep ' %s/' | sed 's,.* %s/,,'\"\n+                % (prefix, prefix)))\n+\n+def util():\n+    r = [fun_desc('_gitdir',\n+                  \"The path to .git, or empty if we're not in a repository.\",\n+                  'echo \"$(git rev-parse --git-dir 2>/dev/null)\"'),\n+         fun_desc('_current_branch',\n+                  \"Name of the current branch, or empty if there isn't one.\",\n+                  'local b=$(git symbolic-ref HEAD 2>/dev/null)',\n+                  'echo ${b#refs/heads/}'),\n+         fun_desc('_other_applied_patches',\n+                  'List of all applied patches except the current patch.',\n+                  'local b=$(_current_branch)',\n+                  'local g=$(_gitdir)',\n+                  ('test \"$g\" && cat \"$g/patches/$b/applied\" | grep -v'\n+                   ' \"^$(tail -n 1 $g/patches/$b/applied 2> /dev/null)$\"')),\n+         fun('_patch_range', 'local patches=\"$1\"', 'local cur=\"$2\"',\n+             'case \"$cur\" in', [\n+                '*..*)', ['local pfx=\"${cur%..*}..\"', 'cur=\"${cur#*..}\"',\n+                          'compgen -P \"$pfx\" -W \"$patches\" -- \"$cur\"', ';;'],\n+                '*)', ['compgen -W \"$patches\" -- \"$cur\"', ';;']],\n+             'esac'),\n+         fun('_stg_branches',\n+             'local g=$(_gitdir)', 'test \"$g\" && (cd $g/patches/ && echo *)'),\n+         ref_list_fun('_all_branches', 'refs/heads'),\n+         ref_list_fun('_tags', 'refs/tags'),\n+         ref_list_fun('_remotes', 'refs/remotes')]\n+    for type in ['applied', 'unapplied', 'hidden']:\n+        r.append(patch_list_fun(type))\n+    for name, cmd in [('conflicting',\n+                       r\"git ls-files --unmerged | sed 's/.*\\t//g' | sort -u\"),\n+                      ('dirty', 'git diff-index --name-only HEAD'),\n+                      ('unknown', 'git ls-files --others --exclude-standard'),\n+                      ('known', 'git ls-files')]:\n+        r.append(file_list_fun(name, cmd))\n+    return flatten(r, '')\n+\n+def command_list(commands):\n+    return ['_stg_commands=\"%s\"\\n' % ' '.join(sorted(commands.iterkeys()))]\n+\n+def command_fun(cmd, modname):\n+    mod = stgit.commands.get_command(modname)\n+    def cg(args, flags):\n+        return argparse.compjoin(list(args) + [argparse.strings(*flags)]\n+                                 ).command('$cur')\n+    return fun(\n+        '_stg_%s' % cmd,\n+        'local flags=\"%s\"' % ' '.join(sorted(\n+                flag for opt in mod.options\n+                for flag in opt.flags if flag.startswith('--'))),\n+        'local prev=\"${COMP_WORDS[COMP_CWORD-1]}\"',\n+        'local cur=\"${COMP_WORDS[COMP_CWORD]}\"',\n+        'case \"$prev\" in', [\n+            '%s) COMPREPLY=($(%s)) ;;' % ('|'.join(opt.flags), cg(opt.args, []))\n+            for opt in mod.options if opt.args] + [\n+            '*) COMPREPLY=($(%s)) ;;' % cg(mod.args, ['$flags'])],\n+        'esac')\n+\n+def main_switch(commands):\n+    return fun(\n+        '_stg',\n+        'local i',\n+        'local c=1',\n+        'local command',\n+        '',\n+        'while test $c -lt $COMP_CWORD; do', [\n+            'if test $c == 1; then', [\n+                'command=\"${COMP_WORDS[c]}\"'],\n+            'fi',\n+            'c=$((++c))'],\n+        'done',\n+        '',\n+        ('# Complete name of subcommand if the user has not finished'\n+         ' typing it yet.'),\n+        'if test $c -eq $COMP_CWORD -a -z \"$command\"; then', [\n+            ('COMPREPLY=($(compgen -W \"$_stg_commands\" --'\n+             ' \"${COMP_WORDS[COMP_CWORD]}\"))'),\n+            'return'],\n+        'fi',\n+        '',\n+        '# Complete arguments to subcommands.',\n+        'case \"$command\" in', [\n+            '%s) _stg_%s ;;' % (cmd, cmd)\n+            for cmd in sorted(commands.iterkeys())],\n+        'esac')\n+\n+def install():\n+    return ['complete -o default -F _stg stg']\n+\n+def write_completion(f):\n+    commands = stgit.commands.get_commands(allow_cached = False)\n+    r = [[\"\"\"# -*- shell-script -*-\n+# bash completion script for StGit (automatically generated)\n+#\n+# To use these routines:\n+#\n+#    1. Copy this file to somewhere (e.g. ~/.stgit-completion.bash).\n+#\n+#    2. Add the following line to your .bashrc:\n+#         . ~/.stgit-completion.bash\"\"\"]]\n+    r += [util(), command_list(commands)]\n+    for cmd, (modname, _, _) in sorted(commands.iteritems()):\n+        r.append(command_fun(cmd, modname))\n+    r += [main_switch(commands), install()]\n+    write(f, flatten(r, ''))\n"},{"id":"91229","messageId":"20080921153757.GA9098@diana.vm.bytemark.co.uk","threadId":"15611","inReplyTo":null,"subject":"[StGit] kha/{safe,experimental} updated","fromName":"Karl Hasselström","fromEmail":"kha@treskal.com","sentAt":"2008-09-21T15:37:57Z","receivedAt":"2008-09-21T15:37:57Z","isPatch":false,"sender":{"key":"kha@treskal.com","avatar":"https://gravatar.com/avatar/f0120c734b5279b345075a28521e1ac66acb20c9913ffe9bf6ae97e53f7f3f13?d=mp&s=160"},"body":"Just pushed out the stack log stuff to kha/safe. It really should be\nready for wider use at this point, and it was getting tiresome to keep\nrebasing it.\n\nOne patch is still in experimental -- it depends on a new git feature\nthat isn't in any release yet.\n\nThere's also a patch for much improved bash completion support; I'll\npost it as a reply to this mail.\n\n\n                                 -+-\n\n\nThe following changes since commit 3ce5fec2ef4154369653a41eb7692aca25878298:\n  Catalin Marinas (1):\n        Merge branch 'stable'\n\nare available in the git repository at:\n\n  git://repo.or.cz/stgit/kha.git safe\n\nDaniel White (1):\n      Fixes for auto-generation of man pages\n\nDavid Kågedal (1):\n      Add support for initializing a branch for stgit from Emacs.\n\nKarl Hasselström (24):\n      Auto-generate man pages for all StGit commands\n      asciidoc.conf: Steal updates from git\n      Generate command lists automatically\n      Write to a stack log when stack is modified\n      New command: stg reset\n      Log conflicts separately\n      Log conflicts separately for all commands\n      Add a --hard flag to stg reset\n      Don't write a log entry if there were no changes\n      Move stack reset function to a shared location\n      New command: stg undo\n      New command: stg redo\n      Log and undo external modifications\n      Test that stg coalesce handles head != top gracefully\n      Check for top == head at the start of every transaction\n      Make \"stg log\" show stack log instead of patch log\n      Convert \"stg refresh\" to the new infrastructure\n      New refresh tests\n      Remove --undo flags from stg commands and docs\n      Refactor stgit.commands.edit\n      Invoke the correct interactive editor\n      Implement \"stg refresh --edit\" again\n      Automatic bash completion\n      Read several objects at once with git cat-file --batch\n\n .gitignore                         |    1 +\n Documentation/.gitignore           |    5 +-\n Documentation/COMMAND-TEMPLATE.txt |   42 ---\n Documentation/Makefile             |   15 +-\n Documentation/asciidoc.conf        |  100 +++++--\n Documentation/stg-branch.txt       |  114 --------\n Documentation/stg-clone.txt        |   32 ---\n Documentation/stg-init.txt         |   29 --\n Documentation/stg-new.txt          |  115 --------\n Documentation/stg-sink.txt         |   49 ----\n Documentation/stg.txt              |  138 +----------\n Documentation/tutorial.txt         |    4 +-\n INSTALL                            |    4 +-\n Makefile                           |   21 ++-\n TODO                               |    2 -\n contrib/stgit-completion.bash      |  270 -------------------\n contrib/stgit.el                   |   12 +-\n setup.py                           |    4 +-\n stg-build                          |   41 +++\n stgit/argparse.py                  |  260 +++++++++++++++---\n stgit/commands/.gitignore          |    1 +\n stgit/commands/__init__.py         |   78 ++++++\n stgit/commands/branch.py           |  148 +++++++----\n stgit/commands/clean.py            |   23 +-\n stgit/commands/clone.py            |   23 +-\n stgit/commands/coalesce.py         |   16 +-\n stgit/commands/commit.py           |   27 ++-\n stgit/commands/common.py           |   18 +-\n stgit/commands/delete.py           |   19 +-\n stgit/commands/diff.py             |   36 ++--\n stgit/commands/edit.py             |  106 ++------\n stgit/commands/export.py           |   53 ++--\n stgit/commands/files.py            |   31 ++-\n stgit/commands/float.py            |   24 +-\n stgit/commands/fold.py             |   26 +-\n stgit/commands/goto.py             |   16 +-\n stgit/commands/hide.py             |   23 +-\n stgit/commands/id.py               |   15 +-\n stgit/commands/imprt.py            |   98 ++++----\n stgit/commands/init.py             |   17 +-\n stgit/commands/log.py              |  192 +++++---------\n stgit/commands/mail.py             |  126 ++++-----\n stgit/commands/new.py              |   39 ++--\n stgit/commands/patches.py          |   27 +-\n stgit/commands/pick.py             |   57 ++--\n stgit/commands/pop.py              |   33 ++--\n stgit/commands/pull.py             |   28 +-\n stgit/commands/push.py             |   65 ++---\n stgit/commands/rebase.py           |   30 +-\n stgit/commands/redo.py             |   56 ++++\n stgit/commands/refresh.py          |  355 ++++++++++++++++--------\n stgit/commands/rename.py           |   32 ++-\n stgit/commands/repair.py           |   21 +-\n stgit/commands/reset.py            |   65 +++++\n stgit/commands/resolved.py         |   33 ++--\n stgit/commands/series.py           |   82 +++---\n stgit/commands/show.py             |   36 ++--\n stgit/commands/sink.py             |   46 +++-\n stgit/commands/status.py           |   61 ++---\n stgit/commands/sync.py             |   56 ++---\n stgit/commands/top.py              |   18 +-\n stgit/commands/uncommit.py         |   34 ++-\n stgit/commands/undo.py             |   53 ++++\n stgit/commands/unhide.py           |   24 +-\n stgit/completion.py                |  140 ++++++++++\n stgit/git.py                       |    4 -\n stgit/lib/edit.py                  |   99 +++++++\n stgit/lib/git.py                   |  116 ++++++++-\n stgit/lib/log.py                   |  524 ++++++++++++++++++++++++++++++++++++\n stgit/lib/stack.py                 |   25 ++\n stgit/lib/transaction.py           |  128 ++++++---\n stgit/main.py                      |  140 +---------\n stgit/run.py                       |   19 ++\n stgit/stack.py                     |   45 +---\n stgit/utils.py                     |   22 +-\n t/t1200-push-modified.sh           |    2 +-\n t/t1201-pull-trailing.sh           |    2 +-\n t/t1202-push-undo.sh               |    8 +-\n t/t1400-patch-history.sh           |  103 -------\n t/t2300-refresh-subdir.sh          |   29 ++-\n t/t2600-coalesce.sh                |   13 +\n t/t2701-refresh-p.sh               |    2 +-\n t/t3100-reset.sh                   |  160 +++++++++++\n t/t3101-reset-hard.sh              |   53 ++++\n t/t3102-undo.sh                    |   81 ++++++\n t/t3103-undo-hard.sh               |   53 ++++\n t/t3104-redo.sh                    |  114 ++++++++\n t/t3105-undo-external-mod.sh       |   65 +++++\n t/t3300-edit.sh                    |   12 +-\n t/test-lib.sh                      |    5 +-\n 90 files changed, 3348 insertions(+), 2241 deletions(-)\n delete mode 100644 Documentation/COMMAND-TEMPLATE.txt\n delete mode 100644 Documentation/stg-branch.txt\n delete mode 100644 Documentation/stg-clone.txt\n delete mode 100644 Documentation/stg-init.txt\n delete mode 100644 Documentation/stg-new.txt\n delete mode 100644 Documentation/stg-sink.txt\n delete mode 100644 contrib/stgit-completion.bash\n create mode 100755 stg-build\n create mode 100644 stgit/commands/.gitignore\n create mode 100644 stgit/commands/redo.py\n create mode 100644 stgit/commands/reset.py\n create mode 100644 stgit/commands/undo.py\n create mode 100644 stgit/completion.py\n create mode 100644 stgit/lib/edit.py\n create mode 100644 stgit/lib/log.py\n delete mode 100755 t/t1400-patch-history.sh\n create mode 100755 t/t3100-reset.sh\n create mode 100755 t/t3101-reset-hard.sh\n create mode 100755 t/t3102-undo.sh\n create mode 100755 t/t3103-undo-hard.sh\n create mode 100755 t/t3104-redo.sh\n create mode 100755 t/t3105-undo-external-mod.sh\n\n\n                                 -+-\n\n\nThe following changes since commit 2f34dbb4625e0c04a983229477a50ba4de7036bf:\n  Karl Hasselström (1):\n        Read several objects at once with git cat-file --batch\n\nare available in the git repository at:\n\n  git://repo.or.cz/stgit/kha.git experimental\n\nKarl Hasselström (1):\n      Diff several trees at once with git diff-tree --stdin\n\n INSTALL          |    5 +++--\n stgit/lib/git.py |   37 +++++++++++++++++++++++++++++++++----\n 2 files changed, 36 insertions(+), 6 deletions(-)\n\n-- \nKarl Hasselström, kha@treskal.com\n      www.treskal.com/kalle\n"},{"id":"91536","messageId":"b0943d9e0809241548t32e8ac24n3ec88698fb907764@mail.gmail.com","threadId":"15611","inReplyTo":"20080921153757.GA9098@diana.vm.bytemark.co.uk","subject":"Re: [StGit] kha/{safe,experimental} updated","fromName":"Catalin Marinas","fromEmail":"catalin.marinas@gmail.com","sentAt":"2008-09-24T22:48:08Z","receivedAt":"2008-09-24T22:48:08Z","isPatch":false,"sender":{"key":"catalin.marinas@gmail.com","avatar":null},"body":"2008/9/21 Karl Hasselström <kha@treskal.com>:\n> Just pushed out the stack log stuff to kha/safe. It really should be\n> ready for wider use at this point, and it was getting tiresome to keep\n> rebasing it.\n\nGreat work, I merged (most of) it. Many thanks.\n\n> One patch is still in experimental -- it depends on a new git feature\n> that isn't in any release yet.\n[...]\n>      Read several objects at once with git cat-file --batch\n\nI skipped this one for now. I'm using Ubuntu (Hardy) and the git\nversion is 1.5.4.3. I would wait until we get at least a stable git\nrelease with this feature.\n\nI haven't looked in detail but can we have a way to drop back to the\nold implementation if the option isn't available?\n\n-- \nCatalin\n"},{"id":"91558","messageId":"20080925073349.GB27632@diana.vm.bytemark.co.uk","threadId":"15611","inReplyTo":"b0943d9e0809241548t32e8ac24n3ec88698fb907764@mail.gmail.com","subject":"Re: [StGit] kha/{safe,experimental} updated","fromName":"Karl Hasselström","fromEmail":"kha@treskal.com","sentAt":"2008-09-25T07:33:49Z","receivedAt":"2008-09-25T07:33:49Z","isPatch":false,"sender":{"key":"kha@treskal.com","avatar":"https://gravatar.com/avatar/f0120c734b5279b345075a28521e1ac66acb20c9913ffe9bf6ae97e53f7f3f13?d=mp&s=160"},"body":"On 2008-09-24 23:48:08 +0100, Catalin Marinas wrote:\n\n> 2008/9/21 Karl Hasselström <kha@treskal.com>:\n>\n> > Just pushed out the stack log stuff to kha/safe. It really should\n> > be ready for wider use at this point, and it was getting tiresome\n> > to keep rebasing it.\n>\n> Great work, I merged (most of) it. Many thanks.\n\nThanks.\n\n> > One patch is still in experimental -- it depends on a new git\n> > feature that isn't in any release yet.\n> [...]\n> >      Read several objects at once with git cat-file --batch\n>\n> I skipped this one for now.\n\nI was talking about kha/experimental~0 here; you're talking about\nkha/safe~0, which is kha/experimental~1. But never mind.\n\n> I'm using Ubuntu (Hardy) and the git version is 1.5.4.3. I would\n> wait until we get at least a stable git release with this feature.\n\nI'm using it too, but I always build my own git, so I never know what\nversion the system comes with ... but I see your point. I'll put that\npatch back in kha/experimental for now.\n\n> I haven't looked in detail but can we have a way to drop back to the\n> old implementation if the option isn't available?\n\nI guess in this case we could, since if the option exists, we can use\nit. (The second patch is not so lucky; the flag exists since quite a\nwhile, but lacks a feature in older gits.)\n\nSince it's just a moderate speed-up in both cases, I'm kind of\ninclined to just wait for relevant distributions to catch up, and\navoid all the hassle.\n\n-- \nKarl Hasselström, kha@treskal.com\n      www.treskal.com/kalle\n"}]}