~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-05-19 08:31:06 UTC
  • Revision ID: mbp@sourcefrog.net-20050519083106-ebe71562d3bda4a7
- fix typo

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
 
18
18
 
19
 
import sys, os
 
19
import sys, os, time, os.path
20
20
 
21
21
import bzrlib
22
22
from bzrlib.trace import mutter, note, log_error
23
23
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
24
 
from bzrlib.osutils import quotefn
25
 
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
 
24
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
 
25
from bzrlib.tree import RevisionTree, EmptyTree, Tree
 
26
from bzrlib.revision import Revision
 
27
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
26
28
     format_date
 
29
from bzrlib import merge
27
30
 
28
31
 
29
32
def _squish_command_name(cmd):
34
37
    assert cmd.startswith("cmd_")
35
38
    return cmd[4:].replace('_','-')
36
39
 
37
 
def _parse_revision_str(revstr):
38
 
    """This handles a revision string -> revno. 
39
 
 
40
 
    There are several possibilities:
41
 
 
42
 
        '234'       -> 234
43
 
        '234:345'   -> [234, 345]
44
 
        ':234'      -> [None, 234]
45
 
        '234:'      -> [234, None]
46
 
 
47
 
    In the future we will also support:
48
 
        'uuid:blah-blah-blah'   -> ?
49
 
        'hash:blahblahblah'     -> ?
50
 
        potentially:
51
 
        'tag:mytag'             -> ?
52
 
    """
53
 
    if revstr.find(':') != -1:
54
 
        revs = revstr.split(':')
55
 
        if len(revs) > 2:
56
 
            raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
57
 
 
58
 
        if not revs[0]:
59
 
            revs[0] = None
60
 
        else:
61
 
            revs[0] = int(revs[0])
62
 
 
63
 
        if not revs[1]:
64
 
            revs[1] = None
65
 
        else:
66
 
            revs[1] = int(revs[1])
67
 
    else:
68
 
        revs = int(revstr)
69
 
    return revs
70
 
 
71
 
def _find_plugins():
72
 
    """Find all python files which are plugins, and load their commands
73
 
    to add to the list of "all commands"
74
 
 
75
 
    The environment variable BZRPATH is considered a delimited set of
76
 
    paths to look through. Each entry is searched for *.py files.
77
 
    If a directory is found, it is also searched, but they are 
78
 
    not searched recursively. This allows you to revctl the plugins.
79
 
    
80
 
    Inside the plugin should be a series of cmd_* function, which inherit from
81
 
    the bzrlib.commands.Command class.
82
 
    """
83
 
    bzrpath = os.environ.get('BZRPLUGINPATH', '')
84
 
 
85
 
    plugin_cmds = {} 
86
 
    if not bzrpath:
87
 
        return plugin_cmds
88
 
    _platform_extensions = {
89
 
        'win32':'.pyd',
90
 
        'cygwin':'.dll',
91
 
        'darwin':'.dylib',
92
 
        'linux2':'.so'
93
 
        }
94
 
    if _platform_extensions.has_key(sys.platform):
95
 
        platform_extension = _platform_extensions[sys.platform]
96
 
    else:
97
 
        platform_extension = None
98
 
    for d in bzrpath.split(os.pathsep):
99
 
        plugin_names = {} # This should really be a set rather than a dict
100
 
        for f in os.listdir(d):
101
 
            if f.endswith('.py'):
102
 
                f = f[:-3]
103
 
            elif f.endswith('.pyc') or f.endswith('.pyo'):
104
 
                f = f[:-4]
105
 
            elif platform_extension and f.endswith(platform_extension):
106
 
                f = f[:-len(platform_extension)]
107
 
                if f.endswidth('module'):
108
 
                    f = f[:-len('module')]
109
 
            else:
110
 
                continue
111
 
            if not plugin_names.has_key(f):
112
 
                plugin_names[f] = True
113
 
 
114
 
        plugin_names = plugin_names.keys()
115
 
        plugin_names.sort()
116
 
        try:
117
 
            sys.path.insert(0, d)
118
 
            for name in plugin_names:
119
 
                try:
120
 
                    old_module = None
121
 
                    try:
122
 
                        if sys.modules.has_key(name):
123
 
                            old_module = sys.modules[name]
124
 
                            del sys.modules[name]
125
 
                        plugin = __import__(name, locals())
126
 
                        for k in dir(plugin):
127
 
                            if k.startswith('cmd_'):
