65
import sys, os, random, time, sha, sets, types, re, shutil, tempfile
66
import traceback, socket, fnmatch, difflib
65
import sys, os, time, types, shutil, tempfile, traceback, fnmatch, difflib, os.path
68
66
from sets import Set
69
67
from pprint import pprint
74
72
from bzrlib.store import ImmutableStore
75
73
from bzrlib.trace import mutter, note, log_error
76
from bzrlib.errors import bailout, BzrError
74
from bzrlib.errors import bailout, BzrError, BzrCheckError
77
75
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
78
76
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
79
77
from bzrlib.revision import Revision
197
# TODO: Maybe a 'mv' command that has the combined move/rename
198
# special behaviour of Unix?
200
def cmd_move(source_list, dest):
203
b.move([b.relpath(s) for s in source_list], b.relpath(dest))
207
def cmd_rename(from_name, to_name):
208
"""Change the name of an entry.
210
usage: bzr rename FROM_NAME TO_NAME
213
bzr rename frob.c frobber.c
214
bzr rename src/frob.c lib/frob.c
216
It is an error if the destination name exists.
218
See also the 'move' command, which moves files into a different
219
directory without changing their name.
221
TODO: Some way to rename multiple files without invoking bzr for each
224
b.rename_one(b.relpath(from_name), b.relpath(to_name))
229
def cmd_renames(dir='.'):
230
"""Show list of renamed files.
232
usage: bzr renames [BRANCH]
234
TODO: Option to show renames between two historical versions.
236
TODO: Only show renames under dir, rather than in the whole branch.
239
old_inv = b.basis_tree().inventory
240
new_inv = b.read_working_inventory()
242
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
244
for old_name, new_name in renames:
245
print "%s => %s" % (old_name, new_name)
199
250
"""info: Show statistical information for this branch
213
264
def cmd_file_id(filename):
265
"""Print file_id of a particular file or directory.
267
usage: bzr file-id FILE
269
The file_id is assigned when the file is first added and remains the
270
same through all revisions where the file exists, even when it is
214
273
b = Branch(filename)
215
274
i = b.inventory.path2id(b.relpath(filename))
217
bailout("%s is not a versioned file" % filename)
276
bailout("%r is not a versioned file" % filename)
222
def cmd_find_filename(fileid):
223
n = find_filename(fileid)
225
bailout("%s is not a live file id" % fileid)
281
def cmd_file_id_path(filename):
282
"""Print path of file_ids to a file or directory.
284
usage: bzr file-id-path FILE
286
This prints one line for each directory down to the target,
287
starting at the branch root."""
290
fid = inv.path2id(b.relpath(filename))
292
bailout("%r is not a versioned file" % filename)
293
for fip in inv.get_idpath(fid):
230
297
def cmd_revision_history():
246
325
Branch('.', init=True)
249
def cmd_diff(revision=None):
328
def cmd_diff(revision=None, file_list=None):
250
329
"""bzr diff: Show differences in working tree.
252
usage: bzr diff [-r REV]
331
usage: bzr diff [-r REV] [FILE...]
255
334
Show changes since REV, rather than predecessor.
336
If files are listed, only the changes in those files are listed.
337
Otherwise, all changes for the tree are listed.
257
339
TODO: Given two revision arguments, show the difference between them.
259
341
TODO: Allow diff across branches.
261
343
TODO: Option to use external diff command; could be GNU diff, wdiff,
262
344
or a graphical diff.
264
TODO: Diff selected files.
346
TODO: If a directory is given, diff everything under that.
348
TODO: Selected-file diff is inefficient and doesn't show you deleted files.
267
351
## TODO: Shouldn't be in the cmd function.
274
358
old_tree = b.revision_tree(b.lookup_revision(revision))
276
360
new_tree = b.working_tree()
277
old_inv = old_tree.inventory
278
new_inv = new_tree.inventory
280
362
# TODO: Options to control putting on a prefix or suffix, perhaps as a format string
290
372
# be usefully made into a much faster special case.
292
374
# TODO: Better to return them in sorted order I think.
376
# FIXME: If given a file list, compare only those files rather
377
# than comparing everything and then throwing stuff away.
294
379
for file_state, fid, old_name, new_name, kind in bzrlib.diff_trees(old_tree, new_tree):
381
if file_list and new_name not in file_list:
297
384
# Don't show this by default; maybe do it if an option is passed
298
385
# idlabel = ' {%s}' % fid
301
388
# FIXME: Something about the diff format makes patch unhappy
302
389
# with newly-added files.
304
def diffit(*a, **kw):
305
sys.stdout.writelines(difflib.unified_diff(*a, **kw))
391
def diffit(oldlines, newlines, **kw):
392
# FIXME: difflib is wrong if there is no trailing newline.
394
# Special workaround for Python2.3, where difflib fails if
395
# both sequences are empty.
396
if oldlines or newlines:
397
sys.stdout.writelines(difflib.unified_diff(oldlines, newlines, **kw))
308
400
if file_state in ['.', '?', 'I']:
434
def cmd_deleted(show_ids=False):
435
"""List files deleted in the working tree.
437
TODO: Show files deleted since a previous revision, or between two revisions.
441
new = b.working_tree()
443
## TODO: Much more efficient way to do this: read in new
444
## directories with readdir, rather than stating each one. Same
445
## level of effort but possibly much less IO. (Or possibly not,
446
## if the directories are very large...)
448
for path, ie in old.inventory.iter_entries():
449
if not new.has_id(ie.file_id):
451
print '%-50s %s' % (path, ie.file_id)
457
def cmd_parse_inventory():
460
cElementTree.ElementTree().parse(file('.bzr/inventory'))
464
def cmd_load_inventory():
465
"""Load inventory for timing purposes"""
466
Branch('.').basis_tree().inventory
469
def cmd_dump_inventory():
470
Branch('.').read_working_inventory().write_xml(sys.stdout)
473
def cmd_dump_new_inventory():
474
import bzrlib.newinventory
475
inv = Branch('.').basis_tree().inventory
476
bzrlib.newinventory.write_inventory(inv, sys.stdout)
479
def cmd_load_new_inventory():
480
import bzrlib.newinventory
481
bzrlib.newinventory.read_new_inventory(sys.stdin)
484
def cmd_dump_slacker_inventory():
485
import bzrlib.newinventory
486
inv = Branch('.').basis_tree().inventory
487
bzrlib.newinventory.write_slacker_inventory(inv, sys.stdout)
342
491
def cmd_root(filename=None):
343
492
"""Print the branch root."""
344
493
print bzrlib.branch.find_branch_root(filename)
538
"""List ignored files and the patterns that matched them.
540
tree = Branch('.').working_tree()
541
for path, file_class, kind, file_id in tree.list_files():
542
if file_class != 'I':
544
## XXX: Slightly inefficient since this was already calculated
545
pat = tree.is_ignored(path)
546
print '%-50s %s' % (path, pat)
387
549
def cmd_lookup_revision(revno):
389
551
revno = int(revno)
402
564
t = b.revision_tree(rh)
567
def cmd_cat(revision, filename):
568
"""Print file to stdout."""
570
b.print_file(b.relpath(filename), int(revision))
407
573
######################################################################
482
648
def cmd_gen_revision_id():
484
649
print bzrlib.branch._gen_revision_id(time.time())
487
def cmd_selftest(verbose=False):
488
653
"""Run internal test suite"""
489
654
## -v, if present, is seen by doctest; the argument is just here
490
655
## so our parser doesn't complain
588
754
# listed take none.
590
756
'add': ['verbose'],
591
758
'commit': ['message', 'verbose'],
759
'deleted': ['show-ids'],
592
760
'diff': ['revision'],
593
761
'inventory': ['revision'],
594
'log': ['show-ids', 'timezone'],
595
763
'ls': ['revision', 'verbose'],
596
764
'remove': ['verbose'],
597
765
'status': ['all'],
602
770
'add': ['file+'],
605
774
'export': ['revno', 'dest'],
606
775
'file-id': ['filename'],
776
'file-id-path': ['filename'],
607
777
'get-file-text': ['text_id'],
608
778
'get-inventory': ['inventory_id'],
609
779
'get-revision': ['revision_id'],
614
784
'lookup-revision': ['revno'],
785
'move': ['source$', 'dest'],
615
786
'relpath': ['filename'],
616
787
'remove': ['file+'],
788
'rename': ['from_name', 'to_name'],
617
790
'root': ['filename?'],
707
879
if ap[-1] == '?':
709
881
argdict[argname] = args.pop(0)
882
elif ap[-1] == '*': # all remaining arguments
884
argdict[argname + '_list'] = args[:]
887
argdict[argname + '_list'] = None
712
888
elif ap[-1] == '+':
714
890
bailout("command %r needs one or more %s"
717
893
argdict[argname + '_list'] = args[:]
895
elif ap[-1] == '$': # all but one
897
bailout("command %r needs one or more %s"
898
% (cmd, argname.upper()))
899
argdict[argname + '_list'] = args[:-1]
720
902
# just a plain arg
761
946
bailout("unknown command " + `cmd`)
763
# TODO: special --profile option to turn on the Python profiler
949
if 'profile' in opts:
765
955
# check options are reasonable
766
956
allowed = cmd_options.get(cmd, [])
769
959
bailout("option %r is not allowed for command %r"
962
# TODO: give an error if there are any mandatory options which are
963
# not specified? Or maybe there shouldn't be any "mandatory
964
# options" (it is an oxymoron)
966
# mix arguments and options into one dictionary
772
967
cmdargs = _match_args(cmd, args)
775
ret = cmd_handler(**cmdargs) or 0
968
for k, v in opts.items():
969
cmdargs[k.replace('-', '_')] = v
973
prof = hotshot.Profile('.bzr.profile')
974
ret = prof.runcall(cmd_handler, **cmdargs) or 0
978
stats = hotshot.stats.load('.bzr.profile')
980
stats.sort_stats('time')
981
stats.print_stats(20)
985
return cmd_handler(**cmdargs) or 0
793
1003
if len(e.args) > 1:
794
1004
for h in e.args[1]:
795
1005
log_error(' ' + h + '\n')
1006
traceback.print_exc(None, bzrlib.trace._tracefile)
1007
log_error('(see $HOME/.bzr.log for debug information)\n')
797
1009
except Exception, e:
798
1010
log_error('bzr: exception: %s\n' % e)
799
log_error(' see .bzr.log for details\n')
1011
log_error('(see $HOME/.bzr.log for debug information)\n')
800
1012
traceback.print_exc(None, bzrlib.trace._tracefile)
801
traceback.print_exc(None, sys.stderr)
1013
## traceback.print_exc(None, sys.stderr)
804
1016
# TODO: Maybe nicer handling of IOError?