96
37
assert cmd.startswith("cmd_")
97
38
return cmd[4:].replace('_','-')
100
def _builtin_commands():
101
import bzrlib.builtins
103
builtins = bzrlib.builtins.__dict__
104
for name in builtins:
105
if name.startswith("cmd_"):
106
real_name = _unsquish_command_name(name)
107
r[real_name] = builtins[name]
111
def builtin_command_names():
112
"""Return list of builtin command names."""
113
return _builtin_commands().keys()
116
def plugin_command_names():
117
return plugin_cmds.keys()
120
def _get_cmd_dict(plugins_override=True):
121
"""Return name->class mapping for all commands."""
122
d = _builtin_commands()
124
d.update(plugin_cmds)
128
def get_all_cmds(plugins_override=True):
40
def _parse_revision_str(revstr):
41
"""This handles a revision string -> revno.
43
There are several possibilities:
46
'234:345' -> [234, 345]
50
In the future we will also support:
51
'uuid:blah-blah-blah' -> ?
52
'hash:blahblahblah' -> ?
56
if revstr.find(':') != -1:
57
revs = revstr.split(':')
59
raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
64
revs[0] = int(revs[0])
69
revs[1] = int(revs[1])
129
75
"""Return canonical name and class for all registered commands."""
130
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
134
def get_cmd_object(cmd_name, plugins_override=True):
76
for k, v in globals().iteritems():
77
if k.startswith("cmd_"):
78
yield _unsquish_command_name(k), v
80
def get_cmd_class(cmd):
135
81
"""Return the canonical name and command class for a command.
138
If true, plugin commands can override builtins.
140
from bzrlib.externalcommand import ExternalCommand
142
# We want only 'ascii' command names, but the user may have typed
143
# in a Unicode name. In that case, they should just get a
144
# 'command not found' error later.
145
# In the future, we may actually support Unicode command names.
83
cmd = str(cmd) # not unicode
147
85
# first look up this command under the specified name
148
cmds = _get_cmd_dict(plugins_override=plugins_override)
150
return cmds[cmd_name]()
87
return cmd, globals()[_squish_command_name(cmd)]
154
91
# look for any command which claims this as an alias
155
for real_cmd_name, cmd_class in cmds.iteritems():
156
if cmd_name in cmd_class.aliases:
159
cmd_obj = ExternalCommand.find_command(cmd_name)
163
raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
92
for cmdname, cmdclass in get_all_cmds():
93
if cmd in cmdclass.aliases:
94
return cmdname, cmdclass
96
cmdclass = ExternalCommand.find_command(cmd)
100
raise BzrCommandError("unknown command %r" % cmd)
166
103
class Command(object):
167
104
"""Base class for commands.
169
Commands are the heart of the command-line bzr interface.
171
The command object mostly handles the mapping of command-line
172
parameters into one or more bzrlib operations, and of the results
175
Commands normally don't have any state. All their arguments are
176
passed in to the run method. (Subclasses may take a different
177
policy if the behaviour of the instance needs to depend on e.g. a
178
shell plugin and not just its Python class.)
180
106
The docstring for an actual command should give a single-line
181
107
summary, then a complete description of the command. A grammar
182
108
description will be inserted.
185
Other accepted names for this command.
188
111
List of argument forms, marked with whether they are optional,
193
['to_location', 'from_branch?', 'file*']
195
'to_location' is required
196
'from_branch' is optional
197
'file' can be specified 0 or more times
200
List of options that may be given for this command. These can
201
be either strings, referring to globally-defined options,
202
or option objects. Retrieve through options().
115
List of options that may be given for this command.
205
If true, this command isn't advertised. This is typically
206
for commands intended for expert users.
209
Command objects will get a 'outf' attribute, which has been
210
setup to properly handle encoding of unicode strings.
211
encoding_type determines what will happen when characters cannot
213
strict - abort if we cannot decode
214
replace - put in a bogus character (typically '?')
215
exact - do not encode sys.stdout
217
NOTE: by default on Windows, sys.stdout is opened as a text
218
stream, therefore LF line-endings are converted to CRLF.
219
When a command uses encoding_type = 'exact', then
220
sys.stdout is forced to be a binary stream, and line-endings
118
If true, this command isn't advertised.
226
123
takes_options = []
227
encoding_type = 'strict'
232
"""Construct an instance of this command."""
233
if self.__doc__ == Command.__doc__:
234
warn("No help message set for %r" % self)
237
"""Return dict of valid options for this command.
239
Maps from long option name to option object."""
241
r['help'] = option.Option.OPTIONS['help']
242
for o in self.takes_options:
243
if isinstance(o, basestring):
244
o = option.Option.OPTIONS[o]
248
def _setup_outf(self):
249
"""Return a file linked to stdout, which has proper encoding."""
250
assert self.encoding_type in ['strict', 'exact', 'replace']
252
# Originally I was using self.stdout, but that looks
253
# *way* too much like sys.stdout
254
if self.encoding_type == 'exact':
255
# force sys.stdout to be binary stream on win32
256
if sys.platform == 'win32':
257
fileno = getattr(sys.stdout, 'fileno', None)
260
msvcrt.setmode(fileno(), os.O_BINARY)
261
self.outf = sys.stdout
264
output_encoding = osutils.get_terminal_encoding()
266
# use 'replace' so that we don't abort if trying to write out
267
# in e.g. the default C locale.
268
self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
269
# For whatever reason codecs.getwriter() does not advertise its encoding
270
# it just returns the encoding of the wrapped file, which is completely
271
# bogus. So set the attribute, so we can find the correct encoding later.
272
self.outf.encoding = output_encoding
274
@deprecated_method(zero_eight)
275
def run_argv(self, argv):
276
"""Parse command line and run.
278
See run_argv_aliases for the 0.8 and beyond api.
280
return self.run_argv_aliases(argv)
282
def run_argv_aliases(self, argv, alias_argv=None):
283
"""Parse the command line and run with extra aliases in alias_argv."""
285
warn("Passing None for [] is deprecated from bzrlib 0.10",
286
DeprecationWarning, stacklevel=2)
288
args, opts = parse_args(self, argv, alias_argv)
289
if 'help' in opts: # e.g. bzr add --help
290
from bzrlib.help import help_on_command
291
help_on_command(self.name())
293
# mix arguments and options into one dictionary
294
cmdargs = _match_argform(self.name(), self.takes_args, args)
296
for k, v in opts.items():
297
cmdopts[k.replace('-', '_')] = v
299
all_cmd_args = cmdargs.copy()
300
all_cmd_args.update(cmdopts)
304
return self.run(**all_cmd_args)
127
def __init__(self, options, arguments):
128
"""Construct and run the command.
130
Sets self.status to the return value of run()."""
131
assert isinstance(options, dict)
132
assert isinstance(arguments, dict)
133
cmdargs = options.copy()
134
cmdargs.update(arguments)
135
assert self.__doc__ != Command.__doc__, \
136
("No help message set for %r" % self)
137
self.status = self.run(**cmdargs)
307
"""Actually run the command.
141
"""Override this in sub-classes.
309
143
This is invoked with the options and arguments bound to
310
144
keyword parameters.
312
Return 0 or None if the command was successful, or a non-zero
313
shell error code if not. It's OK for this method to allow
314
an exception to raise up.
316
raise NotImplementedError('no implementation of command %r'
320
"""Return help message for this class."""
321
from inspect import getdoc
322
if self.__doc__ is Command.__doc__:
327
return _unsquish_command_name(self.__class__.__name__)
329
def plugin_name(self):
330
"""Get the name of the plugin that provides this command.
332
:return: The name of the plugin or None if the command is builtin.
334
mod_parts = self.__module__.split('.')
335
if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
341
# Technically, this function hasn't been use in a *really* long time
342
# but we are only deprecating it now.
343
@deprecated_function(zero_eleven)
146
Return 0 or None if the command was successful, or a shell
152
class ExternalCommand(Command):
153
"""Class to wrap external commands.
155
We cheat a little here, when get_cmd_class() calls us we actually give it back
156
an object we construct that has the appropriate path, help, options etc for the
159
When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
160
method, which we override to call the Command.__init__ method. That then calls
161
our run method which is pretty straight forward.
163
The only wrinkle is that we have to map bzr's dictionary of options and arguments
164
back into command line options and arguments for the script.
167
def find_command(cls, cmd):
168
bzrpath = os.environ.get('BZRPATH', '')
170
for dir in bzrpath.split(':'):
171
path = os.path.join(dir, cmd)
172
if os.path.isfile(path):
173
return ExternalCommand(path)
177
find_command = classmethod(find_command)
179
def __init__(self, path):
182
# TODO: If either of these fail, we should detect that and
183
# assume that path is not really a bzr plugin after all.
185
pipe = os.popen('%s --bzr-usage' % path, 'r')
186
self.takes_options = pipe.readline().split()
187
self.takes_args = pipe.readline().split()
190
pipe = os.popen('%s --bzr-help' % path, 'r')
191
self.__doc__ = pipe.read()
194
def __call__(self, options, arguments):
195
Command.__init__(self, options, arguments)
198
def run(self, **kargs):
206
if OPTIONS.has_key(name):
208
opts.append('--%s' % name)
209
if value is not None and value is not True:
210
opts.append(str(value))
212
# it's an arg, or arg list
213
if type(value) is not list:
219
self.status = os.spawnv(os.P_WAIT, self.path, [self.path] + opts + args)
223
class cmd_status(Command):
224
"""Display status summary.
226
This reports on versioned and unknown files, reporting them
227
grouped by state. Possible states are:
230
Versioned in the working copy but not in the previous revision.
233
Versioned in the previous revision but removed or deleted
237
Path of this file changed from the previous revision;
238
the text may also have changed. This includes files whose
239
parent directory was renamed.
242
Text has changed since the previous revision.
245
Nothing about this file has changed since the previous revision.
246
Only shown with --all.
249
Not versioned and not matching an ignore pattern.
251
To see ignored files use 'bzr ignored'. For details in the
252
changes to file texts, use 'bzr diff'.
254
If no arguments are specified, the status of the entire working
255
directory is shown. Otherwise, only the status of the specified
256
files or directories is reported. If a directory is given, status
257
is reported for everything inside that directory.
259
takes_args = ['file*']
260
takes_options = ['all', 'show-ids']
261
aliases = ['st', 'stat']
263
def run(self, all=False, show_ids=False, file_list=None):
265
b = Branch(file_list[0], lock_mode='r')
266
file_list = [b.relpath(x) for x in file_list]
267
# special case: only one path was given and it's the root
269
if file_list == ['']:
272
b = Branch('.', lock_mode='r')
274
status.show_status(b, show_unchanged=all, show_ids=show_ids,
275
specific_files=file_list)
278
class cmd_cat_revision(Command):
279
"""Write out metadata for a revision."""
282
takes_args = ['revision_id']
284
def run(self, revision_id):
285
Branch('.').get_revision(revision_id).write_xml(sys.stdout)
288
class cmd_revno(Command):
289
"""Show current revision number.
291
This is equal to the number of revisions on this branch."""
293
print Branch('.').revno()
296
class cmd_add(Command):
297
"""Add specified files or directories.
299
In non-recursive mode, all the named items are added, regardless
300
of whether they were previously ignored. A warning is given if
301
any of the named files are already versioned.
303
In recursive mode (the default), files are treated the same way
304
but the behaviour for directories is different. Directories that
305
are already versioned do not give a warning. All directories,
306
whether already versioned or not, are searched for files or
307
subdirectories that are neither versioned or ignored, and these
308
are added. This search proceeds recursively into versioned
311
Therefore simply saying 'bzr add .' will version all files that
312
are currently unknown.
314
TODO: Perhaps adding a file whose directly is not versioned should
315
recursively add that parent, rather than giving an error?
317
takes_args = ['file+']
318
takes_options = ['verbose']
320
def run(self, file_list, verbose=False):
321
bzrlib.add.smart_add(file_list, verbose)
324
class cmd_relpath(Command):
325
"""Show path of a file relative to root"""
326
takes_args = ['filename']
328
def run(self, filename):
329
print Branch(filename).relpath(filename)
333
class cmd_inventory(Command):
334
"""Show inventory of the current working copy or a revision."""
335
takes_options = ['revision']
337
def run(self, revision=None):
340
inv = b.read_working_inventory()
342
inv = b.get_revision_inventory(b.lookup_revision(revision))
344
for path, entry in inv.entries():
345
print '%-50s %s' % (entry.file_id, path)
348
class cmd_move(Command):
349
"""Move files to a different directory.
354
The destination must be a versioned directory in the same branch.
356
takes_args = ['source$', 'dest']
357
def run(self, source_list, dest):
360
b.move([b.relpath(s) for s in source_list], b.relpath(dest))
363
class cmd_rename(Command):
364
"""Change the name of an entry.
367
bzr rename frob.c frobber.c
368
bzr rename src/frob.c lib/frob.c
370
It is an error if the destination name exists.
372
See also the 'move' command, which moves files into a different
373
directory without changing their name.
375
TODO: Some way to rename multiple files without invoking bzr for each
377
takes_args = ['from_name', 'to_name']
379
def run(self, from_name, to_name):
381
b.rename_one(b.relpath(from_name), b.relpath(to_name))
385
class cmd_renames(Command):
386
"""Show list of renamed files.
388
TODO: Option to show renames between two historical versions.
390
TODO: Only show renames under dir, rather than in the whole branch.
392
takes_args = ['dir?']
394
def run(self, dir='.'):
396
old_inv = b.basis_tree().inventory
397
new_inv = b.read_working_inventory()
399
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
401
for old_name, new_name in renames:
402
print "%s => %s" % (old_name, new_name)
405
class cmd_info(Command):
406
"""Show statistical information about a branch."""
407
takes_args = ['branch?']
409
def run(self, branch=None):
412
from branch import find_branch
413
b = find_branch(branch)
417
class cmd_remove(Command):
418
"""Make a file unversioned.
420
This makes bzr stop tracking changes to a versioned file. It does
421
not delete the working copy.
423
takes_args = ['file+']
424
takes_options = ['verbose']
426
def run(self, file_list, verbose=False):
427
b = Branch(file_list[0])
428
b.remove([b.relpath(f) for f in file_list], verbose=verbose)
431
class cmd_file_id(Command):
432
"""Print file_id of a particular file or directory.
434
The file_id is assigned when the file is first added and remains the
435
same through all revisions where the file exists, even when it is
439
takes_args = ['filename']
440
def run(self, filename):
442
i = b.inventory.path2id(b.relpath(filename))
444
bailout("%r is not a versioned file" % filename)
449
class cmd_file_path(Command):
450
"""Print path of file_ids to a file or directory.
452
This prints one line for each directory down to the target,
453
starting at the branch root."""
455
takes_args = ['filename']
456
def run(self, filename):
459
fid = inv.path2id(b.relpath(filename))
461
bailout("%r is not a versioned file" % filename)
462
for fip in inv.get_idpath(fid):
466
class cmd_revision_history(Command):
467
"""Display list of revision ids on this branch."""
469
for patchid in Branch('.').revision_history():
473
class cmd_directories(Command):
474
"""Display list of versioned directories in this branch."""
476
for name, ie in Branch('.').read_working_inventory().directories():
483
class cmd_init(Command):
484
"""Make a directory into a versioned branch.
486
Use this to create an empty branch, or before importing an
489
Recipe for importing a tree of files:
494
bzr commit -m 'imported project'
497
Branch('.', init=True)
500
class cmd_diff(Command):
501
"""Show differences in working tree.
503
If files are listed, only the changes in those files are listed.
504
Otherwise, all changes for the tree are listed.
506
TODO: Given two revision arguments, show the difference between them.
508
TODO: Allow diff across branches.
510
TODO: Option to use external diff command; could be GNU diff, wdiff,
513
TODO: Python difflib is not exactly the same as unidiff; should
514
either fix it up or prefer to use an external diff.
516
TODO: If a directory is given, diff everything under that.
518
TODO: Selected-file diff is inefficient and doesn't show you
521
TODO: This probably handles non-Unix newlines poorly.
524
takes_args = ['file*']
525
takes_options = ['revision']
528
def run(self, revision=None, file_list=None):
529
from bzrlib.diff import show_diff
530
from bzrlib import find_branch
533
b = find_branch(file_list[0], lock_mode='r')
534
file_list = [b.relpath(f) for f in file_list]
535
if file_list == ['']:
536
# just pointing to top-of-tree
539
b = Branch('.', lock_mode='r')
541
show_diff(b, revision, specific_files=file_list)
547
class cmd_deleted(Command):
548
"""List files deleted in the working tree.
550
TODO: Show files deleted since a previous revision, or between two revisions.
552
def run(self, show_ids=False):
555
new = b.working_tree()
557
## TODO: Much more efficient way to do this: read in new
558
## directories with readdir, rather than stating each one. Same
559
## level of effort but possibly much less IO. (Or possibly not,
560
## if the directories are very large...)
562
for path, ie in old.inventory.iter_entries():
563
if not new.has_id(ie.file_id):
565
print '%-50s %s' % (path, ie.file_id)
570
class cmd_modified(Command):
571
"""List files modified in working tree."""
576
inv = b.read_working_inventory()
577
sc = statcache.update_cache(b, inv)
578
basis = b.basis_tree()
579
basis_inv = basis.inventory
581
# We used to do this through iter_entries(), but that's slow
582
# when most of the files are unmodified, as is usually the
583
# case. So instead we iterate by inventory entry, and only
584
# calculate paths as necessary.
586
for file_id in basis_inv:
587
cacheentry = sc.get(file_id)
588
if not cacheentry: # deleted
590
ie = basis_inv[file_id]
591
if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
592
path = inv.id2path(file_id)
597
class cmd_added(Command):
598
"""List files added in working tree."""
602
wt = b.working_tree()
603
basis_inv = b.basis_tree().inventory
606
if file_id in basis_inv:
608
path = inv.id2path(file_id)
609
if not os.access(b.abspath(path), os.F_OK):
615
class cmd_root(Command):
616
"""Show the tree root directory.
618
The root is the nearest enclosing directory with a .bzr control
620
takes_args = ['filename?']
621
def run(self, filename=None):
622
"""Print the branch root."""
623
from branch import find_branch
624
b = find_branch(filename)
625
print getattr(b, 'base', None) or getattr(b, 'baseurl')
628
class cmd_log(Command):
629
"""Show log of this branch.
631
To request a range of logs, you can use the command -r begin:end
632
-r revision requests a specific revision, -r :end or -r begin: are
635
TODO: Make --revision support uuid: and hash: [future tag:] notation.
639
takes_args = ['filename?']
640
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
642
def run(self, filename=None, timezone='original',
647
from bzrlib import show_log, find_branch
650
direction = (forward and 'forward') or 'reverse'
653
b = find_branch(filename, lock_mode='r')
654
fp = b.relpath(filename)
656
file_id = b.read_working_inventory().path2id(fp)
658
file_id = None # points to branch root
660
b = find_branch('.', lock_mode='r')
664
revision = [None, None]
665
elif isinstance(revision, int):
666
revision = [revision, revision]
671
assert len(revision) == 2
673
mutter('encoding log as %r' % bzrlib.user_encoding)
674
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout)
677
show_timezone=timezone,
682
start_revision=revision[0],
683
end_revision=revision[1])
687
class cmd_touching_revisions(Command):
688
"""Return revision-ids which affected a particular file.
690
A more user-friendly interface is "bzr log FILE"."""
692
takes_args = ["filename"]
693
def run(self, filename):
694
b = Branch(filename, lock_mode='r')
695
inv = b.read_working_inventory()
696
file_id = inv.path2id(b.relpath(filename))
697
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
698
print "%6d %s" % (revno, what)
701
class cmd_ls(Command):
702
"""List files in a tree.
704
TODO: Take a revision or remote path and list that tree instead.
707
def run(self, revision=None, verbose=False):
710
tree = b.working_tree()
712
tree = b.revision_tree(b.lookup_revision(revision))
714
for fp, fc, kind, fid in tree.list_files():
716
if kind == 'directory':
723
print '%-8s %s%s' % (fc, fp, kindch)
729
class cmd_unknowns(Command):
730
"""List unknown files"""
732
for f in Branch('.').unknowns():
737
class cmd_ignore(Command):
738
"""Ignore a command or pattern
740
To remove patterns from the ignore list, edit the .bzrignore file.
742
If the pattern contains a slash, it is compared to the whole path
743
from the branch root. Otherwise, it is comapred to only the last
744
component of the path.
746
Ignore patterns are case-insensitive on case-insensitive systems.
748
Note: wildcards must be quoted from the shell on Unix.
751
bzr ignore ./Makefile
754
takes_args = ['name_pattern']
756
def run(self, name_pattern):
757
from bzrlib.atomicfile import AtomicFile
761
ifn = b.abspath('.bzrignore')
763
if os.path.exists(ifn):
766
igns = f.read().decode('utf-8')
772
if igns and igns[-1] != '\n':
774
igns += name_pattern + '\n'
777
f = AtomicFile(ifn, 'wt')
778
f.write(igns.encode('utf-8'))
783
inv = b.working_tree().inventory
784
if inv.path2id('.bzrignore'):
785
mutter('.bzrignore is already versioned')
787
mutter('need to make new .bzrignore file versioned')
788
b.add(['.bzrignore'])
792
class cmd_ignored(Command):
793
"""List ignored files and the patterns that matched them.
795
See also: bzr ignore"""
797
tree = Branch('.').working_tree()
798
for path, file_class, kind, file_id in tree.list_files():
799
if file_class != 'I':
801
## XXX: Slightly inefficient since this was already calculated
802
pat = tree.is_ignored(path)
803
print '%-50s %s' % (path, pat)
806
class cmd_lookup_revision(Command):
807
"""Lookup the revision-id from a revision-number
810
bzr lookup-revision 33
813
takes_args = ['revno']
815
def run(self, revno):
819
raise BzrCommandError("not a valid revision-number: %r" % revno)
821
print Branch('.').lookup_revision(revno)
824
class cmd_export(Command):
825
"""Export past revision to destination directory.
827
If no revision is specified this exports the last committed revision."""
828
takes_args = ['dest']
829
takes_options = ['revision']
830
def run(self, dest, revision=None):
833
rh = b.revision_history()[-1]
835
rh = b.lookup_revision(int(revision))
836
t = b.revision_tree(rh)
840
class cmd_cat(Command):
841
"""Write a file's text from a previous revision."""
843
takes_options = ['revision']
844
takes_args = ['filename']
846
def run(self, filename, revision=None):
848
raise BzrCommandError("bzr cat requires a revision number")
850
b.print_file(b.relpath(filename), int(revision))
853
class cmd_local_time_offset(Command):
854
"""Show the offset in seconds from GMT to local time."""
857
print bzrlib.osutils.local_time_offset()
861
class cmd_commit(Command):
862
"""Commit changes into a new revision.
864
If selected files are specified, only changes to those files are
865
committed. If a directory is specified then its contents are also
868
A selected-file commit may fail in some cases where the committed
869
tree would be invalid, such as trying to commit a file in a
870
newly-added directory that is not itself committed.
872
TODO: Run hooks on tree to-be-committed, and after commit.
874
TODO: Strict commit that fails if there are unknown or deleted files.
876
takes_args = ['selected*']
877
takes_options = ['message', 'file', 'verbose']
878
aliases = ['ci', 'checkin']
880
def run(self, message=None, file=None, verbose=True, selected_list=None):
881
from bzrlib.commit import commit
883
## Warning: shadows builtin file()
884
if not message and not file:
885
raise BzrCommandError("please specify a commit message",
886
["use either --message or --file"])
887
elif message and file:
888
raise BzrCommandError("please specify either --message or --file")
892
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
895
commit(b, message, verbose=verbose, specific_files=selected_list)
898
class cmd_check(Command):
899
"""Validate consistency of branch history.
901
This command checks various invariants about the branch storage to
902
detect data corruption or bzr bugs.
904
takes_args = ['dir?']
905
def run(self, dir='.'):
907
bzrlib.check.check(Branch(dir))
911
class cmd_whoami(Command):
912
"""Show bzr user id."""
913
takes_options = ['email']
915
def run(self, email=False):
917
print bzrlib.osutils.user_email()
919
print bzrlib.osutils.username()
922
class cmd_selftest(Command):
923
"""Run internal test suite"""
926
failures, tests = 0, 0
928
import doctest, bzrlib.store
929
bzrlib.trace.verbose = False
931
for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
932
bzrlib.tree, bzrlib.commands, bzrlib.add:
933
mf, mt = doctest.testmod(m)
936
print '%-40s %3d tests' % (m.__name__, mt),
938
print '%3d FAILED!' % mf
942
print '%-40s %3d tests' % ('total', tests),
944
print '%3d FAILED!' % failures
952
class cmd_version(Command):
953
"""Show version of bzr"""
958
print "bzr (bazaar-ng) %s" % bzrlib.__version__
959
print bzrlib.__copyright__
960
print "http://bazaar-ng.org/"
962
print "bzr comes with ABSOLUTELY NO WARRANTY. bzr is free software, and"
963
print "you may use, modify and redistribute it under the terms of the GNU"
964
print "General Public License version 2 or later."
967
class cmd_rocks(Command):
968
"""Statement of optimism."""
971
print "it sure does!"
344
973
def parse_spec(spec):
350
>>> parse_spec("../@")
352
>>> parse_spec("../f/@35")
354
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
355
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
360
975
parsed = spec.split('/@')
361
976
assert len(parsed) == 2
362
977
if parsed[1] == "":
366
parsed[1] = int(parsed[1])
368
pass # We can allow stuff like ./@revid:blahblahblah
980
parsed[1] = int(parsed[1])
372
983
parsed = [spec, None]
375
def parse_args(command, argv, alias_argv=None):
986
class cmd_merge(Command):
987
"""Perform a three-way merge of trees."""
988
takes_args = ['other_spec', 'base_spec']
990
def run(self, other_spec, base_spec):
991
merge.merge(parse_spec(other_spec), parse_spec(base_spec))
993
class cmd_assert_fail(Command):
994
"""Test reporting of assertion failures"""
997
assert False, "always fails"
1000
class cmd_help(Command):
1001
"""Show help on a command or other topic.
1003
For a list of all available commands, say 'bzr help commands'."""
1004
takes_args = ['topic?']
1007
def run(self, topic=None):
1012
class cmd_update_stat_cache(Command):
1013
"""Update stat-cache mapping inodes to SHA-1 hashes.
1015
For testing only."""
1020
statcache.update_cache(b.base, b.read_working_inventory())
1023
######################################################################
1027
# list of all available options; the rhs can be either None for an
1028
# option that takes no argument, or a constructor function that checks
1037
'revision': _parse_revision_str,
1053
def parse_args(argv):
376
1054
"""Parse command line.
378
1056
Arguments and options are parsed at this level before being passed
379
1057
down to specific command handlers. This routine knows, from a
380
1058
lookup table, something about the available options, what optargs
381
1059
they take, and which commands will accept them.
1061
>>> parse_args('--help'.split())
1062
([], {'help': True})
1063
>>> parse_args('--version'.split())
1064
([], {'version': True})
1065
>>> parse_args('status --all'.split())
1066
(['status'], {'all': True})
1067
>>> parse_args('commit --message=biter'.split())
1068
(['commit'], {'message': u'biter'})
383
# TODO: make it a method of the Command?
384
parser = option.get_optparser(command.options())
385
if alias_argv is not None:
386
args = alias_argv + argv
390
options, args = parser.parse_args(args)
391
opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
392
v is not option.OptionParser.DEFAULT_VALUE])
1073
# TODO: Maybe handle '--' to end options?
1078
# option names must not be unicode
1082
mutter(" got option %r" % a)
1084
optname, optarg = a[2:].split('=', 1)
1087
if optname not in OPTIONS:
1088
bailout('unknown long option %r' % a)
1091
if shortopt not in SHORT_OPTIONS:
1092
bailout('unknown short option %r' % a)
1093
optname = SHORT_OPTIONS[shortopt]
1096
# XXX: Do we ever want to support this, e.g. for -r?
1097
bailout('repeated option %r' % a)
1099
optargfn = OPTIONS[optname]
1103
bailout('option %r needs an argument' % a)
1105
optarg = argv.pop(0)
1106
opts[optname] = optargfn(optarg)
1109
bailout('option %r takes no argument' % optname)
1110
opts[optname] = True
393
1114
return args, opts
396
1119
def _match_argform(cmd, takes_args, args):
410
1133
argdict[argname + '_list'] = None
411
1134
elif ap[-1] == '+':
413
raise errors.BzrCommandError("command %r needs one or more %s"
414
% (cmd, argname.upper()))
1136
raise BzrCommandError("command %r needs one or more %s"
1137
% (cmd, argname.upper()))
416
1139
argdict[argname + '_list'] = args[:]
418
1141
elif ap[-1] == '$': # all but one
419
1142
if len(args) < 2:
420
raise errors.BzrCommandError("command %r needs one or more %s"
421
% (cmd, argname.upper()))
1143
raise BzrCommandError("command %r needs one or more %s"
1144
% (cmd, argname.upper()))
422
1145
argdict[argname + '_list'] = args[:-1]
425
1148
# just a plain arg
428
raise errors.BzrCommandError("command %r requires argument %s"
429
% (cmd, argname.upper()))
1151
raise BzrCommandError("command %r requires argument %s"
1152
% (cmd, argname.upper()))
431
1154
argdict[argname] = args.pop(0)
434
raise errors.BzrCommandError("extra argument to command %s: %s"
1157
raise BzrCommandError("extra argument to command %s: %s"
441
def apply_profiled(the_callable, *args, **kwargs):
445
pffileno, pfname = tempfile.mkstemp()
447
prof = hotshot.Profile(pfname)
449
ret = prof.runcall(the_callable, *args, **kwargs) or 0
452
stats = hotshot.stats.load(pfname)
454
stats.sort_stats('cum') # 'time'
455
## XXX: Might like to write to stderr or the trace file instead but
456
## print_stats seems hardcoded to stdout
457
stats.print_stats(20)
464
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
465
from bzrlib.lsprof import profile
467
ret, stats = profile(the_callable, *args, **kwargs)
473
cPickle.dump(stats, open(filename, 'w'), 2)
474
print 'Profile data written to %r.' % filename
478
def get_alias(cmd, config=None):
479
"""Return an expanded alias, or None if no alias exists.
482
Command to be checked for an alias.
484
Used to specify an alternative config to use,
485
which is especially useful for testing.
486
If it is unspecified, the global config will be used.
490
config = bzrlib.config.GlobalConfig()
491
alias = config.get_alias(cmd)
494
return [a.decode('utf-8') for a in shlex.split(alias.encode('utf-8'))]
498
1164
def run_bzr(argv):
499
1165
"""Execute a command.
501
1167
This is similar to main(), but without all the trappings for
502
1168
logging and error handling.
505
The command-line arguments, without the program name from argv[0]
506
These should already be decoded. All library/test code calling
507
run_bzr should be passing valid strings (don't need decoding).
509
Returns a command status or raises an exception.
511
Special master options: these must come before the command because
512
they control how the command is interpreted.
515
Do not load plugin modules at all
521
Only use builtin commands. (Plugins are still allowed to change
525
Run under the Python hotshot profiler.
528
Run under the Python lsprof profiler.
531
trace.mutter("bzr arguments: %r", argv)
533
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
534
opt_no_aliases = False
535
opt_lsprof_file = None
537
# --no-plugins is handled specially at a very early stage. We need
538
# to load plugins before doing other command parsing so that they
539
# can override commands, but this needs to happen first.
547
elif a == '--lsprof':
549
elif a == '--lsprof-file':
551
opt_lsprof_file = argv[i + 1]
553
elif a == '--no-plugins':
554
opt_no_plugins = True
555
elif a == '--no-aliases':
556
opt_no_aliases = True
557
elif a == '--builtin':
559
elif a in ('--quiet', '-q'):
561
elif a.startswith('-D'):
562
debug.debug_flags.add(a[2:])
569
from bzrlib.builtins import cmd_help
570
cmd_help().run_argv_aliases([])
573
if argv[0] == '--version':
574
from bzrlib.version import show_version
578
if not opt_no_plugins:
579
from bzrlib.plugin import load_plugins
582
from bzrlib.plugin import disable_plugins
587
if not opt_no_aliases:
588
alias_argv = get_alias(argv[0])
590
alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
591
argv[0] = alias_argv.pop(0)
594
# We want only 'ascii' command names, but the user may have typed
595
# in a Unicode name. In that case, they should just get a
596
# 'command not found' error later.
598
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
599
if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
600
run = cmd_obj.run_argv
603
run = cmd_obj.run_argv_aliases
604
run_argv = [argv, alias_argv]
1170
argv = [a.decode(bzrlib.user_encoding) for a in argv]
608
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
610
ret = apply_profiled(run, *run_argv)
615
# reset, in case we may do other commands later within the same process
616
trace.be_quiet(False)
618
def display_command(func):
619
"""Decorator that suppresses pipe/interrupt errors."""
620
def ignore_pipe(*args, **kwargs):
1173
args, opts = parse_args(argv[1:])
1181
elif 'version' in opts:
1184
cmd = str(args.pop(0))
1191
canonical_cmd, cmd_class = get_cmd_class(cmd)
1194
if 'profile' in opts:
1200
# check options are reasonable
1201
allowed = cmd_class.takes_options
1203
if oname not in allowed:
1204
raise BzrCommandError("option '--%s' is not allowed for command %r"
1207
# mix arguments and options into one dictionary
1208
cmdargs = _match_argform(cmd, cmd_class.takes_args, args)
1210
for k, v in opts.items():
1211
cmdopts[k.replace('-', '_')] = v
1214
import hotshot, tempfile
1215
pffileno, pfname = tempfile.mkstemp()
622
result = func(*args, **kwargs)
626
if getattr(e, 'errno', None) is None:
628
if e.errno != errno.EPIPE:
629
# Win32 raises IOError with errno=0 on a broken pipe
630
if sys.platform != 'win32' or e.errno != 0:
633
except KeyboardInterrupt:
1217
prof = hotshot.Profile(pfname)
1218
ret = prof.runcall(cmd_class, cmdopts, cmdargs) or 0
1221
import hotshot.stats
1222
stats = hotshot.stats.load(pfname)
1224
stats.sort_stats('time')
1225
## XXX: Might like to write to stderr or the trace file instead but
1226
## print_stats seems hardcoded to stdout
1227
stats.print_stats(20)
1235
return cmd_class(cmdopts, cmdargs).status
1238
def _report_exception(summary, quiet=False):
1240
log_error('bzr: ' + summary)
1241
bzrlib.trace.log_exception()
1244
tb = sys.exc_info()[2]
1245
exinfo = traceback.extract_tb(tb)
1247
sys.stderr.write(' at %s:%d in %s()\n' % exinfo[-1][:3])
1248
sys.stderr.write(' see ~/.bzr.log for debug information\n')
640
from bzrlib.ui.text import TextUIFactory
641
bzrlib.ui.ui_factory = TextUIFactory()
642
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
643
ret = run_bzr_catch_errors(argv)
644
trace.mutter("return code %d", ret)
648
def run_bzr_catch_errors(argv):
1255
bzrlib.open_tracefile(argv)
651
# do this here inside the exception wrappers to catch EPIPE
653
except (KeyboardInterrupt, Exception), e:
654
# used to handle AssertionError and KeyboardInterrupt
655
# specially here, but hopefully they're handled ok by the logger now
656
trace.report_exception(sys.exc_info(), sys.stderr)
657
if os.environ.get('BZR_PDB'):
658
print '**** entering debugger'
660
pdb.post_mortem(sys.exc_traceback)
1260
return run_bzr(argv)
1262
# do this here inside the exception wrappers to catch EPIPE
1265
quiet = isinstance(e, (BzrCommandError))
1266
_report_exception('error: ' + e.args[0], quiet=quiet)
1269
# some explanation or hints
1272
except AssertionError, e:
1273
msg = 'assertion failed'
1275
msg += ': ' + str(e)
1276
_report_exception(msg)
1278
except KeyboardInterrupt, e:
1279
_report_exception('interrupted', quiet=True)
1281
except Exception, e:
1283
if (isinstance(e, IOError)
1284
and hasattr(e, 'errno')
1285
and e.errno == errno.EPIPE):
1289
msg = str(e).rstrip('\n')
1290
_report_exception(msg, quiet)
1293
bzrlib.trace.close_trace()
663
1296
if __name__ == '__main__':
664
1297
sys.exit(main(sys.argv))