chiark / gitweb /
Allow the abbreviation of StGIT 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
22 from optparse import OptionParser
23
24 import stgit.commands
25
26 #
27 # The commands map
28 #
29 class Commands(dict):
30     """Commands class. It performs on-demand module loading
31     """
32     def __getitem__(self, key):
33         """Return the command python module name based.
34         """
35         global prog
36
37         cmd_mod = self.get(key)
38         if not cmd_mod:
39             candidates = [cmd for cmd in self.keys() if cmd.startswith(key)]
40
41             if not candidates:
42                 print >> sys.stderr, 'Unknown command: %s' % key
43                 print >> sys.stderr, '  Try "%s help" for a list of ' \
44                       'supported commands' % prog
45                 sys.exit(1)
46             elif len(candidates) > 1:
47                 print >> sys.stderr, 'Ambiguous command: %s' % key
48                 print >> sys.stderr, '  Candidates are: %s' \
49                       % ', '.join(candidates)
50                 sys.exit(1)
51
52             cmd_mod = self.get(candidates[0])
53             
54         __import__('stgit.commands.' + cmd_mod)
55         return getattr(stgit.commands, cmd_mod)
56
57 commands = Commands({
58     'add':              'add',
59     'applied':          'applied',
60     'assimilate':       'assimilate',
61     'branch':           'branch',
62     'delete':           'delete',
63     'diff':             'diff',
64     'clean':            'clean',
65     'clone':            'clone',
66     'commit':           'commit',
67     'export':           'export',
68     'files':            'files',
69     'float':            'float',
70     'fold':             'fold',
71     'goto':             'goto',
72     'id':               'id',
73     'import':           'imprt',
74     'init':             'init',
75     'log':              'log',
76     'mail':             'mail',
77     'new':              'new',
78     'patches':          'patches',
79     'pick':             'pick',
80     'pop':              'pop',
81     'pull':             'pull',
82     'push':             'push',
83     'refresh':          'refresh',
84     'rename':           'rename',
85     'resolved':         'resolved',
86     'rm':               'rm',
87     'series':           'series',
88     'show':             'show',
89     'status':           'status',
90     'sync':             'sync',
91     'top':              'top',
92     'unapplied':        'unapplied',
93     'uncommit':         'uncommit'
94     })
95
96 # classification: repository, stack, patch, working copy
97 repocommands = (
98     'branch',
99     'clone',
100     'id',
101     'pull'
102     )
103 stackcommands = (
104     'applied',
105     'assimilate',
106     'clean',
107     'commit',
108     'float',
109     'goto',
110     'init',
111     'pop',
112     'push',
113     'series',
114     'top',
115     'unapplied',
116     'uncommit'
117     )
118 patchcommands = (
119     'delete',
120     'export',
121     'files',
122     'fold',
123     'import',
124     'log',
125     'mail',
126     'new',
127     'pick',
128     'refresh',
129     'rename',
130     'show',
131     'sync'
132     )
133 wccommands = (
134     'add',
135     'diff',
136     'patches',
137     'resolved',
138     'rm',
139     'status'
140     )
141
142 def _print_helpstring(cmd):
143     print '  ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help
144     
145 def print_help():
146     print 'usage: %s <command> [options]' % os.path.basename(sys.argv[0])
147     print
148     print 'Generic commands:'
149     print '  help        print the detailed command usage'
150     print '  version     display version information'
151     print '  copyright   display copyright information'
152     # unclassified commands if any
153     cmds = commands.keys()
154     cmds.sort()
155     for cmd in cmds:
156         if not cmd in repocommands and not cmd in stackcommands \
157                and not cmd in patchcommands and not cmd in wccommands:
158             _print_helpstring(cmd)
159     print
160
161     print 'Repository commands:'
162     for cmd in repocommands:
163         _print_helpstring(cmd)
164     print
165     
166     print 'Stack commands:'
167     for cmd in stackcommands:
168         _print_helpstring(cmd)
169     print
170
171     print 'Patch commands:'
172     for cmd in patchcommands:
173         _print_helpstring(cmd)
174     print
175
176     print 'Working-copy commands:'
177     for cmd in wccommands:
178         _print_helpstring(cmd)
179
180 #
181 # The main function (command dispatcher)
182 #
183 def main():
184     """The main function
185     """
186     global prog
187
188     prog = os.path.basename(sys.argv[0])
189
190     if len(sys.argv) < 2:
191         print >> sys.stderr, 'usage: %s <command>' % prog
192         print >> sys.stderr, \
193               '  Try "%s --help" for a list of supported commands' % prog
194         sys.exit(1)
195
196     cmd = sys.argv[1]
197
198     if cmd in ['-h', '--help']:
199         if len(sys.argv) >= 3 and sys.argv[2] in commands:
200             cmd = sys.argv[2]
201             sys.argv[2] = '--help'
202         else:
203             print_help()
204             sys.exit(0)
205     if cmd == 'help':
206         if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
207             cmd = sys.argv[2]
208             if not cmd in commands:
209                 print >> sys.stderr, '%s help: "%s" command unknown' \
210                       % (prog, cmd)
211                 sys.exit(1)
212
213             sys.argv[0] += ' %s' % cmd
214             command = commands[cmd]
215             parser = OptionParser(usage = command.usage,
216                                   option_list = command.options)
217             from pydoc import pager
218             pager(parser.format_help())
219         else:
220             print_help()
221         sys.exit(0)
222     if cmd in ['-v', '--version', 'version']:
223         from stgit.version import version
224         print 'Stacked GIT %s' % version
225         os.system('git --version')
226         print 'Python version %s' % sys.version
227         sys.exit(0)
228     if cmd in ['copyright']:
229         print __copyright__
230         sys.exit(0)
231
232     # re-build the command line arguments
233     sys.argv[0] += ' %s' % cmd
234     del(sys.argv[1])
235
236     command = commands[cmd]
237     usage = command.usage.split('\n')[0].strip()
238     parser = OptionParser(usage = usage, option_list = command.options)
239     options, args = parser.parse_args()
240
241     # These modules are only used from this point onwards and do not
242     # need to be imported earlier
243     from stgit.config import config_setup
244     from ConfigParser import ParsingError, NoSectionError
245     from stgit.stack import Series, StackException
246     from stgit.git import GitException
247     from stgit.commands.common import CmdException
248     from stgit.gitmergeonefile import GitMergeException
249
250     try:
251         config_setup()
252
253         # 'clone' doesn't expect an already initialised GIT tree. A Series
254         # object will be created after the GIT tree is cloned
255         if cmd != 'clone':
256             if hasattr(options, 'branch') and options.branch:
257                 command.crt_series = Series(options.branch)
258             else:
259                 command.crt_series = Series()
260             stgit.commands.common.crt_series = command.crt_series
261
262         command.func(parser, options, args)
263     except (IOError, ParsingError, NoSectionError, CmdException,
264             StackException, GitException, GitMergeException), err:
265         print >> sys.stderr, '%s %s: %s' % (prog, cmd, err)
266         sys.exit(2)
267     except KeyboardInterrupt:
268         sys.exit(1)
269
270     sys.exit(0)