chiark / gitweb /
7701546be27f1540f7b4caed9618de33d0687d0d
[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 from stgit.out import *
26 from stgit import 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     'applied':          'applied',
63     'branch':           'branch',
64     'delete':           'delete',
65     'diff':             'diff',
66     'clean':            'clean',
67     'clone':            'clone',
68     'commit':           'commit',
69     'cp':               'copy',
70     'edit':             'edit',
71     'export':           'export',
72     'files':            'files',
73     'float':            'float',
74     'fold':             'fold',
75     'goto':             'goto',
76     'hide':             'hide',
77     'id':               'id',
78     'import':           'imprt',
79     'init':             'init',
80     'log':              'log',
81     'mail':             'mail',
82     'new':              'new',
83     'patches':          'patches',
84     'pick':             'pick',
85     'pop':              'pop',
86     'pull':             'pull',
87     'push':             'push',
88     'rebase':           'rebase',
89     'refresh':          'refresh',
90     'rename':           'rename',
91     'repair':           'repair',
92     'resolved':         'resolved',
93     'series':           'series',
94     'show':             'show',
95     'sink':             'sink',
96     'status':           'status',
97     'sync':             'sync',
98     'top':              'top',
99     'unapplied':        'unapplied',
100     'uncommit':         'uncommit',
101     'unhide':           'unhide'
102     })
103
104 # classification: repository, stack, patch, working copy
105 repocommands = (
106     'clone',
107     'id',
108     )
109 stackcommands = (
110     'applied',
111     'branch',
112     'clean',
113     'commit',
114     'float',
115     'goto',
116     'hide',
117     'init',
118     'patches',
119     'pop',
120     'pull',
121     'push',
122     'rebase',
123     'repair',
124     'series',
125     'sink',
126     'top',
127     'unapplied',
128     'uncommit',
129     'unhide',
130     )
131 patchcommands = (
132     'delete',
133     'edit',
134     'export',
135     'files',
136     'fold',
137     'import',
138     'log',
139     'mail',
140     'new',
141     'pick',
142     'refresh',
143     'rename',
144     'show',
145     'sync',
146     )
147 wccommands = (
148     'cp',
149     'diff',
150     'resolved',
151     'status',
152     )
153
154 def _print_helpstring(cmd):
155     print '  ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help
156     
157 def print_help():
158     print 'usage: %s <command> [options]' % os.path.basename(sys.argv[0])
159     print
160     print 'Generic commands:'
161     print '  help        print the detailed command usage'
162     print '  version     display version information'
163     print '  copyright   display copyright information'
164     # unclassified commands if any
165     cmds = commands.keys()
166     cmds.sort()
167     for cmd in cmds:
168         if not cmd in repocommands and not cmd in stackcommands \
169                and not cmd in patchcommands and not cmd in wccommands:
170             _print_helpstring(cmd)
171     print
172
173     print 'Repository commands:'
174     for cmd in repocommands:
175         _print_helpstring(cmd)
176     print
177     
178     print 'Stack commands:'
179     for cmd in stackcommands:
180         _print_helpstring(cmd)
181     print
182
183     print 'Patch commands:'
184     for cmd in patchcommands:
185         _print_helpstring(cmd)
186     print
187
188     print 'Working-copy commands:'
189     for cmd in wccommands:
190         _print_helpstring(cmd)
191
192 #
193 # The main function (command dispatcher)
194 #
195 def main():
196     """The main function
197     """
198     global prog
199
200     prog = os.path.basename(sys.argv[0])
201
202     if len(sys.argv) < 2:
203         print >> sys.stderr, 'usage: %s <command>' % prog
204         print >> sys.stderr, \
205               '  Try "%s --help" for a list of supported commands' % prog
206         sys.exit(utils.STGIT_GENERAL_ERROR)
207
208     cmd = sys.argv[1]
209
210     if cmd in ['-h', '--help']:
211         if len(sys.argv) >= 3:
212             cmd = commands.canonical_cmd(sys.argv[2])
213             sys.argv[2] = '--help'
214         else:
215             print_help()
216             sys.exit(utils.STGIT_SUCCESS)
217     if cmd == 'help':
218         if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
219             cmd = commands.canonical_cmd(sys.argv[2])
220             if not cmd in commands:
221                 out.error('%s help: "%s" command unknown' % (prog, cmd))
222                 sys.exit(utils.STGIT_GENERAL_ERROR)
223
224             sys.argv[0] += ' %s' % cmd
225             command = commands[cmd]
226             parser = OptionParser(usage = command.usage,
227                                   option_list = command.options)
228             from pydoc import pager
229             pager(parser.format_help())
230         else:
231             print_help()
232         sys.exit(utils.STGIT_SUCCESS)
233     if cmd in ['-v', '--version', 'version']:
234         from stgit.version import version
235         print 'Stacked GIT %s' % version
236         os.system('git --version')
237         print 'Python version %s' % sys.version
238         sys.exit(utils.STGIT_SUCCESS)
239     if cmd in ['copyright']:
240         print __copyright__
241         sys.exit(utils.STGIT_SUCCESS)
242
243     # re-build the command line arguments
244     cmd = commands.canonical_cmd(cmd)
245     sys.argv[0] += ' %s' % cmd
246     del(sys.argv[1])
247
248     command = commands[cmd]
249     usage = command.usage.split('\n')[0].strip()
250     parser = OptionParser(usage = usage, option_list = command.options)
251     options, args = parser.parse_args()
252     directory = command.directory
253
254     # These modules are only used from this point onwards and do not
255     # need to be imported earlier
256     from stgit.exception import StgException
257     from stgit.config import config_setup
258     from ConfigParser import ParsingError, NoSectionError
259     from stgit.stack import Series
260
261     try:
262         debug_level = int(os.environ.get('STGIT_DEBUG_LEVEL', 0))
263     except ValueError:
264         out.error('Invalid STGIT_DEBUG_LEVEL environment variable')
265         sys.exit(utils.STGIT_GENERAL_ERROR)
266
267     try:
268         directory.setup()
269         config_setup()
270
271         # Some commands don't (always) need an initialized series.
272         if directory.needs_current_series:
273             if hasattr(options, 'branch') and options.branch:
274                 command.crt_series = Series(options.branch)
275             else:
276                 command.crt_series = Series()
277
278         command.func(parser, options, args)
279     except (StgException, IOError, ParsingError, NoSectionError), err:
280         out.error(str(err), title = '%s %s' % (prog, cmd))
281         if debug_level > 0:
282             raise
283         else:
284             sys.exit(utils.STGIT_COMMAND_ERROR)
285     except KeyboardInterrupt:
286         sys.exit(utils.STGIT_GENERAL_ERROR)
287
288     sys.exit(utils.STGIT_SUCCESS)