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
# Run internal consistency checks.
69
# Show some information about this branch.
73
__copyright__ = "Copyright 2005 Canonical Development Ltd."
74
__author__ = "Martin Pool <mbp@canonical.com>"
75
__docformat__ = "restructuredtext en"
79
import sys, os, random, time, sha, sets, types, re, shutil, tempfile
80
import traceback, socket, fnmatch, difflib
65
import sys, os, time, types, shutil, tempfile, traceback, fnmatch, difflib, os.path
82
66
from sets import Set
83
67
from pprint import pprint
85
69
from glob import glob
70
from inspect import getdoc
88
73
from bzrlib.store import ImmutableStore
89
74
from bzrlib.trace import mutter, note, log_error
90
from bzrlib.errors import bailout, BzrError
75
from bzrlib.errors import bailout, BzrError, BzrCheckError
91
76
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
92
77
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
93
78
from bzrlib.revision import Revision
168
152
print Branch('.').revno()
171
156
def cmd_add(file_list, verbose=False):
172
"""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)
174
Fails if the files are already added.
176
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)
179
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)
196
print 'branch format:', b.controlfile('branch-format', 'r').readline().rstrip('\n')
198
def plural(n, base='', pl=None):
206
count_version_dirs = 0
208
count_status = {'A': 0, 'D': 0, 'M': 0, 'R': 0, '?': 0, 'I': 0, '.': 0}
209
for st_tup in bzrlib.diff_trees(b.basis_tree(), b.working_tree()):
211
count_status[fs] += 1
212
if fs not in ['I', '?'] and st_tup[4] == 'directory':
213
count_version_dirs += 1
216
print 'in the working tree:'
217
for name, fs in (('unchanged', '.'),
218
('modified', 'M'), ('added', 'A'), ('removed', 'D'),
219
('renamed', 'R'), ('unknown', '?'), ('ignored', 'I'),
221
print ' %5d %s' % (count_status[fs], name)
222
print ' %5d versioned subdirector%s' % (count_version_dirs,
223
plural(count_version_dirs, 'y', 'ies'))
226
print 'branch history:'
227
history = b.revision_history()
229
print ' %5d revision%s' % (revno, plural(revno))
232
committers.add(b.get_revision(rev).committer)
233
print ' %5d committer%s' % (len(committers), plural(len(committers)))
235
firstrev = b.get_revision(history[0])
236
age = int((time.time() - firstrev.timestamp) / 3600 / 24)
237
print ' %5d day%s old' % (age, plural(age))
238
print ' first revision: %s' % format_date(firstrev.timestamp,
241
lastrev = b.get_revision(history[-1])
242
print ' latest revision: %s' % format_date(lastrev.timestamp,
251
"""info: Show statistical information for this branch
255
info.show_info(Branch('.'))
248
259
def cmd_remove(file_list, verbose=False):
249
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)
253
265
def cmd_file_id(filename):
254
i = Branch('.').read_working_inventory().path2id(filename)
256
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)
261
def cmd_find_filename(fileid):
262
n = find_filename(fileid)
264
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):
269
298
def cmd_revision_history():
285
326
Branch('.', init=True)
288
def cmd_diff(revision=None):
289
"""Show diff from basis to working copy.
291
:todo: Take one or two revision arguments, look up those trees,
294
:todo: Allow diff across branches.
296
:todo: Mangle filenames in diff to be more relevant.
298
: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.
374
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):
375
511
"""Show log of this branch.
377
513
:todo: Options for utc; to show ids; to limit range; etc.
379
Branch('.').write_log(show_timezone=timezone)
515
Branch('.').write_log(show_timezone=timezone, verbose=verbose)
382
518
def cmd_ls(revision=None, verbose=False):
449
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")
450
621
Branch('.').commit(message, verbose=verbose)
454
"""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))
458
639
def cmd_is(pred, *rest):
481
662
def cmd_gen_revision_id():
483
663
print bzrlib.branch._gen_revision_id(time.time())
487
"""Run internal doctest suite"""
667
"""Run internal test suite"""
488
668
## -v, if present, is seen by doctest; the argument is just here
489
669
## so our parser doesn't complain
491
671
## TODO: --verbose option
673
failures, tests = 0, 0
493
import bzr, doctest, bzrlib.store
675
import doctest, bzrlib.store, bzrlib.tests
494
676
bzrlib.trace.verbose = False
496
doctest.testmod(bzrlib.store)
497
doctest.testmod(bzrlib.inventory)
498
doctest.testmod(bzrlib.branch)
499
doctest.testmod(bzrlib.osutils)
500
doctest.testmod(bzrlib.tree)
502
# more strenuous tests;
504
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
507
701
######################################################################
512
# 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)
516
725
def cmd_version():
517
print "bzr (bazaar-ng) %s" % __version__
726
print "bzr (bazaar-ng) %s" % bzrlib.__version__
727
print bzrlib.__copyright__
519
728
print "http://bazaar-ng.org/"
573
784
'add': ['file+'],
788
'export': ['revno', 'dest'],
576
789
'file-id': ['filename'],
790
'file-id-path': ['filename'],
577
791
'get-file-text': ['text_id'],
578
792
'get-inventory': ['inventory_id'],
579
793
'get-revision': ['revision_id'],
580
794
'get-revision-inventory': ['revision_id'],
582
798
'lookup-revision': ['revno'],
583
'export': ['revno', 'dest'],
799
'move': ['source$', 'dest'],
800
'relpath': ['filename'],
584
801
'remove': ['file+'],
802
'rename': ['from_name', 'to_name'],
804
'root': ['filename?'],
734
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
737
984
cmdargs = _match_args(cmd, args)
740
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
748
1018
## TODO: If the arguments are wrong, give a usage message rather
749
1019
## than just a backtrace.
1021
bzrlib.trace.create_tracefile(argv)
752
t = bzrlib.trace._tracefile
753
t.write('-' * 60 + '\n')
754
t.write('bzr invoked at %s\n' % format_date(time.time()))
755
t.write(' by %s on %s\n' % (bzrlib.osutils.username(), socket.gethostname()))
756
t.write(' arguments: %r\n' % argv)
758
starttime = os.times()[4]
761
t.write(' platform: %s\n' % platform.platform())
762
t.write(' python: %s\n' % platform.python_version())
764
1024
ret = run_bzr(argv)
767
mutter("finished, %.3fu/%.3fs cpu, %.3fu/%.3fs cum"
769
mutter(" %.3f elapsed" % (times[4] - starttime))
772
1026
except BzrError, e:
773
1027
log_error('bzr: error: ' + e.args[0] + '\n')
774
1028
if len(e.args) > 1:
775
1029
for h in e.args[1]:
776
1030
log_error(' ' + h + '\n')
1031
traceback.print_exc(None, bzrlib.trace._tracefile)
1032
log_error('(see $HOME/.bzr.log for debug information)\n')
778
1034
except Exception, e:
779
1035
log_error('bzr: exception: %s\n' % e)
780
log_error(' see .bzr.log for details\n')
1036
log_error('(see $HOME/.bzr.log for debug information)\n')
781
1037
traceback.print_exc(None, bzrlib.trace._tracefile)
782
traceback.print_exc(None, sys.stderr)
1038
## traceback.print_exc(None, sys.stderr)
785
# TODO: Maybe nicer handling of IOError?
1041
## TODO: Trap AssertionError
1043
# TODO: Maybe nicer handling of IOError especially for broken pipe.