1
# Copyright (C) 2004, 2005 by Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""builtin bzr commands"""
19
# DO NOT change this to cStringIO - it results in control files
21
# FIXIT! (Only deal with byte streams OR unicode at any one layer.)
24
from StringIO import StringIO
29
from bzrlib import BZRDIR
30
from bzrlib.commands import Command, display_command
31
from bzrlib.branch import Branch
32
from bzrlib.revision import common_ancestor
33
import bzrlib.errors as errors
34
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError,
35
NotBranchError, DivergedBranches, NotConflicted,
36
NoSuchFile, NoWorkingTree, FileInWrongBranch)
37
from bzrlib.option import Option
38
from bzrlib.revisionspec import RevisionSpec
40
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
41
from bzrlib.workingtree import WorkingTree
42
from bzrlib.log import show_one_log
45
def tree_files(file_list, default_branch=u'.'):
47
return internal_tree_files(file_list, default_branch)
48
except FileInWrongBranch, e:
49
raise BzrCommandError("%s is not in the same branch as %s" %
50
(e.path, file_list[0]))
52
def internal_tree_files(file_list, default_branch=u'.'):
54
Return a branch and list of branch-relative paths.
55
If supplied file_list is empty or None, the branch default will be used,
56
and returned file_list will match the original.
58
if file_list is None or len(file_list) == 0:
59
return WorkingTree.open_containing(default_branch)[0], file_list
60
tree = WorkingTree.open_containing(file_list[0])[0]
62
for filename in file_list:
64
new_list.append(tree.relpath(filename))
65
except errors.PathNotChild:
66
raise FileInWrongBranch(tree.branch, filename)
70
# TODO: Make sure no commands unconditionally use the working directory as a
71
# branch. If a filename argument is used, the first of them should be used to
72
# specify the branch. (Perhaps this can be factored out into some kind of
73
# Argument class, representing a file in a branch, where the first occurrence
76
class cmd_status(Command):
77
"""Display status summary.
79
This reports on versioned and unknown files, reporting them
80
grouped by state. Possible states are:
83
Versioned in the working copy but not in the previous revision.
86
Versioned in the previous revision but removed or deleted
90
Path of this file changed from the previous revision;
91
the text may also have changed. This includes files whose
92
parent directory was renamed.
95
Text has changed since the previous revision.
98
Nothing about this file has changed since the previous revision.
99
Only shown with --all.
102
Not versioned and not matching an ignore pattern.
104
To see ignored files use 'bzr ignored'. For details in the
105
changes to file texts, use 'bzr diff'.
107
If no arguments are specified, the status of the entire working
108
directory is shown. Otherwise, only the status of the specified
109
files or directories is reported. If a directory is given, status
110
is reported for everything inside that directory.
112
If a revision argument is given, the status is calculated against
113
that revision, or between two revisions if two are provided.
116
# TODO: --no-recurse, --recurse options
118
takes_args = ['file*']
119
takes_options = ['all', 'show-ids', 'revision']
120
aliases = ['st', 'stat']
123
def run(self, all=False, show_ids=False, file_list=None, revision=None):
124
tree, file_list = tree_files(file_list)
126
from bzrlib.status import show_status
127
show_status(tree.branch, show_unchanged=all, show_ids=show_ids,
128
specific_files=file_list, revision=revision)
131
class cmd_cat_revision(Command):
132
"""Write out metadata for a revision.
134
The revision to print can either be specified by a specific
135
revision identifier, or you can use --revision.
139
takes_args = ['revision_id?']
140
takes_options = ['revision']
143
def run(self, revision_id=None, revision=None):
145
if revision_id is not None and revision is not None:
146
raise BzrCommandError('You can only supply one of revision_id or --revision')
147
if revision_id is None and revision is None:
148
raise BzrCommandError('You must supply either --revision or a revision_id')
149
b = WorkingTree.open_containing(u'.')[0].branch
150
if revision_id is not None:
151
sys.stdout.write(b.get_revision_xml(revision_id))
152
elif revision is not None:
155
raise BzrCommandError('You cannot specify a NULL revision.')
156
revno, rev_id = rev.in_history(b)
157
sys.stdout.write(b.get_revision_xml(rev_id))
160
class cmd_revno(Command):
161
"""Show current revision number.
163
This is equal to the number of revisions on this branch."""
164
takes_args = ['location?']
166
def run(self, location=u'.'):
167
print Branch.open_containing(location)[0].revno()
170
class cmd_revision_info(Command):
171
"""Show revision number and revision id for a given revision identifier.
174
takes_args = ['revision_info*']
175
takes_options = ['revision']
177
def run(self, revision=None, revision_info_list=[]):
180
if revision is not None:
181
revs.extend(revision)
182
if revision_info_list is not None:
183
for rev in revision_info_list:
184
revs.append(RevisionSpec(rev))
186
raise BzrCommandError('You must supply a revision identifier')
188
b = WorkingTree.open_containing(u'.')[0].branch
191
revinfo = rev.in_history(b)
192
if revinfo.revno is None:
193
print ' %s' % revinfo.rev_id
195
print '%4d %s' % (revinfo.revno, revinfo.rev_id)
198
class cmd_add(Command):
199
"""Add specified files or directories.
201
In non-recursive mode, all the named items are added, regardless
202
of whether they were previously ignored. A warning is given if
203
any of the named files are already versioned.
205
In recursive mode (the default), files are treated the same way
206
but the behaviour for directories is different. Directories that
207
are already versioned do not give a warning. All directories,
208
whether already versioned or not, are searched for files or
209
subdirectories that are neither versioned or ignored, and these
210
are added. This search proceeds recursively into versioned
211
directories. If no names are given '.' is assumed.
213
Therefore simply saying 'bzr add' will version all files that
214
are currently unknown.
216
Adding a file whose parent directory is not versioned will
217
implicitly add the parent, and so on up to the root. This means
218
you should never need to explictly add a directory, they'll just
219
get added when you add a file in the directory.
221
--dry-run will show which files would be added, but not actually
224
takes_args = ['file*']
225
takes_options = ['no-recurse', 'dry-run']
227
def run(self, file_list, no_recurse=False, dry_run=False):
232
# This is pointless, but I'd rather not raise an error
233
action = bzrlib.add.add_action_null
235
action = bzrlib.add.add_action_print
237
action = bzrlib.add.add_action_add
239
action = bzrlib.add.add_action_add_and_print
241
bzrlib.add.smart_add(file_list, not no_recurse, action)
244
class cmd_mkdir(Command):
245
"""Create a new versioned directory.
247
This is equivalent to creating the directory and then adding it.
249
takes_args = ['dir+']
251
def run(self, dir_list):
254
wt, dd = WorkingTree.open_containing(d)
259
class cmd_relpath(Command):
260
"""Show path of a file relative to root"""
261
takes_args = ['filename']
265
def run(self, filename):
266
tree, relpath = WorkingTree.open_containing(filename)
270
class cmd_inventory(Command):
271
"""Show inventory of the current working copy or a revision.
273
It is possible to limit the output to a particular entry
274
type using the --kind option. For example; --kind file.
276
takes_options = ['revision', 'show-ids', 'kind']
279
def run(self, revision=None, show_ids=False, kind=None):
280
if kind and kind not in ['file', 'directory', 'symlink']:
281
raise BzrCommandError('invalid kind specified')
282
tree = WorkingTree.open_containing(u'.')[0]
284
inv = tree.read_working_inventory()
286
if len(revision) > 1:
287
raise BzrCommandError('bzr inventory --revision takes'
288
' exactly one revision identifier')
289
inv = tree.branch.get_revision_inventory(
290
revision[0].in_history(tree.branch).rev_id)
292
for path, entry in inv.entries():
293
if kind and kind != entry.kind:
296
print '%-50s %s' % (path, entry.file_id)
301
class cmd_move(Command):
302
"""Move files to a different directory.
307
The destination must be a versioned directory in the same branch.
309
takes_args = ['source$', 'dest']
310
def run(self, source_list, dest):
311
tree, source_list = tree_files(source_list)
312
# TODO: glob expansion on windows?
313
tree.move(source_list, tree.relpath(dest))
316
class cmd_rename(Command):
317
"""Change the name of an entry.
320
bzr rename frob.c frobber.c
321
bzr rename src/frob.c lib/frob.c
323
It is an error if the destination name exists.
325
See also the 'move' command, which moves files into a different
326
directory without changing their name.
328
# TODO: Some way to rename multiple files without invoking
329
# bzr for each one?"""
330
takes_args = ['from_name', 'to_name']
332
def run(self, from_name, to_name):
333
tree, (from_name, to_name) = tree_files((from_name, to_name))
334
tree.rename_one(from_name, to_name)
337
class cmd_mv(Command):
338
"""Move or rename a file.
341
bzr mv OLDNAME NEWNAME
342
bzr mv SOURCE... DESTINATION
344
If the last argument is a versioned directory, all the other names
345
are moved into it. Otherwise, there must be exactly two arguments
346
and the file is changed to a new name, which must not already exist.
348
Files cannot be moved between branches.
350
takes_args = ['names*']
351
def run(self, names_list):
352
if len(names_list) < 2:
353
raise BzrCommandError("missing file argument")
354
tree, rel_names = tree_files(names_list)
356
if os.path.isdir(names_list[-1]):
357
# move into existing directory
358
for pair in tree.move(rel_names[:-1], rel_names[-1]):
359
print "%s => %s" % pair
361
if len(names_list) != 2:
362
raise BzrCommandError('to mv multiple files the destination '
363
'must be a versioned directory')
364
tree.rename_one(rel_names[0], rel_names[1])
365
print "%s => %s" % (rel_names[0], rel_names[1])
368
class cmd_pull(Command):
369
"""Pull any changes from another branch into the current one.
371
If there is no default location set, the first pull will set it. After
372
that, you can omit the location to use the default. To change the
373
default, use --remember.
375
This command only works on branches that have not diverged. Branches are
376
considered diverged if both branches have had commits without first
377
pulling from the other.
379
If branches have diverged, you can use 'bzr merge' to pull the text changes
380
from one into the other. Once one branch has merged, the other should
381
be able to pull it again.
383
If you want to forget your local changes and just update your branch to
384
match the remote one, use --overwrite.
386
takes_options = ['remember', 'overwrite', 'verbose']
387
takes_args = ['location?']
389
def run(self, location=None, remember=False, overwrite=False, verbose=False):
390
from bzrlib.merge import merge
391
from shutil import rmtree
393
# FIXME: too much stuff is in the command class
394
tree_to = WorkingTree.open_containing(u'.')[0]
395
stored_loc = tree_to.branch.get_parent()
397
if stored_loc is None:
398
raise BzrCommandError("No pull location known or specified.")
400
print "Using saved location: %s" % stored_loc
401
location = stored_loc
402
br_from = Branch.open(location)
403
br_to = tree_to.branch
405
old_rh = br_to.revision_history()
406
count = tree_to.pull(br_from, overwrite)
407
except DivergedBranches:
408
# FIXME: Just make DivergedBranches display the right message
410
raise BzrCommandError("These branches have diverged."
412
if br_to.get_parent() is None or remember:
413
br_to.set_parent(location)
414
note('%d revision(s) pulled.' % (count,))
417
new_rh = tree_to.branch.revision_history()
420
from bzrlib.log import show_changed_revisions
421
show_changed_revisions(tree_to.branch, old_rh, new_rh)
424
class cmd_push(Command):
425
"""Push this branch into another branch.
427
The remote branch will not have its working tree populated because this
428
is both expensive, and may not be supported on the remote file system.
430
Some smart servers or protocols *may* put the working tree in place.
432
If there is no default push location set, the first push will set it.
433
After that, you can omit the location to use the default. To change the
434
default, use --remember.
436
This command only works on branches that have not diverged. Branches are
437
considered diverged if the branch being pushed to is not an older version
440
If branches have diverged, you can use 'bzr push --overwrite' to replace
441
the other branch completely.
443
If you want to ensure you have the different changes in the other branch,
444
do a merge (see bzr help merge) from the other branch, and commit that
445
before doing a 'push --overwrite'.
447
takes_options = ['remember', 'overwrite',
448
Option('create-prefix',
449
help='Create the path leading up to the branch '
450
'if it does not already exist')]
451
takes_args = ['location?']
453
def run(self, location=None, remember=False, overwrite=False,
454
create_prefix=False, verbose=False):
455
# FIXME: Way too big! Put this into a function called from the
458
from shutil import rmtree
459
from bzrlib.transport import get_transport
461
tree_from = WorkingTree.open_containing(u'.')[0]
462
br_from = tree_from.branch
463
stored_loc = tree_from.branch.get_push_location()
465
if stored_loc is None:
466
raise BzrCommandError("No push location known or specified.")
468
print "Using saved location: %s" % stored_loc
469
location = stored_loc
471
br_to = Branch.open(location)
472
except NotBranchError:
474
transport = get_transport(location).clone('..')
475
if not create_prefix:
477
transport.mkdir(transport.relpath(location))
479
raise BzrCommandError("Parent directory of %s "
480
"does not exist." % location)
482
current = transport.base
483
needed = [(transport, transport.relpath(location))]
486
transport, relpath = needed[-1]
487
transport.mkdir(relpath)
490
new_transport = transport.clone('..')
491
needed.append((new_transport,
492
new_transport.relpath(transport.base)))
493
if new_transport.base == transport.base:
494
raise BzrCommandError("Could not creeate "
496
br_to = Branch.initialize(location)
497
old_rh = br_to.revision_history()
500
tree_to = br_to.working_tree()
501
except NoWorkingTree:
502
# TODO: This should be updated for branches which don't have a
503
# working tree, as opposed to ones where we just couldn't
505
warning('Unable to update the working tree of: %s' % (br_to.base,))
506
count = br_to.pull(br_from, overwrite)
508
count = tree_to.pull(br_from, overwrite)
509
except DivergedBranches:
510
raise BzrCommandError("These branches have diverged."
511
" Try a merge then push with overwrite.")
512
if br_from.get_push_location() is None or remember:
513
br_from.set_push_location(location)
514
note('%d revision(s) pushed.' % (count,))
517
new_rh = br_to.revision_history()
520
from bzrlib.log import show_changed_revisions
521
show_changed_revisions(br_to, old_rh, new_rh)
524
class cmd_branch(Command):
525
"""Create a new copy of a branch.
527
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
528
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
530
To retrieve the branch as of a particular revision, supply the --revision
531
parameter, as in "branch foo/bar -r 5".
533
--basis is to speed up branching from remote branches. When specified, it
534
copies all the file-contents, inventory and revision data from the basis
535
branch before copying anything from the remote branch.
537
takes_args = ['from_location', 'to_location?']
538
takes_options = ['revision', 'basis']
539
aliases = ['get', 'clone']
541
def run(self, from_location, to_location=None, revision=None, basis=None):
542
from bzrlib.clone import copy_branch
544
from shutil import rmtree
547
elif len(revision) > 1:
548
raise BzrCommandError(
549
'bzr branch --revision takes exactly 1 revision value')
551
br_from = Branch.open(from_location)
553
if e.errno == errno.ENOENT:
554
raise BzrCommandError('Source location "%s" does not'
555
' exist.' % to_location)
560
if basis is not None:
561
basis_branch = WorkingTree.open_containing(basis)[0].branch
564
if len(revision) == 1 and revision[0] is not None:
565
revision_id = revision[0].in_history(br_from)[1]
568
if to_location is None:
569
to_location = os.path.basename(from_location.rstrip("/\\"))
572
name = os.path.basename(to_location) + '\n'
574
os.mkdir(to_location)
576
if e.errno == errno.EEXIST:
577
raise BzrCommandError('Target directory "%s" already'
578
' exists.' % to_location)
579
if e.errno == errno.ENOENT:
580
raise BzrCommandError('Parent of "%s" does not exist.' %
585
copy_branch(br_from, to_location, revision_id, basis_branch)
586
except bzrlib.errors.NoSuchRevision:
588
msg = "The branch %s has no revision %s." % (from_location, revision[0])
589
raise BzrCommandError(msg)
590
except bzrlib.errors.UnlistableBranch:
592
msg = "The branch %s cannot be used as a --basis"
593
raise BzrCommandError(msg)
594
branch = Branch.open(to_location)
596
name = StringIO(name)
597
branch.put_controlfile('branch-name', name)
598
note('Branched %d revision(s).' % branch.revno())
603
class cmd_renames(Command):
604
"""Show list of renamed files.
606
# TODO: Option to show renames between two historical versions.
608
# TODO: Only show renames under dir, rather than in the whole branch.
609
takes_args = ['dir?']
612
def run(self, dir=u'.'):
613
tree = WorkingTree.open_containing(dir)[0]
614
old_inv = tree.branch.basis_tree().inventory
615
new_inv = tree.read_working_inventory()
617
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
619
for old_name, new_name in renames:
620
print "%s => %s" % (old_name, new_name)
623
class cmd_info(Command):
624
"""Show statistical information about a branch."""
625
takes_args = ['branch?']
628
def run(self, branch=None):
630
b = WorkingTree.open_containing(branch)[0].branch
634
class cmd_remove(Command):
635
"""Make a file unversioned.
637
This makes bzr stop tracking changes to a versioned file. It does
638
not delete the working copy.
640
takes_args = ['file+']
641
takes_options = ['verbose']
644
def run(self, file_list, verbose=False):
645
tree, file_list = tree_files(file_list)
646
tree.remove(file_list, verbose=verbose)
649
class cmd_file_id(Command):
650
"""Print file_id of a particular file or directory.
652
The file_id is assigned when the file is first added and remains the
653
same through all revisions where the file exists, even when it is
657
takes_args = ['filename']
659
def run(self, filename):
660
tree, relpath = WorkingTree.open_containing(filename)
661
i = tree.inventory.path2id(relpath)
663
raise BzrError("%r is not a versioned file" % filename)
668
class cmd_file_path(Command):
669
"""Print path of file_ids to a file or directory.
671
This prints one line for each directory down to the target,
672
starting at the branch root."""
674
takes_args = ['filename']
676
def run(self, filename):
677
tree, relpath = WorkingTree.open_containing(filename)
679
fid = inv.path2id(relpath)
681
raise BzrError("%r is not a versioned file" % filename)
682
for fip in inv.get_idpath(fid):
686
class cmd_revision_history(Command):
687
"""Display list of revision ids on this branch."""
691
branch = WorkingTree.open_containing(u'.')[0].branch
692
for patchid in branch.revision_history():
696
class cmd_ancestry(Command):
697
"""List all revisions merged into this branch."""
701
tree = WorkingTree.open_containing(u'.')[0]
703
# FIXME. should be tree.last_revision
704
for revision_id in b.get_ancestry(b.last_revision()):
708
class cmd_init(Command):
709
"""Make a directory into a versioned branch.
711
Use this to create an empty branch, or before importing an
714
Recipe for importing a tree of files:
719
bzr commit -m 'imported project'
721
takes_args = ['location?']
722
def run(self, location=None):
723
from bzrlib.branch import Branch
727
# The path has to exist to initialize a
728
# branch inside of it.
729
# Just using os.mkdir, since I don't
730
# believe that we want to create a bunch of
731
# locations if the user supplies an extended path
732
if not os.path.exists(location):
734
Branch.initialize(location)
737
class cmd_diff(Command):
738
"""Show differences in working tree.
740
If files are listed, only the changes in those files are listed.
741
Otherwise, all changes for the tree are listed.
748
# TODO: Allow diff across branches.
749
# TODO: Option to use external diff command; could be GNU diff, wdiff,
750
# or a graphical diff.
752
# TODO: Python difflib is not exactly the same as unidiff; should
753
# either fix it up or prefer to use an external diff.
755
# TODO: If a directory is given, diff everything under that.
757
# TODO: Selected-file diff is inefficient and doesn't show you
760
# TODO: This probably handles non-Unix newlines poorly.
762
takes_args = ['file*']
763
takes_options = ['revision', 'diff-options']
764
aliases = ['di', 'dif']
767
def run(self, revision=None, file_list=None, diff_options=None):
768
from bzrlib.diff import show_diff
770
tree, file_list = internal_tree_files(file_list)
773
except FileInWrongBranch:
774
if len(file_list) != 2:
775
raise BzrCommandError("Files are in different branches")
777
b, file1 = Branch.open_containing(file_list[0])
778
b2, file2 = Branch.open_containing(file_list[1])
779
if file1 != "" or file2 != "":
780
# FIXME diff those two files. rbc 20051123
781
raise BzrCommandError("Files are in different branches")
783
if revision is not None:
785
raise BzrCommandError("Can't specify -r with two branches")
786
if len(revision) == 1:
787
return show_diff(tree.branch, revision[0], specific_files=file_list,
788
external_diff_options=diff_options)
789
elif len(revision) == 2:
790
return show_diff(tree.branch, revision[0], specific_files=file_list,
791
external_diff_options=diff_options,
792
revision2=revision[1])
794
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
797
return show_diff(b, None, specific_files=file_list,
798
external_diff_options=diff_options, b2=b2)
800
return show_diff(tree.branch, None, specific_files=file_list,
801
external_diff_options=diff_options)
804
class cmd_deleted(Command):
805
"""List files deleted in the working tree.
807
# TODO: Show files deleted since a previous revision, or
808
# between two revisions.
809
# TODO: Much more efficient way to do this: read in new
810
# directories with readdir, rather than stating each one. Same
811
# level of effort but possibly much less IO. (Or possibly not,
812
# if the directories are very large...)
814
def run(self, show_ids=False):
815
tree = WorkingTree.open_containing(u'.')[0]
816
old = tree.branch.basis_tree()
817
for path, ie in old.inventory.iter_entries():
818
if not tree.has_id(ie.file_id):
820
print '%-50s %s' % (path, ie.file_id)
825
class cmd_modified(Command):
826
"""List files modified in working tree."""
830
from bzrlib.delta import compare_trees
832
tree = WorkingTree.open_containing(u'.')[0]
833
td = compare_trees(tree.branch.basis_tree(), tree)
835
for path, id, kind, text_modified, meta_modified in td.modified:
840
class cmd_added(Command):
841
"""List files added in working tree."""
845
wt = WorkingTree.open_containing(u'.')[0]
846
basis_inv = wt.branch.basis_tree().inventory
849
if file_id in basis_inv:
851
path = inv.id2path(file_id)
852
if not os.access(b.abspath(path), os.F_OK):
858
class cmd_root(Command):
859
"""Show the tree root directory.
861
The root is the nearest enclosing directory with a .bzr control
863
takes_args = ['filename?']
865
def run(self, filename=None):
866
"""Print the branch root."""
867
tree = WorkingTree.open_containing(filename)[0]
871
class cmd_log(Command):
872
"""Show log of this branch.
874
To request a range of logs, you can use the command -r begin..end
875
-r revision requests a specific revision, -r ..end or -r begin.. are
879
# TODO: Make --revision support uuid: and hash: [future tag:] notation.
881
takes_args = ['filename?']
882
takes_options = [Option('forward',
883
help='show from oldest to newest'),
884
'timezone', 'verbose',
885
'show-ids', 'revision',
888
help='show revisions whose message matches this regexp',
893
def run(self, filename=None, timezone='original',
902
from bzrlib.log import log_formatter, show_log
904
assert message is None or isinstance(message, basestring), \
905
"invalid message argument %r" % message
906
direction = (forward and 'forward') or 'reverse'
912
tree, fp = WorkingTree.open_containing(filename)
915
inv = tree.read_working_inventory()
916
except NotBranchError:
919
b, fp = Branch.open_containing(filename)
921
inv = b.get_inventory(b.last_revision())
923
file_id = inv.path2id(fp)
925
file_id = None # points to branch root
927
tree, relpath = WorkingTree.open_containing(u'.')
934
elif len(revision) == 1:
935
rev1 = rev2 = revision[0].in_history(b).revno
936
elif len(revision) == 2:
937
rev1 = revision[0].in_history(b).revno
938
rev2 = revision[1].in_history(b).revno
940
raise BzrCommandError('bzr log --revision takes one or two values.')
942
# By this point, the revision numbers are converted to the +ve
943
# form if they were supplied in the -ve form, so we can do
944
# this comparison in relative safety
946
(rev2, rev1) = (rev1, rev2)
948
mutter('encoding log as %r', bzrlib.user_encoding)
950
# use 'replace' so that we don't abort if trying to write out
951
# in e.g. the default C locale.
952
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
954
log_format = get_log_format(long=long, short=short, line=line)
955
lf = log_formatter(log_format,
958
show_timezone=timezone)
969
def get_log_format(long=False, short=False, line=False, default='long'):
980
class cmd_touching_revisions(Command):
981
"""Return revision-ids which affected a particular file.
983
A more user-friendly interface is "bzr log FILE"."""
985
takes_args = ["filename"]
987
def run(self, filename):
988
tree, relpath = WorkingTree.open_containing(filename)
990
inv = tree.read_working_inventory()
991
file_id = inv.path2id(relpath)
992
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
993
print "%6d %s" % (revno, what)
996
class cmd_ls(Command):
997
"""List files in a tree.
999
# TODO: Take a revision or remote path and list that tree instead.
1001
takes_options = ['verbose', 'revision',
1002
Option('non-recursive',
1003
help='don\'t recurse into sub-directories'),
1005
help='Print all paths from the root of the branch.'),
1006
Option('unknown', help='Print unknown files'),
1007
Option('versioned', help='Print versioned files'),
1008
Option('ignored', help='Print ignored files'),
1010
Option('null', help='Null separate the files'),
1013
def run(self, revision=None, verbose=False,
1014
non_recursive=False, from_root=False,
1015
unknown=False, versioned=False, ignored=False,
1018
if verbose and null:
1019
raise BzrCommandError('Cannot set both --verbose and --null')
1020
all = not (unknown or versioned or ignored)
1022
selection = {'I':ignored, '?':unknown, 'V':versioned}
1024
tree, relpath = WorkingTree.open_containing(u'.')
1029
if revision is not None:
1030
tree = tree.branch.revision_tree(
1031
revision[0].in_history(tree.branch).rev_id)
1032
for fp, fc, kind, fid, entry in tree.list_files():
1033
if fp.startswith(relpath):
1034
fp = fp[len(relpath):]
1035
if non_recursive and '/' in fp:
1037
if not all and not selection[fc]:
1040
kindch = entry.kind_character()
1041
print '%-8s %s%s' % (fc, fp, kindch)
1043
sys.stdout.write(fp)
1044
sys.stdout.write('\0')
1050
class cmd_unknowns(Command):
1051
"""List unknown files."""
1054
from bzrlib.osutils import quotefn
1055
for f in WorkingTree.open_containing(u'.')[0].unknowns():
1059
class cmd_ignore(Command):
1060
"""Ignore a command or pattern.
1062
To remove patterns from the ignore list, edit the .bzrignore file.
1064
If the pattern contains a slash, it is compared to the whole path
1065
from the branch root. Otherwise, it is compared to only the last
1066
component of the path. To match a file only in the root directory,
1069
Ignore patterns are case-insensitive on case-insensitive systems.
1071
Note: wildcards must be quoted from the shell on Unix.
1074
bzr ignore ./Makefile
1075
bzr ignore '*.class'
1077
# TODO: Complain if the filename is absolute
1078
takes_args = ['name_pattern']
1080
def run(self, name_pattern):
1081
from bzrlib.atomicfile import AtomicFile
1084
tree, relpath = WorkingTree.open_containing(u'.')
1085
ifn = tree.abspath('.bzrignore')
1087
if os.path.exists(ifn):
1090
igns = f.read().decode('utf-8')
1096
# TODO: If the file already uses crlf-style termination, maybe
1097
# we should use that for the newly added lines?
1099
if igns and igns[-1] != '\n':
1101
igns += name_pattern + '\n'
1104
f = AtomicFile(ifn, 'wt')
1105
f.write(igns.encode('utf-8'))
1110
inv = tree.inventory
1111
if inv.path2id('.bzrignore'):
1112
mutter('.bzrignore is already versioned')
1114
mutter('need to make new .bzrignore file versioned')
1115
tree.add(['.bzrignore'])
1118
class cmd_ignored(Command):
1119
"""List ignored files and the patterns that matched them.
1121
See also: bzr ignore"""
1124
tree = WorkingTree.open_containing(u'.')[0]
1125
for path, file_class, kind, file_id, entry in tree.list_files():
1126
if file_class != 'I':
1128
## XXX: Slightly inefficient since this was already calculated
1129
pat = tree.is_ignored(path)
1130
print '%-50s %s' % (path, pat)
1133
class cmd_lookup_revision(Command):
1134
"""Lookup the revision-id from a revision-number
1137
bzr lookup-revision 33
1140
takes_args = ['revno']
1143
def run(self, revno):
1147
raise BzrCommandError("not a valid revision-number: %r" % revno)
1149
print WorkingTree.open_containing(u'.')[0].branch.get_rev_id(revno)
1152
class cmd_export(Command):
1153
"""Export past revision to destination directory.
1155
If no revision is specified this exports the last committed revision.
1157
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1158
given, try to find the format with the extension. If no extension
1159
is found exports to a directory (equivalent to --format=dir).
1161
Root may be the top directory for tar, tgz and tbz2 formats. If none
1162
is given, the top directory will be the root name of the file.
1164
Note: export of tree with non-ascii filenames to zip is not supported.
1166
Supported formats Autodetected by extension
1167
----------------- -------------------------
1170
tbz2 .tar.bz2, .tbz2
1174
takes_args = ['dest']
1175
takes_options = ['revision', 'format', 'root']
1176
def run(self, dest, revision=None, format=None, root=None):
1178
from bzrlib.export import export
1179
tree = WorkingTree.open_containing(u'.')[0]
1181
if revision is None:
1182
# should be tree.last_revision FIXME
1183
rev_id = b.last_revision()
1185
if len(revision) != 1:
1186
raise BzrError('bzr export --revision takes exactly 1 argument')
1187
rev_id = revision[0].in_history(b).rev_id
1188
t = b.revision_tree(rev_id)
1190
export(t, dest, format, root)
1191
except errors.NoSuchExportFormat, e:
1192
raise BzrCommandError('Unsupported export format: %s' % e.format)
1195
class cmd_cat(Command):
1196
"""Write a file's text from a previous revision."""
1198
takes_options = ['revision']
1199
takes_args = ['filename']
1202
def run(self, filename, revision=None):
1203
if revision is not None and len(revision) != 1:
1204
raise BzrCommandError("bzr cat --revision takes exactly one number")
1207
tree, relpath = WorkingTree.open_containing(filename)
1209
except NotBranchError:
1213
b, relpath = Branch.open_containing(filename)
1214
if revision is None:
1215
revision_id = b.last_revision()
1217
revision_id = revision[0].in_history(b).rev_id
1218
b.print_file(relpath, revision_id)
1221
class cmd_local_time_offset(Command):
1222
"""Show the offset in seconds from GMT to local time."""
1226
print bzrlib.osutils.local_time_offset()
1230
class cmd_commit(Command):
1231
"""Commit changes into a new revision.
1233
If no arguments are given, the entire tree is committed.
1235
If selected files are specified, only changes to those files are
1236
committed. If a directory is specified then the directory and everything
1237
within it is committed.
1239
A selected-file commit may fail in some cases where the committed
1240
tree would be invalid, such as trying to commit a file in a
1241
newly-added directory that is not itself committed.
1243
# TODO: Run hooks on tree to-be-committed, and after commit.
1245
# TODO: Strict commit that fails if there are deleted files.
1246
# (what does "deleted files" mean ??)
1248
# TODO: Give better message for -s, --summary, used by tla people
1250
# XXX: verbose currently does nothing
1252
takes_args = ['selected*']
1253
takes_options = ['message', 'verbose',
1255
help='commit even if nothing has changed'),
1256
Option('file', type=str,
1258
help='file containing commit message'),
1260
help="refuse to commit if there are unknown "
1261
"files in the working tree."),
1263
aliases = ['ci', 'checkin']
1265
def run(self, message=None, file=None, verbose=True, selected_list=None,
1266
unchanged=False, strict=False):
1267
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1269
from bzrlib.msgeditor import edit_commit_message, \
1270
make_commit_message_template
1271
from bzrlib.status import show_status
1272
from tempfile import TemporaryFile
1275
# TODO: Need a blackbox test for invoking the external editor; may be
1276
# slightly problematic to run this cross-platform.
1278
# TODO: do more checks that the commit will succeed before
1279
# spending the user's valuable time typing a commit message.
1281
# TODO: if the commit *does* happen to fail, then save the commit
1282
# message to a temporary file where it can be recovered
1283
tree, selected_list = tree_files(selected_list)
1284
if message is None and not file:
1285
template = make_commit_message_template(tree, selected_list)
1286
message = edit_commit_message(template)
1288
raise BzrCommandError("please specify a commit message"
1289
" with either --message or --file")
1290
elif message and file:
1291
raise BzrCommandError("please specify either --message or --file")
1295
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1298
raise BzrCommandError("empty commit message specified")
1301
tree.commit(message, specific_files=selected_list,
1302
allow_pointless=unchanged, strict=strict)
1303
except PointlessCommit:
1304
# FIXME: This should really happen before the file is read in;
1305
# perhaps prepare the commit; get the message; then actually commit
1306
raise BzrCommandError("no changes to commit",
1307
["use --unchanged to commit anyhow"])
1308
except ConflictsInTree:
1309
raise BzrCommandError("Conflicts detected in working tree. "
1310
'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
1311
except StrictCommitFailed:
1312
raise BzrCommandError("Commit refused because there are unknown "
1313
"files in the working tree.")
1314
note('Committed revision %d.' % (tree.branch.revno(),))
1317
class cmd_check(Command):
1318
"""Validate consistency of branch history.
1320
This command checks various invariants about the branch storage to
1321
detect data corruption or bzr bugs.
1323
takes_args = ['branch?']
1324
takes_options = ['verbose']
1326
def run(self, branch=None, verbose=False):
1327
from bzrlib.check import check
1329
tree = WorkingTree.open_containing()[0]
1330
branch = tree.branch
1332
branch = Branch.open(branch)
1333
check(branch, verbose)
1336
class cmd_scan_cache(Command):
1339
from bzrlib.hashcache import HashCache
1345
print '%6d stats' % c.stat_count
1346
print '%6d in hashcache' % len(c._cache)
1347
print '%6d files removed from cache' % c.removed_count
1348
print '%6d hashes updated' % c.update_count
1349
print '%6d files changed too recently to cache' % c.danger_count
1356
class cmd_upgrade(Command):
1357
"""Upgrade branch storage to current format.
1359
The check command or bzr developers may sometimes advise you to run
1362
This version of this command upgrades from the full-text storage
1363
used by bzr 0.0.8 and earlier to the weave format (v5).
1365
takes_args = ['dir?']
1367
def run(self, dir=u'.'):
1368
from bzrlib.upgrade import upgrade
1372
class cmd_whoami(Command):
1373
"""Show bzr user id."""
1374
takes_options = ['email']
1377
def run(self, email=False):
1379
b = WorkingTree.open_containing(u'.')[0].branch
1380
config = bzrlib.config.BranchConfig(b)
1381
except NotBranchError:
1382
config = bzrlib.config.GlobalConfig()
1385
print config.user_email()
1387
print config.username()
1389
class cmd_nick(Command):
1391
Print or set the branch nickname.
1392
If unset, the tree root directory name is used as the nickname
1393
To print the current nickname, execute with no argument.
1395
takes_args = ['nickname?']
1396
def run(self, nickname=None):
1397
branch = Branch.open_containing(u'.')[0]
1398
if nickname is None:
1399
self.printme(branch)
1401
branch.nick = nickname
1404
def printme(self, branch):
1407
class cmd_selftest(Command):
1408
"""Run internal test suite.
1410
This creates temporary test directories in the working directory,
1411
but not existing data is affected. These directories are deleted
1412
if the tests pass, or left behind to help in debugging if they
1413
fail and --keep-output is specified.
1415
If arguments are given, they are regular expressions that say
1416
which tests should run.
1418
# TODO: --list should give a list of all available tests
1420
takes_args = ['testspecs*']
1421
takes_options = ['verbose',
1422
Option('one', help='stop when one test fails'),
1423
Option('keep-output',
1424
help='keep output directories when tests fail')
1427
def run(self, testspecs_list=None, verbose=False, one=False,
1430
from bzrlib.tests import selftest
1431
# we don't want progress meters from the tests to go to the
1432
# real output; and we don't want log messages cluttering up
1434
save_ui = bzrlib.ui.ui_factory
1435
bzrlib.trace.info('running tests...')
1437
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1438
if testspecs_list is not None:
1439
pattern = '|'.join(testspecs_list)
1442
result = selftest(verbose=verbose,
1444
stop_on_failure=one,
1445
keep_output=keep_output)
1447
bzrlib.trace.info('tests passed')
1449
bzrlib.trace.info('tests failed')
1450
return int(not result)
1452
bzrlib.ui.ui_factory = save_ui
1456
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1457
# is bzrlib itself in a branch?
1458
bzrrev = bzrlib.get_bzr_revision()
1460
print " (bzr checkout, revision %d {%s})" % bzrrev
1461
print bzrlib.__copyright__
1462
print "http://bazaar-ng.org/"
1464
print "bzr comes with ABSOLUTELY NO WARRANTY. bzr is free software, and"
1465
print "you may use, modify and redistribute it under the terms of the GNU"
1466
print "General Public License version 2 or later."
1469
class cmd_version(Command):
1470
"""Show version of bzr."""
1475
class cmd_rocks(Command):
1476
"""Statement of optimism."""
1480
print "it sure does!"
1483
class cmd_find_merge_base(Command):
1484
"""Find and print a base revision for merging two branches.
1486
# TODO: Options to specify revisions on either side, as if
1487
# merging only part of the history.
1488
takes_args = ['branch', 'other']
1492
def run(self, branch, other):
1493
from bzrlib.revision import common_ancestor, MultipleRevisionSources
1495
branch1 = Branch.open_containing(branch)[0]
1496
branch2 = Branch.open_containing(other)[0]
1498
history_1 = branch1.revision_history()
1499
history_2 = branch2.revision_history()
1501
last1 = branch1.last_revision()
1502
last2 = branch2.last_revision()
1504
source = MultipleRevisionSources(branch1, branch2)
1506
base_rev_id = common_ancestor(last1, last2, source)
1508
print 'merge base is revision %s' % base_rev_id
1512
if base_revno is None:
1513
raise bzrlib.errors.UnrelatedBranches()
1515
print ' r%-6d in %s' % (base_revno, branch)
1517
other_revno = branch2.revision_id_to_revno(base_revid)
1519
print ' r%-6d in %s' % (other_revno, other)
1523
class cmd_merge(Command):
1524
"""Perform a three-way merge.
1526
The branch is the branch you will merge from. By default, it will
1527
merge the latest revision. If you specify a revision, that
1528
revision will be merged. If you specify two revisions, the first
1529
will be used as a BASE, and the second one as OTHER. Revision
1530
numbers are always relative to the specified branch.
1532
By default bzr will try to merge in all new work from the other
1533
branch, automatically determining an appropriate base. If this
1534
fails, you may need to give an explicit base.
1538
To merge the latest revision from bzr.dev
1539
bzr merge ../bzr.dev
1541
To merge changes up to and including revision 82 from bzr.dev
1542
bzr merge -r 82 ../bzr.dev
1544
To merge the changes introduced by 82, without previous changes:
1545
bzr merge -r 81..82 ../bzr.dev
1547
merge refuses to run if there are any uncommitted changes, unless
1550
takes_args = ['branch?']
1551
takes_options = ['revision', 'force', 'merge-type', 'reprocess',
1552
Option('show-base', help="Show base revision text in "
1555
def run(self, branch=None, revision=None, force=False, merge_type=None,
1556
show_base=False, reprocess=False):
1557
from bzrlib.merge import merge
1558
from bzrlib.merge_core import ApplyMerge3
1559
if merge_type is None:
1560
merge_type = ApplyMerge3
1562
branch = WorkingTree.open_containing(u'.')[0].branch.get_parent()
1564
raise BzrCommandError("No merge location known or specified.")
1566
print "Using saved location: %s" % branch
1567
if revision is None or len(revision) < 1:
1569
other = [branch, -1]
1571
if len(revision) == 1:
1573
other_branch = Branch.open_containing(branch)[0]
1574
revno = revision[0].in_history(other_branch).revno
1575
other = [branch, revno]
1577
assert len(revision) == 2
1578
if None in revision:
1579
raise BzrCommandError(
1580
"Merge doesn't permit that revision specifier.")
1581
b = Branch.open_containing(branch)[0]
1583
base = [branch, revision[0].in_history(b).revno]
1584
other = [branch, revision[1].in_history(b).revno]
1587
conflict_count = merge(other, base, check_clean=(not force),
1588
merge_type=merge_type, reprocess=reprocess,
1589
show_base=show_base)
1590
if conflict_count != 0:
1594
except bzrlib.errors.AmbiguousBase, e:
1595
m = ("sorry, bzr can't determine the right merge base yet\n"
1596
"candidates are:\n "
1597
+ "\n ".join(e.bases)
1599
"please specify an explicit base with -r,\n"
1600
"and (if you want) report this to the bzr developers\n")
1604
class cmd_remerge(Command):
1607
takes_args = ['file*']
1608
takes_options = ['merge-type', 'reprocess',
1609
Option('show-base', help="Show base revision text in "
1612
def run(self, file_list=None, merge_type=None, show_base=False,
1614
from bzrlib.merge import merge_inner, transform_tree
1615
from bzrlib.merge_core import ApplyMerge3
1616
if merge_type is None:
1617
merge_type = ApplyMerge3
1618
tree, file_list = tree_files(file_list)
1621
pending_merges = tree.pending_merges()
1622
if len(pending_merges) != 1:
1623
raise BzrCommandError("Sorry, remerge only works after normal"
1624
+ " merges. Not cherrypicking or"
1626
base_revision = common_ancestor(tree.branch.last_revision(),
1627
pending_merges[0], tree.branch)
1628
base_tree = tree.branch.revision_tree(base_revision)
1629
other_tree = tree.branch.revision_tree(pending_merges[0])
1630
interesting_ids = None
1631
if file_list is not None:
1632
interesting_ids = set()
1633
for filename in file_list:
1634
file_id = tree.path2id(filename)
1635
interesting_ids.add(file_id)
1636
if tree.kind(file_id) != "directory":
1639
for name, ie in tree.inventory.iter_entries(file_id):
1640
interesting_ids.add(ie.file_id)
1641
transform_tree(tree, tree.branch.basis_tree(), interesting_ids)
1642
if file_list is None:
1643
restore_files = list(tree.iter_conflicts())
1645
restore_files = file_list
1646
for filename in restore_files:
1648
restore(tree.abspath(filename))
1649
except NotConflicted:
1651
conflicts = merge_inner(tree.branch, other_tree, base_tree,
1652
interesting_ids = interesting_ids,
1653
other_rev_id=pending_merges[0],
1654
merge_type=merge_type,
1655
show_base=show_base,
1656
reprocess=reprocess)
1664
class cmd_revert(Command):
1665
"""Reverse all changes since the last commit.
1667
Only versioned files are affected. Specify filenames to revert only
1668
those files. By default, any files that are changed will be backed up
1669
first. Backup files have a '~' appended to their name.
1671
takes_options = ['revision', 'no-backup']
1672
takes_args = ['file*']
1673
aliases = ['merge-revert']
1675
def run(self, revision=None, no_backup=False, file_list=None):
1676
from bzrlib.merge import merge_inner
1677
from bzrlib.commands import parse_spec
1678
if file_list is not None:
1679
if len(file_list) == 0:
1680
raise BzrCommandError("No files specified")
1683
if revision is None:
1685
tree = WorkingTree.open_containing(u'.')[0]
1686
# FIXME should be tree.last_revision
1687
rev_id = tree.branch.last_revision()
1688
elif len(revision) != 1:
1689
raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1691
tree, file_list = tree_files(file_list)
1692
rev_id = revision[0].in_history(tree.branch).rev_id
1693
tree.revert(file_list, tree.branch.revision_tree(rev_id),
1697
class cmd_assert_fail(Command):
1698
"""Test reporting of assertion failures"""
1701
assert False, "always fails"
1704
class cmd_help(Command):
1705
"""Show help on a command or other topic.
1707
For a list of all available commands, say 'bzr help commands'."""
1708
takes_options = ['long']
1709
takes_args = ['topic?']
1713
def run(self, topic=None, long=False):
1715
if topic is None and long:
1720
class cmd_shell_complete(Command):
1721
"""Show appropriate completions for context.
1723
For a list of all available commands, say 'bzr shell-complete'."""
1724
takes_args = ['context?']
1729
def run(self, context=None):
1730
import shellcomplete
1731
shellcomplete.shellcomplete(context)
1734
class cmd_fetch(Command):
1735
"""Copy in history from another branch but don't merge it.
1737
This is an internal method used for pull and merge."""
1739
takes_args = ['from_branch', 'to_branch']
1740
def run(self, from_branch, to_branch):
1741
from bzrlib.fetch import Fetcher
1742
from bzrlib.branch import Branch
1743
from_b = Branch.open(from_branch)
1744
to_b = Branch.open(to_branch)
1749
Fetcher(to_b, from_b)
1756
class cmd_missing(Command):
1757
"""Show unmerged/unpulled revisions between two branches.
1759
OTHER_BRANCH may be local or remote."""
1760
takes_args = ['other_branch?']
1761
takes_options = [Option('reverse', 'Reverse the order of revisions'),
1763
'Display changes in the local branch only'),
1764
Option('theirs-only',
1765
'Display changes in the remote branch only'),
1773
def run(self, other_branch=None, reverse=False, mine_only=False,
1774
theirs_only=False, long=True, short=False, line=False,
1775
show_ids=False, verbose=False):
1776
from bzrlib.missing import find_unmerged, iter_log_data
1777
from bzrlib.log import log_formatter
1778
local_branch = bzrlib.branch.Branch.open_containing(".")[0]
1779
parent = local_branch.get_parent()
1780
if other_branch is None:
1781
other_branch = parent
1782
if other_branch is None:
1783
raise BzrCommandError("No missing location known or specified.")
1784
print "Using last location: " + local_branch.get_parent()
1785
remote_branch = bzrlib.branch.Branch.open(other_branch)
1786
local_extra, remote_extra = find_unmerged(local_branch, remote_branch)
1787
log_format = get_log_format(long=long, short=short, line=line)
1788
lf = log_formatter(log_format, sys.stdout,
1790
show_timezone='original')
1791
if reverse is False:
1792
local_extra.reverse()
1793
remote_extra.reverse()
1794
if local_extra and not theirs_only:
1795
print "You have %d extra revision(s):" % len(local_extra)
1796
for data in iter_log_data(local_extra, local_branch, verbose):
1798
printed_local = True
1800
printed_local = False
1801
if remote_extra and not mine_only:
1802
if printed_local is True:
1804
print "You are missing %d revision(s):" % len(remote_extra)
1805
for data in iter_log_data(remote_extra, remote_branch, verbose):
1807
if not remote_extra and not local_extra:
1809
print "Branches are up to date."
1812
if parent is None and other_branch is not None:
1813
local_branch.set_parent(other_branch)
1817
class cmd_plugins(Command):
1822
import bzrlib.plugin
1823
from inspect import getdoc
1824
for name, plugin in bzrlib.plugin.all_plugins().items():
1825
if hasattr(plugin, '__path__'):
1826
print plugin.__path__[0]
1827
elif hasattr(plugin, '__file__'):
1828
print plugin.__file__
1834
print '\t', d.split('\n')[0]
1837
class cmd_testament(Command):
1838
"""Show testament (signing-form) of a revision."""
1839
takes_options = ['revision', 'long']
1840
takes_args = ['branch?']
1842
def run(self, branch=u'.', revision=None, long=False):
1843
from bzrlib.testament import Testament
1844
b = WorkingTree.open_containing(branch)[0].branch
1847
if revision is None:
1848
rev_id = b.last_revision()
1850
rev_id = revision[0].in_history(b).rev_id
1851
t = Testament.from_revision(b, rev_id)
1853
sys.stdout.writelines(t.as_text_lines())
1855
sys.stdout.write(t.as_short_text())
1860
class cmd_annotate(Command):
1861
"""Show the origin of each line in a file.
1863
This prints out the given file with an annotation on the left side
1864
indicating which revision, author and date introduced the change.
1866
If the origin is the same for a run of consecutive lines, it is
1867
shown only at the top, unless the --all option is given.
1869
# TODO: annotate directories; showing when each file was last changed
1870
# TODO: annotate a previous version of a file
1871
# TODO: if the working copy is modified, show annotations on that
1872
# with new uncommitted lines marked
1873
aliases = ['blame', 'praise']
1874
takes_args = ['filename']
1875
takes_options = [Option('all', help='show annotations on all lines'),
1876
Option('long', help='show date in annotations'),
1880
def run(self, filename, all=False, long=False):
1881
from bzrlib.annotate import annotate_file
1882
tree, relpath = WorkingTree.open_containing(filename)
1883
branch = tree.branch
1886
file_id = tree.inventory.path2id(relpath)
1887
tree = branch.revision_tree(branch.last_revision())
1888
file_version = tree.inventory[file_id].revision
1889
annotate_file(branch, file_version, file_id, long, all, sys.stdout)
1894
class cmd_re_sign(Command):
1895
"""Create a digital signature for an existing revision."""
1896
# TODO be able to replace existing ones.
1898
hidden = True # is this right ?
1899
takes_args = ['revision_id?']
1900
takes_options = ['revision']
1902
def run(self, revision_id=None, revision=None):
1903
import bzrlib.config as config
1904
import bzrlib.gpg as gpg
1905
if revision_id is not None and revision is not None:
1906
raise BzrCommandError('You can only supply one of revision_id or --revision')
1907
if revision_id is None and revision is None:
1908
raise BzrCommandError('You must supply either --revision or a revision_id')
1909
b = WorkingTree.open_containing(u'.')[0].branch
1910
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1911
if revision_id is not None:
1912
b.sign_revision(revision_id, gpg_strategy)
1913
elif revision is not None:
1914
if len(revision) == 1:
1915
revno, rev_id = revision[0].in_history(b)
1916
b.sign_revision(rev_id, gpg_strategy)
1917
elif len(revision) == 2:
1918
# are they both on rh- if so we can walk between them
1919
# might be nice to have a range helper for arbitrary
1920
# revision paths. hmm.
1921
from_revno, from_revid = revision[0].in_history(b)
1922
to_revno, to_revid = revision[1].in_history(b)
1923
if to_revid is None:
1924
to_revno = b.revno()
1925
if from_revno is None or to_revno is None:
1926
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1927
for revno in range(from_revno, to_revno + 1):
1928
b.sign_revision(b.get_rev_id(revno), gpg_strategy)
1930
raise BzrCommandError('Please supply either one revision, or a range.')
1933
class cmd_uncommit(bzrlib.commands.Command):
1934
"""Remove the last committed revision.
1936
By supplying the --all flag, it will not only remove the entry
1937
from revision_history, but also remove all of the entries in the
1940
--verbose will print out what is being removed.
1941
--dry-run will go through all the motions, but not actually
1944
In the future, uncommit will create a changeset, which can then
1947
takes_options = ['all', 'verbose', 'revision',
1948
Option('dry-run', help='Don\'t actually make changes'),
1949
Option('force', help='Say yes to all questions.')]
1950
takes_args = ['location?']
1953
def run(self, location=None, all=False,
1954
dry_run=False, verbose=False,
1955
revision=None, force=False):
1956
from bzrlib.branch import Branch
1957
from bzrlib.log import log_formatter
1959
from bzrlib.uncommit import uncommit
1961
if location is None:
1963
b, relpath = Branch.open_containing(location)
1965
if revision is None:
1967
rev_id = b.last_revision()
1969
revno, rev_id = revision[0].in_history(b)
1971
print 'No revisions to uncommit.'
1973
for r in range(revno, b.revno()+1):
1974
rev_id = b.get_rev_id(r)
1975
lf = log_formatter('short', to_file=sys.stdout,show_timezone='original')
1976
lf.show(r, b.get_revision(rev_id), None)
1979
print 'Dry-run, pretending to remove the above revisions.'
1981
val = raw_input('Press <enter> to continue')
1983
print 'The above revision(s) will be removed.'
1985
val = raw_input('Are you sure [y/N]? ')
1986
if val.lower() not in ('y', 'yes'):
1990
uncommit(b, remove_files=all,
1991
dry_run=dry_run, verbose=verbose,
1995
# these get imported and then picked up by the scan for cmd_*
1996
# TODO: Some more consistent way to split command definitions across files;
1997
# we do need to load at least some information about them to know of
1999
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore