60
55
Show summary of pending changes.
62
57
Make a file not versioned.
59
Show statistics about this branch.
65
# not currently working:
67
# Show some information about this branch.
71
__copyright__ = "Copyright 2005 Canonical Development Ltd."
72
__author__ = "Martin Pool <mbp@canonical.com>"
73
__docformat__ = "restructuredtext en"
77
import sys, os, random, time, sha, sets, types, re, shutil, tempfile
78
import traceback, socket, fnmatch, difflib
65
import sys, os, time, types, shutil, tempfile, traceback, fnmatch, difflib, os.path
80
66
from sets import Set
81
67
from pprint import pprint
83
69
from glob import glob
70
from inspect import getdoc
86
73
from bzrlib.store import ImmutableStore
87
74
from bzrlib.trace import mutter, note, log_error
88
from bzrlib.errors import bailout, BzrError
75
from bzrlib.errors import bailout, BzrError, BzrCheckError
89
76
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
90
77
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
91
78
from bzrlib.revision import Revision
155
152
print Branch('.').revno()
158
156
def cmd_add(file_list, verbose=False):
159
"""Add specified files.
157
"""Add specified files or directories.
159
In non-recursive mode, all the named items are added, regardless
160
of whether they were previously ignored. A warning is given if
161
any of the named files are already versioned.
163
In recursive mode (the default), files are treated the same way
164
but the behaviour for directories is different. Directories that
165
are already versioned do not give a warning. All directories,
166
whether already versioned or not, are searched for files or
167
subdirectories that are neither versioned or ignored, and these
168
are added. This search proceeds recursively into versioned
171
Therefore simply saying 'bzr add .' will version all files that
172
are currently unknown.
174
bzrlib.add.smart_add(file_list, verbose)
161
Fails if the files are already added.
163
Branch('.').add(file_list, verbose=verbose)
177
def cmd_relpath(filename):
178
"""Show path of file relative to root"""
179
print Branch(filename).relpath(filename)
166
183
def cmd_inventory(revision=None):
198
# TODO: Maybe a 'mv' command that has the combined move/rename
199
# special behaviour of Unix?
201
def cmd_move(source_list, dest):
204
b.move([b.relpath(s) for s in source_list], b.relpath(dest))
208
def cmd_rename(from_name, to_name):
209
"""Change the name of an entry.
211
usage: bzr rename FROM_NAME TO_NAME
214
bzr rename frob.c frobber.c
215
bzr rename src/frob.c lib/frob.c
217
It is an error if the destination name exists.
219
See also the 'move' command, which moves files into a different
220
directory without changing their name.
222
TODO: Some way to rename multiple files without invoking bzr for each
225
b.rename_one(b.relpath(from_name), b.relpath(to_name))
230
def cmd_renames(dir='.'):
231
"""Show list of renamed files.
233
usage: bzr renames [BRANCH]
235
TODO: Option to show renames between two historical versions.
237
TODO: Only show renames under dir, rather than in the whole branch.
240
old_inv = b.basis_tree().inventory
241
new_inv = b.read_working_inventory()
243
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
245
for old_name, new_name in renames:
246
print "%s => %s" % (old_name, new_name)
183
print 'branch format:', b.controlfile('branch-format', 'r').readline().rstrip('\n')
185
def plural(n, base='', pl=None):
193
count_version_dirs = 0
195
count_status = {'A': 0, 'D': 0, 'M': 0, 'R': 0, '?': 0, 'I': 0, '.': 0}
196
for st_tup in bzrlib.diff_trees(b.basis_tree(), b.working_tree()):
198
count_status[fs] += 1
199
if fs not in ['I', '?'] and st_tup[4] == 'directory':
200
count_version_dirs += 1
203
print 'in the working tree:'
204
for name, fs in (('unchanged', '.'),
205
('modified', 'M'), ('added', 'A'), ('removed', 'D'),
206
('renamed', 'R'), ('unknown', '?'), ('ignored', 'I'),
208
print ' %5d %s' % (count_status[fs], name)
209
print ' %5d versioned subdirector%s' % (count_version_dirs,
210
plural(count_version_dirs, 'y', 'ies'))
213
print 'branch history:'
214
history = b.revision_history()
216
print ' %5d revision%s' % (revno, plural(revno))
219
committers.add(b.get_revision(rev).committer)
220
print ' %5d committer%s' % (len(committers), plural(len(committers)))
222
firstrev = b.get_revision(history[0])
223
age = int((time.time() - firstrev.timestamp) / 3600 / 24)
224
print ' %5d day%s old' % (age, plural(age))
225
print ' first revision: %s' % format_date(firstrev.timestamp,
228
lastrev = b.get_revision(history[-1])
229
print ' latest revision: %s' % format_date(lastrev.timestamp,
251
"""info: Show statistical information for this branch
255
info.show_info(Branch('.'))
235
259
def cmd_remove(file_list, verbose=False):
236
Branch('.').remove(file_list, verbose=verbose)
260
b = Branch(file_list[0])
261
b.remove([b.relpath(f) for f in file_list], verbose=verbose)
240
265
def cmd_file_id(filename):
241
i = Branch('.').read_working_inventory().path2id(filename)
243
bailout("%s is not a versioned file" % filename)
266
"""Print file_id of a particular file or directory.
268
usage: bzr file-id FILE
270
The file_id is assigned when the file is first added and remains the
271
same through all revisions where the file exists, even when it is
275
i = b.inventory.path2id(b.relpath(filename))
277
bailout("%r is not a versioned file" % filename)
248
def cmd_find_filename(fileid):
249
n = find_filename(fileid)
251
bailout("%s is not a live file id" % fileid)
282
def cmd_file_id_path(filename):
283
"""Print path of file_ids to a file or directory.
285
usage: bzr file-id-path FILE
287
This prints one line for each directory down to the target,
288
starting at the branch root."""
291
fid = inv.path2id(b.relpath(filename))
293
bailout("%r is not a versioned file" % filename)
294
for fip in inv.get_idpath(fid):
256
298
def cmd_revision_history():
272
326
Branch('.', init=True)
275
def cmd_diff(revision=None):
276
"""Show diff from basis to working copy.
278
:todo: Take one or two revision arguments, look up those trees,
281
:todo: Allow diff across branches.
283
:todo: Mangle filenames in diff to be more relevant.
285
:todo: Shouldn't be in the cmd function.
329
def cmd_diff(revision=None, file_list=None):
330
"""bzr diff: Show differences in working tree.
332
usage: bzr diff [-r REV] [FILE...]
335
Show changes since REV, rather than predecessor.
337
If files are listed, only the changes in those files are listed.
338
Otherwise, all changes for the tree are listed.
340
TODO: Given two revision arguments, show the difference between them.
342
TODO: Allow diff across branches.
344
TODO: Option to use external diff command; could be GNU diff, wdiff,
347
TODO: If a directory is given, diff everything under that.
349
TODO: Selected-file diff is inefficient and doesn't show you deleted files.
352
## TODO: Shouldn't be in the cmd function.
361
def cmd_log(timezone='original'):
435
def cmd_deleted(show_ids=False):
436
"""List files deleted in the working tree.
438
TODO: Show files deleted since a previous revision, or between two revisions.
442
new = b.working_tree()
444
## TODO: Much more efficient way to do this: read in new
445
## directories with readdir, rather than stating each one. Same
446
## level of effort but possibly much less IO. (Or possibly not,
447
## if the directories are very large...)
449
for path, ie in old.inventory.iter_entries():
450
if not new.has_id(ie.file_id):
452
print '%-50s %s' % (path, ie.file_id)
458
def cmd_parse_inventory():
461
cElementTree.ElementTree().parse(file('.bzr/inventory'))
465
def cmd_load_inventory():
466
"""Load inventory for timing purposes"""
467
Branch('.').basis_tree().inventory
470
def cmd_dump_inventory():
471
Branch('.').read_working_inventory().write_xml(sys.stdout)
474
def cmd_dump_new_inventory():
475
import bzrlib.newinventory
476
inv = Branch('.').basis_tree().inventory
477
bzrlib.newinventory.write_inventory(inv, sys.stdout)
480
def cmd_load_new_inventory():
481
import bzrlib.newinventory
482
bzrlib.newinventory.read_new_inventory(sys.stdin)
485
def cmd_dump_slacker_inventory():
486
import bzrlib.newinventory
487
inv = Branch('.').basis_tree().inventory
488
bzrlib.newinventory.write_slacker_inventory(inv, sys.stdout)
492
def cmd_dump_text_inventory():
493
import bzrlib.textinv
494
inv = Branch('.').basis_tree().inventory
495
bzrlib.textinv.write_text_inventory(inv, sys.stdout)
498
def cmd_load_text_inventory():
499
import bzrlib.textinv
500
inv = bzrlib.textinv.read_text_inventory(sys.stdin)
501
print 'loaded %d entries' % len(inv)
505
def cmd_root(filename=None):
506
"""Print the branch root."""
507
print bzrlib.branch.find_branch_root(filename)
510
def cmd_log(timezone='original', verbose=False):
362
511
"""Show log of this branch.
364
:todo: Options for utc; to show ids; to limit range; etc.
513
TODO: Options for utc; to show ids; to limit range; etc.
366
Branch('.').write_log(show_timezone=timezone)
515
Branch('.').write_log(show_timezone=timezone, verbose=verbose)
369
518
def cmd_ls(revision=None, verbose=False):
370
519
"""List files in a tree.
372
:todo: Take a revision or remote path and list that tree instead.
521
TODO: Take a revision or remote path and list that tree instead.
375
524
if revision == None:
436
def cmd_commit(message, verbose=False):
602
def cmd_commit(message=None, verbose=False):
603
"""Commit changes to a new revision.
606
Description of changes in this revision; free form text.
607
It is recommended that the first line be a single-sentence
610
Show status of changed files,
612
TODO: Commit only selected files.
614
TODO: Run hooks on tree to-be-committed, and after commit.
616
TODO: Strict commit that fails if there are unknown or deleted files.
620
bailout("please specify a commit message")
437
621
Branch('.').commit(message, verbose=verbose)
441
"""Check consistency of the branch."""
624
def cmd_check(dir='.'):
625
"""check: Consistency check of branch history.
627
usage: bzr check [-v] [BRANCH]
630
--verbose, -v Show progress of checking.
632
This command checks various invariants about the branch storage to
633
detect data corruption or bzr bugs.
636
bzrlib.check.check(Branch(dir, find_root=False))
445
639
def cmd_is(pred, *rest):
657
TODO: Command to show only the email-address part as parsed out.
461
659
print bzrlib.osutils.username()
464
def cmd_user_email():
465
print bzrlib.osutils.user_email()
468
662
def cmd_gen_revision_id():
470
663
print bzrlib.branch._gen_revision_id(time.time())
474
"""Run internal doctest suite"""
667
"""Run internal test suite"""
475
668
## -v, if present, is seen by doctest; the argument is just here
476
669
## so our parser doesn't complain
478
671
## TODO: --verbose option
673
failures, tests = 0, 0
480
import bzr, doctest, bzrlib.store
675
import doctest, bzrlib.store, bzrlib.tests
481
676
bzrlib.trace.verbose = False
483
doctest.testmod(bzrlib.store)
484
doctest.testmod(bzrlib.inventory)
485
doctest.testmod(bzrlib.branch)
486
doctest.testmod(bzrlib.osutils)
487
doctest.testmod(bzrlib.tree)
489
# more strenuous tests;
491
doctest.testmod(bzrlib.tests)
678
for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
679
bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
680
mf, mt = doctest.testmod(m)
683
print '%-40s %3d tests' % (m.__name__, mt),
685
print '%3d FAILED!' % mf
689
print '%-40s %3d tests' % ('total', tests),
691
print '%3d FAILED!' % failures
698
cmd_doctest = cmd_selftest
494
701
######################################################################
499
# TODO: Specific help for particular commands
705
def cmd_help(topic=None):
710
# otherwise, maybe the name of a command?
712
cmdfn = globals()['cmd_' + topic.replace('-', '_')]
714
bailout("no help for %r" % topic)
718
bailout("sorry, no detailed help yet for %r" % topic)
503
725
def cmd_version():
504
print "bzr (bazaar-ng) %s" % __version__
726
print "bzr (bazaar-ng) %s" % bzrlib.__version__
727
print bzrlib.__copyright__
506
728
print "http://bazaar-ng.org/"
560
784
'add': ['file+'],
788
'export': ['revno', 'dest'],
563
789
'file-id': ['filename'],
790
'file-id-path': ['filename'],
564
791
'get-file-text': ['text_id'],
565
792
'get-inventory': ['inventory_id'],
566
793
'get-revision': ['revision_id'],
567
794
'get-revision-inventory': ['revision_id'],
569
798
'lookup-revision': ['revno'],
570
'export': ['revno', 'dest'],
799
'move': ['source$', 'dest'],
800
'relpath': ['filename'],
571
801
'remove': ['file+'],
802
'rename': ['from_name', 'to_name'],
804
'root': ['filename?'],
689
933
"""Execute a command.
691
935
This is similar to main(), but without all the trappings for
692
logging and error handling.
936
logging and error handling.
939
argv = [a.decode(bzrlib.user_encoding) for a in argv]
695
942
args, opts = parse_args(argv[1:])
696
943
if 'help' in opts:
697
944
# TODO: pass down other arguments in case they asked for
698
945
# help on a command name?
701
951
elif 'version' in opts:
704
954
cmd = args.pop(0)
705
955
except IndexError:
706
log_error('usage: bzr COMMAND\n')
707
log_error(' try "bzr help"\n')
956
log_error('usage: bzr COMMAND')
957
log_error(' try "bzr help"')
711
961
cmd_handler = globals()['cmd_' + cmd.replace('-', '_')]
713
963
bailout("unknown command " + `cmd`)
715
# TODO: special --profile option to turn on the Python profiler
966
if 'profile' in opts:
717
972
# check options are reasonable
718
973
allowed = cmd_options.get(cmd, [])
721
976
bailout("option %r is not allowed for command %r"
979
# TODO: give an error if there are any mandatory options which are
980
# not specified? Or maybe there shouldn't be any "mandatory
981
# options" (it is an oxymoron)
983
# mix arguments and options into one dictionary
724
984
cmdargs = _match_args(cmd, args)
727
ret = cmd_handler(**cmdargs) or 0
985
for k, v in opts.items():
986
cmdargs[str(k.replace('-', '_'))] = v
990
pffileno, pfname = tempfile.mkstemp()
992
prof = hotshot.Profile(pfname)
993
ret = prof.runcall(cmd_handler, **cmdargs) or 0
997
stats = hotshot.stats.load(pfname)
999
stats.sort_stats('time')
1000
## XXX: Might like to write to stderr or the trace file instead but
1001
## print_stats seems hardcoded to stdout
1002
stats.print_stats(20)
1010
return cmd_handler(**cmdargs) or 0
735
1018
## TODO: If the arguments are wrong, give a usage message rather
736
1019
## than just a backtrace.
1021
bzrlib.trace.create_tracefile(argv)
739
# TODO: Lift into separate function in trace.py
740
# TODO: Also show contents of /etc/lsb-release, if it can be parsed.
741
# Perhaps that should eventually go into the platform library?
742
# TODO: If the file doesn't exist, add a note describing it.
743
t = bzrlib.trace._tracefile
744
t.write('-' * 60 + '\n')
745
t.write('bzr invoked at %s\n' % format_date(time.time()))
746
t.write(' by %s on %s\n' % (bzrlib.osutils.username(), socket.getfqdn()))
747
t.write(' arguments: %r\n' % argv)
749
starttime = os.times()[4]
752
t.write(' platform: %s\n' % platform.platform())
753
t.write(' python: %s\n' % platform.python_version())
755
1024
ret = run_bzr(argv)
758
mutter("finished, %.3fu/%.3fs cpu, %.3fu/%.3fs cum"
760
mutter(" %.3f elapsed" % (times[4] - starttime))
763
1026
except BzrError, e:
764
log_error('bzr: error: ' + e.args[0] + '\n')
1027
log_error('bzr: error: ' + e.args[0])
765
1028
if len(e.args) > 1:
766
1029
for h in e.args[1]:
767
log_error(' ' + h + '\n')
1031
traceback.print_exc(None, bzrlib.trace._tracefile)
1032
log_error('(see ~/.bzr.log for debug information)')
769
1034
except Exception, e:
770
log_error('bzr: exception: %s\n' % e)
771
log_error(' see .bzr.log for details\n')
1035
log_error('bzr: exception: %s' % str(e).rstrip('\n'))
1036
log_error('(see $HOME/.bzr.log for debug information)')
772
1037
traceback.print_exc(None, bzrlib.trace._tracefile)
773
traceback.print_exc(None, sys.stderr)
1038
## traceback.print_exc(None, sys.stderr)
776
# TODO: Maybe nicer handling of IOError?
1041
## TODO: Trap AssertionError
1043
# TODO: Maybe nicer handling of IOError especially for broken pipe.