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