15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
# TODO: Split the command framework away from the actual commands.
20
# TODO: probably should say which arguments are candidates for glob
21
# expansion on windows and do that at the command level.
23
# TODO: Help messages for options.
25
# TODO: Define arguments by objects, rather than just using names.
26
# Those objects can specify the expected type of the argument, which
27
# would help with validation and shell completion.
34
from bzrlib.trace import mutter, note, log_error, warning
22
from bzrlib.trace import mutter, note, log_error
35
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
36
from bzrlib.branch import find_branch
37
from bzrlib import BZRDIR
24
from bzrlib.osutils import quotefn
25
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
43
def register_command(cmd):
32
def register_plugin_command(cmd):
44
33
"Utility function to help register a command"
63
52
assert cmd.startswith("cmd_")
64
53
return cmd[4:].replace('_','-')
67
55
def _parse_revision_str(revstr):
68
"""This handles a revision string -> revno.
70
This always returns a list. The list will have one element for
72
It supports integers directly, but everything else it
73
defers for passing to Branch.get_revision_info()
75
>>> _parse_revision_str('234')
77
>>> _parse_revision_str('234..567')
79
>>> _parse_revision_str('..')
81
>>> _parse_revision_str('..234')
83
>>> _parse_revision_str('234..')
85
>>> _parse_revision_str('234..456..789') # Maybe this should be an error
87
>>> _parse_revision_str('234....789') # Error?
89
>>> _parse_revision_str('revid:test@other.com-234234')
90
['revid:test@other.com-234234']
91
>>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
92
['revid:test@other.com-234234', 'revid:test@other.com-234235']
93
>>> _parse_revision_str('revid:test@other.com-234234..23')
94
['revid:test@other.com-234234', 23]
95
>>> _parse_revision_str('date:2005-04-12')
97
>>> _parse_revision_str('date:2005-04-12 12:24:33')
98
['date:2005-04-12 12:24:33']
99
>>> _parse_revision_str('date:2005-04-12T12:24:33')
100
['date:2005-04-12T12:24:33']
101
>>> _parse_revision_str('date:2005-04-12,12:24:33')
102
['date:2005-04-12,12:24:33']
103
>>> _parse_revision_str('-5..23')
105
>>> _parse_revision_str('-5')
107
>>> _parse_revision_str('123a')
109
>>> _parse_revision_str('abc')
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' -> ?
113
old_format_re = re.compile('\d*:\d*')
114
m = old_format_re.match(revstr)
116
warning('Colon separator for revision numbers is deprecated.'
119
for rev in revstr.split(':'):
121
revs.append(int(rev))
126
for x in revstr.split('..'):
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])
137
def get_merge_type(typestring):
138
"""Attempt to find the merge class/factory associated with a string."""
139
from merge import merge_types
141
return merge_types[typestring][0]
143
templ = '%s%%7s: %%s' % (' '*12)
144
lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
145
type_list = '\n'.join(lines)
146
msg = "No known merge type %s. Supported types are:\n%s" %\
147
(typestring, type_list)
148
raise BzrCommandError(msg)
151
def get_merge_type(typestring):
152
"""Attempt to find the merge class/factory associated with a string."""
153
from merge import merge_types
155
return merge_types[typestring][0]
157
templ = '%s%%7s: %%s' % (' '*12)
158
lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
159
type_list = '\n'.join(lines)
160
msg = "No known merge type %s. Supported types are:\n%s" %\
161
(typestring, type_list)
162
raise BzrCommandError(msg)
166
91
def _get_cmd_dict(plugins_override=True):
263
185
class ExternalCommand(Command):
264
186
"""Class to wrap external commands.
266
We cheat a little here, when get_cmd_class() calls us we actually
267
give it back an object we construct that has the appropriate path,
268
help, options etc for the specified command.
270
When run_bzr() tries to instantiate that 'class' it gets caught by
271
the __call__ method, which we override to call the Command.__init__
272
method. That then calls our run method which is pretty straight
275
The only wrinkle is that we have to map bzr's dictionary of options
276
and arguments back into command line options and arguments for the
188
We cheat a little here, when get_cmd_class() calls us we actually give it back
189
an object we construct that has the appropriate path, help, options etc for the
192
When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
193
method, which we override to call the Command.__init__ method. That then calls
194
our run method which is pretty straight forward.
196
The only wrinkle is that we have to map bzr's dictionary of options and arguments
197
back into command line options and arguments for the script.
280
200
def find_command(cls, cmd):
377
297
directory is shown. Otherwise, only the status of the specified
378
298
files or directories is reported. If a directory is given, status
379
299
is reported for everything inside that directory.
381
If a revision is specified, the changes since that revision are shown.
383
301
takes_args = ['file*']
384
takes_options = ['all', 'show-ids', 'revision']
302
takes_options = ['all', 'show-ids']
385
303
aliases = ['st', 'stat']
387
305
def run(self, all=False, show_ids=False, file_list=None):
389
b = find_branch(file_list[0])
307
b = Branch(file_list[0])
390
308
file_list = [b.relpath(x) for x in file_list]
391
309
# special case: only one path was given and it's the root
393
311
if file_list == ['']:
398
from bzrlib.status import show_status
399
show_status(b, show_unchanged=all, show_ids=show_ids,
400
specific_files=file_list)
316
status.show_status(b, show_unchanged=all, show_ids=show_ids,
317
specific_files=file_list)
403
320
class cmd_cat_revision(Command):
454
348
whether already versioned or not, are searched for files or
455
349
subdirectories that are neither versioned or ignored, and these
456
350
are added. This search proceeds recursively into versioned
457
directories. If no names are given '.' is assumed.
459
Therefore simply saying 'bzr add' will version all files that
353
Therefore simply saying 'bzr add .' will version all files that
460
354
are currently unknown.
462
356
TODO: Perhaps adding a file whose directly is not versioned should
463
357
recursively add that parent, rather than giving an error?
465
takes_args = ['file*']
359
takes_args = ['file+']
466
360
takes_options = ['verbose', 'no-recurse']
468
362
def run(self, file_list, verbose=False, no_recurse=False):
469
from bzrlib.add import smart_add
470
smart_add(file_list, verbose, not no_recurse)
474
class cmd_mkdir(Command):
475
"""Create a new versioned directory.
477
This is equivalent to creating the directory and then adding it.
479
takes_args = ['dir+']
481
def run(self, dir_list):
488
b.add([d], verbose=True)
363
bzrlib.add.smart_add(file_list, verbose, not no_recurse)
491
366
class cmd_relpath(Command):
552
423
takes_args = ['from_name', 'to_name']
554
425
def run(self, from_name, to_name):
556
427
b.rename_one(b.relpath(from_name), b.relpath(to_name))
560
class cmd_mv(Command):
561
"""Move or rename a file.
564
bzr mv OLDNAME NEWNAME
565
bzr mv SOURCE... DESTINATION
567
If the last argument is a versioned directory, all the other names
568
are moved into it. Otherwise, there must be exactly two arguments
569
and the file is changed to a new name, which must not already exist.
571
Files cannot be moved between branches.
573
takes_args = ['names*']
574
def run(self, names_list):
575
if len(names_list) < 2:
576
raise BzrCommandError("missing file argument")
577
b = find_branch(names_list[0])
579
rel_names = [b.relpath(x) for x in names_list]
581
if os.path.isdir(names_list[-1]):
582
# move into existing directory
583
b.move(rel_names[:-1], rel_names[-1])
585
if len(names_list) != 2:
586
raise BzrCommandError('to mv multiple files the destination '
587
'must be a versioned directory')
588
b.move(rel_names[0], rel_names[1])
593
433
class cmd_pull(Command):
626
464
print "Using last location: %s" % stored_loc
627
465
location = stored_loc
628
cache_root = tempfile.mkdtemp()
629
from bzrlib.branch import DivergedBranches
466
from branch import find_branch, DivergedBranches
630
467
br_from = find_branch(location)
631
468
location = pull_loc(br_from)
632
469
old_revno = br_to.revno()
634
from branch import find_cached_branch, DivergedBranches
635
br_from = find_cached_branch(location, cache_root)
636
location = pull_loc(br_from)
637
old_revno = br_to.revno()
639
br_to.update_revisions(br_from)
640
except DivergedBranches:
641
raise BzrCommandError("These branches have diverged."
644
merge(('.', -1), ('.', old_revno), check_clean=False)
645
if location != stored_loc:
646
br_to.controlfile("x-pull", "wb").write(location + "\n")
471
br_to.update_revisions(br_from)
472
except DivergedBranches:
473
raise BzrCommandError("These branches have diverged. Try merge.")
475
merge(('.', -1), ('.', old_revno), check_clean=False)
476
if location != stored_loc:
477
br_to.controlfile("x-pull", "wb").write(location + "\n")
661
490
takes_args = ['from_location', 'to_location?']
662
491
takes_options = ['revision']
663
aliases = ['get', 'clone']
665
493
def run(self, from_location, to_location=None, revision=None):
667
495
from bzrlib.merge import merge
668
from bzrlib.branch import DivergedBranches, \
669
find_cached_branch, Branch
496
from branch import find_branch, DivergedBranches, NoSuchRevision
670
497
from shutil import rmtree
671
from meta_store import CachedStore
673
cache_root = tempfile.mkdtemp()
677
elif len(revision) > 1:
678
raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
682
br_from = find_cached_branch(from_location, cache_root)
684
if e.errno == errno.ENOENT:
685
raise BzrCommandError('Source location "%s" does not'
686
' exist.' % to_location)
690
if to_location is None:
691
to_location = os.path.basename(from_location.rstrip("/\\"))
694
os.mkdir(to_location)
696
if e.errno == errno.EEXIST:
697
raise BzrCommandError('Target directory "%s" already'
698
' exists.' % to_location)
699
if e.errno == errno.ENOENT:
700
raise BzrCommandError('Parent of "%s" does not exist.' %
704
br_to = Branch(to_location, init=True)
706
br_to.set_root_id(br_from.get_root_id())
709
if revision[0] is None:
710
revno = br_from.revno()
712
revno, rev_id = br_from.get_revision_info(revision[0])
714
br_to.update_revisions(br_from, stop_revision=revno)
715
except bzrlib.errors.NoSuchRevision:
717
msg = "The branch %s has no revision %d." % (from_location,
719
raise BzrCommandError(msg)
721
merge((to_location, -1), (to_location, 0), this_dir=to_location,
722
check_clean=False, ignore_zero=True)
723
from_location = pull_loc(br_from)
724
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
499
br_from = find_branch(from_location)
501
if e.errno == errno.ENOENT:
502
raise BzrCommandError('Source location "%s" does not exist.' %
507
if to_location is None:
508
to_location = os.path.basename(from_location.rstrip("/\\"))
511
os.mkdir(to_location)
513
if e.errno == errno.EEXIST:
514
raise BzrCommandError('Target directory "%s" already exists.' %
516
if e.errno == errno.ENOENT:
517
raise BzrCommandError('Parent of "%s" does not exist.' %
521
br_to = Branch(to_location, init=True)
524
br_to.update_revisions(br_from, stop_revision=revision)
525
except NoSuchRevision:
527
msg = "The branch %s has no revision %d." % (from_location,
529
raise BzrCommandError(msg)
530
merge((to_location, -1), (to_location, 0), this_dir=to_location,
531
check_clean=False, ignore_zero=True)
532
from_location = pull_loc(br_from)
533
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
729
536
def pull_loc(branch):
939
734
"""List files modified in working tree."""
942
from bzrlib.delta import compare_trees
945
td = compare_trees(b.basis_tree(), b.working_tree())
947
for path, id, kind in td.modified:
739
inv = b.read_working_inventory()
740
sc = statcache.update_cache(b, inv)
741
basis = b.basis_tree()
742
basis_inv = basis.inventory
744
# We used to do this through iter_entries(), but that's slow
745
# when most of the files are unmodified, as is usually the
746
# case. So instead we iterate by inventory entry, and only
747
# calculate paths as necessary.
749
for file_id in basis_inv:
750
cacheentry = sc.get(file_id)
751
if not cacheentry: # deleted
753
ie = basis_inv[file_id]
754
if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
755
path = inv.id2path(file_id)
986
795
-r revision requests a specific revision, -r :end or -r begin: are
989
--message allows you to give a regular expression, which will be evaluated
990
so that only matching entries will be displayed.
992
798
TODO: Make --revision support uuid: and hash: [future tag:] notation.
996
802
takes_args = ['filename?']
997
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
998
'long', 'message', 'short',]
803
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
1000
805
def run(self, filename=None, timezone='original',
1008
from bzrlib.branch import find_branch
1009
from bzrlib.log import log_formatter, show_log
810
from bzrlib import show_log, find_branch
1012
813
direction = (forward and 'forward') or 'reverse'
1211
996
If no revision is specified this exports the last committed revision.
1213
998
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1214
given, try to find the format with the extension. If no extension
1215
is found exports to a directory (equivalent to --format=dir).
1217
Root may be the top directory for tar, tgz and tbz2 formats. If none
1218
is given, the top directory will be the root name of the file."""
999
given, exports to a directory (equivalent to --format=dir)."""
1219
1000
# TODO: list known exporters
1220
1001
takes_args = ['dest']
1221
takes_options = ['revision', 'format', 'root']
1222
def run(self, dest, revision=None, format=None, root=None):
1224
b = find_branch('.')
1225
if revision is None:
1226
rev_id = b.last_patch()
1002
takes_options = ['revision', 'format']
1003
def run(self, dest, revision=None, format='dir'):
1005
if revision == None:
1006
rh = b.revision_history()[-1]
1228
if len(revision) != 1:
1229
raise BzrError('bzr export --revision takes exactly 1 argument')
1230
revno, rev_id = b.get_revision_info(revision[0])
1231
t = b.revision_tree(rev_id)
1232
root, ext = os.path.splitext(dest)
1234
if ext in (".tar",):
1236
elif ext in (".gz", ".tgz"):
1238
elif ext in (".bz2", ".tbz2"):
1242
t.export(dest, format, root)
1008
rh = b.lookup_revision(int(revision))
1009
t = b.revision_tree(rh)
1010
t.export(dest, format)
1245
1013
class cmd_cat(Command):
1283
1047
TODO: Strict commit that fails if there are unknown or deleted files.
1285
1049
takes_args = ['selected*']
1286
takes_options = ['message', 'file', 'verbose', 'unchanged']
1050
takes_options = ['message', 'file', 'verbose']
1287
1051
aliases = ['ci', 'checkin']
1289
# TODO: Give better message for -s, --summary, used by tla people
1291
def run(self, message=None, file=None, verbose=True, selected_list=None,
1293
from bzrlib.errors import PointlessCommit
1294
from bzrlib.osutils import get_text_message
1053
def run(self, message=None, file=None, verbose=True, selected_list=None):
1054
from bzrlib.commit import commit
1296
1056
## Warning: shadows builtin file()
1297
1057
if not message and not file:
1298
# FIXME: Ugly; change status code to send to a provided function?
1302
catcher = cStringIO.StringIO()
1303
sys.stdout = catcher
1304
cmd_status({"file_list":selected_list}, {})
1305
info = catcher.getvalue()
1307
message = get_text_message(info)
1310
raise BzrCommandError("please specify a commit message",
1311
["use either --message or --file"])
1058
raise BzrCommandError("please specify a commit message",
1059
["use either --message or --file"])
1312
1060
elif message and file:
1313
1061
raise BzrCommandError("please specify either --message or --file")
1388
1104
takes_options = ['email']
1390
1106
def run(self, email=False):
1392
b = bzrlib.branch.find_branch('.')
1397
print bzrlib.osutils.user_email(b)
1108
print bzrlib.osutils.user_email()
1399
print bzrlib.osutils.username(b)
1110
print bzrlib.osutils.username()
1402
1113
class cmd_selftest(Command):
1403
1114
"""Run internal test suite"""
1405
takes_options = ['verbose']
1406
def run(self, verbose=False):
1408
1117
from bzrlib.selftest import selftest
1410
# we don't want progress meters from the tests to go to the
1413
save_ui = bzrlib.ui.ui_factory
1415
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1416
return int(not selftest(verbose=verbose))
1418
bzrlib.ui.ui_factory = save_ui
1118
return int(not selftest())
1421
1121
class cmd_version(Command):
1464
1162
if parsed[1] == "":
1468
parsed[1] = int(parsed[1])
1470
pass # We can allow stuff like ./@revid:blahblahblah
1472
assert parsed[1] >=0
1165
parsed[1] = int(parsed[1])
1166
assert parsed[1] >=0
1474
1168
parsed = [spec, None]
1479
class cmd_find_merge_base(Command):
1480
"""Find and print a base revision for merging two branches.
1482
TODO: Options to specify revisions on either side, as if
1483
merging only part of the history.
1485
takes_args = ['branch', 'other']
1488
def run(self, branch, other):
1489
branch1 = find_branch(branch)
1490
branch2 = find_branch(other)
1492
base_revno, base_revid = branch1.common_ancestor(branch2)
1494
if base_revno is None:
1495
raise bzrlib.errors.UnrelatedBranches()
1497
print 'merge base is revision %s' % base_revid
1498
print ' r%-6d in %s' % (base_revno, branch)
1500
other_revno = branch2.revision_id_to_revno(base_revid)
1502
print ' r%-6d in %s' % (other_revno, other)
1506
1173
class cmd_merge(Command):
1507
"""Perform a three-way merge.
1509
The branch is the branch you will merge from. By default, it will merge
1510
the latest revision. If you specify a revision, that revision will be
1511
merged. If you specify two revisions, the first will be used as a BASE,
1512
and the second one as OTHER. Revision numbers are always relative to the
1517
To merge the latest revision from bzr.dev
1518
bzr merge ../bzr.dev
1520
To merge changes up to and including revision 82 from bzr.dev
1521
bzr merge -r 82 ../bzr.dev
1523
To merge the changes introduced by 82, without previous changes:
1524
bzr merge -r 81..82 ../bzr.dev
1174
"""Perform a three-way merge of trees.
1176
The SPEC parameters are working tree or revision specifiers. Working trees
1177
are specified using standard paths or urls. No component of a directory
1178
path may begin with '@'.
1180
Working tree examples: '.', '..', 'foo@', but NOT 'foo/@bar'
1182
Revisions are specified using a dirname/@revno pair, where dirname is the
1183
branch directory and revno is the revision within that branch. If no revno
1184
is specified, the latest revision is used.
1186
Revision examples: './@127', 'foo/@', '../@1'
1188
The OTHER_SPEC parameter is required. If the BASE_SPEC parameter is
1189
not supplied, the common ancestor of OTHER_SPEC the current branch is used
1526
1192
merge refuses to run if there are any uncommitted changes, unless
1527
1193
--force is given.
1529
takes_args = ['branch?']
1530
takes_options = ['revision', 'force', 'merge-type']
1195
takes_args = ['other_spec', 'base_spec?']
1196
takes_options = ['force']
1532
def run(self, branch='.', revision=None, force=False,
1198
def run(self, other_spec, base_spec=None, force=False):
1534
1199
from bzrlib.merge import merge
1535
from bzrlib.merge_core import ApplyMerge3
1536
if merge_type is None:
1537
merge_type = ApplyMerge3
1539
if revision is None or len(revision) < 1:
1541
other = (branch, -1)
1543
if len(revision) == 1:
1544
other = (branch, revision[0])
1547
assert len(revision) == 2
1548
if None in revision:
1549
raise BzrCommandError(
1550
"Merge doesn't permit that revision specifier.")
1551
base = (branch, revision[0])
1552
other = (branch, revision[1])
1554
merge(other, base, check_clean=(not force), merge_type=merge_type)
1200
merge(parse_spec(other_spec), parse_spec(base_spec),
1201
check_clean=(not force))
1557
1204
class cmd_revert(Command):
1558
1205
"""Reverse all changes since the last commit.
1560
Only versioned files are affected. Specify filenames to revert only
1561
those files. By default, any files that are changed will be backed up
1562
first. Backup files have a '~' appended to their name.
1207
Only versioned files are affected.
1209
TODO: Store backups of any files that will be reverted, so
1210
that the revert can be undone.
1564
takes_options = ['revision', 'no-backup']
1565
takes_args = ['file*']
1566
aliases = ['merge-revert']
1212
takes_options = ['revision']
1568
def run(self, revision=None, no_backup=False, file_list=None):
1214
def run(self, revision=-1):
1569
1215
from bzrlib.merge import merge
1570
if file_list is not None:
1571
if len(file_list) == 0:
1572
raise BzrCommandError("No files specified")
1573
if revision is None:
1575
elif len(revision) != 1:
1576
raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1577
merge(('.', revision[0]), parse_spec('.'),
1216
merge(('.', revision), parse_spec('.'),
1578
1217
check_clean=False,
1580
backup_files=not no_backup,
1581
file_list=file_list)
1584
1221
class cmd_assert_fail(Command):
1592
1229
"""Show help on a command or other topic.
1594
1231
For a list of all available commands, say 'bzr help commands'."""
1595
takes_options = ['long']
1596
1232
takes_args = ['topic?']
1597
1233
aliases = ['?']
1599
def run(self, topic=None, long=False):
1235
def run(self, topic=None):
1601
if topic is None and long:
1603
1237
help.help(topic)
1606
class cmd_shell_complete(Command):
1607
"""Show appropriate completions for context.
1609
For a list of all available commands, say 'bzr shell-complete'."""
1610
takes_args = ['context?']
1614
def run(self, context=None):
1615
import shellcomplete
1616
shellcomplete.shellcomplete(context)
1619
class cmd_missing(Command):
1620
"""What is missing in this branch relative to other branch.
1622
takes_args = ['remote?']
1623
aliases = ['mis', 'miss']
1624
# We don't have to add quiet to the list, because
1625
# unknown options are parsed as booleans
1626
takes_options = ['verbose', 'quiet']
1628
def run(self, remote=None, verbose=False, quiet=False):
1629
from bzrlib.branch import find_branch, DivergedBranches
1630
from bzrlib.errors import BzrCommandError
1631
from bzrlib.missing import get_parent, show_missing
1633
if verbose and quiet:
1634
raise BzrCommandError('Cannot pass both quiet and verbose')
1636
b = find_branch('.')
1637
parent = get_parent(b)
1640
raise BzrCommandError("No missing location known or specified.")
1643
print "Using last location: %s" % parent
1645
elif parent is None:
1646
# We only update x-pull if it did not exist, missing should not change the parent
1647
b.controlfile('x-pull', 'wb').write(remote + '\n')
1648
br_remote = find_branch(remote)
1650
return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1654
class cmd_plugins(Command):
1240
class cmd_update_stat_cache(Command):
1241
"""Update stat-cache mapping inodes to SHA-1 hashes.
1243
For testing only."""
1658
import bzrlib.plugin
1659
from inspect import getdoc
1660
from pprint import pprint
1661
for plugin in bzrlib.plugin.all_plugins:
1662
print plugin.__path__[0]
1665
print '\t', d.split('\n')[0]
1667
#pprint(bzrlib.plugin.all_plugins)
1248
statcache.update_cache(b.base, b.read_working_inventory())
1724
1298
>>> parse_args('commit --message=biter'.split())
1725
1299
(['commit'], {'message': u'biter'})
1726
1300
>>> parse_args('log -r 500'.split())
1727
(['log'], {'revision': [500]})
1728
>>> parse_args('log -r500..600'.split())
1301
(['log'], {'revision': 500})
1302
>>> parse_args('log -r500:600'.split())
1729
1303
(['log'], {'revision': [500, 600]})
1730
>>> parse_args('log -vr500..600'.split())
1304
>>> parse_args('log -vr500:600'.split())
1731
1305
(['log'], {'verbose': True, 'revision': [500, 600]})
1732
>>> parse_args('log -rv500..600'.split()) #the r takes an argument
1733
(['log'], {'revision': ['v500', 600]})
1306
>>> parse_args('log -rv500:600'.split()) #the r takes an argument
1307
Traceback (most recent call last):
1309
ValueError: invalid literal for int(): v500
1429
def _parse_master_args(argv):
1430
"""Parse the arguments that always go with the original command.
1431
These are things like bzr --no-plugins, etc.
1433
There are now 2 types of option flags. Ones that come *before* the command,
1434
and ones that come *after* the command.
1435
Ones coming *before* the command are applied against all possible commands.
1436
And are generally applied before plugins are loaded.
1438
The current list are:
1439
--builtin Allow plugins to load, but don't let them override builtin commands,
1440
they will still be allowed if they do not override a builtin.
1441
--no-plugins Don't load any plugins. This lets you get back to official source
1443
--profile Enable the hotspot profile before running the command.
1444
For backwards compatibility, this is also a non-master option.
1445
--version Spit out the version of bzr that is running and exit.
1446
This is also a non-master option.
1447
--help Run help and exit, also a non-master option (I think that should stay, though)
1449
>>> argv, opts = _parse_master_args(['bzr', '--test'])
1450
Traceback (most recent call last):
1452
BzrCommandError: Invalid master option: 'test'
1453
>>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
1456
>>> print opts['version']
1458
>>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
1460
['command', '--more-options']
1461
>>> print opts['profile']
1463
>>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
1466
>>> print opts['no-plugins']
1468
>>> print opts['profile']
1470
>>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
1472
['command', '--profile']
1473
>>> print opts['profile']
1476
master_opts = {'builtin':False,
1483
# This is the point where we could hook into argv[0] to determine
1484
# what front-end is supposed to be run
1485
# For now, we are just ignoring it.
1486
cmd_name = argv.pop(0)
1488
if arg[:2] != '--': # at the first non-option, we return the rest
1490
arg = arg[2:] # Remove '--'
1491
if arg not in master_opts:
1492
# We could say that this is not an error, that we should
1493
# just let it be handled by the main section instead
1494
raise BzrCommandError('Invalid master option: %r' % arg)
1495
argv.pop(0) # We are consuming this entry
1496
master_opts[arg] = True
1497
return argv, master_opts
1854
1501
def run_bzr(argv):
1855
1502
"""Execute a command.
1857
1504
This is similar to main(), but without all the trappings for
1858
1505
logging and error handling.
1861
The command-line arguments, without the program name from argv[0]
1863
Returns a command status or raises an exception.
1865
Special master options: these must come before the command because
1866
they control how the command is interpreted.
1869
Do not load plugin modules at all
1872
Only use builtin commands. (Plugins are still allowed to change
1876
Run under the Python profiler.
1879
1507
argv = [a.decode(bzrlib.user_encoding) for a in argv]
1881
opt_profile = opt_no_plugins = opt_builtin = False
1883
# --no-plugins is handled specially at a very early stage. We need
1884
# to load plugins before doing other command parsing so that they
1885
# can override commands, but this needs to happen first.
1888
if a == '--profile':
1890
elif a == '--no-plugins':
1891
opt_no_plugins = True
1892
elif a == '--builtin':
1898
if not opt_no_plugins:
1899
from bzrlib.plugin import load_plugins
1902
args, opts = parse_args(argv)
1905
from bzrlib.help import help
1912
if 'version' in opts:
1917
from bzrlib.help import help
1921
cmd = str(args.pop(0))
1923
canonical_cmd, cmd_class = \
1924
get_cmd_class(cmd, plugins_override=not opt_builtin)
1510
# some options like --builtin and --no-plugins have special effects
1511
argv, master_opts = _parse_master_args(argv)
1512
if 'no-plugins' not in master_opts:
1513
bzrlib.load_plugins()
1515
args, opts = parse_args(argv)
1517
if master_opts['help']:
1518
from bzrlib.help import help
1526
from bzrlib.help import help
1532
elif 'version' in opts:
1535
elif args and args[0] == 'builtin':
1536
include_plugins=False
1538
cmd = str(args.pop(0))
1545
plugins_override = not (master_opts['builtin'])
1546
canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
1548
profile = master_opts['profile']
1549
# For backwards compatibility, I would rather stick with --profile being a
1550
# master/global option
1551
if 'profile' in opts:
1926
1555
# check options are reasonable
1927
1556
allowed = cmd_class.takes_options
1961
1590
return cmd_class(cmdopts, cmdargs).status
1593
def _report_exception(summary, quiet=False):
1595
log_error('bzr: ' + summary)
1596
bzrlib.trace.log_exception()
1599
tb = sys.exc_info()[2]
1600
exinfo = traceback.extract_tb(tb)
1602
sys.stderr.write(' at %s:%d in %s()\n' % exinfo[-1][:3])
1603
sys.stderr.write(' see ~/.bzr.log for debug information\n')
1964
1607
def main(argv):
1967
bzrlib.trace.log_startup(argv)
1969
bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
1610
bzrlib.open_tracefile(argv)
1973
return run_bzr(argv[1:])
1975
# do this here inside the exception wrappers to catch EPIPE
1977
except BzrCommandError, e:
1978
# command line syntax error, etc
1982
bzrlib.trace.log_exception()
1984
except AssertionError, e:
1985
bzrlib.trace.log_exception('assertion failed: ' + str(e))
1987
except KeyboardInterrupt, e:
1988
bzrlib.trace.note('interrupted')
1990
except Exception, e:
1992
if (isinstance(e, IOError)
1993
and hasattr(e, 'errno')
1994
and e.errno == errno.EPIPE):
1995
bzrlib.trace.note('broken pipe')
1998
bzrlib.trace.log_exception()
1615
return run_bzr(argv)
1617
# do this here inside the exception wrappers to catch EPIPE
1620
quiet = isinstance(e, (BzrCommandError))
1621
_report_exception('error: ' + e.args[0], quiet=quiet)
1624
# some explanation or hints
1627
except AssertionError, e:
1628
msg = 'assertion failed'
1630
msg += ': ' + str(e)
1631
_report_exception(msg)
1633
except KeyboardInterrupt, e:
1634
_report_exception('interrupted', quiet=True)
1636
except Exception, e:
1638
if (isinstance(e, IOError)
1639
and hasattr(e, 'errno')
1640
and e.errno == errno.EPIPE):
1644
msg = str(e).rstrip('\n')
1645
_report_exception(msg, quiet)
1648
bzrlib.trace.close_trace()
2002
1651
if __name__ == '__main__':