128
 
                                k_unsquished = _unsquish_command_name(k)
129
 
                                if not plugin_cmds.has_key(k_unsquished):
130
 
                                    plugin_cmds[k_unsquished] = getattr(plugin, k)
131
 
                                else:
132
 
                                    log_error('Two plugins defined the same command: %r' % k)
133
 
                                    log_error('Not loading the one in %r in dir %r' % (name, d))
134
 
                    finally:
135
 
                        if old_module:
136
 
                            sys.modules[name] = old_module
137
 
                except ImportError, e:
138
 
                    log_error('Unable to load plugin: %r from %r\n%s' % (name, d, e))
139
 
        finally:
140
 
            sys.path.pop(0)
141
 
    return plugin_cmds
142
 
 
143
 
def _get_cmd_dict(include_plugins=True):
144
 
    d = {}
 
40
def get_all_cmds():
 
41
    """Return canonical name and class for all registered commands."""
145
42
    for k, v in globals().iteritems():
146
43
        if k.startswith("cmd_"):
147
 
            d[_unsquish_command_name(k)] = v
148
 
    if include_plugins:
149
 
        d.update(_find_plugins())
150
 
    return d
151
 
    
152
 
def get_all_cmds(include_plugins=True):
153
 
    """Return canonical name and class for all registered commands."""
154
 
    for k, v in _get_cmd_dict(include_plugins=include_plugins).iteritems():
155
 
        yield k,v
156
 
 
157
 
 
158
 
def get_cmd_class(cmd,include_plugins=True):
 
44
            yield _unsquish_command_name(k), v
 
45
 
 
46
def get_cmd_class(cmd):
159
47
    """Return the canonical name and command class for a command.
160
48
    """
161
49
    cmd = str(cmd)                      # not unicode
162
50
 
163
51
    # first look up this command under the specified name
164
 
    cmds = _get_cmd_dict(include_plugins=include_plugins)
165
52
    try:
166
 
        return cmd, cmds[cmd]
 
53
        return cmd, globals()[_squish_command_name(cmd)]
167
54
    except KeyError:
168
55
        pass
169
56
 
170
57
    # look for any command which claims this as an alias
171
 
    for cmdname, cmdclass in cmds.iteritems():
 
58
    for cmdname, cmdclass in get_all_cmds():
172
59
        if cmd in cmdclass.aliases:
173
60
            return cmdname, cmdclass
174
61
 
179
66
    raise BzrCommandError("unknown command %r" % cmd)
180
67
 
181
68
 
182
 
class Command(object):
 
69
class Command:
183
70
    """Base class for commands.
184
71
 
185
72
    The docstring for an actual command should give a single-line
244
131
    """
245
132
 
246
133
    def find_command(cls, cmd):
247
 
        import os.path
248
134
        bzrpath = os.environ.get('BZRPATH', '')
249
135
 
250
 
        for dir in bzrpath.split(os.pathsep):
 
136
        for dir in bzrpath.split(':'):
251
137
            path = os.path.join(dir, cmd)
252
138
            if os.path.isfile(path):
253
139
                return ExternalCommand(path)
342
228
    
343
229
    def run(self, all=False, show_ids=False, file_list=None):
344
230
        if file_list:
345
 
            b = Branch(file_list[0])
 
231
            b = Branch(file_list[0], lock_mode='r')
346
232
            file_list = [b.relpath(x) for x in file_list]
347
233
            # special case: only one path was given and it's the root
348
234
            # of the branch
349
235
            if file_list == ['']:
350
236
                file_list = None
351
237
        else:
352
 
            b = Branch('.')
 
238
            b = Branch('.', lock_mode='r')
353
239
        import status
354
240
        status.show_status(b, show_unchanged=all, show_ids=show_ids,
355
241
                           specific_files=file_list)
395
281
    recursively add that parent, rather than giving an error?
396
282
    """
397
283
    takes_args = ['file+']
398
 
    takes_options = ['verbose', 'no-recurse']
 
284
    takes_options = ['verbose']
399
285
    
400
 
    def run(self, file_list, verbose=False, no_recurse=False):
401
 
        bzrlib.add.smart_add(file_list, verbose, not no_recurse)
 
286
    def run(self, file_list, verbose=False):
 
287
        bzrlib.add.smart_add(file_list, verbose)
402
288
 
403
289
 
404
290
class cmd_relpath(Command):
405
291
    """Show path of a file relative to root"""
406
292
    takes_args = ['filename']
407
 
    hidden = True
408
293
    
409
294
    def run(self, filename):
410
295
        print Branch(filename).relpath(filename)
413
298
 
414
299
class cmd_inventory(Command):
415
300
    """Show inventory of the current working copy or a revision."""
416
 
    takes_options = ['revision', 'show-ids']
 
301
    takes_options = ['revision']
417
302
    
418
 
    def run(self, revision=None, show_ids=False):
 
303
    def run(self, revision=None):
419
304
        b = Branch('.')
420
305
        if revision == None:
421
306
            inv = b.read_working_inventory()
422
307
        else:
423
308
            inv = b.get_revision_inventory(b.lookup_revision(revision))
424
309
 
425
 
        for path, entry in inv.entries():
426
 
            if show_ids:
427
 
                print '%-50s %s' % (path, entry.file_id)
428
 
            else:
429
 
                print path
 
310
        for path, entry in inv.iter_entries():
 
311
            print '%-50s %s' % (entry.file_id, path)
430
312
 
431
313
 
432
314
class cmd_move(Command):
466
348
 
467
349
 
468
350
 
469
 
 
470
 
 
471
 
class cmd_pull(Command):
472
 
    """Pull any changes from another branch into the current one.
473
 
 
474
 
    If the location is omitted, the last-used location will be used.
475
 
    Both the revision history and the working directory will be
476
 
    updated.
477
 
 
478
 
    This command only works on branches that have not diverged.  Branches are
479
 
    considered diverged if both branches have had commits without first
480
 
    pulling from the other.
481
 
 
482
 
    If branches have diverged, you can use 'bzr merge' to pull the text changes
483
 
    from one into the other.
484
 
    """
485
 
    takes_args = ['location?']
486
 
 
487
 
    def run(self, location=None):
488
 
        from bzrlib.merge import merge
489
 
        import errno
490
 
        
491
 
        br_to = Branch('.')
492
 
        stored_loc = None
493
 
        try:
494
 
            stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
495
 
        except IOError, e:
496
 
            if errno == errno.ENOENT:
497
 
                raise
498
 
        if location is None:
499
 
            location = stored_loc
500
 
        if location is None:
501
 
            raise BzrCommandError("No pull location known or specified.")
502
 
        from branch import find_branch, DivergedBranches
503
 
        br_from = find_branch(location)
504
 
        location = pull_loc(br_from)
505
 
        old_revno = br_to.revno()
506
 
        try:
507
 
            br_to.update_revisions(br_from)
508
 
        except DivergedBranches:
509
 
            raise BzrCommandError("These branches have diverged.  Try merge.")
510
 
            
511
 
        merge(('.', -1), ('.', old_revno), check_clean=False)
512
 
        if location != stored_loc:
513
 
            br_to.controlfile("x-pull", "wb").write(location + "\n")
514
 
 
515
 
 
516
 
 
517
 
class cmd_branch(Command):
518
 
    """Create a new copy of a branch.
519
 
 
520
 
    If the TO_LOCATION is omitted, the last component of the
521
 
    FROM_LOCATION will be used.  In other words,
522
 
    "branch ../foo/bar" will attempt to create ./bar.
523
 
    """
524
 
    takes_args = ['from_location', 'to_location?']
525
 
 
526
 
    def run(self, from_location, to_location=None):
527
 
        import errno
528
 
        from bzrlib.merge import merge
529
 
        
530
 
        if to_location is None:
531
 
            to_location = os.path.basename(from_location)
532
 
            # FIXME: If there's a trailing slash, keep removing them
533
 
            # until we find the right bit
534
 
 
535
 
        try:
536
 
            os.mkdir(to_location)
537
 
        except OSError, e:
538
 
            if e.errno == errno.EEXIST:
539
 
                raise BzrCommandError('Target directory "%s" already exists.' %
540
 
                                      to_location)
541
 
            if e.errno == errno.ENOENT:
542
 
                raise BzrCommandError('Parent of "%s" does not exist.' %
543
 
                                      to_location)
544
 
            else:
545
 
                raise
546
 
        br_to = Branch(to_location, init=True)
547
 
        from branch import find_branch, DivergedBranches
548
 
        try:
549
 
            br_from = find_branch(from_location)
550
 
        except OSError, e:
551
 
            if e.errno == errno.ENOENT:
552
 
                raise BzrCommandError('Source location "%s" does not exist.' %
553
 
                                      to_location)
554
 
            else:
555
 
                raise
556
 
 
557
 
        from_location = pull_loc(br_from)
558
 
        br_to.update_revisions(br_from)
559
 
        merge((to_location, -1), (to_location, 0), this_dir=to_location,
560
 
              check_clean=False)
561
 
        br_to.controlfile("x-pull", "wb").write(from_location + "\n")
562
 
 
563
 
 
564
 
def pull_loc(branch):
565
 
    # TODO: Should perhaps just make attribute be 'base' in
566
 
    # RemoteBranch and Branch?
567
 
    if hasattr(branch, "baseurl"):
568
 
        return branch.baseurl
569
 
    else:
570
 
        return branch.base
571
 
 
572
 
 
573
 
 
574
351
class cmd_renames(Command):
575
352
    """Show list of renamed files.
576
353
 
654
431
 
655
432
class cmd_revision_history(Command):
656
433
    """Display list of revision ids on this branch."""
657
 
    hidden = True
658
434
    def run(self):
659
435
        for patchid in Branch('.').revision_history():
660
436
            print patchid
712
488
    """
713
489
    
714
490
    takes_args = ['file*']
715
 
    takes_options = ['revision', 'diff-options']
716
 
    aliases = ['di', 'dif']
 
491
    takes_options = ['revision']
 
492
    aliases = ['di']
717
493
 
718
 
    def run(self, revision=None, file_list=None, diff_options=None):
 
494
    def run(self, revision=None, file_list=None):
719
495
        from bzrlib.diff import show_diff
720
 
        from bzrlib import find_branch
721
 
 
722
 
        if file_list:
723
 
            b = find_branch(file_list[0])
724
 
            file_list = [b.relpath(f) for f in file_list]
725
 
            if file_list == ['']:
726
 
                # just pointing to top-of-tree
727
 
                file_list = None
728
 
        else:
729
 
            b = Branch('.')
730
496
    
731
 
        show_diff(b, revision, specific_files=file_list,
732
 
                  external_diff_options=diff_options)
 
497
        show_diff(Branch('.'), revision, specific_files=file_list)
733
498
 
734
499
 
735
500
        
819
584
class cmd_log(Command):
820
585
    """Show log of this branch.
821
586
 
822
 
    To request a range of logs, you can use the command -r begin:end
823
 
    -r revision requests a specific revision, -r :end or -r begin: are
824
 
    also valid.
 
587
    TODO: Option to limit range.
825
588
 
826
 
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
827
 
  
 
589
    TODO: Option to show in forward order.
828
590
    """
829
 
 
830
591
    takes_args = ['filename?']
831
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
832
 
    
833
 
    def run(self, filename=None, timezone='original',
834
 
            verbose=False,
835
 
            show_ids=False,
836
 
            forward=False,
837
 
            revision=None):
 
592
    takes_options = ['timezone', 'verbose', 'show-ids']
 
593
    def run(self, filename=None, timezone='original', verbose=False, show_ids=False):
838
594
        from bzrlib import show_log, find_branch
839
 
        import codecs
840
 
 
841
 
        direction = (forward and 'forward') or 'reverse'
842
595
        
843
596
        if filename:
844
 
            b = find_branch(filename)
 
597
            b = find_branch(filename, lock_mode='r')
845
598
            fp = b.relpath(filename)
846
599
            if fp:
847
600
                file_id = b.read_working_inventory().path2id(fp)
848
601
            else:
849
602
                file_id = None  # points to branch root
850
603
        else:
851
 
            b = find_branch('.')
 
604
            b = find_branch('.', lock_mode='r')
852
605
            file_id = None
853
606
 
854
 
        if revision == None:
855
 
            revision = [None, None]
856
 
        elif isinstance(revision, int):
857
 
            revision = [revision, revision]
858
 
        else:
859
 
            # pair of revisions?
860
 
            pass
861
 
            
862
 
        assert len(revision) == 2
863
 
 
864
 
        mutter('encoding log as %r' % bzrlib.user_encoding)
865
 
 
866
 
        # use 'replace' so that we don't abort if trying to write out
867
 
        # in e.g. the default C locale.
868
 
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
869
 
 
870
607
        show_log(b, file_id,
871
608
                 show_timezone=timezone,
872
609
                 verbose=verbose,
873
610
                 show_ids=show_ids,
874
 
                 to_file=outf,
875
 
                 direction=direction,
876
 
                 start_revision=revision[0],
877
 
                 end_revision=revision[1])
 
611
                 to_file=sys.stdout)
878
612
 
879
613
 
880
614
 
885
619
    hidden = True
886
620
    takes_args = ["filename"]
887
621
    def run(self, filename):
888
 
        b = Branch(filename)
 
622
        b = Branch(filename, lock_mode='r')
889
623
        inv = b.read_working_inventory()
890
624
        file_id = inv.path2id(b.relpath(filename))
891
625
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
921
655
 
922
656
 
923
657
class cmd_unknowns(Command):
924
 
    """List unknown files."""
 
658
    """List unknown files"""
925
659
    def run(self):
926
660
        for f in Branch('.').unknowns():
927
661
            print quotefn(f)
929
663
 
930
664
 
931
665
class cmd_ignore(Command):
932
 
    """Ignore a command or pattern.
 
