chiark / gitweb /
f9b07d7ed92c72bb114f58312c2c00ba9d8c0c1b
[stgit] / stgit / main.py
1 """Basic quilt-like functionality
2 """
3
4 __copyright__ = """
5 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
6
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.
10
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.
15
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 """
20
21 import sys, os
22 from optparse import OptionParser
23
24 import stgit.commands
25
26 #
27 # The commands map
28 #
29 class Commands(dict):
30     """Commands class. It performs on-demand module loading
31     """
32     def __getitem__(self, key):
33         """Return the command python module name based.
34         """
35         global prog
36
37         cmd_mod = self.get(key)
38         if not cmd_mod:
39             candidates = [cmd for cmd in self.keys() if cmd.startswith(key)]
40
41             if not candidates:
42                 print >> sys.stderr, 'Unknown command: %s' % key
43                 print >> sys.stderr, '  Try "%s help" for a list of ' \
44                       'supported commands' % prog
45                 sys.exit(1)
46             elif len(candidates) > 1:
47                 print >> sys.stderr, 'Ambiguous command: %s' % key
48                 print >> sys.stderr, '  Candidates are: %s' \
49                       % ', '.join(candidates)
50                 sys.exit(1)
51
52             cmd_mod = self.get(candidates[0])
53             
54         __import__('stgit.commands.' + cmd_mod)
55         return getattr(stgit.commands, cmd_mod)
56
57 commands = Commands({
58     'add':              'add',
59     'applied':          'applied',
60     'assimilate':       'assimilate',
61     'branch':           'branch',
62     'delete':           'delete',
63     'diff':             'diff',
64     'clean':            'clean',
65     'clone':            'clone',
66     'commit':           'commit',
67     'export':           'export',
68     'files':            'files',
69     'float':            'float',
70     'fold':             'fold',
71     'goto':             'goto',
72     'id':               'id',
73     'import':           'imprt',
74     'init':             'init',
75     'log':              'log',
76     'mail':             'mail',
77     'new':              'new',
78     'patches':          'patches',
79     'pick':             'pick',
80     'pop':              'pop',
81     'pull':             'pull',
82     'push':             'push',
83     'rebase':           'rebase',
84     'refresh':          'refresh',
85     'rename':           'rename',
86     'resolved':         'resolved',
87     'rm':               'rm',
88     'series':           'series',
89     'show':             'show',
90     'status':           'status',
91     'sync':             'sync',
92     'top':              'top',
93     'unapplied':        'unapplied',
94     'uncommit':         'uncommit'
95     })
96
97 # classification: repository, stack, patch, working copy
98 repocommands = (
99     'branch',
100     'clone',
101     'id',
102     'pull'
103     )
104 stackcommands = (
105     'applied',
106     'assimilate',
107     'clean',
108     'commit',
109     'float',
110     'goto',
111     'init',
112     'pop',
113     'push',
114     'rebase',
115     'series',
116     'top',
117     'unapplied',
118     'uncommit'
119     )
120 patchcommands = (
121     'delete',
122     'export',
123     'files',
124     'fold',
125     'import',
126     'log',
127     'mail',
128     'new',
129     'pick',
130     'refresh',
131     'rename',
132     'show',
133     'sync'
134     )
135 wccommands = (
136     'add',
137     'diff',
138     'patches',
139     'resolved',
140     'rm',
141     'status'
142     )
143
144 def _print_helpstring(cmd):
145     print '  ' + cmd + ' ' * (12 - len(cmd)) + commands[cmd].help
146     
147 def print_help():
148     print 'usage: %s <command> [options]' % os.path.basename(sys.argv[0])
149     print
150     print 'Generic commands:'
151     print '  help        print the detailed command usage'
152     print '  version     display version information'
153     print '  copyright   display copyright information'
154     # unclassified commands if any
155     cmds = commands.keys()
156     cmds.sort()
157     for cmd in cmds:
158         if not cmd in repocommands and not cmd in stackcommands \
159                and not cmd in patchcommands and not cmd in wccommands:
160             _print_helpstring(cmd)
161     print
162
163     print 'Repository commands:'
164     for cmd in repocommands:
165         _print_helpstring(cmd)
166     print
167     
168     print 'Stack commands:'
169     for cmd in stackcommands:
170         _print_helpstring(cmd)
171     print
172
173     print 'Patch commands:'
174     for cmd in patchcommands:
175         _print_helpstring(cmd)
176     print
177
178     print 'Working-copy commands:'
179     for cmd in wccommands:
180         _print_helpstring(cmd)
181
182 #
183 # The main function (command dispatcher)
184 #
185 def main():
186     """The main function
187     """
188     global prog
189
190     prog = os.path.basename(sys.argv[0])
191
192     if len(sys.argv) < 2:
193         print >> sys.stderr, 'usage: %s <command>' % prog
194         print >> sys.stderr, \
195               '  Try "%s --help" for a list of supported commands' % prog
196         sys.exit(1)
197
198     cmd = sys.argv[1]
199
200     if cmd in ['-h', '--help']:
201         if len(sys.argv) >= 3 and sys.argv[2] in commands:
202             cmd = sys.argv[2]
203             sys.argv[2] = '--help'
204         else:
205             print_help()
206             sys.exit(0)
207     if cmd == 'help':
208         if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
209             cmd = sys.argv[2]
210             if not cmd in commands:
211                 print >> sys.stderr, '%s help: "%s" command unknown' \
212                       % (prog, cmd)
213                 sys.exit(1)
214
215             sys.argv[0] += ' %s' % cmd
216             command = commands[cmd]
217             parser = OptionParser(usage = command.usage,
218                                   option_list = command.options)
219             from pydoc import pager
220             pager(parser.format_help())
221         else:
222             print_help()
223         sys.exit(0)
224     if cmd in ['-v', '--version', 'version']:
225         from stgit.version import version
226         print 'Stacked GIT %s' % version
227         os.system('git --version')
228         print 'Python version %s' % sys.version
229         sys.exit(0)
230     if cmd in ['copyright']:
231         print __copyright__
232         sys.exit(0)
233
234     # re-build the command line arguments
235     sys.argv[0] += ' %s' % cmd
236     del(sys.argv[1])
237
238     command = commands[cmd]
239     usage = command.usage.split('\n')[0].strip()
240     parser = OptionParser(usage = usage, option_list = command.options)
241     options, args = parser.parse_args()
242
243     # These modules are only used from this point onwards and do not
244     # need to be imported earlier
245     from stgit.config import config_setup
246     from ConfigParser import ParsingError, NoSectionError
247     from stgit.stack import Series, StackException
248     from stgit.git import GitException
249     from stgit.commands.common import CmdException
250     from stgit.gitmergeonefile import GitMergeException
251
252     try:
253         config_setup()
254
255         # 'clone' doesn't expect an already initialised GIT tree. A Series
256         # object will be created after the GIT tree is cloned
257         if cmd != 'clone':
258             if hasattr(options, 'branch') and options.branch:
259                 command.crt_series = Series(options.branch)
260             else:
261                 command.crt_series = Series()
262             stgit.commands.common.crt_series = command.crt_series
263
264         command.func(parser, options, args)
265     except (IOError, ParsingError, NoSectionError, CmdException,
266             StackException, GitException, GitMergeException), err:
267         print >> sys.stderr, '%s %s: %s' % (prog, cmd, err)
268         sys.exit(2)
269     except KeyboardInterrupt:
270         sys.exit(1)
271
272     sys.exit(0)