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