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
# DO NOT change this to cStringIO - it results in control files
19
# FIXIT! (Only deal with byte streams OR unicode at any one layer.)
21
from StringIO import StringIO
26
from bzrlib import BZRDIR
27
from bzrlib.commands import Command, display_command
28
from bzrlib.branch import Branch
29
from bzrlib.revision import common_ancestor
30
import bzrlib.errors as errors
31
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError,
32
NotBranchError, DivergedBranches, NotConflicted,
33
NoSuchFile, NoWorkingTree, FileInWrongBranch)
34
from bzrlib.option import Option
35
from bzrlib.revisionspec import RevisionSpec
37
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
38
from bzrlib.workingtree import WorkingTree
39
from bzrlib.log import show_one_log
42
def tree_files(file_list, default_branch=u'.'):
44
return internal_tree_files(file_list, default_branch)
45
except FileInWrongBranch, e:
46
raise BzrCommandError("%s is not in the same branch as %s" %
47
(e.path, file_list[0]))
49
def internal_tree_files(file_list, default_branch=u'.'):
51
Return a branch and list of branch-relative paths.
52
If supplied file_list is empty or None, the branch default will be used,
53
and returned file_list will match the original.
55
if file_list is None or len(file_list) == 0:
56
return WorkingTree.open_containing(default_branch)[0], file_list
57
tree = WorkingTree.open_containing(file_list[0])[0]
59
for filename in file_list:
61
new_list.append(tree.relpath(filename))
62
except errors.PathNotChild:
63
raise FileInWrongBranch(tree.branch, filename)
67
# TODO: Make sure no commands unconditionally use the working directory as a
68
# branch. If a filename argument is used, the first of them should be used to
69
# specify the branch. (Perhaps this can be factored out into some kind of
70
# Argument class, representing a file in a branch, where the first occurrence
73
class cmd_status(Command):
74
"""Display status summary.
76
This reports on versioned and unknown files, reporting them
77
grouped by state. Possible states are:
80
Versioned in the working copy but not in the previous revision.
83
Versioned in the previous revision but removed or deleted
87
Path of this file changed from the previous revision;
88
the text may also have changed. This includes files whose
89
parent directory was renamed.
92
Text has changed since the previous revision.
95
Nothing about this file has changed since the previous revision.
96
Only shown with --all.
99
Not versioned and not matching an ignore pattern.
101
To see ignored files use 'bzr ignored'. For details in the
102
changes to file texts, use 'bzr diff'.
104
If no arguments are specified, the status of the entire working
105
directory is shown. Otherwise, only the status of the specified
106
files or directories is reported. If a directory is given, status
107
is reported for everything inside that directory.
109
If a revision argument is given, the status is calculated against
110
that revision, or between two revisions if two are provided.
113
# TODO: --no-recurse, --recurse options
115
takes_args = ['file*']
116
takes_options = ['all', 'show-ids', 'revision']
117
aliases = ['st', 'stat']
120
def run(self, all=False, show_ids=False, file_list=None, revision=None):
121
tree, file_list = tree_files(file_list)
123
from bzrlib.status import show_status
124
show_status(tree.branch, show_unchanged=all, show_ids=show_ids,
125
specific_files=file_list, revision=revision)
128
class cmd_cat_revision(Command):
129
"""Write out metadata for a revision.
131
The revision to print can either be specified by a specific
132
revision identifier, or you can use --revision.
136
takes_args = ['revision_id?']
137
takes_options = ['revision']
140
def run(self, revision_id=None, revision=None):
142
if revision_id is not None and revision is not None:
143
raise BzrCommandError('You can only supply one of revision_id or --revision')
144
if revision_id is None and revision is None:
145
raise BzrCommandError('You must supply either --revision or a revision_id')
146
b = WorkingTree.open_containing(u'.')[0].branch
147
if revision_id is not None:
148
sys.stdout.write(b.get_revision_xml(revision_id))
149
elif revision is not None:
152
raise BzrCommandError('You cannot specify a NULL revision.')
153
revno, rev_id = rev.in_history(b)
154
sys.stdout.write(b.get_revision_xml(rev_id))
157
class cmd_revno(Command):
158
"""Show current revision number.
160
This is equal to the number of revisions on this branch."""
161
takes_args = ['location?']
163
def run(self, location=u'.'):
164
print Branch.open_containing(location)[0].revno()
167
class cmd_revision_info(Command):
168
"""Show revision number and revision id for a given revision identifier.
171
takes_args = ['revision_info*']
172
takes_options = ['revision']
174
def run(self, revision=None, revision_info_list=[]):
177
if revision is not None:
178
revs.extend(revision)
179
if revision_info_list is not None:
180
for rev in revision_info_list:
181
revs.append(RevisionSpec(rev))
183
raise BzrCommandError('You must supply a revision identifier')
185
b = WorkingTree.open_containing(u'.')[0].branch
188
revinfo = rev.in_history(b)
189
if revinfo.revno is None:
190
print ' %s' % revinfo.rev_id
192
print '%4d %s' % (revinfo.revno, revinfo.rev_id)
195
class cmd_add(Command):
196
"""Add specified files or directories.
198
In non-recursive mode, all the named items are added, regardless
199
of whether they were previously ignored. A warning is given if
200
any of the named files are already versioned.
202
In recursive mode (the default), files are treated the same way
203
but the behaviour for directories is different. Directories that
204
are already versioned do not give a warning. All directories,
205
whether already versioned or not, are searched for files or
206
subdirectories that are neither versioned or ignored, and these
207
are added. This search proceeds recursively into versioned
208
directories. If no names are given '.' is assumed.
210
Therefore simply saying 'bzr add' will version all files that
211
are currently unknown.
213
Adding a file whose parent directory is not versioned will
214
implicitly add the parent, and so on up to the root. This means
215
you should never need to explictly add a directory, they'll just
216
get added when you add a file in the directory.
218
takes_args = ['file*']
219
takes_options = ['no-recurse']
221
def run(self, file_list, no_recurse=False):
222
from bzrlib.add import smart_add, add_reporter_print, add_reporter_null
224
reporter = add_reporter_null
226
reporter = add_reporter_print
227
smart_add(file_list, not no_recurse, reporter)
230
class cmd_mkdir(Command):
231
"""Create a new versioned directory.
233
This is equivalent to creating the directory and then adding it.
235
takes_args = ['dir+']
237
def run(self, dir_list):
240
wt, dd = WorkingTree.open_containing(d)
245
class cmd_relpath(Command):
246
"""Show path of a file relative to root"""
247
takes_args = ['filename']
251
def run(self, filename):
252
tree, relpath = WorkingTree.open_containing(filename)
256
class cmd_inventory(Command):
257
"""Show inventory of the current working copy or a revision.
259
It is possible to limit the output to a particular entry
260
type using the --kind option. For example; --kind file.
262
takes_options = ['revision', 'show-ids', 'kind']
265
def run(self, revision=None, show_ids=False, kind=None):
266
if kind and kind not in ['file', 'directory', 'symlink']:
267
raise BzrCommandError('invalid kind specified')
268
tree = WorkingTree.open_containing(u'.')[0]
270
inv = tree.read_working_inventory()
272
if len(revision) > 1:
273
raise BzrCommandError('bzr inventory --revision takes'
274
' exactly one revision identifier')
275
inv = tree.branch.get_revision_inventory(
276
revision[0].in_history(tree.branch).rev_id)
278
for path, entry in inv.entries():
279
if kind and kind != entry.kind:
282
print '%-50s %s' % (path, entry.file_id)
287
class cmd_move(Command):
288
"""Move files to a different directory.
293
The destination must be a versioned directory in the same branch.
295
takes_args = ['source$', 'dest']
296
def run(self, source_list, dest):
297
tree, source_list = tree_files(source_list)
298
# TODO: glob expansion on windows?
299
tree.move(source_list, tree.relpath(dest))
302
class cmd_rename(Command):
303
"""Change the name of an entry.
306
bzr rename frob.c frobber.c
307
bzr rename src/frob.c lib/frob.c
309
It is an error if the destination name exists.
311
See also the 'move' command, which moves files into a different
312
directory without changing their name.
314
# TODO: Some way to rename multiple files without invoking
315
# bzr for each one?"""
316
takes_args = ['from_name', 'to_name']
318
def run(self, from_name, to_name):
319
tree, (from_name, to_name) = tree_files((from_name, to_name))
320
tree.rename_one(from_name, to_name)
323
class cmd_mv(Command):
324
"""Move or rename a file.
327
bzr mv OLDNAME NEWNAME
328
bzr mv SOURCE... DESTINATION
330
If the last argument is a versioned directory, all the other names
331
are moved into it. Otherwise, there must be exactly two arguments
332
and the file is changed to a new name, which must not already exist.
334
Files cannot be moved between branches.
336
takes_args = ['names*']
337
def run(self, names_list):
338
if len(names_list) < 2:
339
raise BzrCommandError("missing file argument")
340
tree, rel_names = tree_files(names_list)
342
if os.path.isdir(names_list[-1]):
343
# move into existing directory
344
for pair in tree.move(rel_names[:-1], rel_names[-1]):
345
print "%s => %s" % pair
347
if len(names_list) != 2:
348
raise BzrCommandError('to mv multiple files the destination '
349
'must be a versioned directory')
350
tree.rename_one(rel_names[0], rel_names[1])
351
print "%s => %s" % (rel_names[0], rel_names[1])
354
class cmd_pull(Command):
355
"""Pull any changes from another branch into the current one.
357
If there is no default location set, the first pull will set it. After
358
that, you can omit the location to use the default. To change the
359
default, use --remember.
361
This command only works on branches that have not diverged. Branches are
362
considered diverged if both branches have had commits without first
363
pulling from the other.
365
If branches have diverged, you can use 'bzr merge' to pull the text changes
366
from one into the other. Once one branch has merged, the other should
367
be able to pull it again.
369
If you want to forget your local changes and just update your branch to
370
match the remote one, use --overwrite.
372
takes_options = ['remember', 'overwrite', 'verbose']
373
takes_args = ['location?']
375
def run(self, location=None, remember=False, overwrite=False, verbose=False):
376
from bzrlib.merge import merge
377
from shutil import rmtree
379
# FIXME: too much stuff is in the command class
380
tree_to = WorkingTree.open_containing(u'.')[0]
381
stored_loc = tree_to.branch.get_parent()
383
if stored_loc is None:
384
raise BzrCommandError("No pull location known or specified.")
386
print "Using saved location: %s" % stored_loc
387
location = stored_loc
388
br_from = Branch.open(location)
389
br_to = tree_to.branch
391
old_rh = br_to.revision_history()
392
count = tree_to.pull(br_from, overwrite)
393
except DivergedBranches:
394
# FIXME: Just make DivergedBranches display the right message
396
raise BzrCommandError("These branches have diverged."
398
if br_to.get_parent() is None or remember:
399
br_to.set_parent(location)
400
note('%d revision(s) pulled.' % (count,))
403
new_rh = tree_to.branch.revision_history()
406
from bzrlib.log import show_changed_revisions
407
show_changed_revisions(tree_to.branch, old_rh, new_rh)
410
class cmd_push(Command):
411
"""Push this branch into another branch.
413
The remote branch will not have its working tree populated because this
414
is both expensive, and may not be supported on the remote file system.
416
Some smart servers or protocols *may* put the working tree in place.
418
If there is no default push location set, the first push will set it.
419
After that, you can omit the location to use the default. To change the
420
default, use --remember.
422
This command only works on branches that have not diverged. Branches are
423
considered diverged if the branch being pushed to is not an older version
426
If branches have diverged, you can use 'bzr push --overwrite' to replace
427
the other branch completely.
429
If you want to ensure you have the different changes in the other branch,
430
do a merge (see bzr help merge) from the other branch, and commit that
431
before doing a 'push --overwrite'.
433
takes_options = ['remember', 'overwrite',
434
Option('create-prefix',
435
help='Create the path leading up to the branch '
436
'if it does not already exist')]
437
takes_args = ['location?']
439
def run(self, location=None, remember=False, overwrite=False,
440
create_prefix=False, verbose=False):
441
# FIXME: Way too big! Put this into a function called from the
444
from shutil import rmtree
445
from bzrlib.transport import get_transport
447
tree_from = WorkingTree.open_containing(u'.')[0]
448
br_from = tree_from.branch
449
stored_loc = tree_from.branch.get_push_location()
451
if stored_loc is None:
452
raise BzrCommandError("No push location known or specified.")
454
print "Using saved location: %s" % stored_loc
455
location = stored_loc
457
br_to = Branch.open(location)
458
except NotBranchError:
460
transport = get_transport(location).clone('..')
461
if not create_prefix:
463
transport.mkdir(transport.relpath(location))
465
raise BzrCommandError("Parent directory of %s "
466
"does not exist." % location)
468
current = transport.base
469
needed = [(transport, transport.relpath(location))]
472
transport, relpath = needed[-1]
473
transport.mkdir(relpath)
476
new_transport = transport.clone('..')
477
needed.append((new_transport,
478
new_transport.relpath(transport.base)))
479
if new_transport.base == transport.base:
480
raise BzrCommandError("Could not creeate "
482
br_to = Branch.initialize(location)
483
old_rh = br_to.revision_history()
486
tree_to = br_to.working_tree()
487
except NoWorkingTree:
488
# TODO: This should be updated for branches which don't have a
489
# working tree, as opposed to ones where we just couldn't
491
warning('Unable to update the working tree of: %s' % (br_to.base,))
492
count = br_to.pull(br_from, overwrite)
494
count = tree_to.pull(br_from, overwrite)
495
except DivergedBranches:
496
raise BzrCommandError("These branches have diverged."
497
" Try a merge then push with overwrite.")
498
if br_from.get_push_location() is None or remember:
499
br_from.set_push_location(location)
500
note('%d revision(s) pushed.' % (count,))
503
new_rh = br_to.revision_history()
506
from bzrlib.log import show_changed_revisions
507
show_changed_revisions(br_to, old_rh, new_rh)
510
class cmd_branch(Command):
511
"""Create a new copy of a branch.
513
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
514
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
516
To retrieve the branch as of a particular revision, supply the --revision
517
parameter, as in "branch foo/bar -r 5".
519
--basis is to speed up branching from remote branches. When specified, it
520
copies all the file-contents, inventory and revision data from the basis
521
branch before copying anything from the remote branch.
523
takes_args = ['from_location', 'to_location?']
524
takes_options = ['revision', 'basis']
525
aliases = ['get', 'clone']
527
def run(self, from_location, to_location=None, revision=None, basis=None):
528
from bzrlib.clone import copy_branch
530
from shutil import rmtree
533
elif len(revision) > 1:
534
raise BzrCommandError(
535
'bzr branch --revision takes exactly 1 revision value')
537
br_from = Branch.open(from_location)
539
if e.errno == errno.ENOENT:
540
raise BzrCommandError('Source location "%s" does not'
541
' exist.' % to_location)
546
if basis is not None:
547
basis_branch = WorkingTree.open_containing(basis)[0].branch
550
if len(revision) == 1 and revision[0] is not None:
551
revision_id = revision[0].in_history(br_from)[1]
554
if to_location is None:
555
to_location = os.path.basename(from_location.rstrip("/\\"))
558
name = os.path.basename(to_location) + '\n'
560
os.mkdir(to_location)
562
if e.errno == errno.EEXIST:
563
raise BzrCommandError('Target directory "%s" already'
564
' exists.' % to_location)
565
if e.errno == errno.ENOENT:
566
raise BzrCommandError('Parent of "%s" does not exist.' %
571
copy_branch(br_from, to_location, revision_id, basis_branch)
572
except bzrlib.errors.NoSuchRevision:
574
msg = "The branch %s has no revision %s." % (from_location, revision[0])
575
raise BzrCommandError(msg)
576
except bzrlib.errors.UnlistableBranch:
578
msg = "The branch %s cannot be used as a --basis"
579
raise BzrCommandError(msg)
580
branch = Branch.open(to_location)
582
name = StringIO(name)
583
branch.put_controlfile('branch-name', name)
584
note('Branched %d revision(s).' % branch.revno())
589
class cmd_renames(Command):
590
"""Show list of renamed files.
592
# TODO: Option to show renames between two historical versions.
594
# TODO: Only show renames under dir, rather than in the whole branch.
595
takes_args = ['dir?']
598
def run(self, dir=u'.'):
599
tree = WorkingTree.open_containing(dir)[0]
600
old_inv = tree.branch.basis_tree().inventory
601
new_inv = tree.read_working_inventory()
603
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
605
for old_name, new_name in renames:
606
print "%s => %s" % (old_name, new_name)
609
class cmd_info(Command):
610
"""Show statistical information about a branch."""
611
takes_args = ['branch?']
614
def run(self, branch=None):
616
b = WorkingTree.open_containing(branch)[0].branch
620
class cmd_remove(Command):
621
"""Make a file unversioned.
623
This makes bzr stop tracking changes to a versioned file. It does
624
not delete the working copy.
626
takes_args = ['file+']
627
takes_options = ['verbose']
630
def run(self, file_list, verbose=False):
631
tree, file_list = tree_files(file_list)
632
tree.remove(file_list, verbose=verbose)
635
class cmd_file_id(Command):
636
"""Print file_id of a particular file or directory.
638
The file_id is assigned when the file is first added and remains the
639
same through all revisions where the file exists, even when it is
643
takes_args = ['filename']
645
def run(self, filename):
646
tree, relpath = WorkingTree.open_containing(filename)
647
i = tree.inventory.path2id(relpath)
649
raise BzrError("%r is not a versioned file" % filename)
654
class cmd_file_path(Command):
655
"""Print path of file_ids to a file or directory.
657
This prints one line for each directory down to the target,
658
starting at the branch root."""
660
takes_args = ['filename']
662
def run(self, filename):
663
tree, relpath = WorkingTree.open_containing(filename)
665
fid = inv.path2id(relpath)
667
raise BzrError("%r is not a versioned file" % filename)
668
for fip in inv.get_idpath(fid):
672
class cmd_revision_history(Command):
673
"""Display list of revision ids on this branch."""
677
branch = WorkingTree.open_containing(u'.')[0].branch
678
for patchid in branch.revision_history():
682
class cmd_ancestry(Command):
683
"""List all revisions merged into this branch."""
687
tree = WorkingTree.open_containing(u'.')[0]
689
# FIXME. should be tree.last_revision
690
for revision_id in b.get_ancestry(b.last_revision()):
694
class cmd_init(Command):
695
"""Make a directory into a versioned branch.
697
Use this to create an empty branch, or before importing an
700
Recipe for importing a tree of files:
705
bzr commit -m 'imported project'
707
takes_args = ['location?']
708
def run(self, location=None):
709
from bzrlib.branch import Branch
713
# The path has to exist to initialize a
714
# branch inside of it.
715
# Just using os.mkdir, since I don't
716
# believe that we want to create a bunch of
717
# locations if the user supplies an extended path
718
if not os.path.exists(location):
720
Branch.initialize(location)
723
class cmd_diff(Command):
724
"""Show differences in working tree.
726
If files are listed, only the changes in those files are listed.
727
Otherwise, all changes for the tree are listed.
734
# TODO: Allow diff across branches.
735
# TODO: Option to use external diff command; could be GNU diff, wdiff,
736
# or a graphical diff.
738
# TODO: Python difflib is not exactly the same as unidiff; should
739
# either fix it up or prefer to use an external diff.
741
# TODO: If a directory is given, diff everything under that.
743
# TODO: Selected-file diff is inefficient and doesn't show you
746
# TODO: This probably handles non-Unix newlines poorly.
748
takes_args = ['file*']
749
takes_options = ['revision', 'diff-options']
750
aliases = ['di', 'dif']
753
def run(self, revision=None, file_list=None, diff_options=None):
754
from bzrlib.diff import show_diff
756
tree, file_list = internal_tree_files(file_list)
759
except FileInWrongBranch:
760
if len(file_list) != 2:
761
raise BzrCommandError("Files are in different branches")
763
b, file1 = Branch.open_containing(file_list[0])
764
b2, file2 = Branch.open_containing(file_list[1])
765
if file1 != "" or file2 != "":
766
# FIXME diff those two files. rbc 20051123
767
raise BzrCommandError("Files are in different branches")
769
if revision is not None:
771
raise BzrCommandError("Can't specify -r with two branches")
772
if len(revision) == 1:
773
return show_diff(tree.branch, revision[0], specific_files=file_list,
774
external_diff_options=diff_options)
775
elif len(revision) == 2:
776
return show_diff(tree.branch, revision[0], specific_files=file_list,
777
external_diff_options=diff_options,
778
revision2=revision[1])
780
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
783
return show_diff(b, None, specific_files=file_list,
784
external_diff_options=diff_options, b2=b2)
786
return show_diff(tree.branch, None, specific_files=file_list,
787
external_diff_options=diff_options)
790
class cmd_deleted(Command):
791
"""List files deleted in the working tree.
793
# TODO: Show files deleted since a previous revision, or
794
# between two revisions.
795
# TODO: Much more efficient way to do this: read in new
796
# directories with readdir, rather than stating each one. Same
797
# level of effort but possibly much less IO. (Or possibly not,
798
# if the directories are very large...)
800
def run(self, show_ids=False):
801
tree = WorkingTree.open_containing(u'.')[0]
802
old = tree.branch.basis_tree()
803
for path, ie in old.inventory.iter_entries():
804
if not tree.has_id(ie.file_id):
806
print '%-50s %s' % (path, ie.file_id)
811
class cmd_modified(Command):
812
"""List files modified in working tree."""
816
from bzrlib.delta import compare_trees
818
tree = WorkingTree.open_containing(u'.')[0]
819
td = compare_trees(tree.branch.basis_tree(), tree)
821
for path, id, kind, text_modified, meta_modified in td.modified:
826
class cmd_added(Command):
827
"""List files added in working tree."""
831
wt = WorkingTree.open_containing(u'.')[0]
832
basis_inv = wt.branch.basis_tree().inventory
835
if file_id in basis_inv:
837
path = inv.id2path(file_id)
838
if not os.access(b.abspath(path), os.F_OK):
844
class cmd_root(Command):
845
"""Show the tree root directory.
847
The root is the nearest enclosing directory with a .bzr control
849
takes_args = ['filename?']
851
def run(self, filename=None):
852
"""Print the branch root."""
853
tree = WorkingTree.open_containing(filename)[0]
857
class cmd_log(Command):
858
"""Show log of this branch.
860
To request a range of logs, you can use the command -r begin..end
861
-r revision requests a specific revision, -r ..end or -r begin.. are
865
# TODO: Make --revision support uuid: and hash: [future tag:] notation.
867
takes_args = ['filename?']
868
takes_options = [Option('forward',
869
help='show from oldest to newest'),
870
'timezone', 'verbose',
871
'show-ids', 'revision',
874
help='show revisions whose message matches this regexp',
879
def run(self, filename=None, timezone='original',
888
from bzrlib.log import log_formatter, show_log
890
assert message is None or isinstance(message, basestring), \
891
"invalid message argument %r" % message
892
direction = (forward and 'forward') or 'reverse'
898
tree, fp = WorkingTree.open_containing(filename)
901
inv = tree.read_working_inventory()
902
except NotBranchError:
905
b, fp = Branch.open_containing(filename)
907
inv = b.get_inventory(b.last_revision())
909
file_id = inv.path2id(fp)
911
file_id = None # points to branch root
913
tree, relpath = WorkingTree.open_containing(u'.')
920
elif len(revision) == 1:
921
rev1 = rev2 = revision[0].in_history(b).revno
922
elif len(revision) == 2:
923
rev1 = revision[0].in_history(b).revno
924
rev2 = revision[1].in_history(b).revno
926
raise BzrCommandError('bzr log --revision takes one or two values.')
928
# By this point, the revision numbers are converted to the +ve
929
# form if they were supplied in the -ve form, so we can do
930
# this comparison in relative safety
932
(rev2, rev1) = (rev1, rev2)
934
mutter('encoding log as %r', bzrlib.user_encoding)
936
# use 'replace' so that we don't abort if trying to write out
937
# in e.g. the default C locale.
938
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
940
log_format = get_log_format(long=long, short=short, line=line)
941
lf = log_formatter(log_format,
944
show_timezone=timezone)
955
def get_log_format(long=False, short=False, line=False, default='long'):
966
class cmd_touching_revisions(Command):
967
"""Return revision-ids which affected a particular file.
969
A more user-friendly interface is "bzr log FILE"."""
971
takes_args = ["filename"]
973
def run(self, filename):
974
tree, relpath = WorkingTree.open_containing(filename)
976
inv = tree.read_working_inventory()
977
file_id = inv.path2id(relpath)
978
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
979
print "%6d %s" % (revno, what)
982
class cmd_ls(Command):
983
"""List files in a tree.
985
# TODO: Take a revision or remote path and list that tree instead.
987
takes_options = ['verbose', 'revision',
988
Option('non-recursive',
989
help='don\'t recurse into sub-directories'),
991
help='Print all paths from the root of the branch.'),
992
Option('unknown', help='Print unknown files'),
993
Option('versioned', help='Print versioned files'),
994
Option('ignored', help='Print ignored files'),
996
Option('null', help='Null separate the files'),
999
def run(self, revision=None, verbose=False,
1000
non_recursive=False, from_root=False,
1001
unknown=False, versioned=False, ignored=False,
1004
if verbose and null:
1005
raise BzrCommandError('Cannot set both --verbose and --null')
1006
all = not (unknown or versioned or ignored)
1008
selection = {'I':ignored, '?':unknown, 'V':versioned}
1010
tree, relpath = WorkingTree.open_containing(u'.')
1015
if revision is not None:
1016
tree = tree.branch.revision_tree(
1017
revision[0].in_history(tree.branch).rev_id)
1018
for fp, fc, kind, fid, entry in tree.list_files():
1019
if fp.startswith(relpath):
1020
fp = fp[len(relpath):]
1021
if non_recursive and '/' in fp:
1023
if not all and not selection[fc]:
1026
kindch = entry.kind_character()
1027
print '%-8s %s%s' % (fc, fp, kindch)
1029
sys.stdout.write(fp)
1030
sys.stdout.write('\0')
1036
class cmd_unknowns(Command):
1037
"""List unknown files."""
1040
from bzrlib.osutils import quotefn
1041
for f in WorkingTree.open_containing(u'.')[0].unknowns():
1045
class cmd_ignore(Command):
1046
"""Ignore a command or pattern.
1048
To remove patterns from the ignore list, edit the .bzrignore file.
1050
If the pattern contains a slash, it is compared to the whole path
1051
from the branch root. Otherwise, it is compared to only the last
1052
component of the path. To match a file only in the root directory,
1055
Ignore patterns are case-insensitive on case-insensitive systems.
1057
Note: wildcards must be quoted from the shell on Unix.
1060
bzr ignore ./Makefile
1061
bzr ignore '*.class'
1063
# TODO: Complain if the filename is absolute
1064
takes_args = ['name_pattern']
1066
def run(self, name_pattern):
1067
from bzrlib.atomicfile import AtomicFile
1070
tree, relpath = WorkingTree.open_containing(u'.')
1071
ifn = tree.abspath('.bzrignore')
1073
if os.path.exists(ifn):
1076
igns = f.read().decode('utf-8')
1082
# TODO: If the file already uses crlf-style termination, maybe
1083
# we should use that for the newly added lines?
1085
if igns and igns[-1] != '\n':
1087
igns += name_pattern + '\n'
1090
f = AtomicFile(ifn, 'wt')
1091
f.write(igns.encode('utf-8'))
1096
inv = tree.inventory
1097
if inv.path2id('.bzrignore'):
1098
mutter('.bzrignore is already versioned')
1100
mutter('need to make new .bzrignore file versioned')
1101
tree.add(['.bzrignore'])
1104
class cmd_ignored(Command):
1105
"""List ignored files and the patterns that matched them.
1107
See also: bzr ignore"""
1110
tree = WorkingTree.open_containing(u'.')[0]
1111
for path, file_class, kind, file_id, entry in tree.list_files():
1112
if file_class != 'I':
1114
## XXX: Slightly inefficient since this was already calculated
1115
pat = tree.is_ignored(path)
1116
print '%-50s %s' % (path, pat)
1119
class cmd_lookup_revision(Command):
1120
"""Lookup the revision-id from a revision-number
1123
bzr lookup-revision 33
1126
takes_args = ['revno']
1129
def run(self, revno):
1133
raise BzrCommandError("not a valid revision-number: %r" % revno)
1135
print WorkingTree.open_containing(u'.')[0].branch.get_rev_id(revno)
1138
class cmd_export(Command):
1139
"""Export past revision to destination directory.
1141
If no revision is specified this exports the last committed revision.
1143
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1144
given, try to find the format with the extension. If no extension
1145
is found exports to a directory (equivalent to --format=dir).
1147
Root may be the top directory for tar, tgz and tbz2 formats. If none
1148
is given, the top directory will be the root name of the file.
1150
Note: export of tree with non-ascii filenames to zip is not supported.
1152
Supported formats Autodetected by extension
1153
----------------- -------------------------
1156
tbz2 .tar.bz2, .tbz2
1160
takes_args = ['dest']
1161
takes_options = ['revision', 'format', 'root']
1162
def run(self, dest, revision=None, format=None, root=None):
1164
from bzrlib.export import export
1165
tree = WorkingTree.open_containing(u'.')[0]
1167
if revision is None:
1168
# should be tree.last_revision FIXME
1169
rev_id = b.last_revision()
1171
if len(revision) != 1:
1172
raise BzrError('bzr export --revision takes exactly 1 argument')
1173
rev_id = revision[0].in_history(b).rev_id
1174
t = b.revision_tree(rev_id)
1176
export(t, dest, format, root)
1177
except errors.NoSuchExportFormat, e:
1178
raise BzrCommandError('Unsupported export format: %s' % e.format)
1181
class cmd_cat(Command):
1182
"""Write a file's text from a previous revision."""
1184
takes_options = ['revision']
1185
takes_args = ['filename']
1188
def run(self, filename, revision=None):
1189
if revision is not None and len(revision) != 1:
1190
raise BzrCommandError("bzr cat --revision takes exactly one number")
1193
tree, relpath = WorkingTree.open_containing(filename)
1195
except NotBranchError:
1199
b, relpath = Branch.open_containing(filename)
1200
if revision is None:
1201
revision_id = b.last_revision()
1203
revision_id = revision[0].in_history(b).rev_id
1204
b.print_file(relpath, revision_id)
1207
class cmd_local_time_offset(Command):
1208
"""Show the offset in seconds from GMT to local time."""
1212
print bzrlib.osutils.local_time_offset()
1216
class cmd_commit(Command):
1217
"""Commit changes into a new revision.
1219
If no arguments are given, the entire tree is committed.
1221
If selected files are specified, only changes to those files are
1222
committed. If a directory is specified then the directory and everything
1223
within it is committed.
1225
A selected-file commit may fail in some cases where the committed
1226
tree would be invalid, such as trying to commit a file in a
1227
newly-added directory that is not itself committed.
1229
# TODO: Run hooks on tree to-be-committed, and after commit.
1231
# TODO: Strict commit that fails if there are deleted files.
1232
# (what does "deleted files" mean ??)
1234
# TODO: Give better message for -s, --summary, used by tla people
1236
# XXX: verbose currently does nothing
1238
takes_args = ['selected*']
1239
takes_options = ['message', 'verbose',
1241
help='commit even if nothing has changed'),
1242
Option('file', type=str,
1244
help='file containing commit message'),
1246
help="refuse to commit if there are unknown "
1247
"files in the working tree."),
1249
aliases = ['ci', 'checkin']
1251
def run(self, message=None, file=None, verbose=True, selected_list=None,
1252
unchanged=False, strict=False):
1253
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1255
from bzrlib.msgeditor import edit_commit_message, \
1256
make_commit_message_template
1257
from bzrlib.status import show_status
1258
from tempfile import TemporaryFile
1261
# TODO: Need a blackbox test for invoking the external editor; may be
1262
# slightly problematic to run this cross-platform.
1264
# TODO: do more checks that the commit will succeed before
1265
# spending the user's valuable time typing a commit message.
1267
# TODO: if the commit *does* happen to fail, then save the commit
1268
# message to a temporary file where it can be recovered
1269
tree, selected_list = tree_files(selected_list)
1270
if message is None and not file:
1271
template = make_commit_message_template(tree, selected_list)
1272
message = edit_commit_message(template)
1274
raise BzrCommandError("please specify a commit message"
1275
" with either --message or --file")
1276
elif message and file:
1277
raise BzrCommandError("please specify either --message or --file")
1281
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1284
raise BzrCommandError("empty commit message specified")
1287
tree.commit(message, specific_files=selected_list,
1288
allow_pointless=unchanged, strict=strict)
1289
except PointlessCommit:
1290
# FIXME: This should really happen before the file is read in;
1291
# perhaps prepare the commit; get the message; then actually commit
1292
raise BzrCommandError("no changes to commit",
1293
["use --unchanged to commit anyhow"])
1294
except ConflictsInTree:
1295
raise BzrCommandError("Conflicts detected in working tree. "
1296
'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
1297
except StrictCommitFailed:
1298
raise BzrCommandError("Commit refused because there are unknown "
1299
"files in the working tree.")
1300
note('Committed revision %d.' % (tree.branch.revno(),))
1303
class cmd_check(Command):
1304
"""Validate consistency of branch history.
1306
This command checks various invariants about the branch storage to
1307
detect data corruption or bzr bugs.
1309
takes_args = ['branch?']
1310
takes_options = ['verbose']
1312
def run(self, branch=None, verbose=False):
1313
from bzrlib.check import check
1315
tree = WorkingTree.open_containing()[0]
1316
branch = tree.branch
1318
branch = Branch.open(branch)
1319
check(branch, verbose)
1322
class cmd_scan_cache(Command):
1325
from bzrlib.hashcache import HashCache
1331
print '%6d stats' % c.stat_count
1332
print '%6d in hashcache' % len(c._cache)
1333
print '%6d files removed from cache' % c.removed_count
1334
print '%6d hashes updated' % c.update_count
1335
print '%6d files changed too recently to cache' % c.danger_count
1342
class cmd_upgrade(Command):
1343
"""Upgrade branch storage to current format.
1345
The check command or bzr developers may sometimes advise you to run
1348
This version of this command upgrades from the full-text storage
1349
used by bzr 0.0.8 and earlier to the weave format (v5).
1351
takes_args = ['dir?']
1353
def run(self, dir=u'.'):
1354
from bzrlib.upgrade import upgrade
1358
class cmd_whoami(Command):
1359
"""Show bzr user id."""
1360
takes_options = ['email']
1363
def run(self, email=False):
1365
b = WorkingTree.open_containing(u'.')[0].branch
1366
config = bzrlib.config.BranchConfig(b)
1367
except NotBranchError:
1368
config = bzrlib.config.GlobalConfig()
1371
print config.user_email()
1373
print config.username()
1375
class cmd_nick(Command):
1377
Print or set the branch nickname.
1378
If unset, the tree root directory name is used as the nickname
1379
To print the current nickname, execute with no argument.
1381
takes_args = ['nickname?']
1382
def run(self, nickname=None):
1383
branch = Branch.open_containing(u'.')[0]
1384
if nickname is None:
1385
self.printme(branch)
1387
branch.nick = nickname
1390
def printme(self, branch):
1393
class cmd_selftest(Command):
1394
"""Run internal test suite.
1396
This creates temporary test directories in the working directory,
1397
but not existing data is affected. These directories are deleted
1398
if the tests pass, or left behind to help in debugging if they
1399
fail and --keep-output is specified.
1401
If arguments are given, they are regular expressions that say
1402
which tests should run.
1404
# TODO: --list should give a list of all available tests
1406
takes_args = ['testspecs*']
1407
takes_options = ['verbose',
1408
Option('one', help='stop when one test fails'),
1409
Option('keep-output',
1410
help='keep output directories when tests fail')
1413
def run(self, testspecs_list=None, verbose=False, one=False,
1416
from bzrlib.tests import selftest
1417
# we don't want progress meters from the tests to go to the
1418
# real output; and we don't want log messages cluttering up
1420
save_ui = bzrlib.ui.ui_factory
1421
bzrlib.trace.info('running tests...')
1423
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1424
if testspecs_list is not None:
1425
pattern = '|'.join(testspecs_list)
1428
result = selftest(verbose=verbose,
1430
stop_on_failure=one,
1431
keep_output=keep_output)
1433
bzrlib.trace.info('tests passed')
1435
bzrlib.trace.info('tests failed')
1436
return int(not result)
1438
bzrlib.ui.ui_factory = save_ui
1442
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1443
# is bzrlib itself in a branch?
1444
bzrrev = bzrlib.get_bzr_revision()
1446
print " (bzr checkout, revision %d {%s})" % bzrrev
1447
print bzrlib.__copyright__
1448
print "http://bazaar-ng.org/"
1450
print "bzr comes with ABSOLUTELY NO WARRANTY. bzr is free software, and"
1451
print "you may use, modify and redistribute it under the terms of the GNU"
1452
print "General Public License version 2 or later."
1455
class cmd_version(Command):
1456
"""Show version of bzr."""
1461
class cmd_rocks(Command):
1462
"""Statement of optimism."""
1466
print "it sure does!"
1469
class cmd_find_merge_base(Command):
1470
"""Find and print a base revision for merging two branches.
1472
# TODO: Options to specify revisions on either side, as if
1473
# merging only part of the history.
1474
takes_args = ['branch', 'other']
1478
def run(self, branch, other):
1479
from bzrlib.revision import common_ancestor, MultipleRevisionSources
1481
branch1 = Branch.open_containing(branch)[0]
1482
branch2 = Branch.open_containing(other)[0]
1484
history_1 = branch1.revision_history()
1485
history_2 = branch2.revision_history()
1487
last1 = branch1.last_revision()
1488
last2 = branch2.last_revision()
1490
source = MultipleRevisionSources(branch1, branch2)
1492
base_rev_id = common_ancestor(last1, last2, source)
1494
print 'merge base is revision %s' % base_rev_id
1498
if base_revno is None:
1499
raise bzrlib.errors.UnrelatedBranches()
1501
print ' r%-6d in %s' % (base_revno, branch)
1503
other_revno = branch2.revision_id_to_revno(base_revid)
1505
print ' r%-6d in %s' % (other_revno, other)
1509
class cmd_merge(Command):
1510
"""Perform a three-way merge.
1512
The branch is the branch you will merge from. By default, it will
1513
merge the latest revision. If you specify a revision, that
1514
revision will be merged. If you specify two revisions, the first
1515
will be used as a BASE, and the second one as OTHER. Revision
1516
numbers are always relative to the specified branch.
1518
By default bzr will try to merge in all new work from the other
1519
branch, automatically determining an appropriate base. If this
1520
fails, you may need to give an explicit base.
1524
To merge the latest revision from bzr.dev
1525
bzr merge ../bzr.dev
1527
To merge changes up to and including revision 82 from bzr.dev
1528
bzr merge -r 82 ../bzr.dev
1530
To merge the changes introduced by 82, without previous changes:
1531
bzr merge -r 81..82 ../bzr.dev
1533
merge refuses to run if there are any uncommitted changes, unless
1536
takes_args = ['branch?']
1537
takes_options = ['revision', 'force', 'merge-type', 'reprocess',
1538
Option('show-base', help="Show base revision text in "
1541
def run(self, branch=None, revision=None, force=False, merge_type=None,
1542
show_base=False, reprocess=False):
1543
from bzrlib.merge import merge
1544
from bzrlib.merge_core import ApplyMerge3
1545
if merge_type is None:
1546
merge_type = ApplyMerge3
1548
branch = WorkingTree.open_containing(u'.')[0].branch.get_parent()
1550
raise BzrCommandError("No merge location known or specified.")
1552
print "Using saved location: %s" % branch
1553
if revision is None or len(revision) < 1:
1555
other = [branch, -1]
1557
if len(revision) == 1:
1559
other_branch = Branch.open_containing(branch)[0]
1560
revno = revision[0].in_history(other_branch).revno
1561
other = [branch, revno]
1563
assert len(revision) == 2
1564
if None in revision:
1565
raise BzrCommandError(
1566
"Merge doesn't permit that revision specifier.")
1567
b = Branch.open_containing(branch)[0]
1569
base = [branch, revision[0].in_history(b).revno]
1570
other = [branch, revision[1].in_history(b).revno]
1573
conflict_count = merge(other, base, check_clean=(not force),
1574
merge_type=merge_type, reprocess=reprocess,
1575
show_base=show_base)
1576
if conflict_count != 0:
1580
except bzrlib.errors.AmbiguousBase, e:
1581
m = ("sorry, bzr can't determine the right merge base yet\n"
1582
"candidates are:\n "
1583
+ "\n ".join(e.bases)
1585
"please specify an explicit base with -r,\n"
1586
"and (if you want) report this to the bzr developers\n")
1590
class cmd_remerge(Command):
1593
takes_args = ['file*']
1594
takes_options = ['merge-type', 'reprocess',
1595
Option('show-base', help="Show base revision text in "
1598
def run(self, file_list=None, merge_type=None, show_base=False,
1600
from bzrlib.merge import merge_inner, transform_tree
1601
from bzrlib.merge_core import ApplyMerge3
1602
if merge_type is None:
1603
merge_type = ApplyMerge3
1604
tree, file_list = tree_files(file_list)
1607
pending_merges = tree.pending_merges()
1608
if len(pending_merges) != 1:
1609
raise BzrCommandError("Sorry, remerge only works after normal"
1610
+ " merges. Not cherrypicking or"
1612
base_revision = common_ancestor(tree.branch.last_revision(),
1613
pending_merges[0], tree.branch)
1614
base_tree = tree.branch.revision_tree(base_revision)
1615
other_tree = tree.branch.revision_tree(pending_merges[0])
1616
interesting_ids = None
1617
if file_list is not None:
1618
interesting_ids = set()
1619
for filename in file_list:
1620
file_id = tree.path2id(filename)
1621
interesting_ids.add(file_id)
1622
if tree.kind(file_id) != "directory":
1625
for name, ie in tree.inventory.iter_entries(file_id):
1626
interesting_ids.add(ie.file_id)
1627
transform_tree(tree, tree.branch.basis_tree(), interesting_ids)
1628
if file_list is None:
1629
restore_files = list(tree.iter_conflicts())
1631
restore_files = file_list
1632
for filename in restore_files:
1634
restore(tree.abspath(filename))
1635
except NotConflicted:
1637
conflicts = merge_inner(tree.branch, other_tree, base_tree,
1638
interesting_ids = interesting_ids,
1639
other_rev_id=pending_merges[0],
1640
merge_type=merge_type,
1641
show_base=show_base,
1642
reprocess=reprocess)
1650
class cmd_revert(Command):
1651
"""Reverse all changes since the last commit.
1653
Only versioned files are affected. Specify filenames to revert only
1654
those files. By default, any files that are changed will be backed up
1655
first. Backup files have a '~' appended to their name.
1657
takes_options = ['revision', 'no-backup']
1658
takes_args = ['file*']
1659
aliases = ['merge-revert']
1661
def run(self, revision=None, no_backup=False, file_list=None):
1662
from bzrlib.merge import merge_inner
1663
from bzrlib.commands import parse_spec
1664
if file_list is not None:
1665
if len(file_list) == 0:
1666
raise BzrCommandError("No files specified")
1669
if revision is None:
1671
tree = WorkingTree.open_containing(u'.')[0]
1672
# FIXME should be tree.last_revision
1673
rev_id = tree.branch.last_revision()
1674
elif len(revision) != 1:
1675
raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1677
tree, file_list = tree_files(file_list)
1678
rev_id = revision[0].in_history(tree.branch).rev_id
1679
tree.revert(file_list, tree.branch.revision_tree(rev_id),
1683
class cmd_assert_fail(Command):
1684
"""Test reporting of assertion failures"""
1687
assert False, "always fails"
1690
class cmd_help(Command):
1691
"""Show help on a command or other topic.
1693
For a list of all available commands, say 'bzr help commands'."""
1694
takes_options = ['long']
1695
takes_args = ['topic?']
1699
def run(self, topic=None, long=False):
1701
if topic is None and long:
1706
class cmd_shell_complete(Command):
1707
"""Show appropriate completions for context.
1709
For a list of all available commands, say 'bzr shell-complete'."""
1710
takes_args = ['context?']
1715
def run(self, context=None):
1716
import shellcomplete
1717
shellcomplete.shellcomplete(context)
1720
class cmd_fetch(Command):
1721
"""Copy in history from another branch but don't merge it.
1723
This is an internal method used for pull and merge."""
1725
takes_args = ['from_branch', 'to_branch']
1726
def run(self, from_branch, to_branch):
1727
from bzrlib.fetch import Fetcher
1728
from bzrlib.branch import Branch
1729
from_b = Branch.open(from_branch)
1730
to_b = Branch.open(to_branch)
1735
Fetcher(to_b, from_b)
1742
class cmd_missing(Command):
1743
"""Show unmerged/unpulled revisions between two branches.
1745
OTHER_BRANCH may be local or remote."""
1746
takes_args = ['other_branch?']
1747
takes_options = [Option('reverse', 'Reverse the order of revisions'),
1749
'Display changes in the local branch only'),
1750
Option('theirs-only',
1751
'Display changes in the remote branch only'),
1759
def run(self, other_branch=None, reverse=False, mine_only=False,
1760
theirs_only=False, long=True, short=False, line=False,
1761
show_ids=False, verbose=False):
1762
from bzrlib.missing import find_unmerged, iter_log_data
1763
from bzrlib.log import log_formatter
1764
local_branch = bzrlib.branch.Branch.open_containing(u".")[0]
1765
parent = local_branch.get_parent()
1766
if other_branch is None:
1767
other_branch = parent
1768
if other_branch is None:
1769
raise BzrCommandError("No missing location known or specified.")
1770
print "Using last location: " + local_branch.get_parent()
1771
remote_branch = bzrlib.branch.Branch.open(other_branch)
1772
local_extra, remote_extra = find_unmerged(local_branch, remote_branch)
1773
log_format = get_log_format(long=long, short=short, line=line)
1774
lf = log_formatter(log_format, sys.stdout,
1776
show_timezone='original')
1777
if reverse is False:
1778
local_extra.reverse()
1779
remote_extra.reverse()
1780
if local_extra and not theirs_only:
1781
print "You have %d extra revision(s):" % len(local_extra)
1782
for data in iter_log_data(local_extra, local_branch, verbose):
1784
printed_local = True
1786
printed_local = False
1787
if remote_extra and not mine_only:
1788
if printed_local is True:
1790
print "You are missing %d revision(s):" % len(remote_extra)
1791
for data in iter_log_data(remote_extra, remote_branch, verbose):
1793
if not remote_extra and not local_extra:
1795
print "Branches are up to date."
1798
if parent is None and other_branch is not None:
1799
local_branch.set_parent(other_branch)
1803
class cmd_plugins(Command):
1808
import bzrlib.plugin
1809
from inspect import getdoc
1810
for name, plugin in bzrlib.plugin.all_plugins().items():
1811
if hasattr(plugin, '__path__'):
1812
print plugin.__path__[0]
1813
elif hasattr(plugin, '__file__'):
1814
print plugin.__file__
1820
print '\t', d.split('\n')[0]
1823
class cmd_testament(Command):
1824
"""Show testament (signing-form) of a revision."""
1825
takes_options = ['revision', 'long']
1826
takes_args = ['branch?']
1828
def run(self, branch=u'.', revision=None, long=False):
1829
from bzrlib.testament import Testament
1830
b = WorkingTree.open_containing(branch)[0].branch
1833
if revision is None:
1834
rev_id = b.last_revision()
1836
rev_id = revision[0].in_history(b).rev_id
1837
t = Testament.from_revision(b, rev_id)
1839
sys.stdout.writelines(t.as_text_lines())
1841
sys.stdout.write(t.as_short_text())
1846
class cmd_annotate(Command):
1847
"""Show the origin of each line in a file.
1849
This prints out the given file with an annotation on the left side
1850
indicating which revision, author and date introduced the change.
1852
If the origin is the same for a run of consecutive lines, it is
1853
shown only at the top, unless the --all option is given.
1855
# TODO: annotate directories; showing when each file was last changed
1856
# TODO: annotate a previous version of a file
1857
# TODO: if the working copy is modified, show annotations on that
1858
# with new uncommitted lines marked
1859
aliases = ['blame', 'praise']
1860
takes_args = ['filename']
1861
takes_options = [Option('all', help='show annotations on all lines'),
1862
Option('long', help='show date in annotations'),
1866
def run(self, filename, all=False, long=False):
1867
from bzrlib.annotate import annotate_file
1868
tree, relpath = WorkingTree.open_containing(filename)
1869
branch = tree.branch
1872
file_id = tree.inventory.path2id(relpath)
1873
tree = branch.revision_tree(branch.last_revision())
1874
file_version = tree.inventory[file_id].revision
1875
annotate_file(branch, file_version, file_id, long, all, sys.stdout)
1880
class cmd_re_sign(Command):
1881
"""Create a digital signature for an existing revision."""
1882
# TODO be able to replace existing ones.
1884
hidden = True # is this right ?
1885
takes_args = ['revision_id?']
1886
takes_options = ['revision']
1888
def run(self, revision_id=None, revision=None):
1889
import bzrlib.config as config
1890
import bzrlib.gpg as gpg
1891
if revision_id is not None and revision is not None:
1892
raise BzrCommandError('You can only supply one of revision_id or --revision')
1893
if revision_id is None and revision is None:
1894
raise BzrCommandError('You must supply either --revision or a revision_id')
1895
b = WorkingTree.open_containing(u'.')[0].branch
1896
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1897
if revision_id is not None:
1898
b.sign_revision(revision_id, gpg_strategy)
1899
elif revision is not None:
1900
if len(revision) == 1:
1901
revno, rev_id = revision[0].in_history(b)
1902
b.sign_revision(rev_id, gpg_strategy)
1903
elif len(revision) == 2:
1904
# are they both on rh- if so we can walk between them
1905
# might be nice to have a range helper for arbitrary
1906
# revision paths. hmm.
1907
from_revno, from_revid = revision[0].in_history(b)
1908
to_revno, to_revid = revision[1].in_history(b)
1909
if to_revid is None:
1910
to_revno = b.revno()
1911
if from_revno is None or to_revno is None:
1912
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1913
for revno in range(from_revno, to_revno + 1):
1914
b.sign_revision(b.get_rev_id(revno), gpg_strategy)
1916
raise BzrCommandError('Please supply either one revision, or a range.')
1919
class cmd_uncommit(bzrlib.commands.Command):
1920
"""Remove the last committed revision.
1922
By supplying the --all flag, it will not only remove the entry
1923
from revision_history, but also remove all of the entries in the
1926
--verbose will print out what is being removed.
1927
--dry-run will go through all the motions, but not actually
1930
In the future, uncommit will create a changeset, which can then
1933
takes_options = ['all', 'verbose', 'revision',
1934
Option('dry-run', help='Don\'t actually make changes'),
1935
Option('force', help='Say yes to all questions.')]
1936
takes_args = ['location?']
1939
def run(self, location=None, all=False,
1940
dry_run=False, verbose=False,
1941
revision=None, force=False):
1942
from bzrlib.branch import Branch
1943
from bzrlib.log import log_formatter
1945
from bzrlib.uncommit import uncommit
1947
if location is None:
1949
b, relpath = Branch.open_containing(location)
1951
if revision is None:
1953
rev_id = b.last_revision()
1955
revno, rev_id = revision[0].in_history(b)
1957
print 'No revisions to uncommit.'
1959
for r in range(revno, b.revno()+1):
1960
rev_id = b.get_rev_id(r)
1961
lf = log_formatter('short', to_file=sys.stdout,show_timezone='original')
1962
lf.show(r, b.get_revision(rev_id), None)
1965
print 'Dry-run, pretending to remove the above revisions.'
1967
val = raw_input('Press <enter> to continue')
1969
print 'The above revision(s) will be removed.'
1971
val = raw_input('Are you sure [y/N]? ')
1972
if val.lower() not in ('y', 'yes'):
1976
uncommit(b, remove_files=all,
1977
dry_run=dry_run, verbose=verbose,
1981
# these get imported and then picked up by the scan for cmd_*
1982
# TODO: Some more consistent way to split command definitions across files;
1983
# we do need to load at least some information about them to know of
1985
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore