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