1
# Copyright (C) 2004, 2005 by Canonical Ltd
 
 
3
# This program is free software; you can redistribute it and/or modify
 
 
4
# it under the terms of the GNU General Public License as published by
 
 
5
# the Free Software Foundation; either version 2 of the License, or
 
 
6
# (at your option) any later version.
 
 
8
# This program is distributed in the hope that it will be useful,
 
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 
11
# GNU General Public License for more details.
 
 
13
# You should have received a copy of the GNU General Public License
 
 
14
# along with this program; if not, write to the Free Software
 
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
18
# TODO: probably should say which arguments are candidates for glob
 
 
19
# expansion on windows and do that at the command level.
 
 
21
# TODO: Help messages for options.
 
 
23
# TODO: Define arguments by objects, rather than just using names.
 
 
24
# Those objects can specify the expected type of the argument, which
 
 
25
# would help with validation and shell completion.
 
 
31
from warnings import warn
 
 
32
from inspect import getdoc
 
 
36
from bzrlib.trace import mutter, note, log_error, warning
 
 
37
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
 
 
38
from bzrlib.branch import find_branch
 
 
39
from bzrlib import BZRDIR
 
 
44
def register_command(cmd):
 
 
45
    "Utility function to help register a command"
 
 
48
    if k.startswith("cmd_"):
 
 
49
        k_unsquished = _unsquish_command_name(k)
 
 
52
    if not plugin_cmds.has_key(k_unsquished):
 
 
53
        plugin_cmds[k_unsquished] = cmd
 
 
54
        mutter('registered plugin command %s', k_unsquished)      
 
 
56
        log_error('Two plugins defined the same command: %r' % k)
 
 
57
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
 
60
def _squish_command_name(cmd):
 
 
61
    return 'cmd_' + cmd.replace('-', '_')
 
 
64
def _unsquish_command_name(cmd):
 
 
65
    assert cmd.startswith("cmd_")
 
 
66
    return cmd[4:].replace('_','-')
 
 
69
def _parse_revision_str(revstr):
 
 
70
    """This handles a revision string -> revno.
 
 
72
    This always returns a list.  The list will have one element for 
 
 
74
    It supports integers directly, but everything else it
 
 
75
    defers for passing to Branch.get_revision_info()
 
 
77
    >>> _parse_revision_str('234')
 
 
79
    >>> _parse_revision_str('234..567')
 
 
81
    >>> _parse_revision_str('..')
 
 
83
    >>> _parse_revision_str('..234')
 
 
85
    >>> _parse_revision_str('234..')
 
 
87
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
 
 
89
    >>> _parse_revision_str('234....789') # Error?
 
 
91
    >>> _parse_revision_str('revid:test@other.com-234234')
 
 
92
    ['revid:test@other.com-234234']
 
 
93
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
 
 
94
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
 
 
95
    >>> _parse_revision_str('revid:test@other.com-234234..23')
 
 
96
    ['revid:test@other.com-234234', 23]
 
 
97
    >>> _parse_revision_str('date:2005-04-12')
 
 
99
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
 
 
100
    ['date:2005-04-12 12:24:33']
 
 
101
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
 
 
102
    ['date:2005-04-12T12:24:33']
 
 
103
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
 
 
104
    ['date:2005-04-12,12:24:33']
 
 
105
    >>> _parse_revision_str('-5..23')
 
 
107
    >>> _parse_revision_str('-5')
 
 
109
    >>> _parse_revision_str('123a')
 
 
111
    >>> _parse_revision_str('abc')
 
 
115
    old_format_re = re.compile('\d*:\d*')
 
 
116
    m = old_format_re.match(revstr)
 
 
118
        warning('Colon separator for revision numbers is deprecated.'
 
 
121
        for rev in revstr.split(':'):
 
 
123
                revs.append(int(rev))
 
 
128
    for x in revstr.split('..'):
 
 
139
def get_merge_type(typestring):
 
 
140
    """Attempt to find the merge class/factory associated with a string."""
 
 
141
    from merge import merge_types
 
 
143
        return merge_types[typestring][0]
 
 
145
        templ = '%s%%7s: %%s' % (' '*12)
 
 
146
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
 
 
147
        type_list = '\n'.join(lines)
 
 
148
        msg = "No known merge type %s. Supported types are:\n%s" %\
 
 
149
            (typestring, type_list)
 
 
150
        raise BzrCommandError(msg)
 
 
153
def _builtin_commands():
 
 
154
    import bzrlib.builtins
 
 
156
    builtins = bzrlib.builtins.__dict__
 
 
157
    for name in builtins:
 
 
158
        if name.startswith("cmd_"):
 
 
159
            real_name = _unsquish_command_name(name)        
 
 
160
            r[real_name] = builtins[name]
 
 
165
def builtin_command_names():
 
 
166
    """Return list of builtin command names."""
 
 
167
    return _builtin_commands().keys()
 
 
170
def plugin_command_names():
 
 
171
    return plugin_cmds.keys()
 
 
174
def _get_cmd_dict(plugins_override=True):
 
 
175
    """Return name->class mapping for all commands."""
 
 
176
    d = _builtin_commands()
 
 
178
        d.update(plugin_cmds)
 
 
182
def get_all_cmds(plugins_override=True):
 
 
183
    """Return canonical name and class for all registered commands."""
 
 
184
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
 
 
188
def get_cmd_object(cmd_name, plugins_override=True):
 
 
189
    """Return the canonical name and command class for a command.
 
 
