git.pretty_commit() re-implemented with "git show" (bug #10018)
[stgit.git] / stgit / main.py
bloba03447fc6b4aee33632a10fbc8bd5f0cce46eb44
1 """Basic quilt-like functionality
2 """
4 __copyright__ = """
5 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
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.
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.
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 """
21 import sys, os
22 from optparse import OptionParser
24 import stgit.commands
25 from stgit.out import *
28 # The commands map
30 class Commands(dict):
31 """Commands class. It performs on-demand module loading
32 """
33 def canonical_cmd(self, key):
34 """Return the canonical name for a possibly-shortenned
35 command name.
36 """
37 candidates = [cmd for cmd in self.keys() if cmd.startswith(key)]
39 if not candidates:
40 out.error('Unknown command: %s' % key,
41 'Try "%s help" for a list of supported commands' % prog)
42 sys.exit(1)
43 elif len(candidates) > 1:
44 out.error('Ambiguous command: %s' % key,
45 'Candidates are: %s' % ', '.join(candidates))
46 sys.exit(1)
48 return candidates[0]
50 def __getitem__(self, key):
51 """Return the command python module name based.
52 """
53 global prog
55 cmd_mod = self.get(key) or self.get(self.canonical_cmd(key))
57 __import__('stgit.commands.' + cmd_mod)
58 return getattr(stgit.commands, cmd_mod)
60 commands = Commands({
61 'add': 'add',
62 'applied': 'applied',
63 'branch': 'branch',
64 'delete': 'delete',
65 'diff': 'diff',
66 'clean': 'clean',
67 'clone': 'clone',
68 'commit': 'commit',
69 'cp': 'copy',
70 'edit': 'edit',
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 'repair': 'repair',
92 'resolved': 'resolved',
93 'rm': 'rm',
94 'series': 'series',
95 'show': 'show',
96 'sink': 'sink',
97 'status': 'status',
98 'sync': 'sync',
99 'top': 'top',
100 'unapplied': 'unapplied',
101 'uncommit': 'uncommit',
102 'unhide': 'unhide'
105 # classification: repository, stack, patch, working copy
106 repocommands = (
107 'clone',
108 'id',
110 stackcommands = (
111 'applied',
112 'branch',
113 'clean',
114 'commit',
115 'float',
116 'goto',
117 'hide',
118 'init',
119 'patches',
120 'pop',
121 'pull',
122 'push',
123 'rebase',
124 'repair',
125 'series',
126 'sink',
127 'top',
128 'unapplied',
129 'uncommit',
130 'unhide',
132 patchcommands = (
133 'delete',
134 'edit',
135 'export',
136 'files',
137 'fold',
138 'import',
139 'log',
140 'mail',
141 'new',
142 'pick',
143 'refresh',
144 'rename',
145 'show',
146 'sync',
148 wccommands = (
149 'add',
150 'cp',
151 'diff',
152 'resolved',
153 'rm',
154 'status',
157 def _print_helpstring(cmd):
158 print ' ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help
160 def print_help():
161 print 'usage: %s <command> [options]' % os.path.basename(sys.argv[0])
162 print
163 print 'Generic commands:'
164 print ' help print the detailed command usage'
165 print ' version display version information'
166 print ' copyright display copyright information'
167 # unclassified commands if any
168 cmds = commands.keys()
169 cmds.sort()
170 for cmd in cmds:
171 if not cmd in repocommands and not cmd in stackcommands \
172 and not cmd in patchcommands and not cmd in wccommands:
173 _print_helpstring(cmd)
174 print
176 print 'Repository commands:'
177 for cmd in repocommands:
178 _print_helpstring(cmd)
179 print
181 print 'Stack commands:'
182 for cmd in stackcommands:
183 _print_helpstring(cmd)
184 print
186 print 'Patch commands:'
187 for cmd in patchcommands:
188 _print_helpstring(cmd)
189 print
191 print 'Working-copy commands:'
192 for cmd in wccommands:
193 _print_helpstring(cmd)
196 # The main function (command dispatcher)
198 def main():
199 """The main function
201 global prog
203 prog = os.path.basename(sys.argv[0])
205 if len(sys.argv) < 2:
206 print >> sys.stderr, 'usage: %s <command>' % prog
207 print >> sys.stderr, \
208 ' Try "%s --help" for a list of supported commands' % prog
209 sys.exit(1)
211 cmd = sys.argv[1]
213 if cmd in ['-h', '--help']:
214 if len(sys.argv) >= 3:
215 cmd = commands.canonical_cmd(sys.argv[2])
216 sys.argv[2] = '--help'
217 else:
218 print_help()
219 sys.exit(0)
220 if cmd == 'help':
221 if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
222 cmd = commands.canonical_cmd(sys.argv[2])
223 if not cmd in commands:
224 out.error('%s help: "%s" command unknown' % (prog, cmd))
225 sys.exit(1)
227 sys.argv[0] += ' %s' % cmd
228 command = commands[cmd]
229 parser = OptionParser(usage = command.usage,
230 option_list = command.options)
231 from pydoc import pager
232 pager(parser.format_help())
233 else:
234 print_help()
235 sys.exit(0)
236 if cmd in ['-v', '--version', 'version']:
237 from stgit.version import version
238 print 'Stacked GIT %s' % version
239 os.system('git --version')
240 print 'Python version %s' % sys.version
241 sys.exit(0)
242 if cmd in ['copyright']:
243 print __copyright__
244 sys.exit(0)
246 # re-build the command line arguments
247 cmd = commands.canonical_cmd(cmd)
248 sys.argv[0] += ' %s' % cmd
249 del(sys.argv[1])
251 command = commands[cmd]
252 usage = command.usage.split('\n')[0].strip()
253 parser = OptionParser(usage = usage, option_list = command.options)
254 options, args = parser.parse_args()
255 directory = command.directory
257 # These modules are only used from this point onwards and do not
258 # need to be imported earlier
259 from stgit.exception import StgException
260 from stgit.config import config_setup
261 from ConfigParser import ParsingError, NoSectionError
262 from stgit.stack import Series
264 try:
265 debug_level = int(os.environ.get('STGIT_DEBUG_LEVEL', 0))
266 except ValueError:
267 out.error('Invalid STGIT_DEBUG_LEVEL environment variable')
268 sys.exit(1)
270 try:
271 directory.setup()
272 config_setup()
274 # Some commands don't (always) need an initialized series.
275 if directory.needs_current_series:
276 if hasattr(options, 'branch') and options.branch:
277 command.crt_series = Series(options.branch)
278 else:
279 command.crt_series = Series()
281 command.func(parser, options, args)
282 except (StgException, IOError, ParsingError, NoSectionError), err:
283 out.error(str(err), title = '%s %s' % (prog, cmd))
284 if debug_level > 0:
285 raise
286 else:
287 sys.exit(2)
288 except KeyboardInterrupt:
289 sys.exit(1)
291 sys.exit(0)