Use canonical command name in help message.
[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 canonical_cmd(self, key):
33 """Return the canonical name for a possibly-shortenned
34 command name.
35 """
36 candidates = [cmd for cmd in self.keys() if cmd.startswith(key)]
37
38 if not candidates:
39 print >> sys.stderr, 'Unknown command: %s' % key
40 print >> sys.stderr, ' Try "%s help" for a list of ' \
41 'supported commands' % prog
42 sys.exit(1)
43 elif len(candidates) > 1:
44 print >> sys.stderr, 'Ambiguous command: %s' % key
45 print >> sys.stderr, ' Candidates are: %s' \
46 % ', '.join(candidates)
47 sys.exit(1)
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 'add': 'add',
63 'applied': 'applied',
64 'assimilate': 'assimilate',
65 'branch': 'branch',
66 'delete': 'delete',
67 'diff': 'diff',
68 'clean': 'clean',
69 'clone': 'clone',
70 'commit': 'commit',
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 'resolved': 'resolved',
92 'rm': 'rm',
93 'series': 'series',
94 'show': 'show',
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 'branch',
106 'clone',
107 'id',
108 'pull'
109 )
110 stackcommands = (
111 'applied',
112 'assimilate',
113 'clean',
114 'commit',
115 'float',
116 'goto',
117 'hide',
118 'init',
119 'pop',
120 'push',
121 'rebase',
122 'series',
123 'top',
124 'unapplied',
125 'uncommit',
126 'unhide'
127 )
128 patchcommands = (
129 'delete',
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 )
143 wccommands = (
144 'add',
145 'diff',
146 'patches',
147 'resolved',
148 'rm',
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(1)
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(0)
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 print >> sys.stderr, '%s help: "%s" command unknown' \
220 % (prog, cmd)
221 sys.exit(1)
222
223 sys.argv[0] += ' %s' % cmd
224 command = commands[cmd]
225 parser = OptionParser(usage = command.usage,
226 option_list = command.options)
227 from pydoc import pager
228 pager(parser.format_help())
229 else:
230 print_help()
231 sys.exit(0)
232 if cmd in ['-v', '--version', 'version']:
233 from stgit.version import version
234 print 'Stacked GIT %s' % version
235 os.system('git --version')
236 print 'Python version %s' % sys.version
237 sys.exit(0)
238 if cmd in ['copyright']:
239 print __copyright__
240 sys.exit(0)
241
242 # re-build the command line arguments
243 sys.argv[0] += ' %s' % commands.canonical_cmd(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
251 # These modules are only used from this point onwards and do not
252 # need to be imported earlier
253 from stgit.config import config_setup
254 from ConfigParser import ParsingError, NoSectionError
255 from stgit.stack import Series, StackException
256 from stgit.git import GitException
257 from stgit.commands.common import CmdException
258 from stgit.gitmergeonefile import GitMergeException
259
260 try:
261 config_setup()
262
263 # 'clone' doesn't expect an already initialised GIT tree. A Series
264 # object will be created after the GIT tree is cloned
265 if cmd != 'clone':
266 if hasattr(options, 'branch') and options.branch:
267 command.crt_series = Series(options.branch)
268 else:
269 command.crt_series = Series()
270 stgit.commands.common.crt_series = command.crt_series
271
272 command.func(parser, options, args)
273 except (IOError, ParsingError, NoSectionError, CmdException,
274 StackException, GitException, GitMergeException), err:
275 print >> sys.stderr, '%s %s: %s' % (prog, cmd, err)
276 sys.exit(2)
277 except KeyboardInterrupt:
278 sys.exit(1)
279
280 sys.exit(0)