192
        If true, plugin commands can override builtins.
 
 
194
    from bzrlib.externalcommand import ExternalCommand
 
 
196
    cmd_name = str(cmd_name)            # not unicode
 
 
198
    # first look up this command under the specified name
 
 
199
    cmds = _get_cmd_dict(plugins_override=plugins_override)
 
 
201
        return cmds[cmd_name]()
 
 
205
    # look for any command which claims this as an alias
 
 
206
    for real_cmd_name, cmd_class in cmds.iteritems():
 
 
207
        if cmd_name in cmd_class.aliases:
 
 
210
    cmd_obj = ExternalCommand.find_command(cmd_name)
 
 
214
    raise BzrCommandError("unknown command %r" % cmd_name)
 
 
217
class Command(object):
 
 
218
    """Base class for commands.
 
 
220
    Commands are the heart of the command-line bzr interface.
 
 
222
    The command object mostly handles the mapping of command-line
 
 
223
    parameters into one or more bzrlib operations, and of the results
 
 
226
    Commands normally don't have any state.  All their arguments are
 
 
227
    passed in to the run method.  (Subclasses may take a different
 
 
228
    policy if the behaviour of the instance needs to depend on e.g. a
 
 
229
    shell plugin and not just its Python class.)
 
 
231
    The docstring for an actual command should give a single-line
 
 
232
    summary, then a complete description of the command.  A grammar
 
 
233
    description will be inserted.
 
 
236
        Other accepted names for this command.
 
 
239
        List of argument forms, marked with whether they are optional,
 
 
243
        List of options that may be given for this command.
 
 
246
        If true, this command isn't advertised.  This is typically
 
 
247
        for commands intended for expert users.
 
 
257
        """Construct an instance of this command."""
 
 
258
        if self.__doc__ == Command.__doc__:
 
 
259
            warn("No help message set for %r" % self)
 
 
262
    def run_argv(self, argv):
 
 
263
        """Parse command line and run."""
 
 
264
        args, opts = parse_args(argv)
 
 
266
        if 'help' in opts:  # e.g. bzr add --help
 
 
267
            from bzrlib.help import help_on_command
 
 
268
            help_on_command(self.name())
 
 
271
        # check options are reasonable
 
 
272
        allowed = self.takes_options
 
 
274
            if oname not in allowed:
 
 
275
                raise BzrCommandError("option '--%s' is not allowed for command %r"
 
 
276
                                      % (oname, self.name()))
 
 
278
        # mix arguments and options into one dictionary
 
 
279
        cmdargs = _match_argform(self.name(), self.takes_args, args)
 
 
281
        for k, v in opts.items():
 
 
282
            cmdopts[k.replace('-', '_')] = v
 
 
284
        all_cmd_args = cmdargs.copy()
 
 
285
        all_cmd_args.update(cmdopts)
 
 
287
        return self.run(**all_cmd_args)
 
 
291
        """Actually run the command.
 
 