666
    """Ignore a command or pattern
933
667
 
934
668
    To remove patterns from the ignore list, edit the .bzrignore file.
935
669
 
949
683
    
950
684
    def run(self, name_pattern):
951
685
        from bzrlib.atomicfile import AtomicFile
952
 
        import os.path
 
686
        import codecs
953
687
 
954
688
        b = Branch('.')
955
689
        ifn = b.abspath('.bzrignore')
963
697
        else:
964
698
            igns = ''
965
699
 
966
 
        # TODO: If the file already uses crlf-style termination, maybe
967
 
        # we should use that for the newly added lines?
968
 
 
969
700
        if igns and igns[-1] != '\n':
970
701
            igns += '\n'
971
702
        igns += name_pattern + '\n'
1120
851
    """Run internal test suite"""
1121
852
    hidden = True
1122
853
    def run(self):
1123
 
        from bzrlib.selftest import selftest
1124
 
        if selftest():
 
854
        failures, tests = 0, 0
 
855
 
 
856
        import doctest, bzrlib.store
 
857
        bzrlib.trace.verbose = False
 
858
 
 
859
        for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
 
860
            bzrlib.tree, bzrlib.commands, bzrlib.add:
 
861
            mf, mt = doctest.testmod(m)
 
862
            failures += mf
 
863
            tests += mt
 
864
            print '%-40s %3d tests' % (m.__name__, mt),
 
865
            if mf:
 
866
                print '%3d FAILED!' % mf
 
867
            else:
 
868
                print
 
869
 
 
870
        print '%-40s %3d tests' % ('total', tests),
 
871
        if failures:
 
872
            print '%3d FAILED!' % failures
 
873
            return 1
 
874
        else:
 
875
            print
1125
876
            return 0
1126
 
        else:
1127
 
            return 1
1128
877
 
1129
878
 
1130
879
 
1131
880
class cmd_version(Command):
1132
 
    """Show version of bzr."""
 
881
    """Show version of bzr"""
1133
882
    def run(self):
1134
883
        show_version()
1135
884
 
1136
885
def show_version():
1137
886
    print "bzr (bazaar-ng) %s" % bzrlib.__version__
1138
 
    # is bzrlib itself in a branch?
1139
 
    bzrrev = bzrlib.get_bzr_revision()
1140
 
    if bzrrev:
1141
 
        print "  (bzr checkout, revision %d {%s})" % bzrrev
1142
887
    print bzrlib.__copyright__
1143
888
    print "http://bazaar-ng.org/"
1144
889
    print
1154
899
        print "it sure does!"
1155
900
 
1156
901
def parse_spec(spec):
1157
 
    """
1158
 
    >>> parse_spec(None)
1159
 
    [None, None]
1160
 
    >>> parse_spec("./")
1161
 
    ['./', None]
1162
 
    >>> parse_spec("../@")
1163
 
    ['..', -1]
1164
 
    >>> parse_spec("../f/@35")
1165
 
    ['../f', 35]
1166
 
    """
1167
 
    if spec is None:
1168
 
        return [None, None]
1169
902
    if '/@' in spec:
1170
903
        parsed = spec.split('/@')
1171
904
        assert len(parsed) == 2
1178
911
        parsed = [spec, None]
1179
912
    return parsed
1180
913
 
1181
 
 
1182
 
 
1183
914
class cmd_merge(Command):
1184
 
    """Perform a three-way merge of trees.
1185
 
    
1186
 
    The SPEC parameters are working tree or revision specifiers.  Working trees
1187
 
    are specified using standard paths or urls.  No component of a directory
