chiark / gitweb /
Failed recursive merging can silently lose date in StGIT
[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
41a6d859 25
41a6d859
CM
26#
27# The commands map
28#
c333afcf
CM
29class Commands(dict):
30 """Commands class. It performs on-demand module loading
31 """
78340ff1
YD
32 def canonical_cmd(self, key):
33 """Return the canonical name for a possibly-shortenned
34 command name.
35 """
36 candidates = [cmd for cmd in self.keys() if cmd.startswith(key)]
37
38 if not candidates:
39 print >> sys.stderr, 'Unknown command: %s' % key
40 print >> sys.stderr, ' Try "%s help" for a list of ' \
41 'supported commands' % prog
42 sys.exit(1)
43 elif len(candidates) > 1:
44 print >> sys.stderr, 'Ambiguous command: %s' % key
45 print >> sys.stderr, ' Candidates are: %s' \
46 % ', '.join(candidates)
47 sys.exit(1)
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({
62 'add': 'add',
63 'applied': 'applied',
64 'assimilate': 'assimilate',
65 'branch': 'branch',
de7a79c4 66 'bury': 'bury',
c333afcf
CM
67 'delete': 'delete',
68 'diff': 'diff',
69 'clean': 'clean',
70 'clone': 'clone',
71 'commit': 'commit',
d5ae2173 72 'cp': 'copy',
c333afcf
CM
73 'export': 'export',
74 'files': 'files',
75 'float': 'float',
76 'fold': 'fold',
77 'goto': 'goto',
841c7b2a 78 'hide': 'hide',
c333afcf
CM
79 'id': 'id',
80 'import': 'imprt',
81 'init': 'init',
82 'log': 'log',
83 'mail': 'mail',
84 'new': 'new',
85 'patches': 'patches',
86 'pick': 'pick',
87 'pop': 'pop',
88 'pull': 'pull',
89 'push': 'push',
22037590 90 'rebase': 'rebase',
c333afcf
CM
91 'refresh': 'refresh',
92 'rename': 'rename',
93 'resolved': 'resolved',
94 'rm': 'rm',
95 'series': 'series',
96 'show': 'show',
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',
de7a79c4 114 'bury',
5dc51fea
YD
115 'clean',
116 'commit',
d98a499c 117 'float',
5dc51fea 118 'goto',
841c7b2a 119 'hide',
5dc51fea 120 'init',
4ec67741 121 'patches',
5dc51fea 122 'pop',
4ec67741 123 'pull',
5dc51fea 124 'push',
22037590 125 'rebase',
5dc51fea
YD
126 'series',
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
CM
222 if not cmd in commands:
223 print >> sys.stderr, '%s help: "%s" command unknown' \
224 % (prog, cmd)
225 sys.exit(1)
226
227 sys.argv[0] += ' %s' % cmd
228 command = commands[cmd]
229 parser = OptionParser(usage = command.usage,
230 option_list = command.options)
0d4cd7ce
CM
231 from pydoc import pager
232 pager(parser.format_help())
f0699cc7 233 else:
4fe42e63 234 print_help()
f0699cc7 235 sys.exit(0)
1e0bdf2a 236 if cmd in ['-v', '--version', 'version']:
c333afcf 237 from stgit.version import version
4df2f866 238 print 'Stacked GIT %s' % version
50725547 239 os.system('git --version')
1e0bdf2a 240 print 'Python version %s' % sys.version
41a6d859 241 sys.exit(0)
22ea9102
CM
242 if cmd in ['copyright']:
243 print __copyright__
244 sys.exit(0)
41a6d859
CM
245
246 # re-build the command line arguments
78340ff1 247 sys.argv[0] += ' %s' % commands.canonical_cmd(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
b2017c38
CM
265 try:
266 debug_level = int(os.environ['STGIT_DEBUG_LEVEL'])
267 except KeyError:
268 debug_level = 0
269 except ValueError:
270 print >> sys.stderr, 'Invalid STGIT_DEBUG_LEVEL environment variable'
271 sys.exit(1)
272
41a6d859 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)
b2017c38
CM
290 if debug_level:
291 raise
292 else:
293 sys.exit(2)
4d9fc826
CM
294 except KeyboardInterrupt:
295 sys.exit(1)
41a6d859
CM
296
297 sys.exit(0)