chiark / gitweb /
Remove the applied/unapplied commands
[stgit] / stgit / main.py
1 """Basic quilt-like functionality
2 """
3
4 __copyright__ = """
5 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 """
20
21 import sys, os, traceback
22 from optparse import OptionParser
23
24 import stgit.commands
25 from stgit.out import *
26 from stgit import run, utils
27
28 #
29 # The commands map
30 #
31 class Commands(dict):
32     """Commands class. It performs on-demand module loading
33     """
34     def canonical_cmd(self, key):
35         """Return the canonical name for a possibly-shortenned
36         command name.
37         """
38         candidates = [cmd for cmd in self.keys() if cmd.startswith(key)]
39
40         if not candidates:
41             out.error('Unknown command: %s' % key,
42                       'Try "%s help" for a list of supported commands' % prog)
43             sys.exit(utils.STGIT_GENERAL_ERROR)
44         elif len(candidates) > 1:
45             out.error('Ambiguous command: %s' % key,
46                       'Candidates are: %s' % ', '.join(candidates))
47             sys.exit(utils.STGIT_GENERAL_ERROR)
48
49         return candidates[0]
50         
51     def __getitem__(self, key):
52         """Return the command python module name based.
53         """
54         global prog
55
56         cmd_mod = self.get(key) or self.get(self.canonical_cmd(key))
57             
58         __import__('stgit.commands.' + cmd_mod)
59         return getattr(stgit.commands, cmd_mod)
60
61 commands = Commands({
62     'branch':           'branch',
63     'delete':           'delete',
64     'diff':             'diff',
65     'clean':            'clean',
66     'clone':            'clone',
67     'coalesce':         'coalesce',
68     'commit':           'commit',
69     'edit':             'edit',
70     'export':           'export',
71     'files':            'files',
72     'float':            'float',
73     'fold':             'fold',
74     'goto':             'goto',
75     'hide':             'hide',
76     'id':               'id',
77     'import':           'imprt',
78     'init':             'init',
79     'log':              'log',
80     'mail':             'mail',
81     'new':              'new',
82     'patches':          'patches',
83     'pick':             'pick',
84     'pop':              'pop',
85     'pull':             'pull',
86     'push':             'push',
87     'rebase':           'rebase',
88     'refresh':          'refresh',
89     'rename':           'rename',
90     'repair':           'repair',
91     'resolved':         'resolved',
92     'series':           'series',
93     'show':             'show',
94     'sink':             'sink',
95     'status':           'status',
96     'sync':             'sync',
97     'top':              'top',
98     'uncommit':         'uncommit',
99     'unhide':           'unhide'
100     })
101
102 # classification: repository, stack, patch, working copy
103 repocommands = (
104     'clone',
105     'id',
106     )
107 stackcommands = (
108     'branch',
109     'clean',
110     'coalesce',
111     'commit',
112     'float',
113     'goto',
114     'hide',
115     'init',
116     'patches',
117     'pop',
118     'pull',
119     'push',
120     'rebase',
121     'repair',
122     'series',
123     'sink',
124     'top',
125     'uncommit',
126     'unhide',
127     )
128 patchcommands = (
129     'delete',
130     'edit',
131     'export',
132     'files',
133     'fold',
134     'import',
135     'log',
136     'mail',
137     'new',
138     'pick',
139     'refresh',
140     'rename',
141     'show',
142     'sync',
143     )
144 wccommands = (
145     'diff',
146     'resolved',
147     'status',
148     )
149
150 def _print_helpstring(cmd):
151     print '  ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help
152     
153 def print_help():
154     print 'usage: %s <command> [options]' % os.path.basename(sys.argv[0])
155     print
156     print 'Generic commands:'
157     print '  help        print the detailed command usage'
158     print '  version     display version information'
159     print '  copyright   display copyright information'
160     # unclassified commands if any
161     cmds = commands.keys()
162     cmds.sort()
163     for cmd in cmds:
164         if not cmd in repocommands and not cmd in stackcommands \
165                and not cmd in patchcommands and not cmd in wccommands:
166             _print_helpstring(cmd)
167     print
168
169     print 'Repository commands:'
170     for cmd in repocommands:
171         _print_helpstring(cmd)
172     print
173     
174     print 'Stack commands:'
175     for cmd in stackcommands:
176         _print_helpstring(cmd)
177     print
178
179     print 'Patch commands:'
180     for cmd in patchcommands:
181         _print_helpstring(cmd)
182     print
183
184     print 'Working-copy commands:'
185     for cmd in wccommands:
186         _print_helpstring(cmd)
187
188 #
189 # The main function (command dispatcher)
190 #
191 def _main():
192     """The main function
193     """
194     global prog
195
196     prog = os.path.basename(sys.argv[0])
197
198     if len(sys.argv) < 2:
199         print >> sys.stderr, 'usage: %s <command>' % prog
200         print >> sys.stderr, \
201               '  Try "%s --help" for a list of supported commands' % prog
202         sys.exit(utils.STGIT_GENERAL_ERROR)
203
204     cmd = sys.argv[1]
205
206     if cmd in ['-h', '--help']:
207         if len(sys.argv) >= 3:
208             cmd = commands.canonical_cmd(sys.argv[2])
209             sys.argv[2] = '--help'
210         else:
211             print_help()
212             sys.exit(utils.STGIT_SUCCESS)
213     if cmd == 'help':
214         if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
215             cmd = commands.canonical_cmd(sys.argv[2])
216             if not cmd in commands:
217                 out.error('%s help: "%s" command unknown' % (prog, cmd))
218                 sys.exit(utils.STGIT_GENERAL_ERROR)
219
220             sys.argv[0] += ' %s' % cmd
221             command = commands[cmd]
222             parser = OptionParser(usage = command.usage,
223                                   option_list = command.options)
224             from pydoc import pager
225             pager(parser.format_help())
226         else:
227             print_help()
228         sys.exit(utils.STGIT_SUCCESS)
229     if cmd in ['-v', '--version', 'version']:
230         from stgit.version import version
231         print 'Stacked GIT %s' % version
232         os.system('git --version')
233         print 'Python version %s' % sys.version
234         sys.exit(utils.STGIT_SUCCESS)
235     if cmd in ['copyright']:
236         print __copyright__
237         sys.exit(utils.STGIT_SUCCESS)
238
239     # re-build the command line arguments
240     cmd = commands.canonical_cmd(cmd)
241     sys.argv[0] += ' %s' % cmd
242     del(sys.argv[1])
243
244     command = commands[cmd]
245     usage = command.usage.split('\n')[0].strip()
246     parser = OptionParser(usage = usage, option_list = command.options)
247     options, args = parser.parse_args()
248     directory = command.directory
249
250     # These modules are only used from this point onwards and do not
251     # need to be imported earlier
252     from stgit.exception import StgException
253     from stgit.config import config_setup
254     from ConfigParser import ParsingError, NoSectionError
255     from stgit.stack import Series
256
257     try:
258         debug_level = int(os.environ.get('STGIT_DEBUG_LEVEL', 0))
259     except ValueError:
260         out.error('Invalid STGIT_DEBUG_LEVEL environment variable')
261         sys.exit(utils.STGIT_GENERAL_ERROR)
262
263     try:
264         directory.setup()
265         config_setup()
266
267         # Some commands don't (always) need an initialized series.
268         if directory.needs_current_series:
269             if hasattr(options, 'branch') and options.branch:
270                 command.crt_series = Series(options.branch)
271             else:
272                 command.crt_series = Series()
273
274         ret = command.func(parser, options, args)
275     except (StgException, IOError, ParsingError, NoSectionError), err:
276         out.error(str(err), title = '%s %s' % (prog, cmd))
277         if debug_level > 0:
278             traceback.print_exc()
279         sys.exit(utils.STGIT_COMMAND_ERROR)
280     except SystemExit:
281         # Triggered by the option parser when it finds bad commandline
282         # parameters.
283         sys.exit(utils.STGIT_COMMAND_ERROR)
284     except KeyboardInterrupt:
285         sys.exit(utils.STGIT_GENERAL_ERROR)
286     except:
287         out.error('Unhandled exception:')
288         traceback.print_exc()
289         sys.exit(utils.STGIT_BUG_ERROR)
290
291     sys.exit(ret or utils.STGIT_SUCCESS)
292
293 def main():
294     try:
295         _main()
296     finally:
297         run.finish_logging()