Use canonical command name in help message.
[stgit] / stgit / main.py
CommitLineData
41a6d859
CM
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
c333afcf 22from optparse import OptionParser
41a6d859 23
c333afcf 24import stgit.commands
41a6d859 25
41a6d859
CM
26#
27# The commands map
28#
c333afcf
CM
29class Commands(dict):
30 """Commands class. It performs on-demand module loading
31 """
78340ff1
YD
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
c333afcf 51 def __getitem__(self, key):
514dd4f2
CM
52 """Return the command python module name based.
53 """
54 global prog
55
78340ff1 56 cmd_mod = self.get(key) or self.get(self.canonical_cmd(key))
514dd4f2 57
c333afcf
CM
58 __import__('stgit.commands.' + cmd_mod)
59 return getattr(stgit.commands, cmd_mod)
60
61commands = 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',
841c7b2a 76 'hide': 'hide',
c333afcf
CM
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',
22037590 88 'rebase': 'rebase',
c333afcf
CM
89 'refresh': 'refresh',
90 'rename': 'rename',
91 'resolved': 'resolved',
92 'rm': 'rm',
93 'series': 'series',
94 'show': 'show',
95 'status': 'status',
06848fab 96 'sync': 'sync',
c333afcf
CM
97 'top': 'top',
98 'unapplied': 'unapplied',
841c7b2a
CM
99 'uncommit': 'uncommit',
100 'unhide': 'unhide'
c333afcf 101 })
41a6d859 102
5dc51fea
YD
103# classification: repository, stack, patch, working copy
104repocommands = (
105 'branch',
106 'clone',
107 'id',
108 'pull'
109 )
110stackcommands = (
111 'applied',
4d0ba818 112 'assimilate',
5dc51fea
YD
113 'clean',
114 'commit',
d98a499c 115 'float',
5dc51fea 116 'goto',
841c7b2a 117 'hide',
5dc51fea
YD
118 'init',
119 'pop',
120 'push',
22037590 121 'rebase',
5dc51fea
YD
122 'series',
123 'top',
124 'unapplied',
841c7b2a
CM
125 'uncommit',
126 'unhide'
5dc51fea
YD
127 )
128patchcommands = (
129 'delete',
130 'export',
131 'files',
132 'fold',
133 'import',
64354a2d 134 'log',
5dc51fea
YD
135 'mail',
136 'new',
137 'pick',
138 'refresh',
139 'rename',
06848fab
CM
140 'show',
141 'sync'
5dc51fea
YD
142 )
143wccommands = (
144 'add',
145 'diff',
146 'patches',
147 'resolved',
148 'rm',
149 'status'
150 )
151
152def _print_helpstring(cmd):
153 print ' ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help
154
41a6d859
CM
155def print_help():
156 print 'usage: %s <command> [options]' % os.path.basename(sys.argv[0])
157 print
5dc51fea 158 print 'Generic commands:'
f0699cc7 159 print ' help print the detailed command usage'
1e0bdf2a 160 print ' version display version information'
22ea9102 161 print ' copyright display copyright information'
5dc51fea 162 # unclassified commands if any
41a6d859
CM
163 cmds = commands.keys()
164 cmds.sort()
165 for cmd in cmds:
5dc51fea
YD
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)
41a6d859
CM
189
190#
191# The main function (command dispatcher)
192#
193def main():
194 """The main function
195 """
514dd4f2
CM
196 global prog
197
41a6d859
CM
198 prog = os.path.basename(sys.argv[0])
199
200 if len(sys.argv) < 2:
d00e708a 201 print >> sys.stderr, 'usage: %s <command>' % prog
41a6d859 202 print >> sys.stderr, \
f0699cc7 203 ' Try "%s --help" for a list of supported commands' % prog
41a6d859
CM
204 sys.exit(1)
205
206 cmd = sys.argv[1]
207
f0699cc7 208 if cmd in ['-h', '--help']:
78340ff1
YD
209 if len(sys.argv) >= 3:
210 cmd = commands.canonical_cmd(sys.argv[2])
f0699cc7 211 sys.argv[2] = '--help'
31c5abf2
PR
212 else:
213 print_help()
214 sys.exit(0)
f0699cc7
CM
215 if cmd == 'help':
216 if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
78340ff1 217 cmd = commands.canonical_cmd(sys.argv[2])
f0699cc7
CM
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)
0d4cd7ce
CM
227 from pydoc import pager
228 pager(parser.format_help())
f0699cc7 229 else:
4fe42e63 230 print_help()
f0699cc7 231 sys.exit(0)
1e0bdf2a 232 if cmd in ['-v', '--version', 'version']:
c333afcf 233 from stgit.version import version
4df2f866 234 print 'Stacked GIT %s' % version
50725547 235 os.system('git --version')
1e0bdf2a 236 print 'Python version %s' % sys.version
41a6d859 237 sys.exit(0)
22ea9102
CM
238 if cmd in ['copyright']:
239 print __copyright__
240 sys.exit(0)
41a6d859
CM
241
242 # re-build the command line arguments
78340ff1 243 sys.argv[0] += ' %s' % commands.canonical_cmd(cmd)
41a6d859
CM
244 del(sys.argv[1])
245
246 command = commands[cmd]
f0699cc7
CM
247 usage = command.usage.split('\n')[0].strip()
248 parser = OptionParser(usage = usage, option_list = command.options)
41a6d859 249 options, args = parser.parse_args()
22d87516
CM
250
251 # These modules are only used from this point onwards and do not
252 # need to be imported earlier
eee7283e 253 from stgit.config import config_setup
9e3f506f 254 from ConfigParser import ParsingError, NoSectionError
22d87516
CM
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
41a6d859 260 try:
eee7283e
CM
261 config_setup()
262
98290387
CM
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:
c333afcf 267 command.crt_series = Series(options.branch)
98290387 268 else:
c333afcf 269 command.crt_series = Series()
98290387 270 stgit.commands.common.crt_series = command.crt_series
9ac09909 271
41a6d859 272 command.func(parser, options, args)
9e3f506f
KH
273 except (IOError, ParsingError, NoSectionError, CmdException,
274 StackException, GitException, GitMergeException), err:
41a6d859
CM
275 print >> sys.stderr, '%s %s: %s' % (prog, cmd, err)
276 sys.exit(2)
4d9fc826
CM
277 except KeyboardInterrupt:
278 sys.exit(1)
41a6d859
CM
279
280 sys.exit(0)