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.
19
import sys, os, time, os.path
35
from bzrlib.trace import mutter, note, log_error, warning
36
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
37
from bzrlib.branch import find_branch
38
from bzrlib import BZRDIR
44
def register_command(cmd):
45
"Utility function to help register a command"
48
if k.startswith("cmd_"):
49
k_unsquished = _unsquish_command_name(k)
52
if not plugin_cmds.has_key(k_unsquished):
53
plugin_cmds[k_unsquished] = cmd
55
log_error('Two plugins defined the same command: %r' % k)
56
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
22
from bzrlib.trace import mutter, note, log_error
23
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
24
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
25
from bzrlib.tree import RevisionTree, EmptyTree, Tree
26
from bzrlib.revision import Revision
27
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
29
from bzrlib import merge
59
32
def _squish_command_name(cmd):
64
37
assert cmd.startswith("cmd_")
65
38
return cmd[4:].replace('_','-')
68
def _parse_revision_str(revstr):
69
"""This handles a revision string -> revno.
71
This always returns a list. The list will have one element for
73
It supports integers directly, but everything else it
74
defers for passing to Branch.get_revision_info()
76
>>> _parse_revision_str('234')
78
>>> _parse_revision_str('234..567')
80
>>> _parse_revision_str('..')
82
>>> _parse_revision_str('..234')
84
>>> _parse_revision_str('234..')
86
>>> _parse_revision_str('234..456..789') # Maybe this should be an error
88
>>> _parse_revision_str('234....789') # Error?
90
>>> _parse_revision_str('revid:test@other.com-234234')
91
['revid:test@other.com-234234']
92
>>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
93
['revid:test@other.com-234234', 'revid:test@other.com-234235']
94
>>> _parse_revision_str('revid:test@other.com-234234..23')
95
['revid:test@other.com-234234', 23]
96
>>> _parse_revision_str('date:2005-04-12')
98
>>> _parse_revision_str('date:2005-04-12 12:24:33')
99
['date:2005-04-12 12:24:33']
100
>>> _parse_revision_str('date:2005-04-12T12:24:33')
101
['date:2005-04-12T12:24:33']
102
>>> _parse_revision_str('date:2005-04-12,12:24:33')
103
['date:2005-04-12,12:24:33']
104
>>> _parse_revision_str('-5..23')
106
>>> _parse_revision_str('-5')
108
>>> _parse_revision_str('123a')
110
>>> _parse_revision_str('abc')
114
old_format_re = re.compile('\d*:\d*')
115
m = old_format_re.match(revstr)
117
warning('Colon separator for revision numbers is deprecated.'
120
for rev in revstr.split(':'):
122
revs.append(int(rev))
127
for x in revstr.split('..'):
138
def get_merge_type(typestring):
139
"""Attempt to find the merge class/factory associated with a string."""
140
from merge import merge_types
142
return merge_types[typestring][0]
144
templ = '%s%%7s: %%s' % (' '*12)
145
lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
146
type_list = '\n'.join(lines)
147
msg = "No known merge type %s. Supported types are:\n%s" %\
148
(typestring, type_list)
149
raise BzrCommandError(msg)
152
def get_merge_type(typestring):
153
"""Attempt to find the merge class/factory associated with a string."""
154
from merge import merge_types
156
return merge_types[typestring][0]
158
templ = '%s%%7s: %%s' % (' '*12)
159
lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
160
type_list = '\n'.join(lines)
161
msg = "No known merge type %s. Supported types are:\n%s" %\
162
(typestring, type_list)
163
raise BzrCommandError(msg)
167
def _get_cmd_dict(plugins_override=True):
41
"""Return canonical name and class for all registered commands."""
169
42
for k, v in globals().iteritems():
170
43
if k.startswith("cmd_"):
171
d[_unsquish_command_name(k)] = v
172
# If we didn't load plugins, the plugin_cmds dict will be empty
174
d.update(plugin_cmds)
176
d2 = plugin_cmds.copy()
182
def get_all_cmds(plugins_override=True):
183
"""Return canonical name and class for all registered commands."""
184
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
188
def get_cmd_class(cmd, plugins_override=True):
44
yield _unsquish_command_name(k), v
46
def get_cmd_class(cmd):
189
47
"""Return the canonical name and command class for a command.
191
49
cmd = str(cmd) # not unicode
193
51
# first look up this command under the specified name
194
cmds = _get_cmd_dict(plugins_override=plugins_override)
196
return cmd, cmds[cmd]
53
return cmd, globals()[_squish_command_name(cmd)]
200
57
# look for any command which claims this as an alias
201
for cmdname, cmdclass in cmds.iteritems():
58
for cmdname, cmdclass in get_all_cmds():
202
59
if cmd in cmdclass.aliases:
203
60
return cmdname, cmdclass
264
118
class ExternalCommand(Command):
265
119
"""Class to wrap external commands.
267
We cheat a little here, when get_cmd_class() calls us we actually
268
give it back an object we construct that has the appropriate path,
269
help, options etc for the specified command.
271
When run_bzr() tries to instantiate that 'class' it gets caught by
272
the __call__ method, which we override to call the Command.__init__
273
method. That then calls our run method which is pretty straight
276
The only wrinkle is that we have to map bzr's dictionary of options
277
and arguments back into command line options and arguments for the
121
We cheat a little here, when get_cmd_class() calls us we actually give it back
122
an object we construct that has the appropriate path, help, options etc for the
125
When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
126
method, which we override to call the Command.__init__ method. That then calls
127
our run method which is pretty straight forward.
129
The only wrinkle is that we have to map bzr's dictionary of options and arguments
130
back into command line options and arguments for the script.
281
133
def find_command(cls, cmd):
283
134
bzrpath = os.environ.get('BZRPATH', '')
285
for dir in bzrpath.split(os.pathsep):
136
for dir in bzrpath.split(':'):
286
137
path = os.path.join(dir, cmd)
287
138
if os.path.isfile(path):
288
139
return ExternalCommand(path)
294
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.
297
151
pipe = os.popen('%s --bzr-usage' % path, 'r')
298
152
self.takes_options = pipe.readline().split()
300
for opt in self.takes_options:
301
if not opt in OPTIONS:
302
raise BzrError("Unknown option '%s' returned by external command %s"
305
# TODO: Is there any way to check takes_args is valid here?
306
153
self.takes_args = pipe.readline().split()
308
if pipe.close() is not None:
309
raise BzrError("Failed funning '%s --bzr-usage'" % path)
311
156
pipe = os.popen('%s --bzr-help' % path, 'r')
312
157
self.__doc__ = pipe.read()
313
if pipe.close() is not None:
314
raise BzrError("Failed funning '%s --bzr-help'" % path)
316
160
def __call__(self, options, arguments):
317
161
Command.__init__(self, options, arguments)
378
221
directory is shown. Otherwise, only the status of the specified
379
222
files or directories is reported. If a directory is given, status
380
223
is reported for everything inside that directory.
382
If a revision is specified, the changes since that revision are shown.
384
225
takes_args = ['file*']
385
takes_options = ['all', 'show-ids', 'revision']
226
takes_options = ['all', 'show-ids']
386
227
aliases = ['st', 'stat']
388
229
def run(self, all=False, show_ids=False, file_list=None):
390
b = find_branch(file_list[0])
231
b = Branch(file_list[0], lock_mode='r')
391
232
file_list = [b.relpath(x) for x in file_list]
392
233
# special case: only one path was given and it's the root
394
235
if file_list == ['']:
399
from bzrlib.status import show_status
400
show_status(b, show_unchanged=all, show_ids=show_ids,
401
specific_files=file_list)
238
b = Branch('.', lock_mode='r')
240
status.show_status(b, show_unchanged=all, show_ids=show_ids,
241
specific_files=file_list)
404
244
class cmd_cat_revision(Command):
455
272
whether already versioned or not, are searched for files or
456
273
subdirectories that are neither versioned or ignored, and these
457
274
are added. This search proceeds recursively into versioned
458
directories. If no names are given '.' is assumed.
460
Therefore simply saying 'bzr add' will version all files that
277
Therefore simply saying 'bzr add .' will version all files that
461
278
are currently unknown.
463
280
TODO: Perhaps adding a file whose directly is not versioned should
464
281
recursively add that parent, rather than giving an error?
466
takes_args = ['file*']
467
takes_options = ['verbose', 'no-recurse']
283
takes_args = ['file+']
284
takes_options = ['verbose']
469
def run(self, file_list, verbose=False, no_recurse=False):
470
from bzrlib.add import smart_add
471
smart_add(file_list, verbose, not no_recurse)
475
class cmd_mkdir(Command):
476
"""Create a new versioned directory.
478
This is equivalent to creating the directory and then adding it.
480
takes_args = ['dir+']
482
def run(self, dir_list):
489
b.add([d], verbose=True)
286
def run(self, file_list, verbose=False):
287
bzrlib.add.smart_add(file_list, verbose)
492
290
class cmd_relpath(Command):
493
291
"""Show path of a file relative to root"""
494
292
takes_args = ['filename']
497
294
def run(self, filename):
498
print find_branch(filename).relpath(filename)
295
print Branch(filename).relpath(filename)
502
299
class cmd_inventory(Command):
503
300
"""Show inventory of the current working copy or a revision."""
504
takes_options = ['revision', 'show-ids']
301
takes_options = ['revision']
506
def run(self, revision=None, show_ids=False):
303
def run(self, revision=None):
508
305
if revision == None:
509
306
inv = b.read_working_inventory()
511
if len(revision) > 1:
512
raise BzrCommandError('bzr inventory --revision takes'
513
' exactly one revision identifier')
514
inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
308
inv = b.get_revision_inventory(b.lookup_revision(revision))
516
310
for path, entry in inv.entries():
518
print '%-50s %s' % (path, entry.file_id)
311
print '%-50s %s' % (entry.file_id, path)
523
314
class cmd_move(Command):
553
343
takes_args = ['from_name', 'to_name']
555
345
def run(self, from_name, to_name):
557
347
b.rename_one(b.relpath(from_name), b.relpath(to_name))
561
class cmd_mv(Command):
562
"""Move or rename a file.
565
bzr mv OLDNAME NEWNAME
566
bzr mv SOURCE... DESTINATION
568
If the last argument is a versioned directory, all the other names
569
are moved into it. Otherwise, there must be exactly two arguments
570
and the file is changed to a new name, which must not already exist.
572
Files cannot be moved between branches.
574
takes_args = ['names*']
575
def run(self, names_list):
576
if len(names_list) < 2:
577
raise BzrCommandError("missing file argument")
578
b = find_branch(names_list[0])
580
rel_names = [b.relpath(x) for x in names_list]
582
if os.path.isdir(names_list[-1]):
583
# move into existing directory
584
b.move(rel_names[:-1], rel_names[-1])
586
if len(names_list) != 2:
587
raise BzrCommandError('to mv multiple files the destination '
588
'must be a versioned directory')
589
b.move(rel_names[0], rel_names[1])
594
class cmd_pull(Command):
595
"""Pull any changes from another branch into the current one.
597
If the location is omitted, the last-used location will be used.
598
Both the revision history and the working directory will be
601
This command only works on branches that have not diverged. Branches are
602
considered diverged if both branches have had commits without first
603
pulling from the other.
605
If branches have diverged, you can use 'bzr merge' to pull the text changes
606
from one into the other.
608
takes_args = ['location?']
610
def run(self, location=None):
611
from bzrlib.merge import merge
613
from shutil import rmtree
616
br_to = find_branch('.')
619
stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
621
if e.errno != errno.ENOENT:
624
if stored_loc is None:
625
raise BzrCommandError("No pull location known or specified.")
627
print "Using last location: %s" % stored_loc
628
location = stored_loc
629
cache_root = tempfile.mkdtemp()
630
from bzrlib.branch import DivergedBranches
631
br_from = find_branch(location)
632
location = pull_loc(br_from)
633
old_revno = br_to.revno()
635
from branch import find_cached_branch, DivergedBranches
636
br_from = find_cached_branch(location, cache_root)
637
location = pull_loc(br_from)
638
old_revno = br_to.revno()
640
br_to.update_revisions(br_from)
641
except DivergedBranches:
642
raise BzrCommandError("These branches have diverged."
645
merge(('.', -1), ('.', old_revno), check_clean=False)
646
if location != stored_loc:
647
br_to.controlfile("x-pull", "wb").write(location + "\n")
653
class cmd_branch(Command):
654
"""Create a new copy of a branch.
656
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
657
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
659
To retrieve the branch as of a particular revision, supply the --revision
660
parameter, as in "branch foo/bar -r 5".
662
takes_args = ['from_location', 'to_location?']
663
takes_options = ['revision']
664
aliases = ['get', 'clone']
666
def run(self, from_location, to_location=None, revision=None):
668
from bzrlib.merge import merge
669
from bzrlib.branch import DivergedBranches, \
670
find_cached_branch, Branch
671
from shutil import rmtree
672
from meta_store import CachedStore
674
cache_root = tempfile.mkdtemp()
678
elif len(revision) > 1:
679
raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
683
br_from = find_cached_branch(from_location, cache_root)
685
if e.errno == errno.ENOENT:
686
raise BzrCommandError('Source location "%s" does not'
687
' exist.' % to_location)
691
if to_location is None:
692
to_location = os.path.basename(from_location.rstrip("/\\"))
695
os.mkdir(to_location)
697
if e.errno == errno.EEXIST:
698
raise BzrCommandError('Target directory "%s" already'
699
' exists.' % to_location)
700
if e.errno == errno.ENOENT:
701
raise BzrCommandError('Parent of "%s" does not exist.' %
705
br_to = Branch(to_location, init=True)
707
br_to.set_root_id(br_from.get_root_id())
710
if revision[0] is None:
711
revno = br_from.revno()
713
revno, rev_id = br_from.get_revision_info(revision[0])
715
br_to.update_revisions(br_from, stop_revision=revno)
716
except bzrlib.errors.NoSuchRevision:
718
msg = "The branch %s has no revision %d." % (from_location,
720
raise BzrCommandError(msg)
722
merge((to_location, -1), (to_location, 0), this_dir=to_location,
723
check_clean=False, ignore_zero=True)
724
from_location = pull_loc(br_from)
725
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
730
def pull_loc(branch):
731
# TODO: Should perhaps just make attribute be 'base' in
732
# RemoteBranch and Branch?
733
if hasattr(branch, "baseurl"):
734
return branch.baseurl
740
351
class cmd_renames(Command):
741
352
"""Show list of renamed files.
875
487
TODO: This probably handles non-Unix newlines poorly.
883
490
takes_args = ['file*']
884
takes_options = ['revision', 'diff-options']
885
aliases = ['di', 'dif']
491
takes_options = ['revision']
887
def run(self, revision=None, file_list=None, diff_options=None):
494
def run(self, revision=None, file_list=None):
888
495
from bzrlib.diff import show_diff
496
from bzrlib import find_branch
891
b = find_branch(file_list[0])
499
b = find_branch(file_list[0], lock_mode='r')
892
500
file_list = [b.relpath(f) for f in file_list]
893
501
if file_list == ['']:
894
502
# just pointing to top-of-tree
505
b = Branch('.', lock_mode='r')
507
show_diff(b, revision, specific_files=file_list)
899
if revision is not None:
900
if len(revision) == 1:
901
show_diff(b, revision[0], specific_files=file_list,
902
external_diff_options=diff_options)
903
elif len(revision) == 2:
904
show_diff(b, revision[0], specific_files=file_list,
905
external_diff_options=diff_options,
906
revision2=revision[1])
908
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
910
show_diff(b, None, specific_files=file_list,
911
external_diff_options=diff_options)
940
537
"""List files modified in working tree."""
943
from bzrlib.delta import compare_trees
946
td = compare_trees(b.basis_tree(), b.working_tree())
948
for path, id, kind in td.modified:
542
inv = b.read_working_inventory()
543
sc = statcache.update_cache(b, inv)
544
basis = b.basis_tree()
545
basis_inv = basis.inventory
547
# We used to do this through iter_entries(), but that's slow
548
# when most of the files are unmodified, as is usually the
549
# case. So instead we iterate by inventory entry, and only
550
# calculate paths as necessary.
552
for file_id in basis_inv:
553
cacheentry = sc.get(file_id)
554
if not cacheentry: # deleted
556
ie = basis_inv[file_id]
557
if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
558
path = inv.id2path(file_id)
983
594
class cmd_log(Command):
984
595
"""Show log of this branch.
986
To request a range of logs, you can use the command -r begin:end
987
-r revision requests a specific revision, -r :end or -r begin: are
990
--message allows you to give a regular expression, which will be evaluated
991
so that only matching entries will be displayed.
993
TODO: Make --revision support uuid: and hash: [future tag:] notation.
597
TODO: Option to limit range.
997
600
takes_args = ['filename?']
998
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
999
'long', 'message', 'short',]
601
takes_options = ['forward', 'timezone', 'verbose', 'show-ids']
1001
603
def run(self, filename=None, timezone='original',
1009
from bzrlib.branch import find_branch
1010
from bzrlib.log import log_formatter, show_log
607
from bzrlib import show_log, find_branch
1013
609
direction = (forward and 'forward') or 'reverse'
1016
b = find_branch(filename)
612
b = find_branch(filename, lock_mode='r')
1017
613
fp = b.relpath(filename)
1019
615
file_id = b.read_working_inventory().path2id(fp)
1021
617
file_id = None # points to branch root
1023
b = find_branch('.')
619
b = find_branch('.', lock_mode='r')
1026
if revision is None:
1029
elif len(revision) == 1:
1030
rev1 = rev2 = b.get_revision_info(revision[0])[0]
1031
elif len(revision) == 2:
1032
rev1 = b.get_revision_info(revision[0])[0]
1033
rev2 = b.get_revision_info(revision[1])[0]
1035
raise BzrCommandError('bzr log --revision takes one or two values.')
1042
mutter('encoding log as %r' % bzrlib.user_encoding)
1044
# use 'replace' so that we don't abort if trying to write out
1045
# in e.g. the default C locale.
1046
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
1051
log_format = 'short'
1052
lf = log_formatter(log_format,
1055
show_timezone=timezone)
623
show_timezone=timezone,
1060
624
verbose=verbose,
1061
direction=direction,
1062
start_revision=rev1,
1203
762
except ValueError:
1204
763
raise BzrCommandError("not a valid revision-number: %r" % revno)
1206
print find_branch('.').lookup_revision(revno)
765
print Branch('.').lookup_revision(revno)
1209
768
class cmd_export(Command):
1210
769
"""Export past revision to destination directory.
1212
If no revision is specified this exports the last committed revision.
1214
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1215
given, try to find the format with the extension. If no extension
1216
is found exports to a directory (equivalent to --format=dir).
1218
Root may be the top directory for tar, tgz and tbz2 formats. If none
1219
is given, the top directory will be the root name of the file."""
1220
# TODO: list known exporters
771
If no revision is specified this exports the last committed revision."""
1221
772
takes_args = ['dest']
1222
takes_options = ['revision', 'format', 'root']
1223
def run(self, dest, revision=None, format=None, root=None):
1225
b = find_branch('.')
1226
if revision is None:
1227
rev_id = b.last_patch()
773
takes_options = ['revision']
774
def run(self, dest, revision=None):
777
rh = b.revision_history()[-1]
1229
if len(revision) != 1:
1230
raise BzrError('bzr export --revision takes exactly 1 argument')
1231
revno, rev_id = b.get_revision_info(revision[0])
1232
t = b.revision_tree(rev_id)
1233
root, ext = os.path.splitext(dest)
1235
if ext in (".tar",):
1237
elif ext in (".gz", ".tgz"):
1239
elif ext in (".bz2", ".tbz2"):
1243
t.export(dest, format, root)
779
rh = b.lookup_revision(int(revision))
780
t = b.revision_tree(rh)
1246
784
class cmd_cat(Command):
1284
818
TODO: Strict commit that fails if there are unknown or deleted files.
1286
820
takes_args = ['selected*']
1287
takes_options = ['message', 'file', 'verbose', 'unchanged']
821
takes_options = ['message', 'file', 'verbose']
1288
822
aliases = ['ci', 'checkin']
1290
# TODO: Give better message for -s, --summary, used by tla people
1292
def run(self, message=None, file=None, verbose=True, selected_list=None,
1294
from bzrlib.errors import PointlessCommit
1295
from bzrlib.osutils import get_text_message
824
def run(self, message=None, file=None, verbose=True, selected_list=None):
825
from bzrlib.commit import commit
1297
827
## Warning: shadows builtin file()
1298
828
if not message and not file:
1299
# FIXME: Ugly; change status code to send to a provided function?
1303
catcher = cStringIO.StringIO()
1304
sys.stdout = catcher
1305
cmd_status({"file_list":selected_list}, {})
1306
info = catcher.getvalue()
1308
message = get_text_message(info)
1311
raise BzrCommandError("please specify a commit message",
1312
["use either --message or --file"])
829
raise BzrCommandError("please specify a commit message",
830
["use either --message or --file"])
1313
831
elif message and file:
1314
832
raise BzrCommandError("please specify either --message or --file")
1338
845
This command checks various invariants about the branch storage to
1339
846
detect data corruption or bzr bugs.
1341
If given the --update flag, it will update some optional fields
1342
to help ensure data consistency.
1344
takes_args = ['dir?']
1346
def run(self, dir='.'):
1347
from bzrlib.check import check
1349
check(find_branch(dir))
1352
class cmd_scan_cache(Command):
1355
from bzrlib.hashcache import HashCache
1362
print '%6d stats' % c.stat_count
1363
print '%6d in hashcache' % len(c._cache)
1364
print '%6d files removed from cache' % c.removed_count
1365
print '%6d hashes updated' % c.update_count
1366
print '%6d files changed too recently to cache' % c.danger_count
1373
class cmd_upgrade(Command):
1374
"""Upgrade branch storage to current format.
1376
This should normally be used only after the check command tells
1379
takes_args = ['dir?']
1381
def run(self, dir='.'):
1382
from bzrlib.upgrade import upgrade
1383
upgrade(find_branch(dir))
848
takes_args = ['dir?']
849
def run(self, dir='.'):
851
bzrlib.check.check(Branch(dir))
1389
857
takes_options = ['email']
1391
859
def run(self, email=False):
1393
b = bzrlib.branch.find_branch('.')
1398
print bzrlib.osutils.user_email(b)
861
print bzrlib.osutils.user_email()
1400
print bzrlib.osutils.username(b)
863
print bzrlib.osutils.username()
1403
866
class cmd_selftest(Command):
1404
867
"""Run internal test suite"""
1406
takes_options = ['verbose']
1407
def run(self, verbose=False):
1409
from bzrlib.selftest import selftest
1411
# we don't want progress meters from the tests to go to the
1412
# real output; and we don't want log messages cluttering up
1415
save_ui = bzrlib.ui.ui_factory
1416
bzrlib.trace.info('running tests...')
1417
bzrlib.trace.disable_default_logging()
1419
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1420
result = selftest(verbose=verbose)
1422
bzrlib.trace.info('tests passed')
870
failures, tests = 0, 0
872
import doctest, bzrlib.store
873
bzrlib.trace.verbose = False
875
for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
876
bzrlib.tree, bzrlib.commands, bzrlib.add:
877
mf, mt = doctest.testmod(m)
880
print '%-40s %3d tests' % (m.__name__, mt),
882
print '%3d FAILED!' % mf
1424
bzrlib.trace.info('tests failed')
1425
return int(not result)
1427
bzrlib.trace.enable_default_logging()
1428
bzrlib.ui.ui_factory = save_ui
886
print '%-40s %3d tests' % ('total', tests),
888
print '%3d FAILED!' % failures
1431
896
class cmd_version(Command):
1432
"""Show version of bzr."""
897
"""Show version of bzr"""
1436
901
def show_version():
1437
902
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1438
# is bzrlib itself in a branch?
1439
bzrrev = bzrlib.get_bzr_revision()
1441
print " (bzr checkout, revision %d {%s})" % bzrrev
1442
903
print bzrlib.__copyright__
1443
904
print "http://bazaar-ng.org/"
1454
915
print "it sure does!"
1456
917
def parse_spec(spec):
1458
>>> parse_spec(None)
1460
>>> parse_spec("./")
1462
>>> parse_spec("../@")
1464
>>> parse_spec("../f/@35")
1466
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
1467
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1471
918
if '/@' in spec:
1472
919
parsed = spec.split('/@')
1473
920
assert len(parsed) == 2
1474
921
if parsed[1] == "":
1478
parsed[1] = int(parsed[1])
1480
pass # We can allow stuff like ./@revid:blahblahblah
1482
assert parsed[1] >=0
924
parsed[1] = int(parsed[1])
1484
927
parsed = [spec, None]
1489
class cmd_find_merge_base(Command):
1490
"""Find and print a base revision for merging two branches.
1492
TODO: Options to specify revisions on either side, as if
1493
merging only part of the history.
1495
takes_args = ['branch', 'other']
1498
def run(self, branch, other):
1499
branch1 = find_branch(branch)
1500
branch2 = find_branch(other)
1502
base_revno, base_revid = branch1.common_ancestor(branch2)
1504
if base_revno is None:
1505
raise bzrlib.errors.UnrelatedBranches()
1507
print 'merge base is revision %s' % base_revid
1508
print ' r%-6d in %s' % (base_revno, branch)
1510
other_revno = branch2.revision_id_to_revno(base_revid)
1512
print ' r%-6d in %s' % (other_revno, other)
1516
930
class cmd_merge(Command):
1517
"""Perform a three-way merge.
1519
The branch is the branch you will merge from. By default, it will merge
1520
the latest revision. If you specify a revision, that revision will be
1521
merged. If you specify two revisions, the first will be used as a BASE,
1522
and the second one as OTHER. Revision numbers are always relative to the
1527
To merge the latest revision from bzr.dev
1528
bzr merge ../bzr.dev
1530
To merge changes up to and including revision 82 from bzr.dev
1531
bzr merge -r 82 ../bzr.dev
1533
To merge the changes introduced by 82, without previous changes:
1534
bzr merge -r 81..82 ../bzr.dev
1536
merge refuses to run if there are any uncommitted changes, unless
1539
takes_args = ['branch?']
1540
takes_options = ['revision', 'force', 'merge-type']
1542
def run(self, branch='.', revision=None, force=False,
1544
from bzrlib.merge import merge
1545
from bzrlib.merge_core import ApplyMerge3
1546
if merge_type is None:
1547
merge_type = ApplyMerge3
1549
if revision is None or len(revision) < 1:
1551
other = (branch, -1)
1553
if len(revision) == 1:
1554
other = (branch, revision[0])
1557
assert len(revision) == 2
1558
if None in revision:
1559
raise BzrCommandError(
1560
"Merge doesn't permit that revision specifier.")
1561
base = (branch, revision[0])
1562
other = (branch, revision[1])
1564
merge(other, base, check_clean=(not force), merge_type=merge_type)
1567
class cmd_revert(Command):
1568
"""Reverse all changes since the last commit.
1570
Only versioned files are affected. Specify filenames to revert only
1571
those files. By default, any files that are changed will be backed up
1572
first. Backup files have a '~' appended to their name.
1574
takes_options = ['revision', 'no-backup']
1575
takes_args = ['file*']
1576
aliases = ['merge-revert']
1578
def run(self, revision=None, no_backup=False, file_list=None):
1579
from bzrlib.merge import merge
1580
if file_list is not None:
1581
if len(file_list) == 0:
1582
raise BzrCommandError("No files specified")
1583
if revision is None:
1585
elif len(revision) != 1:
1586
raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1587
merge(('.', revision[0]), parse_spec('.'),
1590
backup_files=not no_backup,
1591
file_list=file_list)
931
"""Perform a three-way merge of trees."""
932
takes_args = ['other_spec', 'base_spec']
934
def run(self, other_spec, base_spec):
935
merge.merge(parse_spec(other_spec), parse_spec(base_spec))
1594
937
class cmd_assert_fail(Command):
1595
938
"""Test reporting of assertion failures"""
1602
945
"""Show help on a command or other topic.
1604
947
For a list of all available commands, say 'bzr help commands'."""
1605
takes_options = ['long']
1606
948
takes_args = ['topic?']
1609
def run(self, topic=None, long=False):
951
def run(self, topic=None):
1611
if topic is None and long:
1613
953
help.help(topic)
1616
class cmd_shell_complete(Command):
1617
"""Show appropriate completions for context.
1619
For a list of all available commands, say 'bzr shell-complete'."""
1620
takes_args = ['context?']
1624
def run(self, context=None):
1625
import shellcomplete
1626
shellcomplete.shellcomplete(context)
1629
class cmd_missing(Command):
1630
"""What is missing in this branch relative to other branch.
1632
takes_args = ['remote?']
1633
aliases = ['mis', 'miss']
1634
# We don't have to add quiet to the list, because
1635
# unknown options are parsed as booleans
1636
takes_options = ['verbose', 'quiet']
1638
def run(self, remote=None, verbose=False, quiet=False):
1639
from bzrlib.branch import find_branch, DivergedBranches
1640
from bzrlib.errors import BzrCommandError
1641
from bzrlib.missing import get_parent, show_missing
1643
if verbose and quiet:
1644
raise BzrCommandError('Cannot pass both quiet and verbose')
1646
b = find_branch('.')
1647
parent = get_parent(b)
1650
raise BzrCommandError("No missing location known or specified.")
1653
print "Using last location: %s" % parent
1655
elif parent is None:
1656
# We only update x-pull if it did not exist, missing should not change the parent
1657
b.controlfile('x-pull', 'wb').write(remote + '\n')
1658
br_remote = find_branch(remote)
1660
return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1664
class cmd_plugins(Command):
956
class cmd_update_stat_cache(Command):
957
"""Update stat-cache mapping inodes to SHA-1 hashes.
1668
import bzrlib.plugin
1669
from inspect import getdoc
1670
from pprint import pprint
1671
for plugin in bzrlib.plugin.all_plugins:
1672
print plugin.__path__[0]
1675
print '\t', d.split('\n')[0]
1677
#pprint(bzrlib.plugin.all_plugins)
964
statcache.update_cache(b.base, b.read_working_inventory())
967
######################################################################
1681
971
# list of all available options; the rhs can be either None for an
1761
1030
optname = a[2:]
1762
1031
if optname not in OPTIONS:
1763
raise BzrError('unknown long option %r' % a)
1032
bailout('unknown long option %r' % a)
1765
1034
shortopt = a[1:]
1766
if shortopt in SHORT_OPTIONS:
1767
# Multi-character options must have a space to delimit
1769
optname = SHORT_OPTIONS[shortopt]
1771
# Single character short options, can be chained,
1772
# and have their value appended to their name
1774
if shortopt not in SHORT_OPTIONS:
1775
# We didn't find the multi-character name, and we
1776
# didn't find the single char name
1777
raise BzrError('unknown short option %r' % a)
1778
optname = SHORT_OPTIONS[shortopt]
1781
# There are extra things on this option
1782
# see if it is the value, or if it is another
1784
optargfn = OPTIONS[optname]
1785
if optargfn is None:
1786
# This option does not take an argument, so the
1787
# next entry is another short option, pack it back
1789
argv.insert(0, '-' + a[2:])
1791
# This option takes an argument, so pack it
1035
if shortopt not in SHORT_OPTIONS:
1036
bailout('unknown short option %r' % a)
1037
optname = SHORT_OPTIONS[shortopt]
1795
1039
if optname in opts:
1796
1040
# XXX: Do we ever want to support this, e.g. for -r?
1797
raise BzrError('repeated option %r' % a)
1041
bailout('repeated option %r' % a)
1799
1043
optargfn = OPTIONS[optname]
1801
1045
if optarg == None:
1803
raise BzrError('option %r needs an argument' % a)
1047
bailout('option %r needs an argument' % a)
1805
1049
optarg = argv.pop(0)
1806
1050
opts[optname] = optargfn(optarg)
1808
1052
if optarg != None:
1809
raise BzrError('option %r takes no argument' % optname)
1053
bailout('option %r takes no argument' % optname)
1810
1054
opts[optname] = True
1867
1111
This is similar to main(), but without all the trappings for
1868
1112
logging and error handling.
1871
The command-line arguments, without the program name from argv[0]
1873
Returns a command status or raises an exception.
1875
Special master options: these must come before the command because
1876
they control how the command is interpreted.
1879
Do not load plugin modules at all
1882
Only use builtin commands. (Plugins are still allowed to change
1886
Run under the Python profiler.
1889
1114
argv = [a.decode(bzrlib.user_encoding) for a in argv]
1891
opt_profile = opt_no_plugins = opt_builtin = False
1893
# --no-plugins is handled specially at a very early stage. We need
1894
# to load plugins before doing other command parsing so that they
1895
# can override commands, but this needs to happen first.
1898
if a == '--profile':
1900
elif a == '--no-plugins':
1901
opt_no_plugins = True
1902
elif a == '--builtin':
1908
if not opt_no_plugins:
1909
from bzrlib.plugin import load_plugins
1912
args, opts = parse_args(argv)
1915
from bzrlib.help import help
1922
if 'version' in opts:
1927
from bzrlib.help import help
1931
cmd = str(args.pop(0))
1933
canonical_cmd, cmd_class = \
1934
get_cmd_class(cmd, plugins_override=not opt_builtin)
1117
args, opts = parse_args(argv[1:])
1125
elif 'version' in opts:
1128
cmd = str(args.pop(0))
1135
canonical_cmd, cmd_class = get_cmd_class(cmd)
1138
if 'profile' in opts:
1936
1144
# check options are reasonable
1937
1145
allowed = cmd_class.takes_options
1971
1179
return cmd_class(cmdopts, cmdargs).status
1182
def _report_exception(summary, quiet=False):
1184
log_error('bzr: ' + summary)
1185
bzrlib.trace.log_exception()
1188
tb = sys.exc_info()[2]
1189
exinfo = traceback.extract_tb(tb)
1191
sys.stderr.write(' at %s:%d in %s()\n' % exinfo[-1][:3])
1192
sys.stderr.write(' see ~/.bzr.log for debug information\n')
1974
1196
def main(argv):
1977
bzrlib.trace.log_startup(argv)
1979
bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
1199
bzrlib.open_tracefile(argv)
1983
return run_bzr(argv[1:])
1985
# do this here inside the exception wrappers to catch EPIPE
1987
except BzrCommandError, e:
1988
# command line syntax error, etc
1992
bzrlib.trace.log_exception()
1994
except AssertionError, e:
1995
bzrlib.trace.log_exception('assertion failed: ' + str(e))
1997
except KeyboardInterrupt, e:
1998
bzrlib.trace.note('interrupted')
2000
except Exception, e:
2002
if (isinstance(e, IOError)
2003
and hasattr(e, 'errno')
2004
and e.errno == errno.EPIPE):
2005
bzrlib.trace.note('broken pipe')
2008
bzrlib.trace.log_exception()
1204
return run_bzr(argv)
1206
# do this here inside the exception wrappers to catch EPIPE
1209
quiet = isinstance(e, (BzrCommandError))
1210
_report_exception('error: ' + e.args[0], quiet=quiet)
1213
# some explanation or hints
1216
except AssertionError, e:
1217
msg = 'assertion failed'
1219
msg += ': ' + str(e)
1220
_report_exception(msg)
1222
except KeyboardInterrupt, e:
1223
_report_exception('interrupted', quiet=True)
1225
except Exception, e:
1227
if (isinstance(e, IOError)
1228
and hasattr(e, 'errno')
1229
and e.errno == errno.EPIPE):
1233
msg = str(e).rstrip('\n')
1234
_report_exception(msg, quiet)
1237
bzrlib.trace.close_trace()
2012
1240
if __name__ == '__main__':