17
20
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
22
"""Bazaar-NG -- a free distributed version-control tool
22
24
**WARNING: THIS IS AN UNSTABLE DEVELOPMENT VERSION**
26
Current limitation include:
24
28
* Metadata format is not stable yet -- you may need to
25
29
discard history in the future.
31
* No handling of subdirectories, symlinks or any non-text files.
33
* Insufficient error handling.
27
35
* Many commands unimplemented or partially implemented.
29
37
* Space-inefficient storage.
31
39
* No merge operators yet.
41
Interesting commands::
44
Show summary help screen
38
Show software version/licence/non-warranty.
46
Show software version/licence/non-warranty.
40
Start versioning the current directory
48
Start versioning the current directory
44
Show revision history.
47
bzr move FROM... DESTDIR
48
Move one or more files to a different directory.
50
Show changes from last revision to working copy.
52
Show revision history.
54
Show changes from last revision to working copy.
51
55
bzr commit -m 'MESSAGE'
52
Store current state as new revision.
56
Store current state as new revision.
53
57
bzr export REVNO DESTINATION
54
Export the branch state at a previous version.
58
Export the branch state at a previous version.
56
Show summary of pending changes.
60
Show summary of pending changes.
58
Make a file not versioned.
60
Show statistics about this branch.
62
Verify history is stored safely.
63
(for more type 'bzr help commands')
62
Make a file not versioned.
69
import sys, os, time, types, shutil, tempfile, fnmatch, difflib, os.path
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
70
82
from sets import Set
71
83
from pprint import pprint
73
85
from glob import glob
74
from inspect import getdoc
77
88
from bzrlib.store import ImmutableStore
78
89
from bzrlib.trace import mutter, note, log_error
79
from bzrlib.errors import bailout, BzrError, BzrCheckError
90
from bzrlib.errors import bailout, BzrError
80
91
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
81
92
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
82
93
from bzrlib.revision import Revision
170
168
print Branch('.').revno()
174
171
def cmd_add(file_list, verbose=False):
175
"""Add specified files or directories.
177
In non-recursive mode, all the named items are added, regardless
178
of whether they were previously ignored. A warning is given if
179
any of the named files are already versioned.
181
In recursive mode (the default), files are treated the same way
182
but the behaviour for directories is different. Directories that
183
are already versioned do not give a warning. All directories,
184
whether already versioned or not, are searched for files or
185
subdirectories that are neither versioned or ignored, and these
186
are added. This search proceeds recursively into versioned
189
Therefore simply saying 'bzr add .' will version all files that
190
are currently unknown.
192
TODO: Perhaps adding a file whose directly is not versioned should
193
recursively add that parent, rather than giving an error?
172
"""Add specified files.
174
Fails if the files are already added.
195
bzrlib.add.smart_add(file_list, verbose)
198
def cmd_relpath(filename):
199
"""Show path of file relative to root"""
200
print Branch(filename).relpath(filename)
176
Branch('.').add(file_list, verbose=verbose)
204
179
def cmd_inventory(revision=None):
219
# TODO: Maybe a 'mv' command that has the combined move/rename
220
# special behaviour of Unix?
222
def cmd_move(source_list, dest):
225
b.move([b.relpath(s) for s in source_list], b.relpath(dest))
229
def cmd_rename(from_name, to_name):
230
"""Change the name of an entry.
232
usage: bzr rename FROM_NAME TO_NAME
235
bzr rename frob.c frobber.c
236
bzr rename src/frob.c lib/frob.c
238
It is an error if the destination name exists.
240
See also the 'move' command, which moves files into a different
241
directory without changing their name.
243
TODO: Some way to rename multiple files without invoking bzr for each
246
b.rename_one(b.relpath(from_name), b.relpath(to_name))
251
def cmd_renames(dir='.'):
252
"""Show list of renamed files.
254
usage: bzr renames [BRANCH]
256
TODO: Option to show renames between two historical versions.
258
TODO: Only show renames under dir, rather than in the whole branch.
261
old_inv = b.basis_tree().inventory
262
new_inv = b.read_working_inventory()
264
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
266
for old_name, new_name in renames:
267
print "%s => %s" % (old_name, new_name)
272
"""info: Show statistical information for this branch
276
info.show_info(Branch('.'))
196
print 'branch format:', b.controlfile('branch-format', 'r').readline().rstrip('\n')
197
print 'revision number:', b.revno()
198
print 'number of versioned files:', len(b.read_working_inventory())
280
201
def cmd_remove(file_list, verbose=False):
281
b = Branch(file_list[0])
282
b.remove([b.relpath(f) for f in file_list], verbose=verbose)
202
Branch('.').remove(file_list, verbose=verbose)
286
206
def cmd_file_id(filename):
287
"""Print file_id of a particular file or directory.
289
usage: bzr file-id FILE
291
The file_id is assigned when the file is first added and remains the
292
same through all revisions where the file exists, even when it is
296
i = b.inventory.path2id(b.relpath(filename))
298
bailout("%r is not a versioned file" % filename)
207
i = Branch('.').read_working_inventory().path2id(filename)
209
bailout("%s is not a versioned file" % filename)
303
def cmd_file_id_path(filename):
304
"""Print path of file_ids to a file or directory.
306
usage: bzr file-id-path FILE
308
This prints one line for each directory down to the target,
309
starting at the branch root."""
312
fid = inv.path2id(b.relpath(filename))
314
bailout("%r is not a versioned file" % filename)
315
for fip in inv.get_idpath(fid):
214
def cmd_find_filename(fileid):
215
n = find_filename(fileid)
217
bailout("%s is not a live file id" % fileid)
319
222
def cmd_revision_history():
347
238
Branch('.', init=True)
350
def cmd_diff(revision=None, file_list=None):
351
"""bzr diff: Show differences in working tree.
353
usage: bzr diff [-r REV] [FILE...]
356
Show changes since REV, rather than predecessor.
358
If files are listed, only the changes in those files are listed.
359
Otherwise, all changes for the tree are listed.
361
TODO: Given two revision arguments, show the difference between them.
363
TODO: Allow diff across branches.
365
TODO: Option to use external diff command; could be GNU diff, wdiff,
368
TODO: Python difflib is not exactly the same as unidiff; should
369
either fix it up or prefer to use an external diff.
371
TODO: If a directory is given, diff everything under that.
373
TODO: Selected-file diff is inefficient and doesn't show you
376
TODO: This probably handles non-Unix newlines poorly.
379
## TODO: Shouldn't be in the cmd function.
241
def cmd_diff(revision=None):
242
"""Show diff from basis to working copy.
244
:todo: Take one or two revision arguments, look up those trees,
247
:todo: Allow diff across branches.
249
:todo: Mangle filenames in diff to be more relevant.
251
:todo: Shouldn't be in the cmd function.
419
286
# FIXME: Something about the diff format makes patch unhappy
420
287
# with newly-added files.
422
def diffit(oldlines, newlines, **kw):
424
# FIXME: difflib is wrong if there is no trailing newline.
425
# The syntax used by patch seems to be "\ No newline at
426
# end of file" following the last diff line from that
427
# file. This is not trivial to insert into the
428
# unified_diff output and it might be better to just fix
429
# or replace that function.
431
# In the meantime we at least make sure the patch isn't
435
# Special workaround for Python2.3, where difflib fails if
436
# both sequences are empty.
437
if not oldlines and not newlines:
442
if oldlines and (oldlines[-1][-1] != '\n'):
445
if newlines and (newlines[-1][-1] != '\n'):
449
ud = difflib.unified_diff(oldlines, newlines, **kw)
450
sys.stdout.writelines(ud)
452
print "\\ No newline at end of file"
453
sys.stdout.write('\n')
289
def diffit(*a, **kw):
290
sys.stdout.writelines(difflib.unified_diff(*a, **kw))
455
293
if file_state in ['.', '?', 'I']:
489
def cmd_deleted(show_ids=False):
490
"""List files deleted in the working tree.
492
TODO: Show files deleted since a previous revision, or between two revisions.
496
new = b.working_tree()
498
## TODO: Much more efficient way to do this: read in new
499
## directories with readdir, rather than stating each one. Same
500
## level of effort but possibly much less IO. (Or possibly not,
501
## if the directories are very large...)
503
for path, ie in old.inventory.iter_entries():
504
if not new.has_id(ie.file_id):
506
print '%-50s %s' % (path, ie.file_id)
512
def cmd_parse_inventory():
515
cElementTree.ElementTree().parse(file('.bzr/inventory'))
519
def cmd_load_inventory():
520
"""Load inventory for timing purposes"""
521
Branch('.').basis_tree().inventory
524
def cmd_dump_inventory():
525
Branch('.').read_working_inventory().write_xml(sys.stdout)
528
def cmd_dump_new_inventory():
529
import bzrlib.newinventory
530
inv = Branch('.').basis_tree().inventory
531
bzrlib.newinventory.write_inventory(inv, sys.stdout)
534
def cmd_load_new_inventory():
535
import bzrlib.newinventory
536
bzrlib.newinventory.read_new_inventory(sys.stdin)
539
def cmd_dump_slacker_inventory():
540
import bzrlib.newinventory
541
inv = Branch('.').basis_tree().inventory
542
bzrlib.newinventory.write_slacker_inventory(inv, sys.stdout)
546
def cmd_dump_text_inventory():
547
import bzrlib.textinv
548
inv = Branch('.').basis_tree().inventory
549
bzrlib.textinv.write_text_inventory(inv, sys.stdout)
552
def cmd_load_text_inventory():
553
import bzrlib.textinv
554
inv = bzrlib.textinv.read_text_inventory(sys.stdin)
555
print 'loaded %d entries' % len(inv)
559
def cmd_root(filename=None):
560
"""Print the branch root."""
561
print bzrlib.branch.find_branch_root(filename)
564
def cmd_log(timezone='original', verbose=False):
565
328
"""Show log of this branch.
567
TODO: Options for utc; to show ids; to limit range; etc.
330
:todo: Options for utc; to show ids; to limit range; etc.
569
Branch('.').write_log(show_timezone=timezone, verbose=verbose)
332
Branch('.').write_log()
572
335
def cmd_ls(revision=None, verbose=False):
573
336
"""List files in a tree.
575
TODO: Take a revision or remote path and list that tree instead.
338
:todo: Take a revision or remote path and list that tree instead.
578
341
if revision == None:
656
def cmd_commit(message=None, verbose=False):
657
"""Commit changes to a new revision.
660
Description of changes in this revision; free form text.
661
It is recommended that the first line be a single-sentence
664
Show status of changed files,
666
TODO: Commit only selected files.
668
TODO: Run hooks on tree to-be-committed, and after commit.
670
TODO: Strict commit that fails if there are unknown or deleted files.
674
bailout("please specify a commit message")
402
def cmd_commit(message, verbose=False):
675
403
Branch('.').commit(message, verbose=verbose)
678
def cmd_check(dir='.'):
679
"""check: Consistency check of branch history.
681
usage: bzr check [-v] [BRANCH]
684
--verbose, -v Show progress of checking.
686
This command checks various invariants about the branch storage to
687
detect data corruption or bzr bugs.
690
bzrlib.check.check(Branch(dir, find_root=False))
407
"""Check consistency of the branch."""
693
411
def cmd_is(pred, *rest):
708
def cmd_whoami(email=False):
714
--email Show only the email address.
718
print bzrlib.osutils.user_email()
720
print bzrlib.osutils.username()
427
print bzrlib.osutils.username()
430
def cmd_user_email():
431
print bzrlib.osutils.user_email()
723
434
def cmd_gen_revision_id():
724
436
print bzrlib.branch._gen_revision_id(time.time())
728
"""Run internal test suite"""
440
"""Run internal doctest suite"""
729
441
## -v, if present, is seen by doctest; the argument is just here
730
442
## so our parser doesn't complain
732
444
## TODO: --verbose option
734
failures, tests = 0, 0
736
import doctest, bzrlib.store, bzrlib.tests
446
import bzr, doctest, bzrlib.store
737
447
bzrlib.trace.verbose = False
739
for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
740
bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
741
mf, mt = doctest.testmod(m)
744
print '%-40s %3d tests' % (m.__name__, mt),
746
print '%3d FAILED!' % mf
750
print '%-40s %3d tests' % ('total', tests),
752
print '%3d FAILED!' % failures
759
cmd_doctest = cmd_selftest
449
doctest.testmod(bzrlib.store)
450
doctest.testmod(bzrlib.inventory)
451
doctest.testmod(bzrlib.branch)
452
doctest.testmod(bzrlib.osutils)
453
doctest.testmod(bzrlib.tree)
455
# more strenuous tests;
457
doctest.testmod(bzrlib.tests)
762
460
######################################################################
766
def cmd_help(topic=None):
769
elif topic == 'commands':
772
# otherwise, maybe the name of a command?
773
topic, cmdfn = get_cmd_handler(topic)
777
bailout("sorry, no detailed help yet for %r" % topic)
783
"""List all commands"""
785
for k in globals().keys():
786
if k.startswith('cmd_'):
787
accu.append(k[4:].replace('_','-'))
789
print "bzr commands: "
792
print "note: some of these commands are internal-use or obsolete"
793
# TODO: Some kind of marker for internal-use commands?
794
# TODO: Show aliases?
465
# TODO: Specific help for particular commands
799
469
def cmd_version():
800
print "bzr (bazaar-ng) %s" % bzrlib.__version__
801
print bzrlib.__copyright__
470
print "bzr (bazaar-ng) %s" % __version__
802
472
print "http://bazaar-ng.org/"
843
510
# listed take none.
845
512
'add': ['verbose'],
847
513
'commit': ['message', 'verbose'],
848
'deleted': ['show-ids'],
849
514
'diff': ['revision'],
850
515
'inventory': ['revision'],
851
'log': ['timezone', 'verbose'],
852
516
'ls': ['revision', 'verbose'],
853
519
'remove': ['verbose'],
860
525
'add': ['file+'],
864
'export': ['revno', 'dest'],
865
528
'file-id': ['filename'],
866
'file-id-path': ['filename'],
867
529
'get-file-text': ['text_id'],
868
530
'get-inventory': ['inventory_id'],
869
531
'get-revision': ['revision_id'],
870
532
'get-revision-inventory': ['revision_id'],
874
534
'lookup-revision': ['revno'],
875
'move': ['source$', 'dest'],
876
'relpath': ['filename'],
535
'export': ['revno', 'dest'],
877
536
'remove': ['file+'],
878
'rename': ['from_name', 'to_name'],
880
'root': ['filename?'],
890
546
lookup table, something about the available options, what optargs
891
547
they take, and which commands will accept them.
893
>>> parse_args('--help'.split())
549
>>> parse_args('bzr --help'.split())
894
550
([], {'help': True})
895
>>> parse_args('--version'.split())
551
>>> parse_args('bzr --version'.split())
896
552
([], {'version': True})
897
>>> parse_args('status --all'.split())
553
>>> parse_args('bzr status --all'.split())
898
554
(['status'], {'all': True})
899
>>> parse_args('commit --message=biter'.split())
900
(['commit'], {'message': u'biter'})
905
559
# TODO: Maybe handle '--' to end options?
910
# option names must not be unicode
914
566
mutter(" got option %r" % a)
916
optname, optarg = a[2:].split('=', 1)
919
568
if optname not in OPTIONS:
920
569
bailout('unknown long option %r' % a)
1011
645
"""Execute a command.
1013
647
This is similar to main(), but without all the trappings for
1014
logging and error handling.
648
logging and error handling.
1017
argv = [a.decode(bzrlib.user_encoding) for a in argv]
1020
args, opts = parse_args(argv[1:])
651
args, opts = parse_args(argv)
1021
652
if 'help' in opts:
1022
653
# TODO: pass down other arguments in case they asked for
1023
654
# help on a command name?
1029
657
elif 'version' in opts:
1032
cmd = str(args.pop(0))
1033
661
except IndexError:
1034
log_error('usage: bzr COMMAND')
1035
log_error(' try "bzr help"')
662
log_error('usage: bzr COMMAND\n')
663
log_error(' try "bzr help"\n')
1038
canonical_cmd, cmd_handler = get_cmd_handler(cmd)
1041
if 'profile' in opts:
667
cmd_handler = globals()['cmd_' + cmd.replace('-', '_')]
669
bailout("unknown command " + `cmd`)
671
# TODO: special --profile option to turn on the Python profiler
1047
673
# check options are reasonable
1048
allowed = cmd_options.get(canonical_cmd, [])
674
allowed = cmd_options.get(cmd, [])
1049
675
for oname in opts:
1050
676
if oname not in allowed:
1051
677
bailout("option %r is not allowed for command %r"
1054
# TODO: give an error if there are any mandatory options which are
1055
# not specified? Or maybe there shouldn't be any "mandatory
1056
# options" (it is an oxymoron)
1058
# mix arguments and options into one dictionary
1059
cmdargs = _match_args(canonical_cmd, args)
1060
for k, v in opts.items():
1061
cmdargs[k.replace('-', '_')] = v
1065
pffileno, pfname = tempfile.mkstemp()
1067
prof = hotshot.Profile(pfname)
1068
ret = prof.runcall(cmd_handler, **cmdargs) or 0
1071
import hotshot.stats
1072
stats = hotshot.stats.load(pfname)
1074
stats.sort_stats('time')
1075
## XXX: Might like to write to stderr or the trace file instead but
1076
## print_stats seems hardcoded to stdout
1077
stats.print_stats(20)
1085
return cmd_handler(**cmdargs) or 0
1089
def _report_exception(e, summary):
1091
log_error('bzr: ' + summary)
1092
bzrlib.trace.log_exception(e)
1093
tb = sys.exc_info()[2]
1094
exinfo = traceback.extract_tb(tb)
1096
sys.stderr.write(' at %s:%d in %s()\n' % exinfo[-1][:3])
1097
sys.stderr.write(' see ~/.bzr.log for debug information\n')
1100
def cmd_assert_fail():
1101
assert False, "always fails"
680
cmdargs = _match_args(cmd, args)
683
ret = cmd_handler(**cmdargs) or 0
1105
bzrlib.trace.create_tracefile(argv)
688
## TODO: Handle command-line options; probably know what options are valid for
691
## TODO: If the arguments are wrong, give a usage message rather
692
## than just a backtrace.
1112
_report_exception(e, 'error: ' + e.args[0])
1115
# some explanation or hints
1118
except AssertionError, e:
1119
msg = 'assertion failed'
1121
msg += ': ' + str(e)
1122
_report_exception(e, msg)
1123
except Exception, e:
1124
_report_exception(e, 'exception: %s' % str(e).rstrip('\n'))
1127
bzrlib.trace.close_trace()
1129
## TODO: Trap AssertionError
1131
# TODO: Maybe nicer handling of IOError especially for broken pipe.
695
t = bzrlib.trace._tracefile
696
t.write('-' * 60 + '\n')
697
t.write('bzr invoked at %s\n' % format_date(time.time()))
698
t.write(' by %s on %s\n' % (bzrlib.osutils.username(), socket.gethostname()))
699
t.write(' arguments: %r\n' % argv)
701
starttime = os.times()[4]
704
t.write(' platform: %s\n' % platform.platform())
705
t.write(' python: %s\n' % platform.python_version())
710
mutter("finished, %.3fu/%.3fs cpu, %.3fu/%.3fs cum"
712
mutter(" %.3f elapsed" % (times[4] - starttime))
716
log_error('bzr: error: ' + e.args[0] + '\n')
719
log_error(' ' + h + '\n')
722
log_error('bzr: exception: %s\n' % e)
723
log_error(' see .bzr.log for details\n')
724
traceback.print_exc(None, bzrlib.trace._tracefile)
725
traceback.print_exc(None, sys.stderr)
728
# TODO: Maybe nicer handling of IOError?
1135
732
if __name__ == '__main__':
1136
733
sys.exit(main(sys.argv))
735
##profile.run('main(sys.argv)')