293
        This is invoked with the options and arguments bound to
 
 
296
        Return 0 or None if the command was successful, or a non-zero
 
 
297
        shell error code if not.  It's OK for this method to allow
 
 
298
        an exception to raise up.
 
 
300
        raise NotImplementedError()
 
 
304
        """Return help message for this class."""
 
 
305
        if self.__doc__ is Command.__doc__:
 
 
310
        return _unsquish_command_name(self.__class__.__name__)
 
 
313
def parse_spec(spec):
 
 
319
    >>> parse_spec("../@")
 
 
321
    >>> parse_spec("../f/@35")
 
 
323
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
 
 
324
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
 
 
329
        parsed = spec.split('/@')
 
 
330
        assert len(parsed) == 2
 
 
335
                parsed[1] = int(parsed[1])
 
 
337
                pass # We can allow stuff like ./@revid:blahblahblah
 
 
341
        parsed = [spec, None]
 
 
347
# list of all available options; the rhs can be either None for an
 
 
348
# option that takes no argument, or a constructor function that checks
 
 
361
    'revision':               _parse_revision_str,
 
 
373
    'merge-type':             get_merge_type,
 
 
387
def parse_args(argv):
 
 
388
    """Parse command line.
 
 
390
    Arguments and options are parsed at this level before being passed
 
 
391
    down to specific command handlers.  This routine knows, from a
 
 
392
    lookup table, something about the available options, what optargs
 
 
393
    they take, and which commands will accept them.
 
 
395
    >>> parse_args('--help'.split())
 
 
397
    >>> parse_args('help -- --invalidcmd'.split())
 
 
398
    (['help', '--invalidcmd'], {})
 
 
399
    >>> parse_args('--version'.split())
 
 
400
    ([], {'version': True})
 
 
401
    >>> parse_args('status --all'.split())
 
 
402
    (['status'], {'all': True})
 
 
403
    >>> parse_args('commit --message=biter'.split())
 
 
404
    (['commit'], {'message': u'biter'})
 
 
405
    >>> parse_args('log -r 500'.split())
 
 
406
    (['log'], {'revision': [500]})
 
 
407
    >>> parse_args('log -r500..600'.split())
 
 
408
    (['log'], {'revision': [500, 600]})
 
 
409
    >>> parse_args('log -vr500..600'.split())
 
 
410
    (['log'], {'verbose': True, 'revision': [500, 600]})
 
 
411
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
 
 
412
    (['log'], {'revision': ['v500', 600]})
 
 
420
        if not argsover and a[0] == '-':
 
 
421
            # option names must not be unicode
 
 
426
                    # We've received a standalone -- No more flags
 
 
429
                mutter("  got option %r" % a)
 
 
431
                    optname, optarg = a[2:].split('=', 1)
 
 
434
                if optname not in OPTIONS:
 
 
435
                    raise BzrError('unknown long option %r' % a)
 
 
438
                if shortopt in SHORT_OPTIONS:
 
 
439
                    # Multi-character options must have a space to delimit
 
 
441
                    optname = SHORT_OPTIONS[shortopt]
 
 
443
                    # Single character short options, can be chained,
 
 
444
                    # and have their value appended to their name
 
 
446
                    if shortopt not in SHORT_OPTIONS:
 
 
447
                        # We didn't find the multi-character name, and we
 
 
448
                        # didn't find the single char name
 
 
449
                        raise BzrError('unknown short option %r' % a)
 
 
450
                    optname = SHORT_OPTIONS[shortopt]
 
 
453
                        # There are extra things on this option
 
 
454
                        # see if it is the value, or if it is another
 
 
456
                        optargfn = OPTIONS[optname]
 
 
458
                            # This option does not take an argument, so the
 
 
459
                            # next entry is another short option, pack it back
 
 
461
                            argv.insert(0, '-' + a[2:])
 
 
463
                            # This option takes an argument, so pack it
 
 
468
                # XXX: Do we ever want to support this, e.g. for -r?
 
 
469
                raise BzrError('repeated option %r' % a)
 
 
471
            optargfn = OPTIONS[optname]
 
 
475
                        raise BzrError('option %r needs an argument' % a)
 
 
478
                opts[optname] = optargfn(optarg)
 
 
481
                    raise BzrError('option %r takes no argument' % optname)
 
 
491
def _match_argform(cmd, takes_args, args):
 
 
494
    # step through args and takes_args, allowing appropriate 0-many matches
 
 
495
    for ap in takes_args:
 
 
499
                argdict[argname] = args.pop(0)
 
 
500
        elif ap[-1] == '*': # all remaining arguments
 
 
502
                argdict[argname + '_list'] = args[:]
 
 
505
                argdict[argname + '_list'] = None
 
 
508
                raise BzrCommandError("command %r needs one or more %s"
 
 
509
                        % (cmd, argname.upper()))
 
 
511
                argdict[argname + '_list'] = args[:]
 
 
513
        elif ap[-1] == '$': # all but one
 
 
515
                raise BzrCommandError("command %r needs one or more %s"
 
 
516
                        % (cmd, argname.upper()))
 
 
517
            argdict[argname + '_list'] = args[:-1]
 
 
523
                raise BzrCommandError("command %r requires argument %s"
 
 
524
                        % (cmd, argname.upper()))
 
 
526
                argdict[argname] = args.pop(0)
 
 
529
        raise BzrCommandError("extra argument to command %s: %s"
 
 
536
def apply_profiled(the_callable, *args, **kwargs):
 
 
539
    pffileno, pfname = tempfile.mkstemp()
 
 
541
        prof = hotshot.Profile(pfname)
 
 
543
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
 
 
548
        stats = hotshot.stats.load(pfname)
 
 
550
        stats.sort_stats('time')
 
 
551
        ## XXX: Might like to write to stderr or the trace file instead but
 
 
552
        ## print_stats seems hardcoded to stdout
 
 
553
        stats.print_stats(20)
 
 
562
    """Execute a command.
 
 
