Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
-import sys, os
-from optparse import OptionParser, make_option
+import sys, os, traceback
-from stgit.utils import *
-from stgit import stack, git, gitmergeonefile
-from stgit.version import version
+import stgit.commands
+from stgit.out import *
+from stgit import argparse, run, utils
from stgit.config import config
-from stgit.commands.common import *
-
-# The commands
-import stgit.commands.add
-import stgit.commands.applied
-import stgit.commands.assimilate
-import stgit.commands.branch
-import stgit.commands.delete
-import stgit.commands.diff
-import stgit.commands.clean
-import stgit.commands.clone
-import stgit.commands.commit
-import stgit.commands.export
-import stgit.commands.files
-import stgit.commands.float
-import stgit.commands.fold
-import stgit.commands.goto
-import stgit.commands.id
-import stgit.commands.imprt
-import stgit.commands.init
-import stgit.commands.log
-import stgit.commands.mail
-import stgit.commands.new
-import stgit.commands.patches
-import stgit.commands.pick
-import stgit.commands.pop
-import stgit.commands.pull
-import stgit.commands.push
-import stgit.commands.refresh
-import stgit.commands.rename
-import stgit.commands.resolved
-import stgit.commands.rm
-import stgit.commands.series
-import stgit.commands.show
-import stgit.commands.status
-import stgit.commands.top
-import stgit.commands.unapplied
-import stgit.commands.uncommit
+class CommandAlias(object):
+ def __init__(self, name, command):
+ self.__command = command
+ self.__name__ = name
+ self.usage = ['<arguments>']
+ self.help = 'Alias for "%s <arguments>".' % self.__command
+ self.options = []
+
+ def func(self, args):
+ cmd = self.__command.split() + args
+ p = run.Run(*cmd)
+ p.discard_exitcode().run()
+ return p.exitcode
+
+def is_cmd_alias(cmd):
+ return isinstance(cmd, CommandAlias)
+
+def append_alias_commands(cmd_list):
+ for (name, command) in config.getstartswith('stgit.alias.'):
+ name = utils.strip_prefix('stgit.alias.', name)
+ cmd_list[name] = (CommandAlias(name, command),
+ 'Alias commands', command)
#
# The commands map
#
-commands = {
- 'add': stgit.commands.add,
- 'applied': stgit.commands.applied,
- 'assimilate': stgit.commands.assimilate,
- 'branch': stgit.commands.branch,
- 'delete': stgit.commands.delete,
- 'diff': stgit.commands.diff,
- 'clean': stgit.commands.clean,
- 'clone': stgit.commands.clone,
- 'commit': stgit.commands.commit,
- 'export': stgit.commands.export,
- 'files': stgit.commands.files,
- 'float': stgit.commands.float,
- 'fold': stgit.commands.fold,
- 'goto': stgit.commands.goto,
- 'id': stgit.commands.id,
- 'import': stgit.commands.imprt,
- 'init': stgit.commands.init,
- 'log': stgit.commands.log,
- 'mail': stgit.commands.mail,
- 'new': stgit.commands.new,
- 'patches': stgit.commands.patches,
- 'pick': stgit.commands.pick,
- 'pop': stgit.commands.pop,
- 'pull': stgit.commands.pull,
- 'push': stgit.commands.push,
- 'refresh': stgit.commands.refresh,
- 'rename': stgit.commands.rename,
- 'resolved': stgit.commands.resolved,
- 'rm': stgit.commands.rm,
- 'series': stgit.commands.series,
- 'show': stgit.commands.show,
- 'status': stgit.commands.status,
- 'top': stgit.commands.top,
- 'unapplied':stgit.commands.unapplied,
- 'uncommit': stgit.commands.uncommit,
- }
+class Commands(dict):
+ """Commands class. It performs on-demand module loading
+ """
+ def canonical_cmd(self, key):
+ """Return the canonical name for a possibly-shortenned
+ command name.
+ """
+ candidates = [cmd for cmd in self.keys() if cmd.startswith(key)]
+
+ if not candidates:
+ out.error('Unknown command: %s' % key,
+ 'Try "%s help" for a list of supported commands' % prog)
+ sys.exit(utils.STGIT_GENERAL_ERROR)
+ elif len(candidates) > 1:
+ out.error('Ambiguous command: %s' % key,
+ 'Candidates are: %s' % ', '.join(candidates))
+ sys.exit(utils.STGIT_GENERAL_ERROR)
+
+ return candidates[0]
+
+ def __getitem__(self, key):
+ cmd_mod = self.get(key) or self.get(self.canonical_cmd(key))
+ if is_cmd_alias(cmd_mod):
+ return cmd_mod
+ else:
+ return stgit.commands.get_command(cmd_mod)
-# classification: repository, stack, patch, working copy
-repocommands = (
- 'branch',
- 'clone',
- 'id',
- 'pull'
- )
-stackcommands = (
- 'applied',
- 'assimilate',
- 'clean',
- 'commit',
- 'float',
- 'goto',
- 'init',
- 'pop',
- 'push',
- 'series',
- 'top',
- 'unapplied',
- 'uncommit'
- )
-patchcommands = (
- 'delete',
- 'export',
- 'files',
- 'fold',
- 'import',
- 'log',
- 'mail',
- 'new',
- 'pick',
- 'refresh',
- 'rename',
- 'show'
- )
-wccommands = (
- 'add',
- 'diff',
- 'patches',
- 'resolved',
- 'rm',
- 'status'
- )
+cmd_list = stgit.commands.get_commands()
+append_alias_commands(cmd_list)
+commands = Commands((cmd, mod) for cmd, (mod, kind, help)
+ in cmd_list.iteritems())
-def _print_helpstring(cmd):
- print ' ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help
-
def print_help():
print 'usage: %s <command> [options]' % os.path.basename(sys.argv[0])
print
print ' help print the detailed command usage'
print ' version display version information'
print ' copyright display copyright information'
- # unclassified commands if any
- cmds = commands.keys()
- cmds.sort()
- for cmd in cmds:
- if not cmd in repocommands and not cmd in stackcommands \
- and not cmd in patchcommands and not cmd in wccommands:
- _print_helpstring(cmd)
- print
-
- print 'Repository commands:'
- for cmd in repocommands:
- _print_helpstring(cmd)
- print
-
- print 'Stack commands:'
- for cmd in stackcommands:
- _print_helpstring(cmd)
print
-
- print 'Patch commands:'
- for cmd in patchcommands:
- _print_helpstring(cmd)
- print
-
- print 'Working-copy commands:'
- for cmd in wccommands:
- _print_helpstring(cmd)
+ stgit.commands.pretty_command_list(cmd_list, sys.stdout)
#
# The main function (command dispatcher)
#
-def main():
+def _main():
"""The main function
"""
+ global prog
+
prog = os.path.basename(sys.argv[0])
if len(sys.argv) < 2:
print >> sys.stderr, 'usage: %s <command>' % prog
print >> sys.stderr, \
' Try "%s --help" for a list of supported commands' % prog
- sys.exit(1)
+ sys.exit(utils.STGIT_GENERAL_ERROR)
cmd = sys.argv[1]
if cmd in ['-h', '--help']:
- if len(sys.argv) >= 3 and sys.argv[2] in commands:
- cmd = sys.argv[2]
+ if len(sys.argv) >= 3:
+ cmd = commands.canonical_cmd(sys.argv[2])
sys.argv[2] = '--help'
else:
print_help()
- sys.exit(0)
+ sys.exit(utils.STGIT_SUCCESS)
if cmd == 'help':
if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
- cmd = sys.argv[2]
+ cmd = commands.canonical_cmd(sys.argv[2])
if not cmd in commands:
- print >> sys.stderr, '%s help: "%s" command unknown' \
- % (prog, cmd)
- sys.exit(1)
+ out.error('%s help: "%s" command unknown' % (prog, cmd))
+ sys.exit(utils.STGIT_GENERAL_ERROR)
sys.argv[0] += ' %s' % cmd
command = commands[cmd]
- parser = OptionParser(usage = command.usage,
- option_list = command.options)
- parser.print_help()
+ parser = argparse.make_option_parser(command)
+ if is_cmd_alias(command):
+ parser.remove_option('-h')
+ from pydoc import pager
+ pager(parser.format_help())
else:
print_help()
- sys.exit(0)
+ sys.exit(utils.STGIT_SUCCESS)
if cmd in ['-v', '--version', 'version']:
+ from stgit.version import version
print 'Stacked GIT %s' % version
os.system('git --version')
print 'Python version %s' % sys.version
- sys.exit(0)
+ sys.exit(utils.STGIT_SUCCESS)
if cmd in ['copyright']:
print __copyright__
- sys.exit(0)
- if not cmd in commands:
- print >> sys.stderr, 'Unknown command: %s' % cmd
- print >> sys.stderr, ' Try "%s help" for a list of supported ' \
- 'commands' % prog
- sys.exit(1)
+ sys.exit(utils.STGIT_SUCCESS)
# re-build the command line arguments
+ cmd = commands.canonical_cmd(cmd)
sys.argv[0] += ' %s' % cmd
del(sys.argv[1])
command = commands[cmd]
- usage = command.usage.split('\n')[0].strip()
- parser = OptionParser(usage = usage, option_list = command.options)
+ if is_cmd_alias(command):
+ sys.exit(command.func(sys.argv[1:]))
+
+ parser = argparse.make_option_parser(command)
options, args = parser.parse_args()
+ directory = command.directory
+
+ # These modules are only used from this point onwards and do not
+ # need to be imported earlier
+ from stgit.exception import StgException
+ from stgit.config import config_setup
+ from ConfigParser import ParsingError, NoSectionError
+ from stgit.stack import Series
+
+ try:
+ debug_level = int(os.environ.get('STGIT_DEBUG_LEVEL', 0))
+ except ValueError:
+ out.error('Invalid STGIT_DEBUG_LEVEL environment variable')
+ sys.exit(utils.STGIT_GENERAL_ERROR)
+
try:
- # 'clone' doesn't expect an already initialised GIT tree. A Series
- # object will be created after the GIT tree is cloned
- if cmd != 'clone':
+ directory.setup()
+ config_setup()
+
+ # Some commands don't (always) need an initialized series.
+ if directory.needs_current_series:
if hasattr(options, 'branch') and options.branch:
- command.crt_series = stack.Series(options.branch)
+ command.crt_series = Series(options.branch)
else:
- command.crt_series = stack.Series()
- stgit.commands.common.crt_series = command.crt_series
-
- command.func(parser, options, args)
- except (IOError, CmdException, stack.StackException, git.GitException,
- gitmergeonefile.GitMergeException), err:
- print >> sys.stderr, '%s %s: %s' % (prog, cmd, err)
- sys.exit(2)
+ command.crt_series = Series()
+
+ ret = command.func(parser, options, args)
+ except (StgException, IOError, ParsingError, NoSectionError), err:
+ directory.write_log(cmd)
+ out.error(str(err), title = '%s %s' % (prog, cmd))
+ if debug_level > 0:
+ traceback.print_exc()
+ sys.exit(utils.STGIT_COMMAND_ERROR)
+ except SystemExit:
+ # Triggered by the option parser when it finds bad commandline
+ # parameters.
+ sys.exit(utils.STGIT_COMMAND_ERROR)
except KeyboardInterrupt:
- sys.exit(1)
+ sys.exit(utils.STGIT_GENERAL_ERROR)
+ except:
+ out.error('Unhandled exception:')
+ traceback.print_exc()
+ sys.exit(utils.STGIT_BUG_ERROR)
+
+ directory.write_log(cmd)
+ sys.exit(ret or utils.STGIT_SUCCESS)
- sys.exit(0)
+def main():
+ try:
+ _main()
+ finally:
+ run.finish_logging()