~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2005-10-17 21:57:32 UTC
  • mto: This revision was merged to the branch mainline in revision 1462.
  • Revision ID: robertc@robertcollins.net-20051017215732-08f487800e726748
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.

Show diffs side-by-side

added added

removed removed

Lines of Context:
38
38
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
39
39
from bzrlib.revisionspec import RevisionSpec
40
40
from bzrlib import BZRDIR
 
41
from bzrlib.option import Option
41
42
 
42
43
plugin_cmds = {}
43
44
 
67
68
    return cmd[4:].replace('_','-')
68
69
 
69
70
 
70
 
def _parse_revision_str(revstr):
71
 
    """This handles a revision string -> revno.
72
 
 
73
 
    This always returns a list.  The list will have one element for
74
 
    each revision specifier supplied.
75
 
 
76
 
    >>> _parse_revision_str('234')
77
 
    [<RevisionSpec_int 234>]
78
 
    >>> _parse_revision_str('234..567')
79
 
    [<RevisionSpec_int 234>, <RevisionSpec_int 567>]
80
 
    >>> _parse_revision_str('..')
81
 
    [<RevisionSpec None>, <RevisionSpec None>]
82
 
    >>> _parse_revision_str('..234')
83
 
    [<RevisionSpec None>, <RevisionSpec_int 234>]
84
 
    >>> _parse_revision_str('234..')
85
 
    [<RevisionSpec_int 234>, <RevisionSpec None>]
86
 
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
87
 
    [<RevisionSpec_int 234>, <RevisionSpec_int 456>, <RevisionSpec_int 789>]
88
 
    >>> _parse_revision_str('234....789') #Error ?
89
 
    [<RevisionSpec_int 234>, <RevisionSpec None>, <RevisionSpec_int 789>]
90
 
    >>> _parse_revision_str('revid:test@other.com-234234')
91
 
    [<RevisionSpec_revid revid:test@other.com-234234>]
92
 
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
93
 
    [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]
94
 
    >>> _parse_revision_str('revid:test@other.com-234234..23')
95
 
    [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_int 23>]
96
 
    >>> _parse_revision_str('date:2005-04-12')
97
 
    [<RevisionSpec_date date:2005-04-12>]
98
 
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
99
 
    [<RevisionSpec_date date:2005-04-12 12:24:33>]
100
 
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
101
 
    [<RevisionSpec_date date:2005-04-12T12:24:33>]
102
 
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
103
 
    [<RevisionSpec_date date:2005-04-12,12:24:33>]
104
 
    >>> _parse_revision_str('-5..23')
105
 
    [<RevisionSpec_int -5>, <RevisionSpec_int 23>]
106
 
    >>> _parse_revision_str('-5')
107
 
    [<RevisionSpec_int -5>]
108
 
    >>> _parse_revision_str('123a')
109
 
    Traceback (most recent call last):
110
 
      ...
111
 
    BzrError: No namespace registered for string: '123a'
112
 
    >>> _parse_revision_str('abc')
113
 
    Traceback (most recent call last):
114
 
      ...
115
 
    BzrError: No namespace registered for string: 'abc'
116
 
    >>> _parse_revision_str('branch:../branch2')
117
 
    [<RevisionSpec_branch branch:../branch2>]
118
 
    """
119
 
    import re
120
 
    old_format_re = re.compile('\d*:\d*')
121
 
    m = old_format_re.match(revstr)
122
 
    revs = []
123
 
    if m:
124
 
        warning('Colon separator for revision numbers is deprecated.'
125
 
                ' Use .. instead')
126
 
        for rev in revstr.split(':'):
127
 
            if rev:
128
 
                revs.append(RevisionSpec(int(rev)))
129
 
            else:
130
 
                revs.append(RevisionSpec(None))
131
 
    else:
132
 
        next_prefix = None
133
 
        for x in revstr.split('..'):
134
 
            if not x:
135
 
                revs.append(RevisionSpec(None))
136
 
            elif x[-1] == ':':
137
 
                # looks like a namespace:.. has happened
138
 
                next_prefix = x + '..'
139
 
            else:
140
 
                if next_prefix is not None:
141
 
                    x = next_prefix + x
142
 
                revs.append(RevisionSpec(x))
143
 
                next_prefix = None
144
 
        if next_prefix is not None:
145
 
            revs.append(RevisionSpec(next_prefix))