564
    This is similar to main(), but without all the trappings for
 
 
565
    logging and error handling.  
 
 
568
       The command-line arguments, without the program name from argv[0]
 
 
570
    Returns a command status or raises an exception.
 
 
572
    Special master options: these must come before the command because
 
 
573
    they control how the command is interpreted.
 
 
576
        Do not load plugin modules at all
 
 
579
        Only use builtin commands.  (Plugins are still allowed to change
 
 
583
        Run under the Python profiler.
 
 
586
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
 
588
    opt_profile = opt_no_plugins = opt_builtin = False
 
 
590
    # --no-plugins is handled specially at a very early stage. We need
 
 
591
    # to load plugins before doing other command parsing so that they
 
 
592
    # can override commands, but this needs to happen first.
 
 
597
        elif a == '--no-plugins':
 
 
598
            opt_no_plugins = True
 
 
599
        elif a == '--builtin':
 
 
605
    if (not argv) or (argv[0] == '--help'):
 
 
606
        from bzrlib.help import help
 
 
613
    if argv[0] == '--version':
 
 
614
        from bzrlib.builtins import show_version
 
 
618
    if not opt_no_plugins:
 
 
619
        from bzrlib.plugin import load_plugins
 
 
622
    cmd = str(argv.pop(0))
 
 
624
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
 
 
627
        ret = apply_profiled(cmd_obj.run_argv, argv)
 
 
629
        ret = cmd_obj.run_argv(argv)
 
 
635
    bzrlib.trace.log_startup(argv)
 
 
636
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
 
 
640
            return run_bzr(argv[1:])
 
 
642
            # do this here inside the exception wrappers to catch EPIPE
 
 
644
    except BzrCommandError, e:
 
 
645
        # command line syntax error, etc
 
 
649
        bzrlib.trace.log_exception()
 
 
651
    except AssertionError, e:
 
 
652
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
 
 
654
    except KeyboardInterrupt, e:
 
 
655
        bzrlib.trace.log_exception('interrupted')
 
 
659
        if (isinstance(e, IOError) 
 
 
660
            and hasattr(e, 'errno')
 
 
661
            and e.errno == errno.EPIPE):
 
 
662
            bzrlib.trace.note('broken pipe')
 
 
665
            bzrlib.trace.log_exception()
 
 
669
if __name__ == '__main__':
 
 
670
    sys.exit(main(sys.argv))