16
14
# along with this program; if not, write to the Free Software
17
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
"""Bazaar-NG -- a free distributed version-control tool
21
**WARNING: THIS IS AN UNSTABLE DEVELOPMENT VERSION**
23
Current limitation include:
25
* Metadata format is not stable yet -- you may need to
26
discard history in the future.
28
* Insufficient error handling.
30
* Many commands unimplemented or partially implemented.
32
* Space-inefficient storage.
34
* No merge operators yet.
36
Interesting commands::
41
Show software version/licence/non-warranty.
43
Start versioning the current directory
47
Show revision history.
49
Show changes from last revision to working copy.
50
bzr commit -m 'MESSAGE'
51
Store current state as new revision.
52
bzr export REVNO DESTINATION
53
Export the branch state at a previous version.
55
Show summary of pending changes.
57
Make a file not versioned.
59
Show statistics about this branch.
65
import sys, os, random, time, sha, sets, types, re, shutil, tempfile
66
import traceback, socket, fnmatch, difflib
69
from pprint import pprint
19
import sys, os, time, os.path
74
from bzrlib.store import ImmutableStore
75
22
from bzrlib.trace import mutter, note, log_error
76
from bzrlib.errors import bailout, BzrError
23
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
77
24
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
78
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
25
from bzrlib.tree import RevisionTree, EmptyTree, Tree
79
26
from bzrlib.revision import Revision
80
27
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
83
BZR_DIFF_FORMAT = "## Bazaar-NG diff, format 0 ##\n"
84
BZR_PATCHNAME_FORMAT = 'cset:sha1:%s'
86
## standard representation
87
NONE_STRING = '(none)'
91
## TODO: Perhaps a different version of inventory commands that
92
## returns iterators...
94
## TODO: Perhaps an AtomicFile class that writes to a temporary file and then renames.
96
## TODO: Some kind of locking on branches. Perhaps there should be a
97
## parameter to the branch object saying whether we want a read or
98
## write lock; release it from destructor. Perhaps don't even need a
99
## read lock to look at immutable objects?
101
## TODO: Perhaps make UUIDs predictable in test mode to make it easier
102
## to compare output?
104
## TODO: Some kind of global code to generate the right Branch object
105
## to work on. Almost, but not quite all, commands need one, and it
106
## can be taken either from their parameters or their working
109
## TODO: rename command, needed soon: check destination doesn't exist
110
## either in working copy or tree; move working copy; update
111
## inventory; write out
113
## TODO: move command; check destination is a directory and will not
116
## TODO: command to show renames, one per line, as to->from
121
def cmd_status(all=False):
29
from bzrlib import merge
32
def _squish_command_name(cmd):
33
return 'cmd_' + cmd.replace('-', '_')
36
def _unsquish_command_name(cmd):
37
assert cmd.startswith("cmd_")
38
return cmd[4:].replace('_','-')
41
"""Return canonical name and class for all registered commands."""
42
for k, v in globals().iteritems():
43
if k.startswith("cmd_"):
44
yield _unsquish_command_name(k), v
46
def get_cmd_class(cmd):
47
"""Return the canonical name and command class for a command.
49
cmd = str(cmd) # not unicode
51
# first look up this command under the specified name
53
return cmd, globals()[_squish_command_name(cmd)]
57
# look for any command which claims this as an alias
58
for cmdname, cmdclass in get_all_cmds():
59
if cmd in cmdclass.aliases:
60
return cmdname, cmdclass
62
cmdclass = ExternalCommand.find_command(cmd)
66
raise BzrCommandError("unknown command %r" % cmd)
70
"""Base class for commands.
72
The docstring for an actual command should give a single-line
73
summary, then a complete description of the command. A grammar
74
description will be inserted.
77
List of argument forms, marked with whether they are optional,
81
List of options that may be given for this command.
84
If true, this command isn't advertised.
93
def __init__(self, options, arguments):
94
"""Construct and run the command.
96
Sets self.status to the return value of run()."""
97
assert isinstance(options, dict)
98
assert isinstance(arguments, dict)
99
cmdargs = options.copy()
100
cmdargs.update(arguments)
101
assert self.__doc__ != Command.__doc__, \
102
("No help message set for %r" % self)
103
self.status = self.run(**cmdargs)
107
"""Override this in sub-classes.
109
This is invoked with the options and arguments bound to
112
Return 0 or None if the command was successful, or a shell
118
class ExternalCommand(Command):
119
"""Class to wrap external commands.
121
We cheat a little here, when get_cmd_class() calls us we actually give it back
122
an object we construct that has the appropriate path, help, options etc for the
125
When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
126
method, which we override to call the Command.__init__ method. That then calls
127
our run method which is pretty straight forward.
129
The only wrinkle is that we have to map bzr's dictionary of options and arguments
130
back into command line options and arguments for the script.
133
def find_command(cls, cmd):
134
bzrpath = os.environ.get('BZRPATH', '')
136
for dir in bzrpath.split(':'):
137
path = os.path.join(dir, cmd)
138
if os.path.isfile(path):
139
return ExternalCommand(path)
143
find_command = classmethod(find_command)
145
def __init__(self, path):
148
# TODO: If either of these fail, we should detect that and
149
# assume that path is not really a bzr plugin after all.
151
pipe = os.popen('%s --bzr-usage' % path, 'r')
152
self.takes_options = pipe.readline().split()
153
self.takes_args = pipe.readline().split()
156
pipe = os.popen('%s --bzr-help' % path, 'r')
157
self.__doc__ = pipe.read()
160
def __call__(self, options, arguments):
161
Command.__init__(self, options, arguments)
164
def run(self, **kargs):
172
if OPTIONS.has_key(name):
174
opts.append('--%s' % name)
175
if value is not None and value is not True:
176
opts.append(str(value))
178
# it's an arg, or arg list
179
if type(value) is not list:
185
self.status = os.spawnv(os.P_WAIT, self.path, [self.path] + opts + args)
189
class cmd_status(Command):
122
190
"""Display status summary.
124
For each file there is a single line giving its file state and name.
125
The name is that in the current revision unless it is deleted or
126
missing, in which case the old name is shown.
128
:todo: Don't show unchanged files unless ``--all`` is given?
192
This reports on versioned and unknown files, reporting them
193
grouped by state. Possible states are:
196
Versioned in the working copy but not in the previous revision.
199
Versioned in the previous revision but removed or deleted
203
Path of this file changed from the previous revision;
204
the text may also have changed. This includes files whose
205
parent directory was renamed.
208
Text has changed since the previous revision.
211
Nothing about this file has changed since the previous revision.
212
Only shown with --all.
215
Not versioned and not matching an ignore pattern.
217
To see ignored files use 'bzr ignored'. For details in the
218
changes to file texts, use 'bzr diff'.
220
If no arguments are specified, the status of the entire working
221
directory is shown. Otherwise, only the status of the specified
222
files or directories is reported. If a directory is given, status
223
is reported for everything inside that directory.
130
Branch('.').show_status(show_all=all)
134
######################################################################
136
def cmd_get_revision(revision_id):
137
Branch('.').get_revision(revision_id).write_xml(sys.stdout)
140
def cmd_get_file_text(text_id):
141
"""Get contents of a file by hash."""
142
sf = Branch('.').text_store[text_id]
143
pumpfile(sf, sys.stdout)
147
######################################################################
152
"""Show number of revisions on this branch"""
153
print Branch('.').revno()
157
def cmd_add(file_list, verbose=False):
225
takes_args = ['file*']
226
takes_options = ['all', 'show-ids']
227
aliases = ['st', 'stat']
229
def run(self, all=False, show_ids=False, file_list=None):
231
b = Branch(file_list[0], lock_mode='r')
232
file_list = [b.relpath(x) for x in file_list]
233
# special case: only one path was given and it's the root
235
if file_list == ['']:
238
b = Branch('.', lock_mode='r')
240
status.show_status(b, show_unchanged=all, show_ids=show_ids,
241
specific_files=file_list)
244
class cmd_cat_revision(Command):
245
"""Write out metadata for a revision."""
248
takes_args = ['revision_id']
250
def run(self, revision_id):
251
Branch('.').get_revision(revision_id).write_xml(sys.stdout)
254
class cmd_revno(Command):
255
"""Show current revision number.
257
This is equal to the number of revisions on this branch."""
259
print Branch('.').revno()
262
class cmd_add(Command):
158
263
"""Add specified files or directories.
160
265
In non-recursive mode, all the named items are added, regardless
172
277
Therefore simply saying 'bzr add .' will version all files that
173
278
are currently unknown.
175
bzrlib.add.smart_add(file_list, verbose)
178
def cmd_relpath(filename):
179
"""Show path of file relative to root"""
180
print Branch(filename).relpath(filename)
183
def cmd_inventory(revision=None):
184
"""Show inventory of the current working copy."""
185
## TODO: Also optionally show a previous inventory
186
## TODO: Format options
280
TODO: Perhaps adding a file whose directly is not versioned should
281
recursively add that parent, rather than giving an error?
283
takes_args = ['file+']
284
takes_options = ['verbose']
286
def run(self, file_list, verbose=False):
287
bzrlib.add.smart_add(file_list, verbose)
290
class cmd_relpath(Command):
291
"""Show path of a file relative to root"""
292
takes_args = ['filename']
294
def run(self, filename):
295
print Branch(filename).relpath(filename)
299
class cmd_inventory(Command):
300
"""Show inventory of the current working copy or a revision."""
301
takes_options = ['revision']
303
def run(self, revision=None):
306
inv = b.read_working_inventory()
308
inv = b.get_revision_inventory(b.lookup_revision(revision))
310
for path, entry in inv.iter_entries():
311
print '%-50s %s' % (entry.file_id, path)
314
class cmd_move(Command):
315
"""Move files to a different directory.
320
The destination must be a versioned directory in the same branch.
322
takes_args = ['source$', 'dest']
323
def run(self, source_list, dest):
326
b.move([b.relpath(s) for s in source_list], b.relpath(dest))
329
class cmd_rename(Command):
330
"""Change the name of an entry.
333
bzr rename frob.c frobber.c
334
bzr rename src/frob.c lib/frob.c
336
It is an error if the destination name exists.
338
See also the 'move' command, which moves files into a different
339
directory without changing their name.
341
TODO: Some way to rename multiple files without invoking bzr for each
343
takes_args = ['from_name', 'to_name']
345
def run(self, from_name, to_name):
347
b.rename_one(b.relpath(from_name), b.relpath(to_name))
351
class cmd_renames(Command):
352
"""Show list of renamed files.
354
TODO: Option to show renames between two historical versions.
356
TODO: Only show renames under dir, rather than in the whole branch.
358
takes_args = ['dir?']
360
def run(self, dir='.'):
362
old_inv = b.basis_tree().inventory
363
new_inv = b.read_working_inventory()
365
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
367
for old_name, new_name in renames:
368
print "%s => %s" % (old_name, new_name)
371
class cmd_info(Command):
372
"""Show statistical information about a branch."""
373
takes_args = ['branch?']
375
def run(self, branch=None):
378
from branch import find_branch
379
b = find_branch(branch)
383
class cmd_remove(Command):
384
"""Make a file unversioned.
386
This makes bzr stop tracking changes to a versioned file. It does
387
not delete the working copy.
389
takes_args = ['file+']
390
takes_options = ['verbose']
392
def run(self, file_list, verbose=False):
393
b = Branch(file_list[0])
394
b.remove([b.relpath(f) for f in file_list], verbose=verbose)
397
class cmd_file_id(Command):
398
"""Print file_id of a particular file or directory.
400
The file_id is assigned when the file is first added and remains the
401
same through all revisions where the file exists, even when it is
405
takes_args = ['filename']
406
def run(self, filename):
408
i = b.inventory.path2id(b.relpath(filename))
410
bailout("%r is not a versioned file" % filename)
415
class cmd_file_path(Command):
416
"""Print path of file_ids to a file or directory.
418
This prints one line for each directory down to the target,
419
starting at the branch root."""
421
takes_args = ['filename']
422
def run(self, filename):
425
fid = inv.path2id(b.relpath(filename))
427
bailout("%r is not a versioned file" % filename)
428
for fip in inv.get_idpath(fid):
432
class cmd_revision_history(Command):
433
"""Display list of revision ids on this branch."""
435
for patchid in Branch('.').revision_history():
439
class cmd_directories(Command):
440
"""Display list of versioned directories in this branch."""
442
for name, ie in Branch('.').read_working_inventory().directories():
449
class cmd_init(Command):
450
"""Make a directory into a versioned branch.
452
Use this to create an empty branch, or before importing an
455
Recipe for importing a tree of files:
460
bzr commit -m 'imported project'
463
Branch('.', init=True)
466
class cmd_diff(Command):
467
"""Show differences in working tree.
469
If files are listed, only the changes in those files are listed.
470
Otherwise, all changes for the tree are listed.
472
TODO: Given two revision arguments, show the difference between them.
474
TODO: Allow diff across branches.
476
TODO: Option to use external diff command; could be GNU diff, wdiff,
479
TODO: Python difflib is not exactly the same as unidiff; should
480
either fix it up or prefer to use an external diff.
482
TODO: If a directory is given, diff everything under that.
484
TODO: Selected-file diff is inefficient and doesn't show you
487
TODO: This probably handles non-Unix newlines poorly.
490
takes_args = ['file*']
491
takes_options = ['revision']
494
def run(self, revision=None, file_list=None):
495
from bzrlib.diff import show_diff
497
show_diff(Branch('.'), revision, specific_files=file_list)
503
class cmd_deleted(Command):
504
"""List files deleted in the working tree.
506
TODO: Show files deleted since a previous revision, or between two revisions.
508
def run(self, show_ids=False):
511
new = b.working_tree()
513
## TODO: Much more efficient way to do this: read in new
514
## directories with readdir, rather than stating each one. Same
515
## level of effort but possibly much less IO. (Or possibly not,
516
## if the directories are very large...)
518
for path, ie in old.inventory.iter_entries():
519
if not new.has_id(ie.file_id):
521
print '%-50s %s' % (path, ie.file_id)
526
class cmd_modified(Command):
527
"""List files modified in working tree."""
189
532
inv = b.read_working_inventory()
191
inv = b.get_revision_inventory(b.lookup_revision(revision))
193
for path, entry in inv.iter_entries():
194
print '%-50s %s' % (entry.file_id, path)
200
info.show_info(Branch('.'))
204
def cmd_remove(file_list, verbose=False):
205
b = Branch(file_list[0])
206
b.remove([b.relpath(f) for f in file_list], verbose=verbose)
210
def cmd_file_id(filename):
212
i = b.inventory.path2id(b.relpath(filename))
214
bailout("%s is not a versioned file" % filename)
219
def cmd_find_filename(fileid):
220
n = find_filename(fileid)
222
bailout("%s is not a live file id" % fileid)
227
def cmd_revision_history():
228
for patchid in Branch('.').revision_history():
234
# TODO: Check we're not already in a working directory? At the
235
# moment you'll get an ugly error.
237
# TODO: What if we're in a subdirectory of a branch? Would like
238
# to allow that, but then the parent may need to understand that
239
# the children have disappeared, or should they be versioned in
242
# TODO: Take an argument/option for branch name.
243
Branch('.', init=True)
246
def cmd_diff(revision=None):
247
"""Show diff from basis to working copy.
249
:todo: Take one or two revision arguments, look up those trees,
252
:todo: Allow diff across branches.
254
:todo: Mangle filenames in diff to be more relevant.
256
:todo: Shouldn't be in the cmd function.
258
TODO: Option to use external diff command; could be GNU diff,
259
wdiff, or a graphical diff.
265
old_tree = b.basis_tree()
267
old_tree = b.revision_tree(b.lookup_revision(revision))
269
new_tree = b.working_tree()
270
old_inv = old_tree.inventory
271
new_inv = new_tree.inventory
273
# TODO: Options to control putting on a prefix or suffix, perhaps as a format string
277
DEVNULL = '/dev/null'
278
# Windows users, don't panic about this filename -- it is a
279
# special signal to GNU patch that the file should be created or
280
# deleted respectively.
282
# TODO: Generation of pseudo-diffs for added/deleted files could
283
# be usefully made into a much faster special case.
285
# TODO: Better to return them in sorted order I think.
287
for file_state, fid, old_name, new_name, kind in bzrlib.diff_trees(old_tree, new_tree):
290
# Don't show this by default; maybe do it if an option is passed
291
# idlabel = ' {%s}' % fid
294
# FIXME: Something about the diff format makes patch unhappy
295
# with newly-added files.
297
def diffit(*a, **kw):
298
sys.stdout.writelines(difflib.unified_diff(*a, **kw))
301
if file_state in ['.', '?', 'I']:
303
elif file_state == 'A':
304
print '*** added %s %r' % (kind, new_name)
307
new_tree.get_file(fid).readlines(),
309
tofile=new_label + new_name + idlabel)
310
elif file_state == 'D':
311
assert isinstance(old_name, types.StringTypes)
312
print '*** deleted %s %r' % (kind, old_name)
314
diffit(old_tree.get_file(fid).readlines(), [],
315
fromfile=old_label + old_name + idlabel,
317
elif file_state in ['M', 'R']:
318
if file_state == 'M':
319
assert kind == 'file'
320
assert old_name == new_name
321
print '*** modified %s %r' % (kind, new_name)
322
elif file_state == 'R':
323
print '*** renamed %s %r => %r' % (kind, old_name, new_name)
326
diffit(old_tree.get_file(fid).readlines(),
327
new_tree.get_file(fid).readlines(),
328
fromfile=old_label + old_name + idlabel,
329
tofile=new_label + new_name)
331
bailout("can't represent state %s {%s}" % (file_state, fid))
335
def cmd_root(filename=None):
336
"""Print the branch root."""
337
print bzrlib.branch.find_branch_root(filename)
340
def cmd_log(timezone='original'):
533
sc = statcache.update_cache(b, inv)
534
basis = b.basis_tree()
535
basis_inv = basis.inventory
537
# We used to do this through iter_entries(), but that's slow
538
# when most of the files are unmodified, as is usually the
539
# case. So instead we iterate by inventory entry, and only
540
# calculate paths as necessary.
542
for file_id in basis_inv:
543
cacheentry = sc.get(file_id)
544
if not cacheentry: # deleted
546
ie = basis_inv[file_id]
547
if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
548
path = inv.id2path(file_id)
553
class cmd_added(Command):
554
"""List files added in working tree."""
558
wt = b.working_tree()
559
basis_inv = b.basis_tree().inventory
562
if file_id in basis_inv:
564
path = inv.id2path(file_id)
565
if not os.access(b.abspath(path), os.F_OK):
571
class cmd_root(Command):
572
"""Show the tree root directory.
574
The root is the nearest enclosing directory with a .bzr control
576
takes_args = ['filename?']
577
def run(self, filename=None):
578
"""Print the branch root."""
579
from branch import find_branch
580
b = find_branch(filename)
581
print getattr(b, 'base', None) or getattr(b, 'baseurl')
584
class cmd_log(Command):
341
585
"""Show log of this branch.
343
:todo: Options for utc; to show ids; to limit range; etc.
587
TODO: Option to limit range.
345
Branch('.').write_log(show_timezone=timezone)
348
def cmd_ls(revision=None, verbose=False):
590
takes_args = ['filename?']
591
takes_options = ['forward', 'timezone', 'verbose', 'show-ids']
593
def run(self, filename=None, timezone='original',
597
from bzrlib import show_log, find_branch
599
direction = (forward and 'forward') or 'reverse'
602
b = find_branch(filename, lock_mode='r')
603
fp = b.relpath(filename)
605
file_id = b.read_working_inventory().path2id(fp)
607
file_id = None # points to branch root
609
b = find_branch('.', lock_mode='r')
613
show_timezone=timezone,
621
class cmd_touching_revisions(Command):
622
"""Return revision-ids which affected a particular file.
624
A more user-friendly interface is "bzr log FILE"."""
626
takes_args = ["filename"]
627
def run(self, filename):
628
b = Branch(filename, lock_mode='r')
629
inv = b.read_working_inventory()
630
file_id = inv.path2id(b.relpath(filename))
631
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
632
print "%6d %s" % (revno, what)
635
class cmd_ls(Command):
349
636
"""List files in a tree.
351
:todo: Take a revision or remote path and list that tree instead.
638
TODO: Take a revision or remote path and list that tree instead.
355
tree = b.working_tree()
357
tree = b.revision_tree(b.lookup_revision(revision))
359
for fp, fc, kind, fid in tree.list_files():
361
if kind == 'directory':
641
def run(self, revision=None, verbose=False):
644
tree = b.working_tree()
646
tree = b.revision_tree(b.lookup_revision(revision))
648
for fp, fc, kind, fid in tree.list_files():
650
if kind == 'directory':
657
print '%-8s %s%s' % (fc, fp, kindch)
368
print '%-8s %s%s' % (fc, fp, kindch)
663
class cmd_unknowns(Command):
375
664
"""List unknown files"""
376
for f in Branch('.').unknowns():
380
def cmd_lookup_revision(revno):
384
bailout("usage: lookup-revision REVNO",
385
["REVNO is a non-negative revision number for this branch"])
387
print Branch('.').lookup_revision(revno) or NONE_STRING
391
def cmd_export(revno, dest):
392
"""Export past revision to destination directory."""
394
rh = b.lookup_revision(int(revno))
395
t = b.revision_tree(rh)
400
######################################################################
401
# internal/test commands
405
"""Print a newly-generated UUID."""
406
print bzrlib.osutils.uuid()
410
def cmd_local_time_offset():
411
print bzrlib.osutils.local_time_offset()
415
def cmd_commit(message=None, verbose=False):
416
"""Commit changes to a new revision.
419
Description of changes in this revision; free form text.
420
It is recommended that the first line be a single-sentence
423
Show status of changed files,
425
TODO: Commit only selected files.
427
TODO: Run hooks on tree to-be-committed, and after commit.
429
TODO: Strict commit that fails if there are unknown or deleted files.
433
bailout("please specify a commit message")
434
Branch('.').commit(message, verbose=verbose)
438
"""Check consistency of the branch."""
442
def cmd_is(pred, *rest):
443
"""Test whether PREDICATE is true."""
445
cmd_handler = globals()['assert_' + pred.replace('-', '_')]
447
bailout("unknown predicate: %s" % quotefn(pred))
666
for f in Branch('.').unknowns():
671
class cmd_ignore(Command):
672
"""Ignore a command or pattern
674
To remove patterns from the ignore list, edit the .bzrignore file.
676
If the pattern contains a slash, it is compared to the whole path
677
from the branch root. Otherwise, it is comapred to only the last
678
component of the path.
680
Ignore patterns are case-insensitive on case-insensitive systems.
682
Note: wildcards must be quoted from the shell on Unix.
685
bzr ignore ./Makefile
688
takes_args = ['name_pattern']
690
def run(self, name_pattern):
691
from bzrlib.atomicfile import AtomicFile
695
ifn = b.abspath('.bzrignore')
697
if os.path.exists(ifn):
700
igns = f.read().decode('utf-8')
706
if igns and igns[-1] != '\n':
708
igns += name_pattern + '\n'
711
f = AtomicFile(ifn, 'wt')
712
f.write(igns.encode('utf-8'))
717
inv = b.working_tree().inventory
718
if inv.path2id('.bzrignore'):
719
mutter('.bzrignore is already versioned')
721
mutter('need to make new .bzrignore file versioned')
722
b.add(['.bzrignore'])
726
class cmd_ignored(Command):
727
"""List ignored files and the patterns that matched them.
729
See also: bzr ignore"""
731
tree = Branch('.').working_tree()
732
for path, file_class, kind, file_id in tree.list_files():
733
if file_class != 'I':
735
## XXX: Slightly inefficient since this was already calculated
736
pat = tree.is_ignored(path)
737
print '%-50s %s' % (path, pat)
740
class cmd_lookup_revision(Command):
741
"""Lookup the revision-id from a revision-number
744
bzr lookup-revision 33
747
takes_args = ['revno']
749
def run(self, revno):
753
raise BzrCommandError("not a valid revision-number: %r" % revno)
755
print Branch('.').lookup_revision(revno)
758
class cmd_export(Command):
759
"""Export past revision to destination directory.
761
If no revision is specified this exports the last committed revision."""
762
takes_args = ['dest']
763
takes_options = ['revision']
764
def run(self, dest, revision=None):
767
rh = b.revision_history()[-1]
769
rh = b.lookup_revision(int(revision))
770
t = b.revision_tree(rh)
774
class cmd_cat(Command):
775
"""Write a file's text from a previous revision."""
777
takes_options = ['revision']
778
takes_args = ['filename']
780
def run(self, filename, revision=None):
782
raise BzrCommandError("bzr cat requires a revision number")
784
b.print_file(b.relpath(filename), int(revision))
787
class cmd_local_time_offset(Command):
788
"""Show the offset in seconds from GMT to local time."""
791
print bzrlib.osutils.local_time_offset()
795
class cmd_commit(Command):
796
"""Commit changes into a new revision.
798
If selected files are specified, only changes to those files are
799
committed. If a directory is specified then its contents are also
802
A selected-file commit may fail in some cases where the committed
803
tree would be invalid, such as trying to commit a file in a
804
newly-added directory that is not itself committed.
806
TODO: Run hooks on tree to-be-committed, and after commit.
808
TODO: Strict commit that fails if there are unknown or deleted files.
810
takes_args = ['selected*']
811
takes_options = ['message', 'file', 'verbose']
812
aliases = ['ci', 'checkin']
814
def run(self, message=None, file=None, verbose=True, selected_list=None):
815
from bzrlib.commit import commit
817
## Warning: shadows builtin file()
818
if not message and not file:
819
raise BzrCommandError("please specify a commit message",
820
["use either --message or --file"])
821
elif message and file:
822
raise BzrCommandError("please specify either --message or --file")
451
except BzrCheckError:
452
# by default we don't print the message so that this can
453
# be used from shell scripts without producing noise
458
print bzrlib.osutils.username()
461
def cmd_user_email():
462
print bzrlib.osutils.user_email()
465
def cmd_gen_revision_id():
467
print bzrlib.branch._gen_revision_id(time.time())
470
def cmd_selftest(verbose=False):
826
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
829
commit(b, message, verbose=verbose, specific_files=selected_list)
832
class cmd_check(Command):
833
"""Validate consistency of branch history.
835
This command checks various invariants about the branch storage to
836
detect data corruption or bzr bugs.
838
takes_args = ['dir?']
839
def run(self, dir='.'):
841
bzrlib.check.check(Branch(dir))
845
class cmd_whoami(Command):
846
"""Show bzr user id."""
847
takes_options = ['email']
849
def run(self, email=False):
851
print bzrlib.osutils.user_email()
853
print bzrlib.osutils.username()
856
class cmd_selftest(Command):
471
857
"""Run internal test suite"""
472
## -v, if present, is seen by doctest; the argument is just here
473
## so our parser doesn't complain
475
## TODO: --verbose option
477
failures, tests = 0, 0
479
import doctest, bzrlib.store, bzrlib.tests
480
bzrlib.trace.verbose = False
482
for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
483
bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
484
mf, mt = doctest.testmod(m)
487
print '%-40s %3d tests' % (m.__name__, mt),
489
print '%3d FAILED!' % mf
860
failures, tests = 0, 0
862
import doctest, bzrlib.store
863
bzrlib.trace.verbose = False
865
for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
866
bzrlib.tree, bzrlib.commands, bzrlib.add:
867
mf, mt = doctest.testmod(m)
870
print '%-40s %3d tests' % (m.__name__, mt),
872
print '%3d FAILED!' % mf
876
print '%-40s %3d tests' % ('total', tests),
878
print '%3d FAILED!' % failures
493
print '%-40s %3d tests' % ('total', tests),
495
print '%3d FAILED!' % failures
502
cmd_doctest = cmd_selftest
505
######################################################################
509
def cmd_help(topic=None):
514
# otherwise, maybe the name of a command?
516
cmdfn = globals()['cmd_' + topic.replace('-', '_')]
518
bailout("no help for %r" % topic)
522
bailout("sorry, no detailed help yet for %r" % topic)
886
class cmd_version(Command):
887
"""Show version of bzr"""
530
892
print "bzr (bazaar-ng) %s" % bzrlib.__version__
531
893
print bzrlib.__copyright__
532
894
print "http://bazaar-ng.org/"
535
"""bzr comes with ABSOLUTELY NO WARRANTY. bzr is free software, and
536
you may use, modify and redistribute it under the terms of the GNU
537
General Public License version 2 or later."""
896
print "bzr comes with ABSOLUTELY NO WARRANTY. bzr is free software, and"
897
print "you may use, modify and redistribute it under the terms of the GNU"
898
print "General Public License version 2 or later."
901
class cmd_rocks(Command):
541
902
"""Statement of optimism."""
542
print "it sure does!"
905
print "it sure does!"
907
def parse_spec(spec):
909
parsed = spec.split('/@')
910
assert len(parsed) == 2
914
parsed[1] = int(parsed[1])
917
parsed = [spec, None]
920
class cmd_merge(Command):
921
"""Perform a three-way merge of trees."""
922
takes_args = ['other_spec', 'base_spec']
924
def run(self, other_spec, base_spec):
925
merge.merge(parse_spec(other_spec), parse_spec(base_spec))
927
class cmd_assert_fail(Command):
928
"""Test reporting of assertion failures"""
931
assert False, "always fails"
934
class cmd_help(Command):
935
"""Show help on a command or other topic.
937
For a list of all available commands, say 'bzr help commands'."""
938
takes_args = ['topic?']
941
def run(self, topic=None):
946
class cmd_update_stat_cache(Command):
947
"""Update stat-cache mapping inodes to SHA-1 hashes.
954
statcache.update_cache(b.base, b.read_working_inventory())
546
957
######################################################################