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__])
23
from bzrlib.trace import mutter, note, log_error
24
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
25
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
26
from bzrlib.tree import RevisionTree, EmptyTree, Tree
27
from bzrlib.revision import Revision
28
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
30
from bzrlib import merge
59
33
def _squish_command_name(cmd):
64
38
assert cmd.startswith("cmd_")
65
39
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):
42
"""Return canonical name and class for all registered commands."""
169
43
for k, v in globals().iteritems():
170
44
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):
45
yield _unsquish_command_name(k), v
47
def get_cmd_class(cmd):
189
48
"""Return the canonical name and command class for a command.
191
50
cmd = str(cmd) # not unicode
193
52
# first look up this command under the specified name
194
cmds = _get_cmd_dict(plugins_override=plugins_override)
196
return cmd, cmds[cmd]
54
return cmd, globals()[_squish_command_name(cmd)]
200
58
# look for any command which claims this as an alias
201
for cmdname, cmdclass in cmds.iteritems():
59
for cmdname, cmdclass in get_all_cmds():
202
60
if cmd in cmdclass.aliases:
203
61
return cmdname, cmdclass
264
119
class ExternalCommand(Command):
265
120
"""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
122
We cheat a little here, when get_cmd_class() calls us we actually give it back
123
an object we construct that has the appropriate path, help, options etc for the
126
When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
127
method, which we override to call the Command.__init__ method. That then calls
128
our run method which is pretty straight forward.
130
The only wrinkle is that we have to map bzr's dictionary of options and arguments
131
back into command line options and arguments for the script.
281
134
def find_command(cls, cmd):
283
135
bzrpath = os.environ.get('BZRPATH', '')
285
for dir in bzrpath.split(os.pathsep):
137
for dir in bzrpath.split(':'):
286
138
path = os.path.join(dir, cmd)
287
139
if os.path.isfile(path):
288
140
return ExternalCommand(path)
294
146
def __init__(self, path):
149
# TODO: If either of these fail, we should detect that and
150
# assume that path is not really a bzr plugin after all.
297
152
pipe = os.popen('%s --bzr-usage' % path, 'r')
298
153
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
154
self.takes_args = pipe.readline().split()
308
if pipe.close() is not None:
309
raise BzrError("Failed funning '%s --bzr-usage'" % path)
311
157
pipe = os.popen('%s --bzr-help' % path, 'r')
312
158
self.__doc__ = pipe.read()
313
if pipe.close() is not None:
314
raise BzrError("Failed funning '%s --bzr-help'" % path)
316
161
def __call__(self, options, arguments):
317
162
Command.__init__(self, options, arguments)
378
222
directory is shown. Otherwise, only the status of the specified
379
223
files or directories is reported. If a directory is given, status
380
224
is reported for everything inside that directory.
382
If a revision is specified, the changes since that revision are shown.
384
226
takes_args = ['file*']
385
takes_options = ['all', 'show-ids', 'revision']
227
takes_options = ['all', 'show-ids']
386
228
aliases = ['st', 'stat']
388
230
def run(self, all=False, show_ids=False, file_list=None):
390
b = find_branch(file_list[0])
232
b = Branch(file_list[0], lock_mode='r')
391
233
file_list = [b.relpath(x) for x in file_list]
392
234
# special case: only one path was given and it's the root
394
236
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)
239
b = Branch('.', lock_mode='r')
241
status.show_status(b, show_unchanged=all, show_ids=show_ids,
242
specific_files=file_list)
404
245
class cmd_cat_revision(Command):
455
273
whether already versioned or not, are searched for files or
456
274
subdirectories that are neither versioned or ignored, and these
457
275
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
278
Therefore simply saying 'bzr add .' will version all files that
461
279
are currently unknown.
463
281
TODO: Perhaps adding a file whose directly is not versioned should
464
282
recursively add that parent, rather than giving an error?
466
takes_args = ['file*']
467
takes_options = ['verbose', 'no-recurse']
284
takes_args = ['file+']
285
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)
287
def run(self, file_list, verbose=False):
288
bzrlib.add.smart_add(file_list, verbose)
492
291
class cmd_relpath(Command):
493
292
"""Show path of a file relative to root"""
494
293
takes_args = ['filename']
497
295
def run(self, filename):
498
print find_branch(filename).relpath(filename)
296
print Branch(filename).relpath(filename)
502
300
class cmd_inventory(Command):
503
301
"""Show inventory of the current working copy or a revision."""
504
takes_options = ['revision', 'show-ids']
302
takes_options = ['revision']
506
def run(self, revision=None, show_ids=False):
304
def run(self, revision=None):
508
306
if revision == None:
509
307
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]))
309
inv = b.get_revision_inventory(b.lookup_revision(revision))
516
for path, entry in inv.entries():
518
print '%-50s %s' % (path, entry.file_id)
311
for path, entry in inv.iter_entries():
312
print '%-50s %s' % (entry.file_id, path)
523
315
class cmd_move(Command):
553
344
takes_args = ['from_name', 'to_name']
555
346
def run(self, from_name, to_name):
557
348
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
352
class cmd_renames(Command):
741
353
"""Show list of renamed files.
875
488
TODO: This probably handles non-Unix newlines poorly.
883
491
takes_args = ['file*']
884
takes_options = ['revision', 'diff-options']
885
aliases = ['di', 'dif']
492
takes_options = ['revision']
887
def run(self, revision=None, file_list=None, diff_options=None):
495
def run(self, revision=None, file_list=None):
888
496
from bzrlib.diff import show_diff
891
b = find_branch(file_list[0])
892
file_list = [b.relpath(f) for f in file_list]
893
if file_list == ['']:
894
# just pointing to top-of-tree
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)
498
show_diff(Branch('.'), revision, specific_files=file_list)
940
528
"""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:
533
inv = b.read_working_inventory()
534
sc = statcache.update_cache(b, inv)
535
basis = b.basis_tree()
536
basis_inv = basis.inventory
538
# We used to do this through iter_entries(), but that's slow
539
# when most of the files are unmodified, as is usually the
540
# case. So instead we iterate by inventory entry, and only
541
# calculate paths as necessary.
543
for file_id in basis_inv:
544
cacheentry = sc.get(file_id)
545
if not cacheentry: # deleted
547
ie = basis_inv[file_id]
548
if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
549
path = inv.id2path(file_id)
983
585
class cmd_log(Command):
984
586
"""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.
588
TODO: Option to limit range.
590
TODO: Perhaps show most-recent first with an option for last.
997
592
takes_args = ['filename?']
998
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
999
'long', 'message', 'short',]
1001
def run(self, filename=None, timezone='original',
1009
from bzrlib.branch import find_branch
1010
from bzrlib.log import log_formatter, show_log
1013
direction = (forward and 'forward') or 'reverse'
593
takes_options = ['timezone', 'verbose', 'show-ids']
594
def run(self, filename=None, timezone='original', verbose=False, show_ids=False):
595
from branch import find_branch
596
b = find_branch((filename or '.'), lock_mode='r')
1016
b = find_branch(filename)
1017
fp = b.relpath(filename)
1019
file_id = b.read_working_inventory().path2id(fp)
1021
file_id = None # points to branch root
1023
b = find_branch('.')
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)
1061
direction=direction,
1062
start_revision=rev1,
598
filename = b.relpath(filename)
599
bzrlib.show_log(b, filename,
600
show_timezone=timezone,
1068
606
class cmd_touching_revisions(Command):
1069
"""Return revision-ids which affected a particular file.
1071
A more user-friendly interface is "bzr log FILE"."""
607
"""Return revision-ids which affected a particular file."""
1073
609
takes_args = ["filename"]
1074
610
def run(self, filename):
1075
b = find_branch(filename)
611
b = Branch(filename, lock_mode='r')
1076
612
inv = b.read_working_inventory()
1077
613
file_id = inv.path2id(b.relpath(filename))
1078
614
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
1203
735
except ValueError:
1204
736
raise BzrCommandError("not a valid revision-number: %r" % revno)
1206
print find_branch('.').lookup_revision(revno)
738
print Branch('.').lookup_revision(revno)
1209
741
class cmd_export(Command):
1210
742
"""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
744
If no revision is specified this exports the last committed revision."""
1221
745
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()
746
takes_options = ['revision']
747
def run(self, dest, revision=None):
750
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)
752
rh = b.lookup_revision(int(revision))
753
t = b.revision_tree(rh)
1246
757
class cmd_cat(Command):
1284
791
TODO: Strict commit that fails if there are unknown or deleted files.
1286
793
takes_args = ['selected*']
1287
takes_options = ['message', 'file', 'verbose', 'unchanged']
794
takes_options = ['message', 'file', 'verbose']
1288
795
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
797
def run(self, message=None, file=None, verbose=True, selected_list=None):
798
from bzrlib.commit import commit
1297
800
## Warning: shadows builtin file()
1298
801
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"])
802
raise BzrCommandError("please specify a commit message",
803
["use either --message or --file"])
1313
804
elif message and file:
1314
805
raise BzrCommandError("please specify either --message or --file")
1338
818
This command checks various invariants about the branch storage to
1339
819
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))
821
takes_args = ['dir?']
822
def run(self, dir='.'):
824
bzrlib.check.check(Branch(dir))
1389
830
takes_options = ['email']
1391
832
def run(self, email=False):
1393
b = bzrlib.branch.find_branch('.')
1398
print bzrlib.osutils.user_email(b)
834
print bzrlib.osutils.user_email()
1400
print bzrlib.osutils.username(b)
836
print bzrlib.osutils.username()
1403
839
class cmd_selftest(Command):
1404
840
"""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')
843
failures, tests = 0, 0
845
import doctest, bzrlib.store, bzrlib.tests
846
bzrlib.trace.verbose = False
848
for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
849
bzrlib.tree, bzrlib.commands, bzrlib.add:
850
mf, mt = doctest.testmod(m)
853
print '%-40s %3d tests' % (m.__name__, mt),
855
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
859
print '%-40s %3d tests' % ('total', tests),
861
print '%3d FAILED!' % failures
1431
869
class cmd_version(Command):
1432
"""Show version of bzr."""
870
"""Show version of bzr"""
1436
874
def show_version():
1437
875
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
876
print bzrlib.__copyright__
1443
877
print "http://bazaar-ng.org/"
1454
888
print "it sure does!"
1456
890
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
891
if '/@' in spec:
1472
892
parsed = spec.split('/@')
1473
893
assert len(parsed) == 2
1474
894
if parsed[1] == "":
1478
parsed[1] = int(parsed[1])
1480
pass # We can allow stuff like ./@revid:blahblahblah
1482
assert parsed[1] >=0
897
parsed[1] = int(parsed[1])
1484
900
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
903
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)
904
"""Perform a three-way merge of trees."""
905
takes_args = ['other_spec', 'base_spec']
907
def run(self, other_spec, base_spec):
908
merge.merge(parse_spec(other_spec), parse_spec(base_spec))
1594
910
class cmd_assert_fail(Command):
1595
911
"""Test reporting of assertion failures"""
1602
918
"""Show help on a command or other topic.
1604
920
For a list of all available commands, say 'bzr help commands'."""
1605
takes_options = ['long']
1606
921
takes_args = ['topic?']
1609
def run(self, topic=None, long=False):
924
def run(self, topic=None):
1611
if topic is None and long:
1613
926
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):
929
class cmd_update_stat_cache(Command):
930
"""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)
937
statcache.update_cache(b.base, b.read_working_inventory())
940
######################################################################
1681
944
# list of all available options; the rhs can be either None for an
1761
1002
optname = a[2:]
1762
1003
if optname not in OPTIONS:
1763
raise BzrError('unknown long option %r' % a)
1004
bailout('unknown long option %r' % a)
1765
1006
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
1007
if shortopt not in SHORT_OPTIONS:
1008
bailout('unknown short option %r' % a)
1009
optname = SHORT_OPTIONS[shortopt]
1795
1011
if optname in opts:
1796
1012
# XXX: Do we ever want to support this, e.g. for -r?
1797
raise BzrError('repeated option %r' % a)
1013
bailout('repeated option %r' % a)
1799
1015
optargfn = OPTIONS[optname]
1801
1017
if optarg == None:
1803
raise BzrError('option %r needs an argument' % a)
1019
bailout('option %r needs an argument' % a)
1805
1021
optarg = argv.pop(0)
1806
1022
opts[optname] = optargfn(optarg)
1808
1024
if optarg != None:
1809
raise BzrError('option %r takes no argument' % optname)
1025
bailout('option %r takes no argument' % optname)
1810
1026
opts[optname] = True
1867
1083
This is similar to main(), but without all the trappings for
1868
1084
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
1086
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)
1089
args, opts = parse_args(argv[1:])
1097
elif 'version' in opts:
1100
cmd = str(args.pop(0))
1107
canonical_cmd, cmd_class = get_cmd_class(cmd)
1110
if 'profile' in opts:
1936
1116
# check options are reasonable
1937
1117
allowed = cmd_class.takes_options
1971
1151
return cmd_class(cmdopts, cmdargs).status
1154
def _report_exception(summary, quiet=False):
1156
log_error('bzr: ' + summary)
1157
bzrlib.trace.log_exception()
1160
tb = sys.exc_info()[2]
1161
exinfo = traceback.extract_tb(tb)
1163
sys.stderr.write(' at %s:%d in %s()\n' % exinfo[-1][:3])
1164
sys.stderr.write(' see ~/.bzr.log for debug information\n')
1974
1168
def main(argv):
1977
bzrlib.trace.log_startup(argv)
1979
bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
1171
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()
1176
return run_bzr(argv)
1178
# do this here inside the exception wrappers to catch EPIPE
1181
quiet = isinstance(e, (BzrCommandError))
1182
_report_exception('error: ' + e.args[0], quiet=quiet)
1185
# some explanation or hints
1188
except AssertionError, e:
1189
msg = 'assertion failed'
1191
msg += ': ' + str(e)
1192
_report_exception(msg)
1194
except KeyboardInterrupt, e:
1195
_report_exception('interrupted', quiet=True)
1197
except Exception, e:
1199
if (isinstance(e, IOError)
1200
and hasattr(e, 'errno')
1201
and e.errno == errno.EPIPE):
1205
msg = str(e).rstrip('\n')
1206
_report_exception(msg, quiet)
1209
bzrlib.trace.close_trace()
2012
1212
if __name__ == '__main__':