146
 
    return revs
147
 
 
148
 
 
149
71
def _builtin_commands():
150
72
    import bzrlib.builtins
151
73
    r = {}
236
158
        repeated, etc.
237
159
 
238
160
    takes_options
239
 
        List of options that may be given for this command.
 
161
        List of options that may be given for this command.  These can
 
162
        be either strings, referring to globally-defined options,
 
163
        or option objects.  Retrieve through options().
240
164
 
241
165
    hidden
242
166
        If true, this command isn't advertised.  This is typically
243
167
        for commands intended for expert users.
244
168
    """
245
169
    aliases = []
246
 
    
247
170
    takes_args = []
248
171
    takes_options = []
249
172
 
254
177
        if self.__doc__ == Command.__doc__:
255
178
            warn("No help message set for %r" % self)
256
179
 
 
180
    def options(self):
 
181
        """Return dict of valid options for this command.
 
182
 
 
183
        Maps from long option name to option object."""
 
184
        r = dict()
 
185
        r['help'] = Option.OPTIONS['help']
 
186
        for o in self.takes_options:
 
187
            if not isinstance(o, Option):
 
188
                o = Option.OPTIONS[o]
 
189
            r[o.name] = o
 
190
        return r
257
191
 
258
192
    def run_argv(self, argv):
259
193
        """Parse command line and run."""
260
 
        args, opts = parse_args(argv)
261
 
 
 
194
        args, opts = parse_args(self, argv)
262
195
        if 'help' in opts:  # e.g. bzr add --help
263
196
            from bzrlib.help import help_on_command
264
197
            help_on_command(self.name())
265
198
            return 0
266
 
 
267
 
        # check options are reasonable
268
 
        allowed = self.takes_options
 
199
        # XXX: This should be handled by the parser
 
200
        allowed_names = self.options().keys()
269
201
        for oname in opts:
270
 
            if oname not in allowed:
 
202
            if oname not in allowed_names:
271
203
                raise BzrCommandError("option '--%s' is not allowed for command %r"
272
204
                                      % (oname, self.name()))
273
 
 
274
205
        # mix arguments and options into one dictionary
275
206
        cmdargs = _match_argform(self.name(), self.takes_args, args)
276
207
        cmdopts = {}
337
268
        parsed = [spec, None]
338
269
    return parsed
339
270
 
340
 
 
341
 
# list of all available options; the rhs can be either None for an
342
 
# option that takes no argument, or a constructor function that checks
343
 
# the type.
344
 
OPTIONS = {
345
 
    'all':                    None,
346
 
    'basis':                  str,
347
 
    'diff-options':           str,
348
 
    'help':                   None,
349
 
    'file':                   unicode,
350
 
    'force':                  None,
351
 
    'format':                 unicode,
352
 
    'forward':                None,
353
 
    'quiet':                  None,
354
 
    'message':                unicode,
355
 
    'no-recurse':             None,
356
 
    'profile':                None,
357
 
    'revision':               _parse_revision_str,
358
 
    'short':                  None,
359
 
    'show-ids':               None,
360
 
    'timezone':               str,
361
 
    'verbose':                None,
362
 
    'version':                None,
363
 
    'email':                  None,
364
 
    'unchanged':              None,
365
 
    'update':                 None,
366
 
    'long':                   None,
367
 
    'root':                   str,
368
 
    'no-backup':              None,
369
 
    'pattern':                str,
370
 
    'remember':               None,
371
 
    }
372
 
 
373
 
SHORT_OPTIONS = {
374
 
    'F':                      'file', 
375
 
    'h':                      'help',
376
 
    'm':                      'message',
377
 
    'r':                      'revision',
378
 
    'v':                      'verbose',
379
 
    'l':                      'long',
380
 
    'q':                      'quiet',
381
 
}
382
 
 
383
 
 
384
 
def parse_args(argv):
 
271
def parse_args(command, argv):
385
272
    """Parse command line.
386
273
    
387
274
    Arguments and options are parsed at this level before being passed
388
275
    down to specific command handlers.  This routine knows, from a
389
276
    lookup table, something about the available options, what optargs
390
277
    they take, and which commands will accept them.
391
 
 
392
 
    >>> parse_args('--help'.split())
393
 
    ([], {'help': True})
394
 
    >>> parse_args('help -- --invalidcmd'.split())
395
 
    (['help', '--invalidcmd'], {})
396
 
    >>> parse_args('--version'.split())
397
 
    ([], {'version': True})
398
 
    >>> parse_args('status --all'.split())
399
 
    (['status'], {'all': True})
400
 
    >>> parse_args('commit --message=biter'.split())
401
 
    (['commit'], {'message': u'biter'})
402
 
    >>> parse_args('log -r 500'.split())
403
 
    (['log'], {'revision': [<RevisionSpec_int 500>]})
404
 
    >>> parse_args('log -r500..600'.split())
405
 
    (['log'], {'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
406
 
    >>> parse_args('log -vr500..600'.split())
407
 
    (['log'], {'verbose': True, 'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
408
 
    >>> parse_args('log -rrevno:500..600'.split()) #the r takes an argument
409
 
    (['log'], {'revision': [<RevisionSpec_revno revno:500>, <RevisionSpec_int 600>]})
410
278
    """
 
279
    # TODO: chop up this beast; make it a method of the Command
411
280
    args = []
412
281
    opts = {}
413
282
 
 
283
    cmd_options = command.options()
414
284
    argsover = False
415
285
    while argv:
416
286
        a = argv.pop(0)
417
 
        if not argsover and a[0] == '-':
 
287
        if argsover:
 
288
            args.append(a)
 
289
            continue
 
290
        elif a == '--':
 
291
            # We've received a standalone -- No more flags
 
292
            argsover = True
 
293
            continue
 
294
        if a[0] == '-':
418
295
            # option names must not be unicode
419
296
            a = str(a)
420
297
            optarg = None
421
298
            if a[1] == '-':
422
 
                if a == '--':
423
 
                    # We've received a standalone -- No more flags
424
 
                    argsover = True
425
 
                    continue
426
299
                mutter("  got option %r" % a)
427
300
                if '=' in a:
428
301
                    optname, optarg = a[2:].split('=', 1)
429
302
                else:
430
303
                    optname = a[2:]
431
 
                if optname not in OPTIONS:
432
 
                    raise BzrError('unknown long option %r' % a)
 
304
                if optname not in cmd_options:
 
305
                    raise BzrCommandError('unknown long option %r for command %s' 
 
306
                            % (a, command.name))
433
307
            else:
434
308
                shortopt = a[1:]
435
 
                if shortopt in SHORT_OPTIONS:
 
309
                if shortopt in Option.SHORT_OPTIONS:
436
310
                    # Multi-character options must have a space to delimit
437
311
                    # their value
438
 
                    optname = SHORT_OPTIONS[shortopt]
 
312
                    # ^^^ what does this mean? mbp 20051014
 
313
                    optname = Option.SHORT_OPTIONS[shortopt].name
439
314
                else:
440
315
                    # Single character short options, can be chained,
441
316
                    # and have their value appended to their name
442
317
                    shortopt = a[1:2]
443
 
                    if shortopt not in SHORT_OPTIONS:
 
318
                    if shortopt not in Option.SHORT_OPTIONS:
444
319
                        # We didn't find the multi-character name, and we
445
320
                        # didn't find the single char name
446
321
                        raise BzrError('unknown short option %r' % a)
447
 
                    optname = SHORT_OPTIONS[shortopt]
 
322
                    optname = Option.SHORT_OPTIONS[shortopt].name
448
323
 
449
324
                    if a[2:]:
450
325
                        # There are extra things on this option
451
326
                        # see if it is the value, or if it is another
452
327
                        # short option
453
 
                        optargfn = OPTIONS[optname]
 
328
                        optargfn = Option.OPTIONS[optname].type
454
329
                        if optargfn is None:
455
330
                            # This option does not take an argument, so the
456
331
                            # next entry is another short option, pack it back
465
340
                # XXX: Do we ever want to support this, e.g. for -r?
466
341
                raise BzrError('repeated option %r' % a)
467
342
                
468
 
            optargfn = OPTIONS[optname]
 
343
            option_obj = cmd_options[optname]
 
344
            optargfn = option_obj.type
469
345
            if optargfn:
470
346
                if optarg == None:
471
347
                    if not argv:
479
355
                opts[optname] = True
480
356
        else:
481
357
            args.append(a)
482
 
 
483
358
    return args, opts
484
359
 
485
360
 
486
 
 
487
 
 
488
361
def _match_argform(cmd, takes_args, args):
489
362
    argdict = {}
490
363