1188
 
    path may begin with '@'.
1189
 
    
1190
 
    Working tree examples: '.', '..', 'foo@', but NOT 'foo/@bar'
1191
 
 
1192
 
    Revisions are specified using a dirname/@revno pair, where dirname is the
1193
 
    branch directory and revno is the revision within that branch.  If no revno
1194
 
    is specified, the latest revision is used.
1195
 
 
1196
 
    Revision examples: './@127', 'foo/@', '../@1'
1197
 
 
1198
 
    The OTHER_SPEC parameter is required.  If the BASE_SPEC parameter is
1199
 
    not supplied, the common ancestor of OTHER_SPEC the current branch is used
1200
 
    as the BASE.
1201
 
 
1202
 
    merge refuses to run if there are any uncommitted changes, unless
1203
 
    --force is given.
1204
 
    """
1205
 
    takes_args = ['other_spec', 'base_spec?']
1206
 
    takes_options = ['force']
1207
 
 
1208
 
    def run(self, other_spec, base_spec=None, force=False):
1209
 
        from bzrlib.merge import merge
1210
 
        merge(parse_spec(other_spec), parse_spec(base_spec),
1211
 
              check_clean=(not force))
1212
 
 
1213
 
 
1214
 
class cmd_revert(Command):
1215
 
    """Reverse all changes since the last commit.
1216
 
 
1217
 
    Only versioned files are affected.
1218
 
 
1219
 
    TODO: Store backups of any files that will be reverted, so
1220
 
          that the revert can be undone.          
1221
 
    """
1222
 
    takes_options = ['revision']
1223
 
 
1224
 
    def run(self, revision=-1):
1225
 
        from bzrlib.merge import merge
1226
 
        merge(('.', revision), parse_spec('.'),
1227
 
              check_clean=False,
1228
 
              ignore_zero=True)
1229
 
 
 
915
    """Perform a three-way merge of trees."""
 
916
    takes_args = ['other_spec', 'base_spec']
 
917
 
 
918
    def run(self, other_spec, base_spec):
 
919
        merge.merge(parse_spec(other_spec), parse_spec(base_spec))
1230
920
 
1231
921
class cmd_assert_fail(Command):
1232
922
    """Test reporting of assertion failures"""
1258
948
        statcache.update_cache(b.base, b.read_working_inventory())
1259
949
 
1260
950
 
 
951
######################################################################
 
952
# main routine
 
953
 
1261
954
 
1262
955
# list of all available options; the rhs can be either None for an
1263
956
# option that takes no argument, or a constructor function that checks
1264
957
# the type.
1265
958
OPTIONS = {
1266
959
    'all':                    None,
1267
 
    'diff-options':           str,
1268
960
    'help':                   None,
1269
961
    'file':                   unicode,
1270
 
    'force':                  None,
1271
 
    'forward':                None,
1272
962
    'message':                unicode,
1273
 
    'no-recurse':             None,
1274
963
    'profile':                None,
1275
 
    'revision':               _parse_revision_str,
 
964
    'revision':               int,
1276
965
    'show-ids':               None,
1277
966
    'timezone':               str,
1278
967
    'verbose':                None,
1281
970
    }
1282
971
 
1283
972
SHORT_OPTIONS = {
 
973
    'm':                      'message',
1284
974
    'F':                      'file', 
1285
 
    'h':                      'help',
1286
 
    'm':                      'message',
1287
975
    'r':                      'revision',
1288
976
    'v':                      'verbose',
1289
977
}
1408
1096
    """
1409
1097
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
1410
1098
    
1411
 
    include_plugins=True
1412
1099
    try:
1413
1100
        args, opts = parse_args(argv[1:])
1414
1101
        if 'help' in opts:
1421
1108
        elif 'version' in opts:
1422
1109
            show_version()
1423
1110
            return 0
1424
 
        elif args and args[0] == 'builtin':
1425
 
            include_plugins=False
1426
 
            args = args[1:]
1427
1111
        cmd = str(args.pop(0))
1428
1112
    except IndexError:
1429
1113
        import help
1431
1115
        return 1
1432
1116
          
1433
1117
 
1434
 
    canonical_cmd, cmd_class = get_cmd_class(cmd,include_plugins=include_plugins)
 
1118
    canonical_cmd, cmd_class = get_cmd_class(cmd)
1435
1119
 
1436
1120
    # global option
1437
1121
    if 'profile' in opts: