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
86
from ElementTree import Element, ElementTree, SubElement
70
from inspect import getdoc
89
73
from bzrlib.store import ImmutableStore
90
74
from bzrlib.trace import mutter, note, log_error
91
from bzrlib.errors import bailout, BzrError
75
from bzrlib.errors import bailout, BzrError, BzrCheckError
92
76
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
93
77
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
94
78
from bzrlib.revision import Revision
116
100
## TODO: Perhaps make UUIDs predictable in test mode to make it easier
117
101
## to compare output?
119
## TODO: Is ElementTree really all that much better for our purposes?
120
## Perhaps using the standard MiniDOM would be enough?
127
######################################################################
103
## TODO: Some kind of global code to generate the right Branch object
104
## to work on. Almost, but not quite all, commands need one, and it
105
## can be taken either from their parameters or their working
108
## TODO: rename command, needed soon: check destination doesn't exist
109
## either in working copy or tree; move working copy; update
110
## inventory; write out
112
## TODO: move command; check destination is a directory and will not
115
## TODO: command to show renames, one per line, as to->from
131
120
def cmd_status(all=False):
147
136
Branch('.').get_revision(revision_id).write_xml(sys.stdout)
150
def cmd_get_inventory(inventory_id):
151
"""Return inventory in XML by hash"""
152
Branch('.').get_inventory(inventory_hash).write_xml(sys.stdout)
155
def cmd_get_revision_inventory(revision_id):
156
"""Output inventory for a revision."""
158
b.get_revision_inventory(revision_id).write_xml(sys.stdout)
161
139
def cmd_get_file_text(text_id):
162
140
"""Get contents of a file by hash."""
163
141
sf = Branch('.').text_store[text_id]
174
152
print Branch('.').revno()
177
156
def cmd_add(file_list, verbose=False):
178
"""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)
180
Fails if the files are already added.
182
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)
185
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)
202
print 'branch format:', b.controlfile('branch-format', 'r').readline().rstrip('\n')
203
print 'revision number:', b.revno()
204
print 'number of versioned files:', len(b.read_working_inventory())
251
"""info: Show statistical information for this branch
255
info.show_info(Branch('.'))
207
259
def cmd_remove(file_list, verbose=False):
208
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)
212
265
def cmd_file_id(filename):
213
i = Branch('.').read_working_inventory().path2id(filename)
215
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)
220
def cmd_find_filename(fileid):
221
n = find_filename(fileid)
223
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):
228
298
def cmd_revision_history():
244
326
Branch('.', init=True)
247
def cmd_diff(revision=None):
248
"""Show diff from basis to working copy.
250
:todo: Take one or two revision arguments, look up those trees,
253
:todo: Allow diff across branches.
255
:todo: Mangle filenames in diff to be more relevant.
257
: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.
281
373
# be usefully made into a much faster special case.
283
375
# TODO: Better to return them in sorted order I think.
377
# FIXME: If given a file list, compare only those files rather
378
# than comparing everything and then throwing stuff away.
285
380
for file_state, fid, old_name, new_name, kind in bzrlib.diff_trees(old_tree, new_tree):
382
if file_list and new_name not in file_list:
288
385
# Don't show this by default; maybe do it if an option is passed
289
386
# idlabel = ' {%s}' % fid
292
389
# FIXME: Something about the diff format makes patch unhappy
293
390
# with newly-added files.
295
def diffit(*a, **kw):
296
sys.stdout.writelines(difflib.unified_diff(*a, **kw))
392
def diffit(oldlines, newlines, **kw):
393
# FIXME: difflib is wrong if there is no trailing newline.
395
# Special workaround for Python2.3, where difflib fails if
396
# both sequences are empty.
397
if oldlines or newlines:
398
sys.stdout.writelines(difflib.unified_diff(oldlines, newlines, **kw))
299
401
if file_state in ['.', '?', 'I']:
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):
334
511
"""Show log of this branch.
336
:todo: Options for utc; to show ids; to limit range; etc.
513
TODO: Options for utc; to show ids; to limit range; etc.
338
Branch('.').write_log()
515
Branch('.').write_log(show_timezone=timezone, verbose=verbose)
341
518
def cmd_ls(revision=None, verbose=False):
342
519
"""List files in a tree.
344
: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.
347
524
if revision == None:
552
"""List ignored files and the patterns that matched them.
554
tree = Branch('.').working_tree()
555
for path, file_class, kind, file_id in tree.list_files():
556
if file_class != 'I':
558
## XXX: Slightly inefficient since this was already calculated
559
pat = tree.is_ignored(path)
560
print '%-50s %s' % (path, pat)
373
563
def cmd_lookup_revision(revno):
375
565
revno = int(revno)
398
592
"""Print a newly-generated UUID."""
403
def cmd_commit(message, verbose=False):
593
print bzrlib.osutils.uuid()
597
def cmd_local_time_offset():
598
print bzrlib.osutils.local_time_offset()
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")
404
621
Branch('.').commit(message, verbose=verbose)
408
"""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))
412
639
def cmd_is(pred, *rest):
435
662
def cmd_gen_revision_id():
437
663
print bzrlib.branch._gen_revision_id(time.time())
441
"""Run internal doctest suite"""
667
"""Run internal test suite"""
442
668
## -v, if present, is seen by doctest; the argument is just here
443
669
## so our parser doesn't complain
445
671
## TODO: --verbose option
673
failures, tests = 0, 0
447
import bzr, doctest, bzrlib.store
675
import doctest, bzrlib.store, bzrlib.tests
448
676
bzrlib.trace.verbose = False
450
doctest.testmod(bzrlib.store)
451
doctest.testmod(bzrlib.inventory)
452
doctest.testmod(bzrlib.branch)
453
doctest.testmod(bzrlib.osutils)
454
doctest.testmod(bzrlib.tree)
456
# more strenuous tests;
458
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
461
701
######################################################################
466
# 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)
470
725
def cmd_version():
471
print "bzr (bazaar-ng) %s" % __version__
726
print "bzr (bazaar-ng) %s" % bzrlib.__version__
727
print bzrlib.__copyright__
473
728
print "http://bazaar-ng.org/"
511
768
# listed take none.
513
770
'add': ['verbose'],
514
772
'commit': ['message', 'verbose'],
773
'deleted': ['show-ids'],
515
774
'diff': ['revision'],
516
775
'inventory': ['revision'],
776
'log': ['timezone', 'verbose'],
517
777
'ls': ['revision', 'verbose'],
778
'remove': ['verbose'],
518
779
'status': ['all'],
520
'remove': ['verbose'],
526
784
'add': ['file+'],
788
'export': ['revno', 'dest'],
529
789
'file-id': ['filename'],
790
'file-id-path': ['filename'],
530
791
'get-file-text': ['text_id'],
531
792
'get-inventory': ['inventory_id'],
532
793
'get-revision': ['revision_id'],
533
794
'get-revision-inventory': ['revision_id'],
535
798
'lookup-revision': ['revno'],
536
'export': ['revno', 'dest'],
799
'move': ['source$', 'dest'],
800
'relpath': ['filename'],
537
801
'remove': ['file+'],
802
'rename': ['from_name', 'to_name'],
804
'root': ['filename?'],
547
814
lookup table, something about the available options, what optargs
548
815
they take, and which commands will accept them.
550
>>> parse_args('bzr --help'.split())
817
>>> parse_args('--help'.split())
551
818
([], {'help': True})
552
>>> parse_args('bzr --version'.split())
819
>>> parse_args('--version'.split())
553
820
([], {'version': True})
554
>>> parse_args('bzr status --all'.split())
821
>>> parse_args('status --all'.split())
555
822
(['status'], {'all': True})
823
>>> parse_args('commit --message=biter'.split())
824
(['commit'], {'message': u'biter'})
560
829
# TODO: Maybe handle '--' to end options?
567
836
mutter(" got option %r" % a)
838
optname, optarg = a[2:].split('=', 1)
569
841
if optname not in OPTIONS:
570
842
bailout('unknown long option %r' % a)
577
849
if optname in opts:
578
850
# XXX: Do we ever want to support this, e.g. for -r?
579
851
bailout('repeated option %r' % a)
580
853
optargfn = OPTIONS[optname]
583
bailout('option %r needs an argument' % a)
584
opts[optname] = optargfn(it.next())
585
mutter(" option argument %r" % opts[optname])
857
bailout('option %r needs an argument' % a)
860
opts[optname] = optargfn(optarg)
587
# takes no option argument
863
bailout('option %r takes no argument' % optname)
588
864
opts[optname] = True
590
bailout('unknown short option %r' % a)
646
933
"""Execute a command.
648
935
This is similar to main(), but without all the trappings for
649
logging and error handling.
936
logging and error handling.
939
argv = [a.decode(bzrlib.user_encoding) for a in argv]
652
args, opts = parse_args(argv)
942
args, opts = parse_args(argv[1:])
653
943
if 'help' in opts:
654
944
# TODO: pass down other arguments in case they asked for
655
945
# help on a command name?
658
951
elif 'version' in opts:
678
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
681
984
cmdargs = _match_args(cmd, args)
684
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
692
1018
## TODO: If the arguments are wrong, give a usage message rather
693
1019
## than just a backtrace.
1021
bzrlib.trace.create_tracefile(argv)
696
t = bzrlib.trace._tracefile
697
t.write('-' * 60 + '\n')
698
t.write('bzr invoked at %s\n' % format_date(time.time()))
699
t.write(' by %s on %s\n' % (bzrlib.osutils.username(), socket.gethostname()))
700
t.write(' arguments: %r\n' % argv)
702
starttime = os.times()[4]
705
t.write(' platform: %s\n' % platform.platform())
706
t.write(' python: %s\n' % platform.python_version())
708
1024
ret = run_bzr(argv)
711
mutter("finished, %.3fu/%.3fs cpu, %.3fu/%.3fs cum"
713
mutter(" %.3f elapsed" % (times[4] - starttime))
716
1026
except BzrError, e:
717
1027
log_error('bzr: error: ' + e.args[0] + '\n')
718
1028
if len(e.args) > 1:
719
1029
for h in e.args[1]:
720
1030
log_error(' ' + h + '\n')
1031
traceback.print_exc(None, bzrlib.trace._tracefile)
1032
log_error('(see $HOME/.bzr.log for debug information)\n')
722
1034
except Exception, e:
723
1035
log_error('bzr: exception: %s\n' % e)
724
log_error(' see .bzr.log for details\n')
1036
log_error('(see $HOME/.bzr.log for debug information)\n')
725
1037
traceback.print_exc(None, bzrlib.trace._tracefile)
726
traceback.print_exc(None, sys.stderr)
1038
## traceback.print_exc(None, sys.stderr)
729
# TODO: Maybe nicer handling of IOError?
1041
## TODO: Trap AssertionError
1043
# TODO: Maybe nicer handling of IOError especially for broken pipe.