diff --git a/.gitignore b/.gitignore
index 91dbad2..e7fffb0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ patches-*
release.sh
setup.cfg.rpm
snapshot.sh
+stgit-completion.bash
diff --git a/Makefile b/Makefile
index 288622a..7183670 100644
--- a/Makefile
+++ b/Makefile
@@ -7,13 +7,16 @@ TEST_PATCHES ?= ..
all: build
$(PYTHON) setup.py build
-build: stgit/commands/cmdlist.py
+build: stgit/commands/cmdlist.py stgit-completion.bash
ALL_PY = $(shell find stgit -name '*.py')
stgit/commands/cmdlist.py: $(ALL_PY)
$(PYTHON) stg-build --py-cmd-list > $@
+stgit-completion.bash: $(ALL_PY)
+ $(PYTHON) stg-build --bash-completion > $@
+
install: build
$(PYTHON) setup.py install --prefix=$(prefix) --root=$(DESTDIR) --force
diff --git a/contrib/stgit-completion.bash b/contrib/stgit-completion.bash
deleted file mode 100644
index 1467c28..0000000
--- a/contrib/stgit-completion.bash
+++ /dev/null
@@ -1,270 +0,0 @@
-# bash completion support for StGIT -*- shell-script -*-
-#
-# Copyright (C) 2006, Karl Hasselström <kha@treskal.com>
-# Based on git-completion.sh
-#
-# To use these routines:
-#
-# 1. Copy this file to somewhere (e.g. ~/.stgit-completion.bash).
-#
-# 2. Add the following line to your .bashrc:
-# . ~/.stgit-completion.bash
-
-_stg_commands="
- branch
- delete
- diff
- clean
- clone
- coalesce
- commit
- edit
- export
- files
- float
- fold
- goto
- hide
- id
- import
- init
- log
- mail
- new
- patches
- pick
- pop
- pull
- push
- rebase
- refresh
- rename
- repair
- resolved
- series
- show
- sink
- status
- sync
- top
- uncommit
- unhide
-"
-
-# The path to .git, or empty if we're not in a repository.
-_gitdir ()
-{
- echo "$(git rev-parse --git-dir 2>/dev/null)"
-}
-
-# Name of the current branch, or empty if there isn't one.
-_current_branch ()
-{
- local b=$(git symbolic-ref HEAD 2>/dev/null)
- echo ${b#refs/heads/}
-}
-
-# List of all applied patches.
-_applied_patches ()
-{
- local g=$(_gitdir)
- [ "$g" ] && cat "$g/patches/$(_current_branch)/applied"
-}
-
-# List of all unapplied patches.
-_unapplied_patches ()
-{
- local g=$(_gitdir)
- [ "$g" ] && cat "$g/patches/$(_current_branch)/unapplied"
-}
-
-# List of all applied patches.
-_hidden_patches ()
-{
- local g=$(_gitdir)
- [ "$g" ] && cat "$g/patches/$(_current_branch)/hidden"
-}
-
-# List of all patches.
-_all_patches ()
-{
- local b=$(_current_branch)
- local g=$(_gitdir)
- [ "$g" ] && cat "$g/patches/$b/applied" "$g/patches/$b/unapplied"
-}
-
-# List of all patches except the current patch.
-_all_other_patches ()
-{
- local b=$(_current_branch)
- local g=$(_gitdir)
- [ "$g" ] && cat "$g/patches/$b/applied" "$g/patches/$b/unapplied" \
- | grep -v "^$(cat $g/patches/$b/current 2> /dev/null)$"
-}
-
-_all_branches ()
-{
- local g=$(_gitdir)
- [ "$g" ] && (cd $g/patches/ && echo *)
-}
-
-_conflicting_files ()
-{
- local g=$(_gitdir)
- [ "$g" ] && stg status --conflict
-}
-
-_dirty_files ()
-{
- local g=$(_gitdir)
- [ "$g" ] && stg status --modified --new --deleted
-}
-
-_unknown_files ()
-{
- local g=$(_gitdir)
- [ "$g" ] && stg status --unknown
-}
-
-_known_files ()
-{
- local g=$(_gitdir)
- [ "$g" ] && git ls-files
-}
-
-# List the command options
-_cmd_options ()
-{
- stg $1 --help 2>/dev/null | grep -e " --[A-Za-z]" | sed -e "s/.*\(--[^ =]\+\).*/\1/"
-}
-
-# Generate completions for patches and patch ranges from the given
-# patch list function, and options from the given list.
-_complete_patch_range ()
-{
- local patchlist="$1" options="$2"
- local pfx cur="${COMP_WORDS[COMP_CWORD]}"
- case "$cur" in
- *..*)
- pfx="${cur%..*}.."
- cur="${cur#*..}"
- COMPREPLY=($(compgen -P "$pfx" -W "$($patchlist)" -- "$cur"))
- ;;
- *)
- COMPREPLY=($(compgen -W "$options $($patchlist)" -- "$cur"))
- ;;
- esac
-}
-
-_complete_patch_range_options ()
-{
- local patchlist="$1" options="$2" patch_options="$3"
- local prev="${COMP_WORDS[COMP_CWORD-1]}"
- local cur="${COMP_WORDS[COMP_CWORD]}"
- local popt
- for popt in $patch_options; do
- if [ $prev == $popt ]; then
- _complete_patch_range $patchlist
- return
- fi
- done
- COMPREPLY=($(compgen -W "$options" -- "$cur"))
-}
-
-_complete_branch ()
-{
- COMPREPLY=($(compgen -W "$(_cmd_options $1) $($2)" -- "${COMP_WORDS[COMP_CWORD]}"))
-}
-
-# Generate completions for options from the given list.
-_complete_options ()
-{
- local options="$1"
- COMPREPLY=($(compgen -W "$options" -- "${COMP_WORDS[COMP_CWORD]}"))
-}
-
-_complete_files ()
-{
- COMPREPLY=($(compgen -W "$(_cmd_options $1) $2" -- "${COMP_WORDS[COMP_CWORD]}"))
-}
-
-_stg_common ()
-{
- _complete_options "$(_cmd_options $1)"
-}
-
-_stg_patches ()
-{
- _complete_patch_range "$2" "$(_cmd_options $1)"
-}
-
-_stg_patches_options ()
-{
- _complete_patch_range_options "$2" "$(_cmd_options $1)" "$3"
-}
-
-_stg_help ()
-{
- _complete_options "$_stg_commands"
-}
-
-_stg ()
-{
- local i c=1 command
-
- while [ $c -lt $COMP_CWORD ]; do
- if [ $c == 1 ]; then
- command="${COMP_WORDS[c]}"
- fi
- c=$((++c))
- done
-
- # Complete name of subcommand.
- if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
- COMPREPLY=($(compgen \
- -W "--help --version copyright help $_stg_commands" \
- -- "${COMP_WORDS[COMP_CWORD]}"))
- return;
- fi
-
- # Complete arguments to subcommands.
- case "$command" in
- # generic commands
- help) _stg_help ;;
- # repository commands
- id) _stg_patches $command _all_patches ;;
- # stack commands
- coalesce) _stg_patches $command _applied_patches ;;
- float) _stg_patches $command _all_patches ;;
- goto) _stg_patches $command _all_other_patches ;;
- hide) _stg_patches $command _unapplied_patches ;;
- pop) _stg_patches $command _applied_patches ;;
- push) _stg_patches $command _unapplied_patches ;;
- series) _stg_patches $command _all_patches ;;
- sink) _stg_patches $command _all_patches ;;
- unhide) _stg_patches $command _hidden_patches ;;
- # patch commands
- delete) _stg_patches $command _all_patches ;;
- edit) _stg_patches $command _applied_patches ;;
- export) _stg_patches $command _all_patches ;;
- files) _stg_patches $command _all_patches ;;
- log) _stg_patches $command _all_patches ;;
- mail) _stg_patches $command _all_patches ;;
- pick) _stg_patches $command _unapplied_patches ;;
-# refresh)_stg_patches_options $command _applied_patches "-p --patch" ;;
- refresh) _complete_files $command "$(_dirty_files)" ;;
- rename) _stg_patches $command _all_patches ;;
- show) _stg_patches $command _all_patches ;;
- sync) _stg_patches $command _applied_patches ;;
- # working-copy commands
- diff) _stg_patches_options $command _applied_patches "-r --range" ;;
- resolved) _complete_files $command "$(_conflicting_files)" ;;
- # commands that usually raher accept branches
- branch) _complete_branch $command _all_branches ;;
- rebase) _complete_branch $command _all_branches ;;
- # all the other commands
- *) _stg_common $command ;;
- esac
-}
-
-complete -o default -F _stg stgdiff --git a/setup.py b/setup.py
index 81854d3..fb67958 100755
--- a/setup.py
+++ b/setup.py
@@ -59,8 +59,8 @@ def __run_setup():
('share/stgit/examples', glob.glob('examples/*.tmpl')),
('share/stgit/examples', ['examples/gitconfig']),
('share/stgit/contrib', ['contrib/diffcol.sh',
- 'contrib/stgbashprompt.sh',
- 'contrib/stgit-completion.bash']),
+ 'contrib/stgbashprompt.sh']),
+ ('share/stgit/completion', ['stgit-completion.bash'])
])
# Check the minimum versions requireddiff --git a/stg-build b/stg-build
index 3c9dbfa..2af6523 100755
--- a/stg-build
+++ b/stg-build
@@ -2,7 +2,7 @@
# -*- python -*-
import optparse, sys
import stgit.main
-from stgit import argparse, commands
+from stgit import argparse, commands, completion
def main():
op = optparse.OptionParser()@@ -14,6 +14,8 @@ def main():
help = 'Print asciidoc command list')
op.add_option('--py-cmd-list', action = 'store_true',
help = 'Write Python command list')
+ op.add_option('--bash-completion', action = 'store_true',
+ help = 'Write bash completion code')
options, args = op.parse_args()
if args:
op.error('Wrong number of arguments')@@ -30,6 +32,8 @@ def main():
elif options.py_cmd_list:
commands.py_commands(commands.get_commands(allow_cached = False),
sys.stdout)
+ elif options.bash_completion:
+ completion.write_completion(sys.stdout)
else:
op.error('No command')
diff --git a/stgit/argparse.py b/stgit/argparse.py
index bd71817..406ada3 100644
--- a/stgit/argparse.py
+++ b/stgit/argparse.py
@@ -26,28 +26,27 @@ def _paragraphs(s):
class opt(object):
"""Represents a command-line flag."""
- def __init__(self, *args, **kwargs):
- self.args = args
+ def __init__(self, *pargs, **kwargs):
+ self.pargs = pargs
self.kwargs = kwargs
def get_option(self):
kwargs = dict(self.kwargs)
kwargs['help'] = kwargs['short']
- del kwargs['short']
- if 'long' in kwargs:
- del kwargs['long']
- return optparse.make_option(*self.args, **kwargs)
+ for k in ['short', 'long', 'args']:
+ kwargs.pop(k, None)
+ return optparse.make_option(*self.pargs, **kwargs)
def metavar(self):
o = self.get_option()
if not o.nargs:
return None
if o.metavar:
return o.metavar
- for flag in self.args:
+ for flag in self.pargs:
if flag.startswith('--'):
return utils.strip_prefix('--', flag).upper()
raise Exception('Cannot determine metavar')
def write_asciidoc(self, f):
- for flag in self.args:
+ for flag in self.pargs:
f.write(flag)
m = self.metavar()
if m:@@ -60,6 +59,16 @@ class opt(object):
f.write('+\n')
for line in para:
f.write(line + '\n')
+ @property
+ def flags(self):
+ return self.pargs
+ @property
+ def args(self):
+ if self.kwargs.get('action', None) in ['store_true', 'store_false']:
+ default = []
+ else:
+ default = [files]
+ return self.kwargs.get('args', default)
def _cmd_name(cmd_mod):
return getattr(cmd_mod, 'name', cmd_mod.__name__.split('.')[-1])@@ -103,11 +112,11 @@ def sign_options():
'--ack and --sign were both specified')
parser.values.sign_str = sign_str
return [
- opt('--sign', action = 'callback', dest = 'sign_str',
+ opt('--sign', action = 'callback', dest = 'sign_str', args = [],
callback = callback, callback_args = ('Signed-off-by',),
short = 'Add "Signed-off-by:" line', long = """
Add a "Signed-off-by:" to the end of the patch."""),
- opt('--ack', action = 'callback', dest = 'sign_str',
+ opt('--ack', action = 'callback', dest = 'sign_str', args = [],
callback = callback, callback_args = ('Acked-by',),
short = 'Add "Acked-by:" line', long = """
Add an "Acked-by:" line to the end of the patch.""")]@@ -151,7 +160,7 @@ def message_options(save_template):
callback = msg_callback, dest = 'message', type = 'string',
short = 'Use MESSAGE instead of invoking the editor'),
opt('-f', '--file', action = 'callback', callback = file_callback,
- dest = 'message', type = 'string',
+ dest = 'message', type = 'string', args = [files],
short = 'Use FILE instead of invoking the editor', long = """
Use the contents of FILE instead of invoking the editor.
(If FILE is "-", write to stdout.)""")]@@ -181,6 +190,7 @@ def diff_opts_option():
default = (config.get('stgit.diff-opts') or '').split(),
action = 'callback', callback = diff_opts_callback,
type = 'string', metavar = 'OPTIONS',
+ args = [strings('-M', '-C')],
short = 'Extra options to pass to "git diff"')]
def _person_opts(person, short):@@ -212,3 +222,63 @@ def author_options():
def author_committer_options():
return _person_opts('author', 'auth') + _person_opts('committer', 'comm')
+
+class CompgenBase(object):
+ def actions(self, var): return set()
+ def words(self, var): return set()
+ def command(self, var):
+ cmd = ['compgen']
+ for act in self.actions(var):
+ cmd += ['-A', act]
+ words = self.words(var)
+ if words:
+ cmd += ['-W', '"%s"' % ' '.join(words)]
+ cmd += ['--', '"%s"' % var]
+ return ' '.join(cmd)
+
+class CompgenJoin(CompgenBase):
+ def __init__(self, a, b):
+ assert isinstance(a, CompgenBase)
+ assert isinstance(b, CompgenBase)
+ self.__a = a
+ self.__b = b
+ def words(self, var): return self.__a.words(var) | self.__b.words(var)
+ def actions(self, var): return self.__a.actions(var) | self.__b.actions(var)
+
+class Compgen(CompgenBase):
+ def __init__(self, words = frozenset(), actions = frozenset()):
+ self.__words = set(words)
+ self.__actions = set(actions)
+ def actions(self, var): return self.__actions
+ def words(self, var): return self.__words
+
+def compjoin(compgens):
+ comp = Compgen()
+ for c in compgens:
+ comp = CompgenJoin(comp, c)
+ return comp
+
+all_branches = Compgen(['$(_all_branches)'])
+stg_branches = Compgen(['$(_stg_branches)'])
+applied_patches = Compgen(['$(_applied_patches)'])
+other_applied_patches = Compgen(['$(_other_applied_patches)'])
+unapplied_patches = Compgen(['$(_unapplied_patches)'])
+hidden_patches = Compgen(['$(_hidden_patches)'])
+commit = Compgen(['$(_all_branches) $(_tags) $(_remotes)'])
+conflicting_files = Compgen(['$(_conflicting_files)'])
+dirty_files = Compgen(['$(_dirty_files)'])
+unknown_files = Compgen(['$(_unknown_files)'])
+known_files = Compgen(['$(_known_files)'])
+repo = Compgen(actions = ['directory'])
+dir = Compgen(actions = ['directory'])
+files = Compgen(actions = ['file'])
+def strings(*ss): return Compgen(ss)
+class patch_range(CompgenBase):
+ def __init__(self, *endpoints):
+ self.__endpoints = endpoints
+ def words(self, var):
+ words = set()
+ for e in self.__endpoints:
+ assert not e.actions(var)
+ words |= e.words(var)
+ return set(['$(_patch_range "%s" "%s")' % (' '.join(words), var)])diff --git a/stgit/commands/branch.py b/stgit/commands/branch.py
index ef71547..3d912fc 100644
--- a/stgit/commands/branch.py
+++ b/stgit/commands/branch.py
@@ -20,7 +20,7 @@ from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
from stgit.out import *
-from stgit import stack, git, basedir
+from stgit import argparse, stack, git, basedir
from stgit.lib import log
help = 'Branch operations: switch, list, create, rename, delete, ...'
@@ -45,6 +45,7 @@ within a git repository.
'stg branch' <branch>::
Switch to the given branch."""
+args = [argparse.all_branches]
options = [
opt('-l', '--list', action = 'store_true',
short = 'List the branches contained in this repository', long = """diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py
index 27a7716..9b48e7b 100644
--- a/stgit/commands/clean.py
+++ b/stgit/commands/clean.py
@@ -28,6 +28,7 @@ Delete the empty patches in the whole series or only those applied or
unapplied. A patch is considered empty if the two commit objects
representing its boundaries refer to the same tree object."""
+args = []
options = [
opt('-a', '--applied', action = 'store_true',
short = 'Delete the empty applied patches'),diff --git a/stgit/commands/clone.py b/stgit/commands/clone.py
index 659712d..efb7198 100644
--- a/stgit/commands/clone.py
+++ b/stgit/commands/clone.py
@@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import sys, os
from stgit.commands.common import *
from stgit.utils import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Make a local clone of a remote repository'
kind = 'repo'
@@ -35,6 +35,7 @@ commands of stglink:branch[].
The target directory <dir> will be created by this command, and must
not already exist."""
+args = [argparse.repo, argparse.dir]
options = []
directory = DirectoryAnywhere(needs_current_series = False, log = False)
diff --git a/stgit/commands/coalesce.py b/stgit/commands/coalesce.py
index ef8e912..4b5c00a 100644
--- a/stgit/commands/coalesce.py
+++ b/stgit/commands/coalesce.py
@@ -34,6 +34,8 @@ If there are conflicts when reordering the patches to match the order
you specify, you will have to resolve them manually just as if you had
done a sequence of pushes and pops yourself."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches)]
options = [opt('-n', '--name', short = 'Name of coalesced patch')
] + argparse.message_options(save_template = True)
diff --git a/stgit/commands/commit.py b/stgit/commands/commit.py
index 99b7b5d..dd8d6e6 100644
--- a/stgit/commands/commit.py
+++ b/stgit/commands/commit.py
@@ -19,6 +19,7 @@ from stgit.argparse import opt
from stgit.commands import common
from stgit.lib import transaction
from stgit.out import *
+from stgit import argparse
help = 'Permanently store the applied patches into the stack base'
kind = 'stack'
@@ -40,6 +41,8 @@ The -n/--number option specifies the number of applied patches to
commit (counting from the bottom of the stack). If -a/--all is given,
all applied patches are committed."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches)]
options = [
opt('-n', '--number', type = 'int',
short = 'Commit the specified number of patches'),diff --git a/stgit/commands/delete.py b/stgit/commands/delete.py
index b92a039..40cef3f 100644
--- a/stgit/commands/delete.py
+++ b/stgit/commands/delete.py
@@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from stgit.argparse import opt
from stgit.commands import common
from stgit.lib import transaction
+from stgit import argparse
help = 'Delete patches'
kind = 'patch'
@@ -28,8 +29,10 @@ Delete the patches passed as arguments.
Note that the 'delete' operation is irreversible."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches)]
options = [
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default branch')]
directory = common.DirectoryHasRepositoryLib()diff --git a/stgit/commands/diff.py b/stgit/commands/diff.py
index 05f4f4c..7d2f719 100644
--- a/stgit/commands/diff.py
+++ b/stgit/commands/diff.py
@@ -38,8 +38,12 @@ representing the bottom of the current stack.
rev = '[branch:](<patch>|{base}) | <tree-ish>'"""
+args = [argparse.known_files, argparse.dirty_files]
options = [
opt('-r', '--range', metavar = 'rev1[..[rev2]]', dest = 'revs',
+ args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches,
+ argparse.hidden_patches)],
short = 'Show the diff between revisions'),
opt('-s', '--stat', action = 'store_true',
short = 'Show the stat instead of the diff'),diff --git a/stgit/commands/edit.py b/stgit/commands/edit.py
index b370f5c..4904f68 100644
--- a/stgit/commands/edit.py
+++ b/stgit/commands/edit.py
@@ -54,6 +54,8 @@ If the patch diff is edited but does not apply, no changes are made to
the patch at all. The edited patch is saved to a file which you can
feed to "stg edit --file", once you have made sure it does apply."""
+args = [argparse.applied_patches, argparse.unapplied_patches,
+ argparse.hidden_patches]
options = [
opt('-d', '--diff', action = 'store_true',
short = 'Edit the patch diff'),diff --git a/stgit/commands/export.py b/stgit/commands/export.py
index c7ed802..dfdcea1 100644
--- a/stgit/commands/export.py
+++ b/stgit/commands/export.py
@@ -47,8 +47,11 @@ file:
%(commname)s - committer's name
%(commemail)s - committer's e-mail"""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches,
+ argparse.hidden_patches)]
options = [
- opt('-d', '--dir',
+ opt('-d', '--dir', args = [argparse.dir],
short = 'Export patches to DIR instead of the default'),
opt('-p', '--patch', action = 'store_true',
short = 'Append .patch to the patch names'),@@ -56,9 +59,9 @@ options = [
short = 'Append .EXTENSION to the patch names'),
opt('-n', '--numbered', action = 'store_true',
short = 'Prefix the patch names with order numbers'),
- opt('-t', '--template', metavar = 'FILE',
+ opt('-t', '--template', metavar = 'FILE', args = [argparse.files],
short = 'Use FILE as a template'),
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default branch'),
opt('-s', '--stdout', action = 'store_true',
short = 'Dump the patches to the standard output'),diff --git a/stgit/commands/files.py b/stgit/commands/files.py
index d63a33e..46d43c1 100644
--- a/stgit/commands/files.py
+++ b/stgit/commands/files.py
@@ -34,6 +34,8 @@ given patch. Note that this command doesn't show the files modified in
the working tree and not yet included in the patch by a 'refresh'
command. Use the 'diff' or 'status' commands for these files."""
+args = [argparse.applied_patches, argparse.unapplied_patches,
+ argparse.hidden_patches]
options = [
opt('-s', '--stat', action = 'store_true',
short = 'Show the diffstat'),diff --git a/stgit/commands/float.py b/stgit/commands/float.py
index 93bb69b..7c3dcdf 100644
--- a/stgit/commands/float.py
+++ b/stgit/commands/float.py
@@ -20,7 +20,7 @@ import sys, os
from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Push patches to the top, even if applied'
kind = 'stack'
@@ -32,6 +32,8 @@ necessary pop and push operations will be performed to accomplish
this. The '--series' option can be used to rearrange the (top) patches
as specified by the given series file (or the standard input)."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches)]
options = [
opt('-s', '--series', action = 'store_true',
short = 'Rearrange according to a series file')]diff --git a/stgit/commands/fold.py b/stgit/commands/fold.py
index 165ff52..66a2dd9 100644
--- a/stgit/commands/fold.py
+++ b/stgit/commands/fold.py
@@ -20,7 +20,7 @@ from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
from stgit.out import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Integrate a GNU diff patch into the current patch'
kind = 'patch'
@@ -33,10 +33,11 @@ performed with the current top. With the --base option, the patch is
applied onto the specified base and a three-way merged is performed
with the current top."""
+args = [argparse.files]
options = [
opt('-t', '--threeway', action = 'store_true',
short = 'Perform a three-way merge with the current patch'),
- opt('-b', '--base',
+ opt('-b', '--base', args = [argparse.commit],
short = 'Use BASE instead of HEAD applying the patch')]
directory = DirectoryHasRepository(log = True)diff --git a/stgit/commands/goto.py b/stgit/commands/goto.py
index 0d4cd29..60a917e 100644
--- a/stgit/commands/goto.py
+++ b/stgit/commands/goto.py
@@ -17,6 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from stgit.commands import common
from stgit.lib import transaction
+from stgit import argparse
help = 'Push or pop patches to the given one'
kind = 'stack'
@@ -25,6 +26,7 @@ description = """
Push/pop patches to/from the stack until the one given on the command
line becomes current."""
+args = [argparse.other_applied_patches, argparse.unapplied_patches]
options = []
directory = common.DirectoryHasRepositoryLib()
diff --git a/stgit/commands/hide.py b/stgit/commands/hide.py
index 1bcb5f1..014febb 100644
--- a/stgit/commands/hide.py
+++ b/stgit/commands/hide.py
@@ -20,7 +20,7 @@ from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
from stgit.out import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Hide a patch in the series'
kind = 'stack'
@@ -29,8 +29,10 @@ description = """
Hide a range of unapplied patches so that they are no longer shown in
the plain 'series' command output."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches)]
options = [
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default branch')]
directory = DirectoryHasRepository(log = True)diff --git a/stgit/commands/id.py b/stgit/commands/id.py
index 857ec33..566edcc 100644
--- a/stgit/commands/id.py
+++ b/stgit/commands/id.py
@@ -18,6 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from stgit.out import out
from stgit.commands import common
from stgit.lib import stack
+from stgit import argparse
help = 'Print the git hash value of a StGit reference'
kind = 'repo'
@@ -30,6 +31,8 @@ or the base of the stack. If no branch is specified, it defaults to the
current one. The bottom of a patch is accessible with the
'[<branch>:]<patch>^' format."""
+args = [argparse.applied_patches, argparse.unapplied_patches,
+ argparse.hidden_patches]
options = []
directory = common.DirectoryHasRepositoryLib()
diff --git a/stgit/commands/imprt.py b/stgit/commands/imprt.py
index 9f2df05..de5e9a5 100644
--- a/stgit/commands/imprt.py
+++ b/stgit/commands/imprt.py
@@ -44,6 +44,7 @@ stack.
The patch description has to be separated from the data with a '---'
line."""
+args = [argparse.files]
options = [
opt('-m', '--mail', action = 'store_true',
short = 'Import the patch from a standard e-mail file'),@@ -61,7 +62,7 @@ options = [
short = 'Ignore the applied patches in the series'),
opt('--replace', action = 'store_true',
short = 'Replace the unapplied patches in the series'),
- opt('-b', '--base',
+ opt('-b', '--base', args = [argparse.commit],
short = 'Use BASE instead of HEAD for file importing'),
opt('-e', '--edit', action = 'store_true',
short = 'Invoke an editor for the patch description'),diff --git a/stgit/commands/init.py b/stgit/commands/init.py
index 67d20d1..6ffb93e 100644
--- a/stgit/commands/init.py
+++ b/stgit/commands/init.py
@@ -27,6 +27,7 @@ Initialise the current git branch to be used as an StGIT stack. The
branch (and the git repository it is in) must already exist and
contain at least one commit."""
+args = []
options = []
directory = common.DirectoryHasRepositoryLib()
diff --git a/stgit/commands/log.py b/stgit/commands/log.py
index 1f63ef5..39cdfe7 100644
--- a/stgit/commands/log.py
+++ b/stgit/commands/log.py
@@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os.path
from optparse import make_option
-from stgit import run
+from stgit import argparse, run
from stgit.argparse import opt
from stgit.commands import common
from stgit.lib import log
@@ -37,8 +37,11 @@ the named patches.
"stg undo" and "stg redo" let you step back and forth in the patch
stack. "stg reset" lets you go directly to any state."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches,
+ argparse.hidden_patches)]
options = [
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default one'),
opt('-p', '--patch', action = 'store_true',
short = 'Show the refresh diffs'),diff --git a/stgit/commands/mail.py b/stgit/commands/mail.py
index e0a5521..0b3157e 100644
--- a/stgit/commands/mail.py
+++ b/stgit/commands/mail.py
@@ -93,6 +93,9 @@ the following:
%(prefix)s - 'prefix ' string passed on the command line
%(shortdescr)s - the first line of the patch description"""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches,
+ argparse.hidden_patches)]
options = [
opt('-a', '--all', action = 'store_true',
short = 'E-mail all the applied patches'),@@ -134,7 +137,7 @@ options = [
short = 'Password for SMTP authentication'),
opt('-T', '--smtp-tls', action = 'store_true',
short = 'Use SMTP with TLS encryption'),
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default branch'),
opt('-m', '--mbox', action = 'store_true',
short = 'Generate an mbox file instead of sending')diff --git a/stgit/commands/new.py b/stgit/commands/new.py
index 4117e4e..067882a 100644
--- a/stgit/commands/new.py
+++ b/stgit/commands/new.py
@@ -39,6 +39,7 @@ the patch, unless the '--message' flag already specified one. The
'patchdescr.tmpl' template file (if available) is used to pre-fill the
editor."""
+args = []
options = (argparse.author_committer_options()
+ argparse.message_options(save_template = True)
+ argparse.sign_options())diff --git a/stgit/commands/patches.py b/stgit/commands/patches.py
index e877171..54fac21 100644
--- a/stgit/commands/patches.py
+++ b/stgit/commands/patches.py
@@ -21,7 +21,7 @@ from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
from stgit.out import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Show the applied patches modifying a file'
kind = 'stack'
@@ -32,10 +32,11 @@ it shows the patches affected by the local tree modifications. The
'--diff' option also lists the patch log and the diff for the given
files."""
+args = [argparse.known_files]
options = [
opt('-d', '--diff', action = 'store_true',
short = 'Show the diff for the given files'),
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default branch')]
directory = DirectoryHasRepository(log = False)diff --git a/stgit/commands/pick.py b/stgit/commands/pick.py
index e1c531d..760918b 100644
--- a/stgit/commands/pick.py
+++ b/stgit/commands/pick.py
@@ -20,7 +20,7 @@ from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
from stgit.out import *
-from stgit import stack, git
+from stgit import argparse, stack, git
from stgit.stack import Series
help = 'Import a patch from a different branch or a commit object'
@@ -34,14 +34,17 @@ used as the name of the current patch. It can be overridden with the
option. The log and author information are those of the commit
object."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches,
+ argparse.hidden_patches)]
options = [
opt('-n', '--name',
short = 'Use NAME as the patch name'),
- opt('-B', '--ref-branch',
+ opt('-B', '--ref-branch', args = [argparse.stg_branches],
short = 'Pick patches from BRANCH'),
opt('-r', '--reverse', action = 'store_true',
short = 'Reverse the commit object before importing'),
- opt('-p', '--parent', metavar = 'COMMITID',
+ opt('-p', '--parent', metavar = 'COMMITID', args = [argparse.commit],
short = 'Use COMMITID as parent'),
opt('-x', '--expose', action = 'store_true',
short = 'Append the imported commit id to the patch log'),diff --git a/stgit/commands/pop.py b/stgit/commands/pop.py
index 855dc09..2c78ac2 100644
--- a/stgit/commands/pop.py
+++ b/stgit/commands/pop.py
@@ -20,7 +20,7 @@ import sys, os
from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Pop one or more patches from the stack'
kind = 'stack'
@@ -35,6 +35,7 @@ patches passed on the command line are popped from the stack. Some of
the push operations may fail because of conflicts ("stg undo" would
revert the last push operation)."""
+args = [argparse.patch_range(argparse.applied_patches)]
options = [
opt('-a', '--all', action = 'store_true',
short = 'Pop all the applied patches'),diff --git a/stgit/commands/pull.py b/stgit/commands/pull.py
index 82035c6..f6d1398 100644
--- a/stgit/commands/pull.py
+++ b/stgit/commands/pull.py
@@ -21,7 +21,7 @@ from stgit.commands.common import *
from stgit.utils import *
from stgit.out import *
from stgit.config import GitConfigException
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Pull changes from a remote repository'
kind = 'stack'
@@ -37,6 +37,7 @@ resolved and the patch pushed again.
Check the 'git fetch' documentation for the <repository> format."""
+args = [argparse.repo]
options = [
opt('-n', '--nopush', action = 'store_true',
short = 'Do not push the patches back after pulling'),diff --git a/stgit/commands/push.py b/stgit/commands/push.py
index a7c7578..818e02d 100644
--- a/stgit/commands/push.py
+++ b/stgit/commands/push.py
@@ -21,7 +21,7 @@ from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
from stgit.out import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Push one or more patches onto the stack'
kind = 'stack'
@@ -38,6 +38,7 @@ conflicting push with 'stg undo').
The command also notifies when the patch becomes empty (fully merged
upstream) or is modified (three-way merged) by the 'push' operation."""
+args = [argparse.patch_range(argparse.unapplied_patches)]
options = [
opt('-a', '--all', action = 'store_true',
short = 'Push all the unapplied patches'),diff --git a/stgit/commands/rebase.py b/stgit/commands/rebase.py
index 60168ab..a4bc6e7 100644
--- a/stgit/commands/rebase.py
+++ b/stgit/commands/rebase.py
@@ -19,7 +19,7 @@ import sys, os
from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Move the stack base to another point in history'
kind = 'stack'
@@ -40,6 +40,7 @@ Or if you want to skip that patch:
$ stg undo --hard
$ stg push next-patch..top-patch"""
+args = [argparse.commit]
options = [
opt('-n', '--nopush', action = 'store_true',
short = 'Do not push the patches back after rebasing'),diff --git a/stgit/commands/redo.py b/stgit/commands/redo.py
index 8e62a1d..eb8b20f 100644
--- a/stgit/commands/redo.py
+++ b/stgit/commands/redo.py
@@ -32,6 +32,7 @@ the effects of consecutive invocations of "stg undo".
It is an error to run "stg redo" if the last command was not an
undo."""
+args = []
options = [
opt('-n', '--number', type = 'int', metavar = 'N', default = 1,
short = 'Undo the last N undos'),diff --git a/stgit/commands/refresh.py b/stgit/commands/refresh.py
index 3c82906..27cccc5 100644
--- a/stgit/commands/refresh.py
+++ b/stgit/commands/refresh.py
@@ -48,6 +48,7 @@ the patch stack log; this means that one undo step will undo the merge
between the other patch and the temp patch, and two undo steps will
additionally get rid of the temp patch."""
+args = [argparse.dirty_files]
options = [
opt('-u', '--update', action = 'store_true',
short = 'Only update the current patch files'),@@ -55,7 +56,8 @@ options = [
short = 'Refresh from index instead of worktree', long = """
Instead of setting the patch top to the current contents of
the worktree, set it to the current contents of the index."""),
- opt('-p', '--patch',
+ opt('-p', '--patch', args = [argparse.other_applied_patches,
+ argparse.unapplied_patches],
short = 'Refresh (applied) PATCH instead of the top patch'),
opt('-e', '--edit', action = 'store_true',
short = 'Invoke an editor for the patch description'),diff --git a/stgit/commands/rename.py b/stgit/commands/rename.py
index 7e0fbf5..8a593ac 100644
--- a/stgit/commands/rename.py
+++ b/stgit/commands/rename.py
@@ -20,7 +20,7 @@ from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
from stgit.out import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Rename a patch'
kind = 'patch'
@@ -29,8 +29,10 @@ description = """
Rename <oldpatch> into <newpatch> in a series. If <oldpatch> is not
given, the top-most patch will be renamed."""
+args = [argparse.applied_patches, argparse.unapplied_patches,
+ argparse.hidden_patches]
options = [
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'use BRANCH instead of the default one')]
directory = DirectoryHasRepository(log = True)diff --git a/stgit/commands/repair.py b/stgit/commands/repair.py
index ff9bb61..37c4bab 100644
--- a/stgit/commands/repair.py
+++ b/stgit/commands/repair.py
@@ -69,6 +69,7 @@ NOTE: If using git commands on the stack was a mistake, running "stg
repair" is _not_ what you want. In that case, what you want is option
(1) above."""
+args = []
options = []
directory = DirectoryGotoToplevel(log = True)
diff --git a/stgit/commands/reset.py b/stgit/commands/reset.py
index 3ccbf1b..7dfd4a0 100644
--- a/stgit/commands/reset.py
+++ b/stgit/commands/reset.py
@@ -21,6 +21,7 @@ from stgit.argparse import opt
from stgit.commands import common
from stgit.lib import git, log, transaction
from stgit.out import out
+from stgit import argparse
help = 'Reset the patch stack to an earlier state'
kind = 'stack'
@@ -33,6 +34,9 @@ a commit id from a stack log; "stg log" lets you view this log, and
If one or more patch names are given, reset only those patches, and
leave the rest alone."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches,
+ argparse.hidden_patches)]
options = [
opt('--hard', action = 'store_true',
short = 'Discard changes in your index/worktree')]diff --git a/stgit/commands/resolved.py b/stgit/commands/resolved.py
index ce8630d..2ce7ec3 100644
--- a/stgit/commands/resolved.py
+++ b/stgit/commands/resolved.py
@@ -20,7 +20,7 @@ import sys, os
from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
-from stgit import stack, git, basedir
+from stgit import argparse, stack, git, basedir
from stgit.config import config, file_extensions
from stgit.gitmergeonefile import interactive_merge
@@ -32,10 +32,12 @@ Mark a merge conflict as resolved. The conflicts can be seen with the
'status' command, the corresponding files being prefixed with a
'C'."""
+args = [argparse.conflicting_files]
options = [
opt('-a', '--all', action = 'store_true',
short = 'Mark all conflicts as solved'),
opt('-r', '--reset', metavar = '(ancestor|current|patched)',
+ args = [argparse.strings('ancestor', 'current', 'patched')],
short = 'Reset the file(s) to the given state'),
opt('-i', '--interactive', action = 'store_true',
short = 'Run the interactive merging tool')]diff --git a/stgit/commands/series.py b/stgit/commands/series.py
index e9d148a..95196d3 100644
--- a/stgit/commands/series.py
+++ b/stgit/commands/series.py
@@ -21,6 +21,7 @@ from stgit.commands import common
from stgit.commands.common import parse_patches
from stgit.out import out
from stgit.config import config
+from stgit import argparse
help = 'Print the patch series'
kind = 'stack'
@@ -31,8 +32,11 @@ range. The applied patches are prefixed with a '+', the unapplied ones
with a '-' and the hidden ones with a '!'. The current patch is
prefixed with a '>'. Empty patches are prefixed with a '0'."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches,
+ argparse.hidden_patches)]
options = [
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default branch'),
opt('-a', '--all', action = 'store_true',
short = 'Show all patches, including the hidden ones'),@@ -42,7 +46,7 @@ options = [
short = 'Show the unapplied patches only'),
opt('-H', '--hidden', action = 'store_true',
short = 'Show the hidden patches only'),
- opt('-m', '--missing', metavar = 'BRANCH',
+ opt('-m', '--missing', metavar = 'BRANCH', args = [argparse.stg_branches],
short = 'Show patches in BRANCH missing in current'),
opt('-c', '--count', action = 'store_true',
short = 'Print the number of patches in the series'),diff --git a/stgit/commands/show.py b/stgit/commands/show.py
index e08551b..9a1f48b 100644
--- a/stgit/commands/show.py
+++ b/stgit/commands/show.py
@@ -28,8 +28,11 @@ description = """
Show the commit log and the diff corresponding to the given patches.
The output is similar to that generated by 'git show'."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches,
+ argparse.hidden_patches)]
options = [
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default branch'),
opt('-a', '--applied', action = 'store_true',
short = 'Show the applied patches'),diff --git a/stgit/commands/sink.py b/stgit/commands/sink.py
index 34f81c9..d4561ed 100644
--- a/stgit/commands/sink.py
+++ b/stgit/commands/sink.py
@@ -20,7 +20,7 @@ import sys, os
from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Send patches deeper down the stack'
kind = 'stack'
@@ -41,12 +41,14 @@ including <target patch>), then pushing the patches to sink, and then
(unless '--nopush' is also given) pushing back into place the
formerly-applied patches."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches)]
options = [
opt('-n', '--nopush', action = 'store_true',
short = 'Do not push the patches back after sinking', long = """
Do not push back on the stack the formerly-applied patches.
Only the patches to sink are pushed."""),
- opt('-t', '--to', metavar = 'TARGET',
+ opt('-t', '--to', metavar = 'TARGET', args = [argparse.applied_patches],
short = 'Sink patches below the TARGET patch', long = """
Specify a target patch to place the patches below, instead of
sinking them to the bottom of the stack.""")]diff --git a/stgit/commands/status.py b/stgit/commands/status.py
index c78bc1b..730b47c 100644
--- a/stgit/commands/status.py
+++ b/stgit/commands/status.py
@@ -20,7 +20,7 @@ import sys, os
from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Show the tree status'
kind = 'wc'
@@ -39,6 +39,7 @@ under revision control. The files are prefixed as follows:
An 'stg refresh' command clears the status of the modified, new and
deleted files."""
+args = [argparse.files]
options = [
opt('-m', '--modified', action = 'store_true',
short = 'Show modified files only'),diff --git a/stgit/commands/sync.py b/stgit/commands/sync.py
index 966ac55..ea949d6 100644
--- a/stgit/commands/sync.py
+++ b/stgit/commands/sync.py
@@ -21,7 +21,7 @@ from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
from stgit.out import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Synchronise patches with a branch or a series'
kind = 'patch'
@@ -33,12 +33,14 @@ for keeping patches on several branches in sync. Note that the
operation may fail for some patches because of conflicts. The patches
in the series must apply cleanly."""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches)]
options = [
opt('-a', '--all', action = 'store_true',
short = 'Synchronise all the applied patches'),
- opt('-B', '--ref-branch',
+ opt('-B', '--ref-branch', args = [argparse.stg_branches],
short = 'Syncronise patches with BRANCH'),
- opt('-s', '--series',
+ opt('-s', '--series', args = [argparse.files],
short = 'Syncronise patches with SERIES')]
directory = DirectoryGotoToplevel(log = True)diff --git a/stgit/commands/top.py b/stgit/commands/top.py
index 523afa4..4ec37b4 100644
--- a/stgit/commands/top.py
+++ b/stgit/commands/top.py
@@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from stgit.argparse import opt
from stgit.commands import common
from stgit.out import out
+from stgit import argparse
help = 'Print the name of the top patch'
kind = 'stack'
@@ -26,8 +27,9 @@ usage = ['']
description = """
Print the name of the current (topmost) patch."""
+args = []
options = [
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default branch')]
directory = common.DirectoryHasRepositoryLib()diff --git a/stgit/commands/uncommit.py b/stgit/commands/uncommit.py
index b9950ca..bcc8bac 100644
--- a/stgit/commands/uncommit.py
+++ b/stgit/commands/uncommit.py
@@ -21,7 +21,7 @@ from stgit.argparse import opt
from stgit.commands import common
from stgit.lib import transaction
from stgit.out import *
-from stgit import utils
+from stgit import argparse, utils
help = 'Turn regular git commits into StGit patches'
kind = 'stack'
@@ -49,10 +49,12 @@ given commit should be uncommitted.
Only commits with exactly one parent can be uncommitted; in other
words, you can't uncommit a merge."""
+args = []
options = [
opt('-n', '--number', type = 'int',
short = 'Uncommit the specified number of commits'),
- opt('-t', '--to', short = 'Uncommit to the specified commit'),
+ opt('-t', '--to', args = [argparse.commit],
+ short = 'Uncommit to the specified commit'),
opt('-x', '--exclusive', action = 'store_true',
short = 'Exclude the commit specified by the --to option')]
diff --git a/stgit/commands/undo.py b/stgit/commands/undo.py
index b7b1b73..6a04363 100644
--- a/stgit/commands/undo.py
+++ b/stgit/commands/undo.py
@@ -29,6 +29,7 @@ description = """
Reset the patch stack to the previous state. Consecutive invocations
of "stg undo" will take you ever further into the past."""
+args = []
options = [
opt('-n', '--number', type = 'int', metavar = 'N', default = 1,
short = 'Undo the last N commands'),diff --git a/stgit/commands/unhide.py b/stgit/commands/unhide.py
index acfef29..0c0832a 100644
--- a/stgit/commands/unhide.py
+++ b/stgit/commands/unhide.py
@@ -20,7 +20,7 @@ from stgit.argparse import opt
from stgit.commands.common import *
from stgit.utils import *
from stgit.out import *
-from stgit import stack, git
+from stgit import argparse, stack, git
help = 'Unhide a hidden patch'
kind = 'stack'
@@ -29,8 +29,9 @@ description = """
Unhide a hidden range of patches so that they are shown in the plain
'stg series' command output."""
+args = [argparse.patch_range(argparse.hidden_patches)]
options = [
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default branch')]
directory = DirectoryHasRepository(log = True)diff --git a/stgit/completion.py b/stgit/completion.py
new file mode 100644
index 0000000..e461e3b
--- /dev/null
+++ b/stgit/completion.py
@@ -0,0 +1,140 @@
+import textwrap
+import stgit.commands
+from stgit import argparse
+
+def fun(name, *body):
+ return ['%s ()' % name, '{', list(body), '}']
+
+def fun_desc(name, desc, *body):
+ return ['# %s' % desc] + fun(name, *body)
+
+def flatten(stuff, sep):
+ r = stuff[0]
+ for s in stuff[1:]:
+ r.append(sep)
+ r.extend(s)
+ return r
+
+def write(f, stuff, indent = 0):
+ for s in stuff:
+ if isinstance(s, str):
+ f.write((' '*4*indent + s).rstrip() + '\n')
+ else:
+ write(f, s, indent + 1)
+
+def patch_list_fun(type):
+ return fun('_%s_patches' % type, 'local g=$(_gitdir)',
+ 'test "$g" && cat "$g/patches/$(_current_branch)/%s"' % type)
+
+def file_list_fun(name, cmd):
+ return fun('_%s_files' % name, 'local g=$(_gitdir)',
+ 'test "$g" && %s' % cmd)
+
+def ref_list_fun(name, prefix):
+ return fun(name, 'local g=$(_gitdir)',
+ ("test \"$g\" && git show-ref | grep ' %s/' | sed 's,.* %s/,,'"
+ % (prefix, prefix)))
+
+def util():
+ r = [fun_desc('_gitdir',
+ "The path to .git, or empty if we're not in a repository.",
+ 'echo "$(git rev-parse --git-dir 2>/dev/null)"'),
+ fun_desc('_current_branch',
+ "Name of the current branch, or empty if there isn't one.",
+ 'local b=$(git symbolic-ref HEAD 2>/dev/null)',
+ 'echo ${b#refs/heads/}'),
+ fun_desc('_other_applied_patches',
+ 'List of all applied patches except the current patch.',
+ 'local b=$(_current_branch)',
+ 'local g=$(_gitdir)',
+ ('test "$g" && cat "$g/patches/$b/applied" | grep -v'
+ ' "^$(tail -n 1 $g/patches/$b/applied 2> /dev/null)$"')),
+ fun('_patch_range', 'local patches="$1"', 'local cur="$2"',
+ 'case "$cur" in', [
+ '*..*)', ['local pfx="${cur%..*}.."', 'cur="${cur#*..}"',
+ 'compgen -P "$pfx" -W "$patches" -- "$cur"', ';;'],
+ '*)', ['compgen -W "$patches" -- "$cur"', ';;']],
+ 'esac'),
+ fun('_stg_branches',
+ 'local g=$(_gitdir)', 'test "$g" && (cd $g/patches/ && echo *)'),
+ ref_list_fun('_all_branches', 'refs/heads'),
+ ref_list_fun('_tags', 'refs/tags'),
+ ref_list_fun('_remotes', 'refs/remotes')]
+ for type in ['applied', 'unapplied', 'hidden']:
+ r.append(patch_list_fun(type))
+ for name, cmd in [('conflicting',
+ r"git ls-files --unmerged | sed 's/.*\t//g' | sort -u"),
+ ('dirty', 'git diff-index --name-only HEAD'),
+ ('unknown', 'git ls-files --others --exclude-standard'),
+ ('known', 'git ls-files')]:
+ r.append(file_list_fun(name, cmd))
+ return flatten(r, '')
+
+def command_list(commands):
+ return ['_stg_commands="%s"\n' % ' '.join(sorted(commands.iterkeys()))]
+
+def command_fun(cmd, modname):
+ mod = stgit.commands.get_command(modname)
+ def cg(args, flags):
+ return argparse.compjoin(list(args) + [argparse.strings(*flags)]
+ ).command('$cur')
+ return fun(
+ '_stg_%s' % cmd,
+ 'local flags="%s"' % ' '.join(sorted(
+ flag for opt in mod.options
+ for flag in opt.flags if flag.startswith('--'))),
+ 'local prev="${COMP_WORDS[COMP_CWORD-1]}"',
+ 'local cur="${COMP_WORDS[COMP_CWORD]}"',
+ 'case "$prev" in', [
+ '%s) COMPREPLY=($(%s)) ;;' % ('|'.join(opt.flags), cg(opt.args, []))
+ for opt in mod.options if opt.args] + [
+ '*) COMPREPLY=($(%s)) ;;' % cg(mod.args, ['$flags'])],
+ 'esac')
+
+def main_switch(commands):
+ return fun(
+ '_stg',
+ 'local i',
+ 'local c=1',
+ 'local command',
+ '',
+ 'while test $c -lt $COMP_CWORD; do', [
+ 'if test $c == 1; then', [
+ 'command="${COMP_WORDS[c]}"'],
+ 'fi',
+ 'c=$((++c))'],
+ 'done',
+ '',
+ ('# Complete name of subcommand if the user has not finished'
+ ' typing it yet.'),
+ 'if test $c -eq $COMP_CWORD -a -z "$command"; then', [
+ ('COMPREPLY=($(compgen -W "$_stg_commands" --'
+ ' "${COMP_WORDS[COMP_CWORD]}"))'),
+ 'return'],
+ 'fi',
+ '',
+ '# Complete arguments to subcommands.',
+ 'case "$command" in', [
+ '%s) _stg_%s ;;' % (cmd, cmd)
+ for cmd in sorted(commands.iterkeys())],
+ 'esac')
+
+def install():
+ return ['complete -o default -F _stg stg']
+
+def write_completion(f):
+ commands = stgit.commands.get_commands(allow_cached = False)
+ r = [["""# -*- shell-script -*-
+# bash completion script for StGit (automatically generated)
+#
+# To use these routines:
+#
+# 1. Copy this file to somewhere (e.g. ~/.stgit-completion.bash).
+#
+# 2. Add the following line to your .bashrc:
+# . ~/.stgit-completion.bash"""]]
+ r += [util(), command_list(commands)]
+ for cmd, (modname, _, _) in sorted(commands.iteritems()):
+ r.append(command_fun(cmd, modname))
+ r += [main_switch(commands), install()]
+ write(f, flatten(r, ''))