19
import sys, os, time, os.path
22
23
from bzrlib.trace import mutter, note, log_error
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
24
from bzrlib.osutils import quotefn
25
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
24
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
25
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
26
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
27
from bzrlib.revision import Revision
28
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
32
def register_plugin_command(cmd):
33
"Utility function to help register a command"
36
if k.startswith("cmd_"):
37
k_unsquished = _unsquish_command_name(k)
40
if not plugin_cmds.has_key(k_unsquished):
41
plugin_cmds[k_unsquished] = cmd
43
log_error('Two plugins defined the same command: %r' % k)
44
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
47
32
def _squish_command_name(cmd):
48
33
return 'cmd_' + cmd.replace('-', '_')
52
37
assert cmd.startswith("cmd_")
53
38
return cmd[4:].replace('_','-')
55
def _parse_revision_str(revstr):
56
"""This handles a revision string -> revno.
58
There are several possibilities:
61
'234:345' -> [234, 345]
65
In the future we will also support:
66
'uuid:blah-blah-blah' -> ?
67
'hash:blahblahblah' -> ?
71
if revstr.find(':') != -1:
72
revs = revstr.split(':')
74
raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
79
revs[0] = int(revs[0])
84
revs[1] = int(revs[1])
91
def _get_cmd_dict(plugins_override=True):
41
"""Return canonical name and class for all registered commands."""
93
42
for k, v in globals().iteritems():
94
43
if k.startswith("cmd_"):
95
d[_unsquish_command_name(k)] = v
96
# If we didn't load plugins, the plugin_cmds dict will be empty
100
d2 = plugin_cmds.copy()
106
def get_all_cmds(plugins_override=True):
107
"""Return canonical name and class for all registered commands."""
108
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
112
def get_cmd_class(cmd, plugins_override=True):
44
yield _unsquish_command_name(k), v
46
def get_cmd_class(cmd):
113
47
"""Return the canonical name and command class for a command.
115
49
cmd = str(cmd) # not unicode
117
51
# first look up this command under the specified name
118
cmds = _get_cmd_dict(plugins_override=plugins_override)
120
return cmd, cmds[cmd]
53
return cmd, globals()[_squish_command_name(cmd)]
124
57
# look for any command which claims this as an alias
125
for cmdname, cmdclass in cmds.iteritems():
58
for cmdname, cmdclass in get_all_cmds():
126
59
if cmd in cmdclass.aliases:
127
60
return cmdname, cmdclass
213
145
def __init__(self, path):
148
# TODO: If either of these fail, we should detect that and
149
# assume that path is not really a bzr plugin after all.
216
151
pipe = os.popen('%s --bzr-usage' % path, 'r')
217
152
self.takes_options = pipe.readline().split()
219
for opt in self.takes_options:
220
if not opt in OPTIONS:
221
raise BzrError("Unknown option '%s' returned by external command %s"
224
# TODO: Is there any way to check takes_args is valid here?
225
153
self.takes_args = pipe.readline().split()
227
if pipe.close() is not None:
228
raise BzrError("Failed funning '%s --bzr-usage'" % path)
230
156
pipe = os.popen('%s --bzr-help' % path, 'r')
231
157
self.__doc__ = pipe.read()
232
if pipe.close() is not None:
233
raise BzrError("Failed funning '%s --bzr-help'" % path)
235
160
def __call__(self, options, arguments):
236
161
Command.__init__(self, options, arguments)
265
189
class cmd_status(Command):
266
190
"""Display status summary.
268
This reports on versioned and unknown files, reporting them
269
grouped by state. Possible states are:
272
Versioned in the working copy but not in the previous revision.
275
Versioned in the previous revision but removed or deleted
279
Path of this file changed from the previous revision;
280
the text may also have changed. This includes files whose
281
parent directory was renamed.
284
Text has changed since the previous revision.
287
Nothing about this file has changed since the previous revision.
288
Only shown with --all.
291
Not versioned and not matching an ignore pattern.
293
To see ignored files use 'bzr ignored'. For details in the
294
changes to file texts, use 'bzr diff'.
296
If no arguments are specified, the status of the entire working
297
directory is shown. Otherwise, only the status of the specified
298
files or directories is reported. If a directory is given, status
299
is reported for everything inside that directory.
192
For each file there is a single line giving its file state and name.
193
The name is that in the current revision unless it is deleted or
194
missing, in which case the old name is shown.
301
196
takes_args = ['file*']
302
takes_options = ['all', 'show-ids']
197
takes_options = ['all']
303
198
aliases = ['st', 'stat']
305
def run(self, all=False, show_ids=False, file_list=None):
307
b = Branch(file_list[0])
308
file_list = [b.relpath(x) for x in file_list]
309
# special case: only one path was given and it's the root
311
if file_list == ['']:
316
status.show_status(b, show_unchanged=all, show_ids=show_ids,
317
specific_files=file_list)
200
def run(self, all=False, file_list=None):
201
b = Branch('.', lock_mode='r')
202
b.show_status(show_all=all, file_list=file_list)
320
205
class cmd_cat_revision(Command):
357
242
recursively add that parent, rather than giving an error?
359
244
takes_args = ['file+']
360
takes_options = ['verbose', 'no-recurse']
245
takes_options = ['verbose']
362
def run(self, file_list, verbose=False, no_recurse=False):
363
bzrlib.add.smart_add(file_list, verbose, not no_recurse)
367
class cmd_mkdir(Command):
368
"""Create a new versioned directory.
370
This is equivalent to creating the directory and then adding it.
372
takes_args = ['dir+']
374
def run(self, dir_list):
383
b = bzrlib.branch.Branch(d)
384
b.add([d], verbose=True)
247
def run(self, file_list, verbose=False):
248
bzrlib.add.smart_add(file_list, verbose)
387
251
class cmd_relpath(Command):
388
252
"""Show path of a file relative to root"""
389
253
takes_args = ['filename']
392
255
def run(self, filename):
393
256
print Branch(filename).relpath(filename)
397
260
class cmd_inventory(Command):
398
261
"""Show inventory of the current working copy or a revision."""
399
takes_options = ['revision', 'show-ids']
262
takes_options = ['revision']
401
def run(self, revision=None, show_ids=False):
264
def run(self, revision=None):
403
266
if revision == None:
404
267
inv = b.read_working_inventory()
406
269
inv = b.get_revision_inventory(b.lookup_revision(revision))
408
for path, entry in inv.entries():
410
print '%-50s %s' % (path, entry.file_id)
271
for path, entry in inv.iter_entries():
272
print '%-50s %s' % (entry.file_id, path)
415
275
class cmd_move(Command):
454
class cmd_pull(Command):
455
"""Pull any changes from another branch into the current one.
457
If the location is omitted, the last-used location will be used.
458
Both the revision history and the working directory will be
461
This command only works on branches that have not diverged. Branches are
462
considered diverged if both branches have had commits without first
463
pulling from the other.
465
If branches have diverged, you can use 'bzr merge' to pull the text changes
466
from one into the other.
468
takes_args = ['location?']
470
def run(self, location=None):
471
from bzrlib.merge import merge
477
stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
479
if errno == errno.ENOENT:
482
if stored_loc is None:
483
raise BzrCommandError("No pull location known or specified.")
485
print "Using last location: %s" % stored_loc
486
location = stored_loc
487
from branch import find_branch, DivergedBranches
488
br_from = find_branch(location)
489
location = pull_loc(br_from)
490
old_revno = br_to.revno()
492
br_to.update_revisions(br_from)
493
except DivergedBranches:
494
raise BzrCommandError("These branches have diverged. Try merge.")
496
merge(('.', -1), ('.', old_revno), check_clean=False)
497
if location != stored_loc:
498
br_to.controlfile("x-pull", "wb").write(location + "\n")
502
class cmd_branch(Command):
503
"""Create a new copy of a branch.
505
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
506
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
508
To retrieve the branch as of a particular revision, supply the --revision
509
parameter, as in "branch foo/bar -r 5".
511
takes_args = ['from_location', 'to_location?']
512
takes_options = ['revision']
514
def run(self, from_location, to_location=None, revision=None):
516
from bzrlib.merge import merge
517
from branch import find_branch, DivergedBranches, NoSuchRevision
518
from shutil import rmtree
520
br_from = find_branch(from_location)
522
if e.errno == errno.ENOENT:
523
raise BzrCommandError('Source location "%s" does not exist.' %
528
if to_location is None:
529
to_location = os.path.basename(from_location.rstrip("/\\"))
532
os.mkdir(to_location)
534
if e.errno == errno.EEXIST:
535
raise BzrCommandError('Target directory "%s" already exists.' %
537
if e.errno == errno.ENOENT:
538
raise BzrCommandError('Parent of "%s" does not exist.' %
542
br_to = Branch(to_location, init=True)
545
br_to.update_revisions(br_from, stop_revision=revision)
546
except NoSuchRevision:
548
msg = "The branch %s has no revision %d." % (from_location,
550
raise BzrCommandError(msg)
551
merge((to_location, -1), (to_location, 0), this_dir=to_location,
552
check_clean=False, ignore_zero=True)
553
from_location = pull_loc(br_from)
554
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
557
def pull_loc(branch):
558
# TODO: Should perhaps just make attribute be 'base' in
559
# RemoteBranch and Branch?
560
if hasattr(branch, "baseurl"):
561
return branch.baseurl
567
312
class cmd_renames(Command):
568
313
"""Show list of renamed files.
707
446
takes_args = ['file*']
708
takes_options = ['revision', 'diff-options']
709
aliases = ['di', 'dif']
447
takes_options = ['revision']
711
def run(self, revision=None, file_list=None, diff_options=None):
450
def run(self, revision=None, file_list=None):
712
451
from bzrlib.diff import show_diff
713
from bzrlib import find_branch
716
b = find_branch(file_list[0])
717
file_list = [b.relpath(f) for f in file_list]
718
if file_list == ['']:
719
# just pointing to top-of-tree
724
show_diff(b, revision, specific_files=file_list,
725
external_diff_options=diff_options)
453
show_diff(Branch('.'), revision, file_list)
812
540
class cmd_log(Command):
813
541
"""Show log of this branch.
815
To request a range of logs, you can use the command -r begin:end
816
-r revision requests a specific revision, -r :end or -r begin: are
543
TODO: Option to limit range.
819
TODO: Make --revision support uuid: and hash: [future tag:] notation.
545
TODO: Perhaps show most-recent first with an option for last.
823
547
takes_args = ['filename?']
824
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
826
def run(self, filename=None, timezone='original',
831
from bzrlib import show_log, find_branch
834
direction = (forward and 'forward') or 'reverse'
548
takes_options = ['timezone', 'verbose', 'show-ids']
549
def run(self, filename=None, timezone='original', verbose=False, show_ids=False):
550
from branch import find_branch
551
b = find_branch((filename or '.'), lock_mode='r')
837
b = find_branch(filename)
838
fp = b.relpath(filename)
840
file_id = b.read_working_inventory().path2id(fp)
842
file_id = None # points to branch root
848
revision = [None, None]
849
elif isinstance(revision, int):
850
revision = [revision, revision]
855
assert len(revision) == 2
857
mutter('encoding log as %r' % bzrlib.user_encoding)
859
# use 'replace' so that we don't abort if trying to write out
860
# in e.g. the default C locale.
861
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
864
show_timezone=timezone,
869
start_revision=revision[0],
870
end_revision=revision[1])
553
filename = b.relpath(filename)
554
bzrlib.show_log(b, filename,
555
show_timezone=timezone,
874
561
class cmd_touching_revisions(Command):
875
"""Return revision-ids which affected a particular file.
877
A more user-friendly interface is "bzr log FILE"."""
562
"""Return revision-ids which affected a particular file."""
879
564
takes_args = ["filename"]
880
565
def run(self, filename):
566
b = Branch(filename, lock_mode='r')
882
567
inv = b.read_working_inventory()
883
568
file_id = inv.path2id(b.relpath(filename))
884
569
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
943
628
def run(self, name_pattern):
944
629
from bzrlib.atomicfile import AtomicFile
948
633
ifn = b.abspath('.bzrignore')
635
# FIXME: probably doesn't handle non-ascii patterns
950
637
if os.path.exists(ifn):
953
igns = f.read().decode('utf-8')
638
f = b.controlfile(ifn, 'rt')
959
# TODO: If the file already uses crlf-style termination, maybe
960
# we should use that for the newly added lines?
962
644
if igns and igns[-1] != '\n':
964
646
igns += name_pattern + '\n'
967
f = AtomicFile(ifn, 'wt')
968
f.write(igns.encode('utf-8'))
648
f = AtomicFile(ifn, 'wt')
973
652
inv = b.working_tree().inventory
974
653
if inv.path2id('.bzrignore'):
1014
693
class cmd_export(Command):
1015
694
"""Export past revision to destination directory.
1017
If no revision is specified this exports the last committed revision.
1019
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1020
given, exports to a directory (equivalent to --format=dir)."""
1021
# TODO: list known exporters
696
If no revision is specified this exports the last committed revision."""
1022
697
takes_args = ['dest']
1023
takes_options = ['revision', 'format']
1024
def run(self, dest, revision=None, format='dir'):
698
takes_options = ['revision']
699
def run(self, dest, revision=None):
1026
701
if revision == None:
1027
702
rh = b.revision_history()[-1]
1029
704
rh = b.lookup_revision(int(revision))
1030
705
t = b.revision_tree(rh)
1031
t.export(dest, format)
1034
709
class cmd_cat(Command):
1055
730
class cmd_commit(Command):
1056
731
"""Commit changes into a new revision.
1058
If selected files are specified, only changes to those files are
1059
committed. If a directory is specified then its contents are also
1062
A selected-file commit may fail in some cases where the committed
1063
tree would be invalid, such as trying to commit a file in a
1064
newly-added directory that is not itself committed.
733
TODO: Commit only selected files.
1066
735
TODO: Run hooks on tree to-be-committed, and after commit.
1068
737
TODO: Strict commit that fails if there are unknown or deleted files.
1070
takes_args = ['selected*']
1071
739
takes_options = ['message', 'file', 'verbose']
1072
740
aliases = ['ci', 'checkin']
1074
def run(self, message=None, file=None, verbose=True, selected_list=None):
1075
from bzrlib.commit import commit
742
def run(self, message=None, file=None, verbose=False):
1077
743
## Warning: shadows builtin file()
1078
744
if not message and not file:
1079
745
raise BzrCommandError("please specify a commit message",
1095
760
This command checks various invariants about the branch storage to
1096
761
detect data corruption or bzr bugs.
1098
If given the --update flag, it will update some optional fields
1099
to help ensure data consistency.
1101
763
takes_args = ['dir?']
1103
764
def run(self, dir='.'):
1104
765
import bzrlib.check
1105
bzrlib.check.check(Branch(dir))
1109
class cmd_upgrade(Command):
1110
"""Upgrade branch storage to current format.
1112
This should normally be used only after the check command tells
1115
takes_args = ['dir?']
1117
def run(self, dir='.'):
1118
from bzrlib.upgrade import upgrade
1119
upgrade(Branch(dir))
766
bzrlib.check.check(Branch(dir, find_root=False))
1135
782
"""Run internal test suite"""
1138
from bzrlib.selftest import selftest
1139
return int(not selftest())
785
failures, tests = 0, 0
787
import doctest, bzrlib.store, bzrlib.tests
788
bzrlib.trace.verbose = False
790
for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
791
bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
792
mf, mt = doctest.testmod(m)
795
print '%-40s %3d tests' % (m.__name__, mt),
797
print '%3d FAILED!' % mf
801
print '%-40s %3d tests' % ('total', tests),
803
print '%3d FAILED!' % failures
1142
809
class cmd_version(Command):
1143
"""Show version of bzr."""
810
"""Show version of bzr"""
1147
814
def show_version():
1148
815
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1149
# is bzrlib itself in a branch?
1150
bzrrev = bzrlib.get_bzr_revision()
1152
print " (bzr checkout, revision %d {%s})" % bzrrev
1153
816
print bzrlib.__copyright__
1154
817
print "http://bazaar-ng.org/"
1165
828
print "it sure does!"
1167
def parse_spec(spec):
1169
>>> parse_spec(None)
1171
>>> parse_spec("./")
1173
>>> parse_spec("../@")
1175
>>> parse_spec("../f/@35")
1181
parsed = spec.split('/@')
1182
assert len(parsed) == 2
1186
parsed[1] = int(parsed[1])
1187
assert parsed[1] >=0
1189
parsed = [spec, None]
1194
class cmd_merge(Command):
1195
"""Perform a three-way merge of trees.
1197
The SPEC parameters are working tree or revision specifiers. Working trees
1198
are specified using standard paths or urls. No component of a directory
1199
path may begin with '@'.
1201
Working tree examples: '.', '..', 'foo@', but NOT 'foo/@bar'
1203
Revisions are specified using a dirname/@revno pair, where dirname is the
1204
branch directory and revno is the revision within that branch. If no revno
1205
is specified, the latest revision is used.
1207
Revision examples: './@127', 'foo/@', '../@1'
1209
The OTHER_SPEC parameter is required. If the BASE_SPEC parameter is
1210
not supplied, the common ancestor of OTHER_SPEC the current branch is used
1213
merge refuses to run if there are any uncommitted changes, unless
1216
takes_args = ['other_spec', 'base_spec?']
1217
takes_options = ['force']
1219
def run(self, other_spec, base_spec=None, force=False):
1220
from bzrlib.merge import merge
1221
merge(parse_spec(other_spec), parse_spec(base_spec),
1222
check_clean=(not force))
1225
class cmd_revert(Command):
1226
"""Reverse all changes since the last commit.
1228
Only versioned files are affected.
1230
TODO: Store backups of any files that will be reverted, so
1231
that the revert can be undone.
1233
takes_options = ['revision']
1235
def run(self, revision=-1):
1236
from bzrlib.merge import merge
1237
merge(('.', revision), parse_spec('.'),
1242
831
class cmd_assert_fail(Command):
1243
832
"""Test reporting of assertion failures"""
1318
903
(['status'], {'all': True})
1319
904
>>> parse_args('commit --message=biter'.split())
1320
905
(['commit'], {'message': u'biter'})
1321
>>> parse_args('log -r 500'.split())
1322
(['log'], {'revision': 500})
1323
>>> parse_args('log -r500:600'.split())
1324
(['log'], {'revision': [500, 600]})
1325
>>> parse_args('log -vr500:600'.split())
1326
(['log'], {'verbose': True, 'revision': [500, 600]})
1327
>>> parse_args('log -rv500:600'.split()) #the r takes an argument
1328
Traceback (most recent call last):
1330
ValueError: invalid literal for int(): v500
1349
924
if optname not in OPTIONS:
1350
raise BzrError('unknown long option %r' % a)
925
bailout('unknown long option %r' % a)
1352
927
shortopt = a[1:]
1353
if shortopt in SHORT_OPTIONS:
1354
# Multi-character options must have a space to delimit
1356
optname = SHORT_OPTIONS[shortopt]
1358
# Single character short options, can be chained,
1359
# and have their value appended to their name
1361
if shortopt not in SHORT_OPTIONS:
1362
# We didn't find the multi-character name, and we
1363
# didn't find the single char name
1364
raise BzrError('unknown short option %r' % a)
1365
optname = SHORT_OPTIONS[shortopt]
1368
# There are extra things on this option
1369
# see if it is the value, or if it is another
1371
optargfn = OPTIONS[optname]
1372
if optargfn is None:
1373
# This option does not take an argument, so the
1374
# next entry is another short option, pack it back
1376
argv.insert(0, '-' + a[2:])
1378
# This option takes an argument, so pack it
928
if shortopt not in SHORT_OPTIONS:
929
bailout('unknown short option %r' % a)
930
optname = SHORT_OPTIONS[shortopt]
1382
932
if optname in opts:
1383
933
# XXX: Do we ever want to support this, e.g. for -r?
1384
raise BzrError('repeated option %r' % a)
934
bailout('repeated option %r' % a)
1386
936
optargfn = OPTIONS[optname]
1388
938
if optarg == None:
1390
raise BzrError('option %r needs an argument' % a)
940
bailout('option %r needs an argument' % a)
1392
942
optarg = argv.pop(0)
1393
943
opts[optname] = optargfn(optarg)
1395
945
if optarg != None:
1396
raise BzrError('option %r takes no argument' % optname)
946
bailout('option %r takes no argument' % optname)
1397
947
opts[optname] = True
1450
def _parse_master_args(argv):
1451
"""Parse the arguments that always go with the original command.
1452
These are things like bzr --no-plugins, etc.
1454
There are now 2 types of option flags. Ones that come *before* the command,
1455
and ones that come *after* the command.
1456
Ones coming *before* the command are applied against all possible commands.
1457
And are generally applied before plugins are loaded.
1459
The current list are:
1460
--builtin Allow plugins to load, but don't let them override builtin commands,
1461
they will still be allowed if they do not override a builtin.
1462
--no-plugins Don't load any plugins. This lets you get back to official source
1464
--profile Enable the hotspot profile before running the command.
1465
For backwards compatibility, this is also a non-master option.
1466
--version Spit out the version of bzr that is running and exit.
1467
This is also a non-master option.
1468
--help Run help and exit, also a non-master option (I think that should stay, though)
1470
>>> argv, opts = _parse_master_args(['bzr', '--test'])
1471
Traceback (most recent call last):
1473
BzrCommandError: Invalid master option: 'test'
1474
>>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
1477
>>> print opts['version']
1479
>>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
1481
['command', '--more-options']
1482
>>> print opts['profile']
1484
>>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
1487
>>> print opts['no-plugins']
1489
>>> print opts['profile']
1491
>>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
1493
['command', '--profile']
1494
>>> print opts['profile']
1497
master_opts = {'builtin':False,
1504
# This is the point where we could hook into argv[0] to determine
1505
# what front-end is supposed to be run
1506
# For now, we are just ignoring it.
1507
cmd_name = argv.pop(0)
1509
if arg[:2] != '--': # at the first non-option, we return the rest
1511
arg = arg[2:] # Remove '--'
1512
if arg not in master_opts:
1513
# We could say that this is not an error, that we should
1514
# just let it be handled by the main section instead
1515
raise BzrCommandError('Invalid master option: %r' % arg)
1516
argv.pop(0) # We are consuming this entry
1517
master_opts[arg] = True
1518
return argv, master_opts
1522
1001
def run_bzr(argv):
1523
1002
"""Execute a command.
1528
1007
argv = [a.decode(bzrlib.user_encoding) for a in argv]
1531
# some options like --builtin and --no-plugins have special effects
1532
argv, master_opts = _parse_master_args(argv)
1533
if 'no-plugins' not in master_opts:
1534
bzrlib.load_plugins()
1536
args, opts = parse_args(argv)
1538
if master_opts['help']:
1539
from bzrlib.help import help
1010
args, opts = parse_args(argv[1:])
1546
1011
if 'help' in opts:
1547
from bzrlib.help import help
1553
1018
elif 'version' in opts:
1556
elif args and args[0] == 'builtin':
1557
include_plugins=False
1559
1021
cmd = str(args.pop(0))
1560
1022
except IndexError:
1023
log_error('usage: bzr COMMAND')
1024
log_error(' try "bzr help"')
1566
plugins_override = not (master_opts['builtin'])
1567
canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
1569
profile = master_opts['profile']
1570
# For backwards compatibility, I would rather stick with --profile being a
1571
# master/global option
1027
canonical_cmd, cmd_class = get_cmd_class(cmd)
1572
1030
if 'profile' in opts:
1574
1032
del opts['profile']
1576
1036
# check options are reasonable
1577
1037
allowed = cmd_class.takes_options