Allow the abbreviation of StGIT commands
[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 __getitem__(self, key):
33 """Return the command python module name based.
34 """
35 global prog
36
37 cmd_mod = self.get(key)
38 if not cmd_mod:
39 candidates = [cmd for cmd in self.keys() if cmd.startswith(key)]
40
41 if not candidates:
42 print >> sys.stderr, 'Unknown command: %s' % key
43 print >> sys.stderr, ' Try "%s help" for a list of ' \
44 'supported commands' % prog
45 sys.exit(1)
46 elif len(candidates) > 1:
47 print >> sys.stderr, 'Ambiguous command: %s' % key
48 print >> sys.stderr, ' Candidates are: %s' \
49 % ', '.join(candidates)
50 sys.exit(1)
51
52 cmd_mod = self.get(candidates[0])
53
54 __import__('stgit.commands.' + cmd_mod)
55 return getattr(stgit.commands, cmd_mod)
56
57 commands = Commands({
58 'add': 'add',
59 'applied': 'applied',
60 'assimilate': 'assimilate',
61 'branch': 'branch',
62 'delete': 'delete',
63 'diff': 'diff',
64 'clean': 'clean',
65 'clone': 'clone',
66 'commit': 'commit',
67 'export': 'export',
68 'files': 'files',
69 'float': 'float',
70 'fold': 'fold',
71 'goto': 'goto',
72 'id': 'id',
73 'import': 'imprt',
74 'init': 'init',
75 'log': 'log',
76 'mail': 'mail',
77 'new': 'new',
78 'patches': 'patches',
79 'pick': 'pick',
80 'pop': 'pop',
81 'pull': 'pull',
82 'push': 'push',
83 'refresh': 'refresh',
84 'rename': 'rename',
85 'resolved': 'resolved',
86 'rm': 'rm',
87 'series': 'series',
88 'show': 'show',
89 'status': 'status',
90 'sync': 'sync',
91 'top': 'top',
92 'unapplied': 'unapplied',
93 'uncommit': 'uncommit'
94 })
95
96 # classification: repository, stack, patch, working copy
97 repocommands = (
98 'branch',
99 'clone',
100 'id',
101 'pull'
102 )
103 stackcommands = (
104 'applied',
105 'assimilate',
106 'clean',
107 'commit',
108 'float',
109 'goto',
110 'init',
111 'pop',
112 'push',
113 'series',
114 'top',
115 'unapplied',
116 'uncommit'
117 )
118 patchcommands = (
119 'delete',
120 'export',
121 'files',
122 'fold',
123 'import',
124 'log',
125 'mail',
126 'new',
127 'pick',
128 'refresh',
129 'rename',
130 'show',
131 'sync'
132 )
133 wccommands = (
134 'add',
135 'diff',
136 'patches',
137 'resolved',
138 'rm',
139 'status'
140 )
141
142 def _print_helpstring(cmd):
143 print ' ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help
144
145 def print_help():
146 print 'usage: %s <command> [options]' % os.path.basename(sys.argv[0])
147 print
148 print 'Generic commands:'
149 print ' help print the detailed command usage'
150 print ' version display version information'
151 print ' copyright display copyright information'
152 # unclassified commands if any
153 cmds = commands.keys()
154 cmds.sort()
155 for cmd in cmds:
156 if not cmd in repocommands and not cmd in stackcommands \
157 and not cmd in patchcommands and not cmd in wccommands:
158 _print_helpstring(cmd)
159 print
160
161 print 'Repository commands:'
162 for cmd in repocommands:
163 _print_helpstring(cmd)
164 print
165
166 print 'Stack commands:'
167 for cmd in stackcommands:
168 _print_helpstring(cmd)
169 print
170
171 print 'Patch commands:'
172 for cmd in patchcommands:
173 _print_helpstring(cmd)
174 print
175
176 print 'Working-copy commands:'
177 for cmd in wccommands:
178 _print_helpstring(cmd)
179
180 #
181 # The main function (command dispatcher)
182 #
183 def main():
184 """The main function
185 """
186 global prog
187
188 prog = os.path.basename(sys.argv[0])
189
190 if len(sys.argv) < 2:
191 print >> sys.stderr, 'usage: %s <command>' % prog
192 print >> sys.stderr, \
193 ' Try "%s --help" for a list of supported commands' % prog
194 sys.exit(1)
195
196 cmd = sys.argv[1]
197
198 if cmd in ['-h', '--help']:
199 if len(sys.argv) >= 3 and sys.argv[2] in commands:
200 cmd = sys.argv[2]
201 sys.argv[2] = '--help'
202 else:
203 print_help()
204 sys.exit(0)
205 if cmd == 'help':
206 if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
207 cmd = sys.argv[2]
208 if not cmd in commands:
209 print >> sys.stderr, '%s help: "%s" command unknown' \
210 % (prog, cmd)
211 sys.exit(1)
212
213 sys.argv[0] += ' %s' % cmd
214 command = commands[cmd]
215 parser = OptionParser(usage = command.usage,
216 option_list = command.options)
217 from pydoc import pager
218 pager(parser.format_help())
219 else:
220 print_help()
221 sys.exit(0)
222 if cmd in ['-v', '--version', 'version']:
223 from stgit.version import version
224 print 'Stacked GIT %s' % version
225 os.system('git --version')
226 print 'Python version %s' % sys.version
227 sys.exit(0)
228 if cmd in ['copyright']:
229 print __copyright__
230 sys.exit(0)
231
232 # re-build the command line arguments
233 sys.argv[0] += ' %s' % cmd
234 del(sys.argv[1])
235
236 command = commands[cmd]
237 usage = command.usage.split('\n')[0].strip()
238 parser = OptionParser(usage = usage, option_list = command.options)
239 options, args = parser.parse_args()
240
241 # These modules are only used from this point onwards and do not
242 # need to be imported earlier
243 from stgit.config import config_setup
244 from ConfigParser import ParsingError, NoSectionError
245 from stgit.stack import Series, StackException
246 from stgit.git import GitException
247 from stgit.commands.common import CmdException
248 from stgit.gitmergeonefile import GitMergeException
249
250 try:
251 config_setup()
252
253 # 'clone' doesn't expect an already initialised GIT tree. A Series
254 # object will be created after the GIT tree is cloned
255 if cmd != 'clone':
256 if hasattr(options, 'branch') and options.branch:
257 command.crt_series = Series(options.branch)
258 else:
259 command.crt_series = Series()
260 stgit.commands.common.crt_series = command.crt_series
261
262 command.func(parser, options, args)
263 except (IOError, ParsingError, NoSectionError, CmdException,
264 StackException, GitException, GitMergeException), err:
265 print >> sys.stderr, '%s %s: %s' % (prog, cmd, err)
266 sys.exit(2)
267 except KeyboardInterrupt:
268 sys.exit(1)
269
270 sys.exit(0)