Add patch editing command
[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
5e888f30 25from stgit.out import *
41a6d859 26
41a6d859
CM
27#
28# The commands map
29#
c333afcf
CM
30class Commands(dict):
31 """Commands class. It performs on-demand module loading
32 """
78340ff1
YD
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)]
38
39 if not candidates:
27ac2b7e
KH
40 out.error('Unknown command: %s' % key,
41 'Try "%s help" for a list of supported commands' % prog)
78340ff1
YD
42 sys.exit(1)
43 elif len(candidates) > 1:
27ac2b7e
KH
44 out.error('Ambiguous command: %s' % key,
45 'Candidates are: %s' % ', '.join(candidates))
78340ff1
YD
46 sys.exit(1)
47
48 return candidates[0]
49
c333afcf 50 def __getitem__(self, key):
514dd4f2
CM
51 """Return the command python module name based.
52 """
53 global prog
54
78340ff1 55 cmd_mod = self.get(key) or self.get(self.canonical_cmd(key))
514dd4f2 56
c333afcf
CM
57 __import__('stgit.commands.' + cmd_mod)
58 return getattr(stgit.commands, cmd_mod)
59
60commands = Commands({
61 'add': 'add',
62 'applied': 'applied',
63 'assimilate': 'assimilate',
64 'branch': 'branch',
65 'delete': 'delete',
66 'diff': 'diff',
67 'clean': 'clean',
68 'clone': 'clone',
69 'commit': 'commit',
d5ae2173 70 'cp': 'copy',
ed60fdae 71 'edit': 'edit',
c333afcf
CM
72 'export': 'export',
73 'files': 'files',
74 'float': 'float',
75 'fold': 'fold',
76 'goto': 'goto',
841c7b2a 77 'hide': 'hide',
c333afcf
CM
78 'id': 'id',
79 'import': 'imprt',
80 'init': 'init',
81 'log': 'log',
82 'mail': 'mail',
83 'new': 'new',
84 'patches': 'patches',
85 'pick': 'pick',
86 'pop': 'pop',
87 'pull': 'pull',
88 'push': 'push',
22037590 89 'rebase': 'rebase',
c333afcf
CM
90 'refresh': 'refresh',
91 'rename': 'rename',
92 'resolved': 'resolved',
93 'rm': 'rm',
94 'series': 'series',
95 'show': 'show',
6f1c5e3c 96 'sink': 'sink',
c333afcf 97 'status': 'status',
06848fab 98 'sync': 'sync',
c333afcf
CM
99 'top': 'top',
100 'unapplied': 'unapplied',
841c7b2a
CM
101 'uncommit': 'uncommit',
102 'unhide': 'unhide'
c333afcf 103 })
41a6d859 104
5dc51fea
YD
105# classification: repository, stack, patch, working copy
106repocommands = (
5dc51fea
YD
107 'clone',
108 'id',
5dc51fea
YD
109 )
110stackcommands = (
111 'applied',
4d0ba818 112 'assimilate',
4ec67741 113 'branch',
5dc51fea
YD
114 'clean',
115 'commit',
d98a499c 116 'float',
5dc51fea 117 'goto',
841c7b2a 118 'hide',
5dc51fea 119 'init',
4ec67741 120 'patches',
5dc51fea 121 'pop',
4ec67741 122 'pull',
5dc51fea 123 'push',
22037590 124 'rebase',
5dc51fea 125 'series',
6f1c5e3c 126 'sink',
5dc51fea
YD
127 'top',
128 'unapplied',
841c7b2a 129 'uncommit',
4ec67741 130 'unhide',
5dc51fea
YD
131 )
132patchcommands = (
133 'delete',
134 'export',
135 'files',
136 'fold',
137 'import',
64354a2d 138 'log',
5dc51fea
YD
139 'mail',
140 'new',
141 'pick',
142 'refresh',
143 'rename',
06848fab 144 'show',
4ec67741 145 'sync',
5dc51fea
YD
146 )
147wccommands = (
148 'add',
d5ae2173 149 'cp',
5dc51fea 150 'diff',
5dc51fea
YD
151 'resolved',
152 'rm',
4ec67741 153 'status',
5dc51fea
YD
154 )
155
156def _print_helpstring(cmd):
157 print ' ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help
158
41a6d859
CM
159def print_help():
160 print 'usage: %s <command> [options]' % os.path.basename(sys.argv[0])
161 print
5dc51fea 162 print 'Generic commands:'
f0699cc7 163 print ' help print the detailed command usage'
1e0bdf2a 164 print ' version display version information'
22ea9102 165 print ' copyright display copyright information'
5dc51fea 166 # unclassified commands if any
41a6d859
CM
167 cmds = commands.keys()
168 cmds.sort()
169 for cmd in cmds:
5dc51fea
YD
170 if not cmd in repocommands and not cmd in stackcommands \
171 and not cmd in patchcommands and not cmd in wccommands:
172 _print_helpstring(cmd)
173 print
174
175 print 'Repository commands:'
176 for cmd in repocommands:
177 _print_helpstring(cmd)
178 print
179
180 print 'Stack commands:'
181 for cmd in stackcommands:
182 _print_helpstring(cmd)
183 print
184
185 print 'Patch commands:'
186 for cmd in patchcommands:
187 _print_helpstring(cmd)
188 print
189
190 print 'Working-copy commands:'
191 for cmd in wccommands:
192 _print_helpstring(cmd)
41a6d859
CM
193
194#
195# The main function (command dispatcher)
196#
197def main():
198 """The main function
199 """
514dd4f2
CM
200 global prog
201
41a6d859
CM
202 prog = os.path.basename(sys.argv[0])
203
204 if len(sys.argv) < 2:
d00e708a 205 print >> sys.stderr, 'usage: %s <command>' % prog
41a6d859 206 print >> sys.stderr, \
f0699cc7 207 ' Try "%s --help" for a list of supported commands' % prog
41a6d859
CM
208 sys.exit(1)
209
210 cmd = sys.argv[1]
211
f0699cc7 212 if cmd in ['-h', '--help']:
78340ff1
YD
213 if len(sys.argv) >= 3:
214 cmd = commands.canonical_cmd(sys.argv[2])
f0699cc7 215 sys.argv[2] = '--help'
31c5abf2
PR
216 else:
217 print_help()
218 sys.exit(0)
f0699cc7
CM
219 if cmd == 'help':
220 if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
78340ff1 221 cmd = commands.canonical_cmd(sys.argv[2])
f0699cc7 222 if not cmd in commands:
27ac2b7e 223 out.error('%s help: "%s" command unknown' % (prog, cmd))
f0699cc7
CM
224 sys.exit(1)
225
226 sys.argv[0] += ' %s' % cmd
227 command = commands[cmd]
228 parser = OptionParser(usage = command.usage,
229 option_list = command.options)
0d4cd7ce
CM
230 from pydoc import pager
231 pager(parser.format_help())
f0699cc7 232 else:
4fe42e63 233 print_help()
f0699cc7 234 sys.exit(0)
1e0bdf2a 235 if cmd in ['-v', '--version', 'version']:
c333afcf 236 from stgit.version import version
4df2f866 237 print 'Stacked GIT %s' % version
50725547 238 os.system('git --version')
1e0bdf2a 239 print 'Python version %s' % sys.version
41a6d859 240 sys.exit(0)
22ea9102
CM
241 if cmd in ['copyright']:
242 print __copyright__
243 sys.exit(0)
41a6d859
CM
244
245 # re-build the command line arguments
3faeaeb1
CM
246 cmd = commands.canonical_cmd(cmd)
247 sys.argv[0] += ' %s' % cmd
41a6d859
CM
248 del(sys.argv[1])
249
250 command = commands[cmd]
f0699cc7
CM
251 usage = command.usage.split('\n')[0].strip()
252 parser = OptionParser(usage = usage, option_list = command.options)
41a6d859 253 options, args = parser.parse_args()
22d87516
CM
254
255 # These modules are only used from this point onwards and do not
256 # need to be imported earlier
eee7283e 257 from stgit.config import config_setup
9e3f506f 258 from ConfigParser import ParsingError, NoSectionError
22d87516
CM
259 from stgit.stack import Series, StackException
260 from stgit.git import GitException
261 from stgit.commands.common import CmdException
262 from stgit.gitmergeonefile import GitMergeException
b3ae3e14 263 from stgit.utils import EditorException
22d87516 264
41a6d859 265 try:
b2017c38
CM
266 debug_level = int(os.environ['STGIT_DEBUG_LEVEL'])
267 except KeyError:
268 debug_level = 0
269 except ValueError:
27ac2b7e 270 out.error('Invalid STGIT_DEBUG_LEVEL environment variable')
b2017c38
CM
271 sys.exit(1)
272
273 try:
eee7283e
CM
274 config_setup()
275
98290387
CM
276 # 'clone' doesn't expect an already initialised GIT tree. A Series
277 # object will be created after the GIT tree is cloned
278 if cmd != 'clone':
279 if hasattr(options, 'branch') and options.branch:
c333afcf 280 command.crt_series = Series(options.branch)
98290387 281 else:
c333afcf 282 command.crt_series = Series()
98290387 283 stgit.commands.common.crt_series = command.crt_series
9ac09909 284
41a6d859 285 command.func(parser, options, args)
9e3f506f 286 except (IOError, ParsingError, NoSectionError, CmdException,
b3ae3e14
CM
287 StackException, GitException, GitMergeException,
288 EditorException), err:
41a6d859 289 print >> sys.stderr, '%s %s: %s' % (prog, cmd, err)
fe104619 290 if debug_level > 0:
b2017c38
CM
291 raise
292 else:
293 sys.exit(2)
4d9fc826
CM
294 except KeyboardInterrupt:
295 sys.exit(1)
41a6d859
CM
296
297 sys.exit(0)