Merge branch 'stable'
[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
08986eda 21import sys, os, traceback
c333afcf 22from optparse import OptionParser
41a6d859 23
c333afcf 24import stgit.commands
5e888f30 25from stgit.out import *
36a06e01 26from stgit import run, utils
41a6d859 27
41a6d859
CM
28#
29# The commands map
30#
c333afcf
CM
31class Commands(dict):
32 """Commands class. It performs on-demand module loading
33 """
78340ff1
YD
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:
27ac2b7e
KH
41 out.error('Unknown command: %s' % key,
42 'Try "%s help" for a list of supported commands' % prog)
5be69cd8 43 sys.exit(utils.STGIT_GENERAL_ERROR)
78340ff1 44 elif len(candidates) > 1:
27ac2b7e
KH
45 out.error('Ambiguous command: %s' % key,
46 'Candidates are: %s' % ', '.join(candidates))
5be69cd8 47 sys.exit(utils.STGIT_GENERAL_ERROR)
78340ff1
YD
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({
c333afcf
CM
62 'branch': 'branch',
63 'delete': 'delete',
64 'diff': 'diff',
65 'clean': 'clean',
66 'clone': 'clone',
48b209cd 67 'coalesce': 'coalesce',
c333afcf 68 'commit': 'commit',
ed60fdae 69 'edit': 'edit',
c333afcf
CM
70 'export': 'export',
71 'files': 'files',
72 'float': 'float',
73 'fold': 'fold',
74 'goto': 'goto',
841c7b2a 75 'hide': 'hide',
c333afcf
CM
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',
22037590 87 'rebase': 'rebase',
c333afcf
CM
88 'refresh': 'refresh',
89 'rename': 'rename',
051090dd 90 'repair': 'repair',
c333afcf 91 'resolved': 'resolved',
c333afcf
CM
92 'series': 'series',
93 'show': 'show',
6f1c5e3c 94 'sink': 'sink',
c333afcf 95 'status': 'status',
06848fab 96 'sync': 'sync',
c333afcf 97 'top': 'top',
841c7b2a
CM
98 'uncommit': 'uncommit',
99 'unhide': 'unhide'
c333afcf 100 })
41a6d859 101
5dc51fea
YD
102# classification: repository, stack, patch, working copy
103repocommands = (
5dc51fea
YD
104 'clone',
105 'id',
5dc51fea
YD
106 )
107stackcommands = (
4ec67741 108 'branch',
5dc51fea 109 'clean',
48b209cd 110 'coalesce',
5dc51fea 111 'commit',
d98a499c 112 'float',
5dc51fea 113 'goto',
841c7b2a 114 'hide',
5dc51fea 115 'init',
4ec67741 116 'patches',
5dc51fea 117 'pop',
4ec67741 118 'pull',
5dc51fea 119 'push',
22037590 120 'rebase',
051090dd 121 'repair',
5dc51fea 122 'series',
6f1c5e3c 123 'sink',
5dc51fea 124 'top',
841c7b2a 125 'uncommit',
4ec67741 126 'unhide',
5dc51fea
YD
127 )
128patchcommands = (
129 'delete',
d8b2e601 130 'edit',
5dc51fea
YD
131 'export',
132 'files',
133 'fold',
134 'import',
64354a2d 135 'log',
5dc51fea
YD
136 'mail',
137 'new',
138 'pick',
139 'refresh',
140 'rename',
06848fab 141 'show',
4ec67741 142 'sync',
5dc51fea
YD
143 )
144wccommands = (
5dc51fea 145 'diff',
5dc51fea 146 'resolved',
4ec67741 147 'status',
5dc51fea
YD
148 )
149
150def _print_helpstring(cmd):
151 print ' ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help
152
41a6d859
CM
153def print_help():
154 print 'usage: %s <command> [options]' % os.path.basename(sys.argv[0])
155 print
5dc51fea 156 print 'Generic commands:'
f0699cc7 157 print ' help print the detailed command usage'
1e0bdf2a 158 print ' version display version information'
22ea9102 159 print ' copyright display copyright information'
5dc51fea 160 # unclassified commands if any
41a6d859
CM
161 cmds = commands.keys()
162 cmds.sort()
163 for cmd in cmds:
5dc51fea
YD
164 if not cmd in repocommands and not cmd in stackcommands \
165 and not cmd in patchcommands and not cmd in wccommands:
166 _print_helpstring(cmd)
167 print
168
169 print 'Repository commands:'
170 for cmd in repocommands:
171 _print_helpstring(cmd)
172 print
173
174 print 'Stack commands:'
175 for cmd in stackcommands:
176 _print_helpstring(cmd)
177 print
178
179 print 'Patch commands:'
180 for cmd in patchcommands:
181 _print_helpstring(cmd)
182 print
183
184 print 'Working-copy commands:'
185 for cmd in wccommands:
186 _print_helpstring(cmd)
41a6d859
CM
187
188#
189# The main function (command dispatcher)
190#
36a06e01 191def _main():
41a6d859
CM
192 """The main function
193 """
514dd4f2
CM
194 global prog
195
41a6d859
CM
196 prog = os.path.basename(sys.argv[0])
197
198 if len(sys.argv) < 2:
d00e708a 199 print >> sys.stderr, 'usage: %s <command>' % prog
41a6d859 200 print >> sys.stderr, \
f0699cc7 201 ' Try "%s --help" for a list of supported commands' % prog
5be69cd8 202 sys.exit(utils.STGIT_GENERAL_ERROR)
41a6d859
CM
203
204 cmd = sys.argv[1]
205
f0699cc7 206 if cmd in ['-h', '--help']:
78340ff1
YD
207 if len(sys.argv) >= 3:
208 cmd = commands.canonical_cmd(sys.argv[2])
f0699cc7 209 sys.argv[2] = '--help'
31c5abf2
PR
210 else:
211 print_help()
5be69cd8 212 sys.exit(utils.STGIT_SUCCESS)
f0699cc7
CM
213 if cmd == 'help':
214 if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
78340ff1 215 cmd = commands.canonical_cmd(sys.argv[2])
f0699cc7 216 if not cmd in commands:
27ac2b7e 217 out.error('%s help: "%s" command unknown' % (prog, cmd))
5be69cd8 218 sys.exit(utils.STGIT_GENERAL_ERROR)
f0699cc7
CM
219
220 sys.argv[0] += ' %s' % cmd
221 command = commands[cmd]
222 parser = OptionParser(usage = command.usage,
223 option_list = command.options)
0d4cd7ce
CM
224 from pydoc import pager
225 pager(parser.format_help())
f0699cc7 226 else:
4fe42e63 227 print_help()
5be69cd8 228 sys.exit(utils.STGIT_SUCCESS)
1e0bdf2a 229 if cmd in ['-v', '--version', 'version']:
c333afcf 230 from stgit.version import version
4df2f866 231 print 'Stacked GIT %s' % version
50725547 232 os.system('git --version')
1e0bdf2a 233 print 'Python version %s' % sys.version
5be69cd8 234 sys.exit(utils.STGIT_SUCCESS)
22ea9102
CM
235 if cmd in ['copyright']:
236 print __copyright__
5be69cd8 237 sys.exit(utils.STGIT_SUCCESS)
41a6d859
CM
238
239 # re-build the command line arguments
3faeaeb1
CM
240 cmd = commands.canonical_cmd(cmd)
241 sys.argv[0] += ' %s' % cmd
41a6d859
CM
242 del(sys.argv[1])
243
244 command = commands[cmd]
f0699cc7
CM
245 usage = command.usage.split('\n')[0].strip()
246 parser = OptionParser(usage = usage, option_list = command.options)
41a6d859 247 options, args = parser.parse_args()
6dd8fafa 248 directory = command.directory
22d87516
CM
249
250 # These modules are only used from this point onwards and do not
251 # need to be imported earlier
87c93eab 252 from stgit.exception import StgException
eee7283e 253 from stgit.config import config_setup
9e3f506f 254 from ConfigParser import ParsingError, NoSectionError
87c93eab 255 from stgit.stack import Series
22d87516 256
41a6d859 257 try:
4200335f 258 debug_level = int(os.environ.get('STGIT_DEBUG_LEVEL', 0))
b2017c38 259 except ValueError:
27ac2b7e 260 out.error('Invalid STGIT_DEBUG_LEVEL environment variable')
5be69cd8 261 sys.exit(utils.STGIT_GENERAL_ERROR)
b2017c38
CM
262
263 try:
6dd8fafa 264 directory.setup()
eee7283e
CM
265 config_setup()
266
44a01a58
KH
267 # Some commands don't (always) need an initialized series.
268 if directory.needs_current_series:
98290387 269 if hasattr(options, 'branch') and options.branch:
c333afcf 270 command.crt_series = Series(options.branch)
98290387 271 else:
c333afcf 272 command.crt_series = Series()
9ac09909 273
f9cc5e69 274 ret = command.func(parser, options, args)
87c93eab 275 except (StgException, IOError, ParsingError, NoSectionError), err:
762f6c8c 276 out.error(str(err), title = '%s %s' % (prog, cmd))
fe104619 277 if debug_level > 0:
08986eda
KH
278 traceback.print_exc()
279 sys.exit(utils.STGIT_COMMAND_ERROR)
f3167489
KH
280 except SystemExit:
281 # Triggered by the option parser when it finds bad commandline
282 # parameters.
283 sys.exit(utils.STGIT_COMMAND_ERROR)
4d9fc826 284 except KeyboardInterrupt:
5be69cd8 285 sys.exit(utils.STGIT_GENERAL_ERROR)
08986eda
KH
286 except:
287 out.error('Unhandled exception:')
288 traceback.print_exc()
289 sys.exit(utils.STGIT_BUG_ERROR)
41a6d859 290
f9cc5e69 291 sys.exit(ret or utils.STGIT_SUCCESS)
36a06e01
KH
292
293def main():
294 try:
295 _main()
296 finally:
297 run.finish_logging()