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
41
def tree_files(file_list, default_branch=u'.'):
43
return internal_tree_files(file_list, default_branch)
44
except FileInWrongBranch, e:
45
raise BzrCommandError("%s is not in the same branch as %s" %
46
(e.path, file_list[0]))
48
def internal_tree_files(file_list, default_branch=u'.'):
50
Return a branch and list of branch-relative paths.
51
If supplied file_list is empty or None, the branch default will be used,
52
and returned file_list will match the original.
54
if file_list is None or len(file_list) == 0:
55
return WorkingTree.open_containing(default_branch)[0], file_list
56
tree = WorkingTree.open_containing(file_list[0])[0]
58
for filename in file_list:
60
new_list.append(tree.relpath(filename))
61
except errors.PathNotChild:
62
raise FileInWrongBranch(tree.branch, filename)
66
# TODO: Make sure no commands unconditionally use the working directory as a
67
# branch. If a filename argument is used, the first of them should be used to
68
# specify the branch. (Perhaps this can be factored out into some kind of
69
# Argument class, representing a file in a branch, where the first occurrence
72
class cmd_status(Command):
73
"""Display status summary.
75
This reports on versioned and unknown files, reporting them
76
grouped by state. Possible states are:
79
Versioned in the working copy but not in the previous revision.
82
Versioned in the previous revision but removed or deleted
86
Path of this file changed from the previous revision;
87
the text may also have changed. This includes files whose
88
parent directory was renamed.
91
Text has changed since the previous revision.
94
Nothing about this file has changed since the previous revision.
95
Only shown with --all.
98
Not versioned and not matching an ignore pattern.
100
To see ignored files use 'bzr ignored'. For details in the
101
changes to file texts, use 'bzr diff'.
103
If no arguments are specified, the status of the entire working
104
directory is shown. Otherwise, only the status of the specified
105
files or directories is reported. If a directory is given, status
106
is reported for everything inside that directory.
108
If a revision argument is given, the status is calculated against
109
that revision, or between two revisions if two are provided.
112
# TODO: --no-recurse, --recurse options
114
takes_args = ['file*']
115
takes_options = ['all', 'show-ids', 'revision']
116
aliases = ['st', 'stat']
119
def run(self, all=False, show_ids=False, file_list=None, revision=None):
120
tree, file_list = tree_files(file_list)
122
from bzrlib.status import show_status
123
show_status(tree.branch, show_unchanged=all, show_ids=show_ids,
124
specific_files=file_list, revision=revision)
127
class cmd_cat_revision(Command):
128
"""Write out metadata for a revision.
130
The revision to print can either be specified by a specific
131
revision identifier, or you can use --revision.
135
takes_args = ['revision_id?']
136
takes_options = ['revision']
139
def run(self, revision_id=None, revision=None):
141
if revision_id is not None and revision is not None:
142
raise BzrCommandError('You can only supply one of revision_id or --revision')
143
if revision_id is None and revision is None:
144
raise BzrCommandError('You must supply either --revision or a revision_id')
145
b = WorkingTree.open_containing(u'.')[0].branch
146
if revision_id is not None:
147
sys.stdout.write(b.get_revision_xml(revision_id))
148
elif revision is not None:
151
raise BzrCommandError('You cannot specify a NULL revision.')
152
revno, rev_id = rev.in_history(b)
153
sys.stdout.write(b.get_revision_xml(rev_id))
156
class cmd_revno(Command):
157
"""Show current revision number.
159
This is equal to the number of revisions on this branch."""
160
takes_args = ['location?']
162
def run(self, location=u'.'):
163
print Branch.open_containing(location)[0].revno()
166
class cmd_revision_info(Command):
167
"""Show revision number and revision id for a given revision identifier.
170
takes_args = ['revision_info*']
171
takes_options = ['revision']
173
def run(self, revision=None, revision_info_list=[]):
176
if revision is not None:
177
revs.extend(revision)
178
if revision_info_list is not None:
179
for rev in revision_info_list:
180
revs.append(RevisionSpec(rev))
182
raise BzrCommandError('You must supply a revision identifier')
184
b = WorkingTree.open_containing(u'.')[0].branch
187
revinfo = rev.in_history(b)
188
if revinfo.revno is None:
189
print ' %s' % revinfo.rev_id
191
print '%4d %s' % (revinfo.revno, revinfo.rev_id)
194
class cmd_add(Command):
195
"""Add specified files or directories.
197
In non-recursive mode, all the named items are added, regardless
198
of whether they were previously ignored. A warning is given if
199
any of the named files are already versioned.
201
In recursive mode (the default), files are treated the same way
202
but the behaviour for directories is different. Directories that
203
are already versioned do not give a warning. All directories,
204
whether already versioned or not, are searched for files or
205
subdirectories that are neither versioned or ignored, and these
206
are added. This search proceeds recursively into versioned
207
directories. If no names are given '.' is assumed.
209
Therefore simply saying 'bzr add' will version all files that
210
are currently unknown.
212
Adding a file whose parent directory is not versioned will
213
implicitly add the parent, and so on up to the root. This means
214
you should never need to explictly add a directory, they'll just
215
get added when you add a file in the directory.
217
takes_args = ['file*']
218
takes_options = ['no-recurse']
220
def run(self, file_list, no_recurse=False):
221
from bzrlib.add import smart_add, add_reporter_print, add_reporter_null
223
reporter = add_reporter_null
225
reporter = add_reporter_print
226
smart_add(file_list, not no_recurse, reporter)
229
class cmd_mkdir(Command):
230
"""Create a new versioned directory.
232
This is equivalent to creating the directory and then adding it.
234
takes_args = ['dir+']
236
def run(self, dir_list):
239
wt, dd = WorkingTree.open_containing(d)
244
class cmd_relpath(Command):
245
"""Show path of a file relative to root"""
246
takes_args = ['filename']
250
def run(self, filename):
251
tree, relpath = WorkingTree.open_containing(filename)
255
class cmd_inventory(Command):
256
"""Show inventory of the current working copy or a revision.
258
It is possible to limit the output to a particular entry
259
type using the --kind option. For example; --kind file.
261
takes_options = ['revision', 'show-ids', 'kind']
264
def run(self, revision=None, show_ids=False, kind=None):
265
if kind and kind not in ['file', 'directory', 'symlink']:
266
raise BzrCommandError('invalid kind specified')
267
tree = WorkingTree.open_containing(u'.')[0]
269
inv = tree.read_working_inventory()
271
if len(revision) > 1:
272
raise BzrCommandError('bzr inventory --revision takes'
273
' exactly one revision identifier')
274
inv = tree.branch.get_revision_inventory(
275
revision[0].in_history(tree.branch).rev_id)
277
for path, entry in inv.entries():
278
if kind and kind != entry.kind:
281
print '%-50s %s' % (path, entry.file_id)
286
class cmd_move(Command):
287
"""Move files to a different directory.
292
The destination must be a versioned directory in the same branch.
294
takes_args = ['source$', 'dest']
295
def run(self, source_list, dest):
296
tree, source_list = tree_files(source_list)
297
# TODO: glob expansion on windows?
298
tree.move(source_list, tree.relpath(dest))
301
class cmd_rename(Command):
302
"""Change the name of an entry.
305
bzr rename frob.c frobber.c
306
bzr rename src/frob.c lib/frob.c
308
It is an error if the destination name exists.
310
See also the 'move' command, which moves files into a different
311
directory without changing their name.
313
# TODO: Some way to rename multiple files without invoking
314
# bzr for each one?"""
315
takes_args = ['from_name', 'to_name']
317
def run(self, from_name, to_name):
318
tree, (from_name, to_name) = tree_files((from_name, to_name))
319
tree.rename_one(from_name, to_name)
322
class cmd_mv(Command):
323
"""Move or rename a file.
326
bzr mv OLDNAME NEWNAME
327
bzr mv SOURCE... DESTINATION
329
If the last argument is a versioned directory, all the other names
330
are moved into it. Otherwise, there must be exactly two arguments
331
and the file is changed to a new name, which must not already exist.
333
Files cannot be moved between branches.
335
takes_args = ['names*']
336
def run(self, names_list):
337
if len(names_list) < 2:
338
raise BzrCommandError("missing file argument")
339
tree, rel_names = tree_files(names_list)
341
if os.path.isdir(names_list[-1]):
342
# move into existing directory
343
for pair in tree.move(rel_names[:-1], rel_names[-1]):
344
print "%s => %s" % pair
346
if len(names_list) != 2:
347
raise BzrCommandError('to mv multiple files the destination '
348
'must be a versioned directory')
349
tree.rename_one(rel_names[0], rel_names[1])
350
print "%s => %s" % (rel_names[0], rel_names[1])
353
class cmd_pull(Command):
354
"""Pull any changes from another branch into the current one.
356
If there is no default location set, the first pull will set it. After
357
that, you can omit the location to use the default. To change the
358
default, use --remember.
360
This command only works on branches that have not diverged. Branches are
361
considered diverged if both branches have had commits without first
362
pulling from the other.
364
If branches have diverged, you can use 'bzr merge' to pull the text changes
365
from one into the other. Once one branch has merged, the other should
366
be able to pull it again.
368
If you want to forget your local changes and just update your branch to
369
match the remote one, use --overwrite.
371
takes_options = ['remember', 'overwrite', 'verbose']
372
takes_args = ['location?']
374
def run(self, location=None, remember=False, overwrite=False, verbose=False):
375
from bzrlib.merge import merge
376
from shutil import rmtree
378
# FIXME: too much stuff is in the command class
379
tree_to = WorkingTree.open_containing(u'.')[0]
380
stored_loc = tree_to.branch.get_parent()
382
if stored_loc is None:
383
raise BzrCommandError("No pull location known or specified.")
385
print "Using saved location: %s" % stored_loc
386
location = stored_loc
387
br_from = Branch.open(location)
388
br_to = tree_to.branch
390
old_rh = br_to.revision_history()
391
count = tree_to.pull(br_from, overwrite)
392
except DivergedBranches:
393
# FIXME: Just make DivergedBranches display the right message
395
raise BzrCommandError("These branches have diverged."
397
if br_to.get_parent() is None or remember:
398
br_to.set_parent(location)
399
note('%d revision(s) pulled.' % (count,))
402
new_rh = tree_to.branch.revision_history()
405
from bzrlib.log import show_changed_revisions
406
show_changed_revisions(tree_to.branch, old_rh, new_rh)
409
class cmd_push(Command):
410
"""Push this branch into another branch.
412
The remote branch will not have its working tree populated because this
413
is both expensive, and may not be supported on the remote file system.
415
Some smart servers or protocols *may* put the working tree in place.
417
If there is no default push location set, the first push will set it.
418
After that, you can omit the location to use the default. To change the
419
default, use --remember.
421
This command only works on branches that have not diverged. Branches are
422
considered diverged if the branch being pushed to is not an older version
425
If branches have diverged, you can use 'bzr push --overwrite' to replace
426
the other branch completely.
428
If you want to ensure you have the different changes in the other branch,
429
do a merge (see bzr help merge) from the other branch, and commit that
430
before doing a 'push --overwrite'.
432
takes_options = ['remember', 'overwrite',
433
Option('create-prefix',
434
help='Create the path leading up to the branch '
435
'if it does not already exist')]
436
takes_args = ['location?']
438
def run(self, location=None, remember=False, overwrite=False,
439
create_prefix=False, verbose=False):
440
# FIXME: Way too big! Put this into a function called from the
443
from shutil import rmtree
444
from bzrlib.transport import get_transport
446
tree_from = WorkingTree.open_containing(u'.')[0]
447
br_from = tree_from.branch
448
stored_loc = tree_from.branch.get_push_location()
450
if stored_loc is None:
451
raise BzrCommandError("No push location known or specified.")
453
print "Using saved location: %s" % stored_loc
454
location = stored_loc
456
br_to = Branch.open(location)
457
except NotBranchError:
459
transport = get_transport(location).clone('..')
460
if not create_prefix:
462
transport.mkdir(transport.relpath(location))
464
raise BzrCommandError("Parent directory of %s "
465
"does not exist." % location)
467
current = transport.base
468
needed = [(transport, transport.relpath(location))]
471
transport, relpath = needed[-1]
472
transport.mkdir(relpath)
475
new_transport = transport.clone('..')
476
needed.append((new_transport,
477
new_transport.relpath(transport.base)))
478
if new_transport.base == transport.base:
479
raise BzrCommandError("Could not creeate "
481
br_to = Branch.initialize(location)
482
old_rh = br_to.revision_history()
485
tree_to = br_to.working_tree()
486
except NoWorkingTree:
487
# TODO: This should be updated for branches which don't have a
488
# working tree, as opposed to ones where we just couldn't
490
warning('Unable to update the working tree of: %s' % (br_to.base,))
491
count = br_to.pull(br_from, overwrite)
493
count = tree_to.pull(br_from, overwrite)
494
except DivergedBranches:
495
raise BzrCommandError("These branches have diverged."
496
" Try a merge then push with overwrite.")
497
if br_from.get_push_location() is None or remember:
498
br_from.set_push_location(location)
499
note('%d revision(s) pushed.' % (count,))
502
new_rh = br_to.revision_history()
505
from bzrlib.log import show_changed_revisions
506
show_changed_revisions(br_to, old_rh, new_rh)
509
class cmd_branch(Command):
510
"""Create a new copy of a branch.
512
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
513
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
515
To retrieve the branch as of a particular revision, supply the --revision
516
parameter, as in "branch foo/bar -r 5".
518
--basis is to speed up branching from remote branches. When specified, it
519
copies all the file-contents, inventory and revision data from the basis
520
branch before copying anything from the remote branch.
522
takes_args = ['from_location', 'to_location?']
523
takes_options = ['revision', 'basis']
524
aliases = ['get', 'clone']
526
def run(self, from_location, to_location=None, revision=None, basis=None):
527
from bzrlib.clone import copy_branch
529
from shutil import rmtree
532
elif len(revision) > 1:
533
raise BzrCommandError(
534
'bzr branch --revision takes exactly 1 revision value')
536
br_from = Branch.open(from_location)
538
if e.errno == errno.ENOENT:
539
raise BzrCommandError('Source location "%s" does not'
540
' exist.' % to_location)
545
if basis is not None:
546
basis_branch = WorkingTree.open_containing(basis)[0].branch
549
if len(revision) == 1 and revision[0] is not None:
550
revision_id = revision[0].in_history(br_from)[1]
553
if to_location is None:
554
to_location = os.path.basename(from_location.rstrip("/\\"))
557
name = os.path.basename(to_location) + '\n'
559
os.mkdir(to_location)
561
if e.errno == errno.EEXIST:
562
raise BzrCommandError('Target directory "%s" already'
563
' exists.' % to_location)
564
if e.errno == errno.ENOENT:
565
raise BzrCommandError('Parent of "%s" does not exist.' %
570
copy_branch(br_from, to_location, revision_id, basis_branch)
571
except bzrlib.errors.NoSuchRevision:
573
msg = "The branch %s has no revision %s." % (from_location, revision[0])
574
raise BzrCommandError(msg)
575
except bzrlib.errors.UnlistableBranch:
577
msg = "The branch %s cannot be used as a --basis"
578
raise BzrCommandError(msg)
579
branch = Branch.open(to_location)
581
name = StringIO(name)
582
branch.put_controlfile('branch-name', name)
583
note('Branched %d revision(s).' % branch.revno())
588
class cmd_renames(Command):
589
"""Show list of renamed files.
591
# TODO: Option to show renames between two historical versions.
593
# TODO: Only show renames under dir, rather than in the whole branch.
594
takes_args = ['dir?']
597
def run(self, dir=u'.'):
598
tree = WorkingTree.open_containing(dir)[0]
599
old_inv = tree.branch.basis_tree().inventory
600
new_inv = tree.read_working_inventory()
602
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
604
for old_name, new_name in renames:
605
print "%s => %s" % (old_name, new_name)
608
class cmd_info(Command):
609
"""Show statistical information about a branch."""
610
takes_args = ['branch?']
613
def run(self, branch=None):
615
b = WorkingTree.open_containing(branch)[0].branch
619
class cmd_remove(Command):
620
"""Make a file unversioned.
622
This makes bzr stop tracking changes to a versioned file. It does
623
not delete the working copy.
625
takes_args = ['file+']
626
takes_options = ['verbose']
629
def run(self, file_list, verbose=False):
630
tree, file_list = tree_files(file_list)
631
tree.remove(file_list, verbose=verbose)
634
class cmd_file_id(Command):
635
"""Print file_id of a particular file or directory.
637
The file_id is assigned when the file is first added and remains the
638
same through all revisions where the file exists, even when it is
642
takes_args = ['filename']
644
def run(self, filename):
645
tree, relpath = WorkingTree.open_containing(filename)
646
i = tree.inventory.path2id(relpath)
648
raise BzrError("%r is not a versioned file" % filename)
653
class cmd_file_path(Command):
654
"""Print path of file_ids to a file or directory.
656
This prints one line for each directory down to the target,
657
starting at the branch root."""
659
takes_args = ['filename']
661
def run(self, filename):
662
tree, relpath = WorkingTree.open_containing(filename)
664
fid = inv.path2id(relpath)
666
raise BzrError("%r is not a versioned file" % filename)
667
for fip in inv.get_idpath(fid):
671
class cmd_revision_history(Command):
672
"""Display list of revision ids on this branch."""
676
branch = WorkingTree.open_containing(u'.')[0].branch
677
for patchid in branch.revision_history():
681
class cmd_ancestry(Command):
682
"""List all revisions merged into this branch."""
686
tree = WorkingTree.open_containing(u'.')[0]
688
# FIXME. should be tree.last_revision
689
for revision_id in b.get_ancestry(b.last_revision()):
693
class cmd_init(Command):
694
"""Make a directory into a versioned branch.
696
Use this to create an empty branch, or before importing an
699
Recipe for importing a tree of files:
704
bzr commit -m 'imported project'
706
takes_args = ['location?']
707
def run(self, location=None):
708
from bzrlib.branch import Branch
712
# The path has to exist to initialize a
713
# branch inside of it.
714
# Just using os.mkdir, since I don't
715
# believe that we want to create a bunch of
716
# locations if the user supplies an extended path
717
if not os.path.exists(location):
719
Branch.initialize(location)
722
class cmd_diff(Command):
723
"""Show differences in working tree.
725
If files are listed, only the changes in those files are listed.
726
Otherwise, all changes for the tree are listed.
733
# TODO: Allow diff across branches.
734
# TODO: Option to use external diff command; could be GNU diff, wdiff,
735
# or a graphical diff.
737
# TODO: Python difflib is not exactly the same as unidiff; should
738
# either fix it up or prefer to use an external diff.
740
# TODO: If a directory is given, diff everything under that.
742
# TODO: Selected-file diff is inefficient and doesn't show you
745
# TODO: This probably handles non-Unix newlines poorly.
747
takes_args = ['file*']
748
takes_options = ['revision', 'diff-options']
749
aliases = ['di', 'dif']
752
def run(self, revision=None, file_list=None, diff_options=None):
753
from bzrlib.diff import show_diff
755
tree, file_list = internal_tree_files(file_list)
758
except FileInWrongBranch:
759
if len(file_list) != 2:
760
raise BzrCommandError("Files are in different branches")
762
b, file1 = Branch.open_containing(file_list[0])
763
b2, file2 = Branch.open_containing(file_list[1])
764
if file1 != "" or file2 != "":
765
# FIXME diff those two files. rbc 20051123
766
raise BzrCommandError("Files are in different branches")
768
if revision is not None:
770
raise BzrCommandError("Can't specify -r with two branches")
771
if len(revision) == 1:
772
return show_diff(tree.branch, revision[0], specific_files=file_list,
773
external_diff_options=diff_options)
774
elif len(revision) == 2:
775
return show_diff(tree.branch, revision[0], specific_files=file_list,
776
external_diff_options=diff_options,
777
revision2=revision[1])
779
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
782
return show_diff(b, None, specific_files=file_list,
783
external_diff_options=diff_options, b2=b2)
785
return show_diff(tree.branch, None, specific_files=file_list,
786
external_diff_options=diff_options)
789
class cmd_deleted(Command):
790
"""List files deleted in the working tree.
792
# TODO: Show files deleted since a previous revision, or
793
# between two revisions.
794
# TODO: Much more efficient way to do this: read in new
795
# directories with readdir, rather than stating each one. Same
796
# level of effort but possibly much less IO. (Or possibly not,
797
# if the directories are very large...)
799
def run(self, show_ids=False):
800
tree = WorkingTree.open_containing(u'.')[0]
801
old = tree.branch.basis_tree()
802
for path, ie in old.inventory.iter_entries():
803
if not tree.has_id(ie.file_id):
805
print '%-50s %s' % (path, ie.file_id)
810
class cmd_modified(Command):
811
"""List files modified in working tree."""
815
from bzrlib.delta import compare_trees
817
tree = WorkingTree.open_containing(u'.')[0]
818
td = compare_trees(tree.branch.basis_tree(), tree)
820
for path, id, kind, text_modified, meta_modified in td.modified:
825
class cmd_added(Command):
826
"""List files added in working tree."""
830
wt = WorkingTree.open_containing(u'.')[0]
831
basis_inv = wt.branch.basis_tree().inventory
834
if file_id in basis_inv:
836
path = inv.id2path(file_id)
837
if not os.access(b.abspath(path), os.F_OK):
843
class cmd_root(Command):
844
"""Show the tree root directory.
846
The root is the nearest enclosing directory with a .bzr control
848
takes_args = ['filename?']
850
def run(self, filename=None):
851
"""Print the branch root."""
852
tree = WorkingTree.open_containing(filename)[0]
856
class cmd_log(Command):
857
"""Show log of this branch.
859
To request a range of logs, you can use the command -r begin..end
860
-r revision requests a specific revision, -r ..end or -r begin.. are
864
# TODO: Make --revision support uuid: and hash: [future tag:] notation.
866
takes_args = ['filename?']
867
takes_options = [Option('forward',
868
help='show from oldest to newest'),
869
'timezone', 'verbose',
870
'show-ids', 'revision',
871
Option('line', help='format with one line per revision'),
874
help='show revisions whose message matches this regexp',
876
Option('short', help='use moderately short format'),
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')
945
lf = log_formatter(log_format,
948
show_timezone=timezone)
961
class cmd_touching_revisions(Command):
962
"""Return revision-ids which affected a particular file.
964
A more user-friendly interface is "bzr log FILE"."""
966
takes_args = ["filename"]
968
def run(self, filename):
969
tree, relpath = WorkingTree.open_containing(filename)
971
inv = tree.read_working_inventory()
972
file_id = inv.path2id(relpath)
973
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
974
print "%6d %s" % (revno, what)
977
class cmd_ls(Command):
978
"""List files in a tree.
980
# TODO: Take a revision or remote path and list that tree instead.
982
takes_options = ['verbose', 'revision',
983
Option('non-recursive',
984
help='don\'t recurse into sub-directories'),
986
help='Print all paths from the root of the branch.'),
987
Option('unknown', help='Print unknown files'),
988
Option('versioned', help='Print versioned files'),
989
Option('ignored', help='Print ignored files'),
991
Option('null', help='Null separate the files'),
994
def run(self, revision=None, verbose=False,
995
non_recursive=False, from_root=False,
996
unknown=False, versioned=False, ignored=False,
1000
raise BzrCommandError('Cannot set both --verbose and --null')
1001
all = not (unknown or versioned or ignored)
1003
selection = {'I':ignored, '?':unknown, 'V':versioned}
1005
tree, relpath = WorkingTree.open_containing(u'.')
1010
if revision is not None:
1011
tree = tree.branch.revision_tree(
1012
revision[0].in_history(tree.branch).rev_id)
1013
for fp, fc, kind, fid, entry in tree.list_files():
1014
if fp.startswith(relpath):
1015
fp = fp[len(relpath):]
1016
if non_recursive and '/' in fp:
1018
if not all and not selection[fc]:
1021
kindch = entry.kind_character()
1022
print '%-8s %s%s' % (fc, fp, kindch)
1024
sys.stdout.write(fp)
1025
sys.stdout.write('\0')
1031
class cmd_unknowns(Command):
1032
"""List unknown files."""
1035
from bzrlib.osutils import quotefn
1036
for f in WorkingTree.open_containing(u'.')[0].unknowns():
1040
class cmd_ignore(Command):
1041
"""Ignore a command or pattern.
1043
To remove patterns from the ignore list, edit the .bzrignore file.
1045
If the pattern contains a slash, it is compared to the whole path
1046
from the branch root. Otherwise, it is compared to only the last
1047
component of the path. To match a file only in the root directory,
1050
Ignore patterns are case-insensitive on case-insensitive systems.
1052
Note: wildcards must be quoted from the shell on Unix.
1055
bzr ignore ./Makefile
1056
bzr ignore '*.class'
1058
# TODO: Complain if the filename is absolute
1059
takes_args = ['name_pattern']
1061
def run(self, name_pattern):
1062
from bzrlib.atomicfile import AtomicFile
1065
tree, relpath = WorkingTree.open_containing(u'.')
1066
ifn = tree.abspath('.bzrignore')
1068
if os.path.exists(ifn):
1071
igns = f.read().decode('utf-8')
1077
# TODO: If the file already uses crlf-style termination, maybe
1078
# we should use that for the newly added lines?
1080
if igns and igns[-1] != '\n':
1082
igns += name_pattern + '\n'
1085
f = AtomicFile(ifn, 'wt')
1086
f.write(igns.encode('utf-8'))
1091
inv = tree.inventory
1092
if inv.path2id('.bzrignore'):
1093
mutter('.bzrignore is already versioned')
1095
mutter('need to make new .bzrignore file versioned')
1096
tree.add(['.bzrignore'])
1099
class cmd_ignored(Command):
1100
"""List ignored files and the patterns that matched them.
1102
See also: bzr ignore"""
1105
tree = WorkingTree.open_containing(u'.')[0]
1106
for path, file_class, kind, file_id, entry in tree.list_files():
1107
if file_class != 'I':
1109
## XXX: Slightly inefficient since this was already calculated
1110
pat = tree.is_ignored(path)
1111
print '%-50s %s' % (path, pat)
1114
class cmd_lookup_revision(Command):
1115
"""Lookup the revision-id from a revision-number
1118
bzr lookup-revision 33
1121
takes_args = ['revno']
1124
def run(self, revno):
1128
raise BzrCommandError("not a valid revision-number: %r" % revno)
1130
print WorkingTree.open_containing(u'.')[0].branch.get_rev_id(revno)
1133
class cmd_export(Command):
1134
"""Export past revision to destination directory.
1136
If no revision is specified this exports the last committed revision.
1138
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1139
given, try to find the format with the extension. If no extension
1140
is found exports to a directory (equivalent to --format=dir).
1142
Root may be the top directory for tar, tgz and tbz2 formats. If none
1143
is given, the top directory will be the root name of the file.
1145
Note: export of tree with non-ascii filenames to zip is not supported.
1147
Supported formats Autodetected by extension
1148
----------------- -------------------------
1151
tbz2 .tar.bz2, .tbz2
1155
takes_args = ['dest']
1156
takes_options = ['revision', 'format', 'root']
1157
def run(self, dest, revision=None, format=None, root=None):
1159
from bzrlib.export import export
1160
tree = WorkingTree.open_containing(u'.')[0]
1162
if revision is None:
1163
# should be tree.last_revision FIXME
1164
rev_id = b.last_revision()
1166
if len(revision) != 1:
1167
raise BzrError('bzr export --revision takes exactly 1 argument')
1168
rev_id = revision[0].in_history(b).rev_id
1169
t = b.revision_tree(rev_id)
1171
export(t, dest, format, root)
1172
except errors.NoSuchExportFormat, e:
1173
raise BzrCommandError('Unsupported export format: %s' % e.format)
1176
class cmd_cat(Command):
1177
"""Write a file's text from a previous revision."""
1179
takes_options = ['revision']
1180
takes_args = ['filename']
1183
def run(self, filename, revision=None):
1184
if revision is not None and len(revision) != 1:
1185
raise BzrCommandError("bzr cat --revision takes exactly one number")
1188
tree, relpath = WorkingTree.open_containing(filename)
1190
except NotBranchError:
1194
b, relpath = Branch.open_containing(filename)
1195
if revision is None:
1196
revision_id = b.last_revision()
1198
revision_id = revision[0].in_history(b).rev_id
1199
b.print_file(relpath, revision_id)
1202
class cmd_local_time_offset(Command):
1203
"""Show the offset in seconds from GMT to local time."""
1207
print bzrlib.osutils.local_time_offset()
1211
class cmd_commit(Command):
1212
"""Commit changes into a new revision.
1214
If no arguments are given, the entire tree is committed.
1216
If selected files are specified, only changes to those files are
1217
committed. If a directory is specified then the directory and everything
1218
within it is committed.
1220
A selected-file commit may fail in some cases where the committed
1221
tree would be invalid, such as trying to commit a file in a
1222
newly-added directory that is not itself committed.
1224
# TODO: Run hooks on tree to-be-committed, and after commit.
1226
# TODO: Strict commit that fails if there are deleted files.
1227
# (what does "deleted files" mean ??)
1229
# TODO: Give better message for -s, --summary, used by tla people
1231
# XXX: verbose currently does nothing
1233
takes_args = ['selected*']
1234
takes_options = ['message', 'verbose',
1236
help='commit even if nothing has changed'),
1237
Option('file', type=str,
1239
help='file containing commit message'),
1241
help="refuse to commit if there are unknown "
1242
"files in the working tree."),
1244
aliases = ['ci', 'checkin']
1246
def run(self, message=None, file=None, verbose=True, selected_list=None,
1247
unchanged=False, strict=False):
1248
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1250
from bzrlib.msgeditor import edit_commit_message, \
1251
make_commit_message_template
1252
from bzrlib.status import show_status
1253
from tempfile import TemporaryFile
1256
# TODO: Need a blackbox test for invoking the external editor; may be
1257
# slightly problematic to run this cross-platform.
1259
# TODO: do more checks that the commit will succeed before
1260
# spending the user's valuable time typing a commit message.
1262
# TODO: if the commit *does* happen to fail, then save the commit
1263
# message to a temporary file where it can be recovered
1264
tree, selected_list = tree_files(selected_list)
1265
if message is None and not file:
1266
template = make_commit_message_template(tree, selected_list)
1267
message = edit_commit_message(template)
1269
raise BzrCommandError("please specify a commit message"
1270
" with either --message or --file")
1271
elif message and file:
1272
raise BzrCommandError("please specify either --message or --file")
1276
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1279
raise BzrCommandError("empty commit message specified")
1282
tree.commit(message, specific_files=selected_list,
1283
allow_pointless=unchanged, strict=strict)
1284
except PointlessCommit:
1285
# FIXME: This should really happen before the file is read in;
1286
# perhaps prepare the commit; get the message; then actually commit
1287
raise BzrCommandError("no changes to commit",
1288
["use --unchanged to commit anyhow"])
1289
except ConflictsInTree:
1290
raise BzrCommandError("Conflicts detected in working tree. "
1291
'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
1292
except StrictCommitFailed:
1293
raise BzrCommandError("Commit refused because there are unknown "
1294
"files in the working tree.")
1295
note('Committed revision %d.' % (tree.branch.revno(),))
1298
class cmd_check(Command):
1299
"""Validate consistency of branch history.
1301
This command checks various invariants about the branch storage to
1302
detect data corruption or bzr bugs.
1304
takes_args = ['branch?']
1305
takes_options = ['verbose']
1307
def run(self, branch=None, verbose=False):
1308
from bzrlib.check import check
1310
tree = WorkingTree.open_containing()[0]
1311
branch = tree.branch
1313
branch = Branch.open(branch)
1314
check(branch, verbose)
1317
class cmd_scan_cache(Command):
1320
from bzrlib.hashcache import HashCache
1326
print '%6d stats' % c.stat_count
1327
print '%6d in hashcache' % len(c._cache)
1328
print '%6d files removed from cache' % c.removed_count
1329
print '%6d hashes updated' % c.update_count
1330
print '%6d files changed too recently to cache' % c.danger_count
1337
class cmd_upgrade(Command):
1338
"""Upgrade branch storage to current format.
1340
The check command or bzr developers may sometimes advise you to run
1343
This version of this command upgrades from the full-text storage
1344
used by bzr 0.0.8 and earlier to the weave format (v5).
1346
takes_args = ['dir?']
1348
def run(self, dir=u'.'):
1349
from bzrlib.upgrade import upgrade
1353
class cmd_whoami(Command):
1354
"""Show bzr user id."""
1355
takes_options = ['email']
1358
def run(self, email=False):
1360
b = WorkingTree.open_containing(u'.')[0].branch
1361
config = bzrlib.config.BranchConfig(b)
1362
except NotBranchError:
1363
config = bzrlib.config.GlobalConfig()
1366
print config.user_email()
1368
print config.username()
1370
class cmd_nick(Command):
1372
Print or set the branch nickname.
1373
If unset, the tree root directory name is used as the nickname
1374
To print the current nickname, execute with no argument.
1376
takes_args = ['nickname?']
1377
def run(self, nickname=None):
1378
branch = Branch.open_containing(u'.')[0]
1379
if nickname is None:
1380
self.printme(branch)
1382
branch.nick = nickname
1385
def printme(self, branch):
1388
class cmd_selftest(Command):
1389
"""Run internal test suite.
1391
This creates temporary test directories in the working directory,
1392
but not existing data is affected. These directories are deleted
1393
if the tests pass, or left behind to help in debugging if they
1394
fail and --keep-output is specified.
1396
If arguments are given, they are regular expressions that say
1397
which tests should run.
1399
# TODO: --list should give a list of all available tests
1401
takes_args = ['testspecs*']
1402
takes_options = ['verbose',
1403
Option('one', help='stop when one test fails'),
1404
Option('keep-output',
1405
help='keep output directories when tests fail')
1408
def run(self, testspecs_list=None, verbose=False, one=False,
1411
from bzrlib.tests import selftest
1412
# we don't want progress meters from the tests to go to the
1413
# real output; and we don't want log messages cluttering up
1415
save_ui = bzrlib.ui.ui_factory
1416
bzrlib.trace.info('running tests...')
1418
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1419
if testspecs_list is not None:
1420
pattern = '|'.join(testspecs_list)
1423
result = selftest(verbose=verbose,
1425
stop_on_failure=one,
1426
keep_output=keep_output)
1428
bzrlib.trace.info('tests passed')
1430
bzrlib.trace.info('tests failed')
1431
return int(not result)
1433
bzrlib.ui.ui_factory = save_ui
1437
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1438
# is bzrlib itself in a branch?
1439
bzrrev = bzrlib.get_bzr_revision()
1441
print " (bzr checkout, revision %d {%s})" % bzrrev
1442
print bzrlib.__copyright__
1443
print "http://bazaar-ng.org/"
1445
print "bzr comes with ABSOLUTELY NO WARRANTY. bzr is free software, and"
1446
print "you may use, modify and redistribute it under the terms of the GNU"
1447
print "General Public License version 2 or later."
1450
class cmd_version(Command):
1451
"""Show version of bzr."""
1456
class cmd_rocks(Command):
1457
"""Statement of optimism."""
1461
print "it sure does!"
1464
class cmd_find_merge_base(Command):
1465
"""Find and print a base revision for merging two branches.
1467
# TODO: Options to specify revisions on either side, as if
1468
# merging only part of the history.
1469
takes_args = ['branch', 'other']
1473
def run(self, branch, other):
1474
from bzrlib.revision import common_ancestor, MultipleRevisionSources
1476
branch1 = Branch.open_containing(branch)[0]
1477
branch2 = Branch.open_containing(other)[0]
1479
history_1 = branch1.revision_history()
1480
history_2 = branch2.revision_history()
1482
last1 = branch1.last_revision()
1483
last2 = branch2.last_revision()
1485
source = MultipleRevisionSources(branch1, branch2)
1487
base_rev_id = common_ancestor(last1, last2, source)
1489
print 'merge base is revision %s' % base_rev_id
1493
if base_revno is None:
1494
raise bzrlib.errors.UnrelatedBranches()
1496
print ' r%-6d in %s' % (base_revno, branch)
1498
other_revno = branch2.revision_id_to_revno(base_revid)
1500
print ' r%-6d in %s' % (other_revno, other)
1504
class cmd_merge(Command):
1505
"""Perform a three-way merge.
1507
The branch is the branch you will merge from. By default, it will
1508
merge the latest revision. If you specify a revision, that
1509
revision will be merged. If you specify two revisions, the first
1510
will be used as a BASE, and the second one as OTHER. Revision
1511
numbers are always relative to the specified branch.
1513
By default bzr will try to merge in all new work from the other
1514
branch, automatically determining an appropriate base. If this
1515
fails, you may need to give an explicit base.
1519
To merge the latest revision from bzr.dev
1520
bzr merge ../bzr.dev
1522
To merge changes up to and including revision 82 from bzr.dev
1523
bzr merge -r 82 ../bzr.dev
1525
To merge the changes introduced by 82, without previous changes:
1526
bzr merge -r 81..82 ../bzr.dev
1528
merge refuses to run if there are any uncommitted changes, unless
1531
takes_args = ['branch?']
1532
takes_options = ['revision', 'force', 'merge-type', 'reprocess',
1533
Option('show-base', help="Show base revision text in "
1536
def run(self, branch=None, revision=None, force=False, merge_type=None,
1537
show_base=False, reprocess=False):
1538
from bzrlib.merge import merge
1539
from bzrlib.merge_core import ApplyMerge3
1540
if merge_type is None:
1541
merge_type = ApplyMerge3
1543
branch = WorkingTree.open_containing(u'.')[0].branch.get_parent()
1545
raise BzrCommandError("No merge location known or specified.")
1547
print "Using saved location: %s" % branch
1548
if revision is None or len(revision) < 1:
1550
other = [branch, -1]
1552
if len(revision) == 1:
1554
other_branch = Branch.open_containing(branch)[0]
1555
revno = revision[0].in_history(other_branch).revno
1556
other = [branch, revno]
1558
assert len(revision) == 2
1559
if None in revision:
1560
raise BzrCommandError(
1561
"Merge doesn't permit that revision specifier.")
1562
b = Branch.open_containing(branch)[0]
1564
base = [branch, revision[0].in_history(b).revno]
1565
other = [branch, revision[1].in_history(b).revno]
1568
conflict_count = merge(other, base, check_clean=(not force),
1569
merge_type=merge_type, reprocess=reprocess,
1570
show_base=show_base)
1571
if conflict_count != 0:
1575
except bzrlib.errors.AmbiguousBase, e:
1576
m = ("sorry, bzr can't determine the right merge base yet\n"
1577
"candidates are:\n "
1578
+ "\n ".join(e.bases)
1580
"please specify an explicit base with -r,\n"
1581
"and (if you want) report this to the bzr developers\n")
1585
class cmd_remerge(Command):
1588
takes_args = ['file*']
1589
takes_options = ['merge-type', 'reprocess',
1590
Option('show-base', help="Show base revision text in "
1593
def run(self, file_list=None, merge_type=None, show_base=False,
1595
from bzrlib.merge import merge_inner, transform_tree
1596
from bzrlib.merge_core import ApplyMerge3
1597
if merge_type is None:
1598
merge_type = ApplyMerge3
1599
tree, file_list = tree_files(file_list)
1602
pending_merges = tree.pending_merges()
1603
if len(pending_merges) != 1:
1604
raise BzrCommandError("Sorry, remerge only works after normal"
1605
+ " merges. Not cherrypicking or"
1607
base_revision = common_ancestor(tree.branch.last_revision(),
1608
pending_merges[0], tree.branch)
1609
base_tree = tree.branch.revision_tree(base_revision)
1610
other_tree = tree.branch.revision_tree(pending_merges[0])
1611
interesting_ids = None
1612
if file_list is not None:
1613
interesting_ids = set()
1614
for filename in file_list:
1615
file_id = tree.path2id(filename)
1616
interesting_ids.add(file_id)
1617
if tree.kind(file_id) != "directory":
1620
for name, ie in tree.inventory.iter_entries(file_id):
1621
interesting_ids.add(ie.file_id)
1622
transform_tree(tree, tree.branch.basis_tree(), interesting_ids)
1623
if file_list is None:
1624
restore_files = list(tree.iter_conflicts())
1626
restore_files = file_list
1627
for filename in restore_files:
1629
restore(tree.abspath(filename))
1630
except NotConflicted:
1632
conflicts = merge_inner(tree.branch, other_tree, base_tree,
1633
interesting_ids = interesting_ids,
1634
other_rev_id=pending_merges[0],
1635
merge_type=merge_type,
1636
show_base=show_base,
1637
reprocess=reprocess)
1645
class cmd_revert(Command):
1646
"""Reverse all changes since the last commit.
1648
Only versioned files are affected. Specify filenames to revert only
1649
those files. By default, any files that are changed will be backed up
1650
first. Backup files have a '~' appended to their name.
1652
takes_options = ['revision', 'no-backup']
1653
takes_args = ['file*']
1654
aliases = ['merge-revert']
1656
def run(self, revision=None, no_backup=False, file_list=None):
1657
from bzrlib.merge import merge_inner
1658
from bzrlib.commands import parse_spec
1659
if file_list is not None:
1660
if len(file_list) == 0:
1661
raise BzrCommandError("No files specified")
1664
if revision is None:
1666
tree = WorkingTree.open_containing(u'.')[0]
1667
# FIXME should be tree.last_revision
1668
rev_id = tree.branch.last_revision()
1669
elif len(revision) != 1:
1670
raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1672
tree, file_list = tree_files(file_list)
1673
rev_id = revision[0].in_history(tree.branch).rev_id
1674
tree.revert(file_list, tree.branch.revision_tree(rev_id),
1678
class cmd_assert_fail(Command):
1679
"""Test reporting of assertion failures"""
1682
assert False, "always fails"
1685
class cmd_help(Command):
1686
"""Show help on a command or other topic.
1688
For a list of all available commands, say 'bzr help commands'."""
1689
takes_options = ['long']
1690
takes_args = ['topic?']
1694
def run(self, topic=None, long=False):
1696
if topic is None and long:
1701
class cmd_shell_complete(Command):
1702
"""Show appropriate completions for context.
1704
For a list of all available commands, say 'bzr shell-complete'."""
1705
takes_args = ['context?']
1710
def run(self, context=None):
1711
import shellcomplete
1712
shellcomplete.shellcomplete(context)
1715
class cmd_fetch(Command):
1716
"""Copy in history from another branch but don't merge it.
1718
This is an internal method used for pull and merge."""
1720
takes_args = ['from_branch', 'to_branch']
1721
def run(self, from_branch, to_branch):
1722
from bzrlib.fetch import Fetcher
1723
from bzrlib.branch import Branch
1724
from_b = Branch.open(from_branch)
1725
to_b = Branch.open(to_branch)
1730
Fetcher(to_b, from_b)
1737
class cmd_missing(Command):
1738
"""What is missing in this branch relative to other branch.
1740
# TODO: rewrite this in terms of ancestry so that it shows only
1743
takes_args = ['remote?']
1744
aliases = ['mis', 'miss']
1745
takes_options = ['verbose']
1748
def run(self, remote=None, verbose=False):
1749
from bzrlib.errors import BzrCommandError
1750
from bzrlib.missing import show_missing
1752
if verbose and is_quiet():
1753
raise BzrCommandError('Cannot pass both quiet and verbose')
1755
tree = WorkingTree.open_containing(u'.')[0]
1756
parent = tree.branch.get_parent()
1759
raise BzrCommandError("No missing location known or specified.")
1762
print "Using last location: %s" % parent
1764
elif parent is None:
1765
# We only update parent if it did not exist, missing
1766
# should not change the parent
1767
tree.branch.set_parent(remote)
1768
br_remote = Branch.open_containing(remote)[0]
1769
return show_missing(tree.branch, br_remote, verbose=verbose,
1773
class cmd_plugins(Command):
1778
import bzrlib.plugin
1779
from inspect import getdoc
1780
for name, plugin in bzrlib.plugin.all_plugins().items():
1781
if hasattr(plugin, '__path__'):
1782
print plugin.__path__[0]
1783
elif hasattr(plugin, '__file__'):
1784
print plugin.__file__
1790
print '\t', d.split('\n')[0]
1793
class cmd_testament(Command):
1794
"""Show testament (signing-form) of a revision."""
1795
takes_options = ['revision', 'long']
1796
takes_args = ['branch?']
1798
def run(self, branch=u'.', revision=None, long=False):
1799
from bzrlib.testament import Testament
1800
b = WorkingTree.open_containing(branch)[0].branch
1803
if revision is None:
1804
rev_id = b.last_revision()
1806
rev_id = revision[0].in_history(b).rev_id
1807
t = Testament.from_revision(b, rev_id)
1809
sys.stdout.writelines(t.as_text_lines())
1811
sys.stdout.write(t.as_short_text())
1816
class cmd_annotate(Command):
1817
"""Show the origin of each line in a file.
1819
This prints out the given file with an annotation on the left side
1820
indicating which revision, author and date introduced the change.
1822
If the origin is the same for a run of consecutive lines, it is
1823
shown only at the top, unless the --all option is given.
1825
# TODO: annotate directories; showing when each file was last changed
1826
# TODO: annotate a previous version of a file
1827
# TODO: if the working copy is modified, show annotations on that
1828
# with new uncommitted lines marked
1829
aliases = ['blame', 'praise']
1830
takes_args = ['filename']
1831
takes_options = [Option('all', help='show annotations on all lines'),
1832
Option('long', help='show date in annotations'),
1836
def run(self, filename, all=False, long=False):
1837
from bzrlib.annotate import annotate_file
1838
tree, relpath = WorkingTree.open_containing(filename)
1839
branch = tree.branch
1842
file_id = tree.inventory.path2id(relpath)
1843
tree = branch.revision_tree(branch.last_revision())
1844
file_version = tree.inventory[file_id].revision
1845
annotate_file(branch, file_version, file_id, long, all, sys.stdout)
1850
class cmd_re_sign(Command):
1851
"""Create a digital signature for an existing revision."""
1852
# TODO be able to replace existing ones.
1854
hidden = True # is this right ?
1855
takes_args = ['revision_id?']
1856
takes_options = ['revision']
1858
def run(self, revision_id=None, revision=None):
1859
import bzrlib.config as config
1860
import bzrlib.gpg as gpg
1861
if revision_id is not None and revision is not None:
1862
raise BzrCommandError('You can only supply one of revision_id or --revision')
1863
if revision_id is None and revision is None:
1864
raise BzrCommandError('You must supply either --revision or a revision_id')
1865
b = WorkingTree.open_containing(u'.')[0].branch
1866
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1867
if revision_id is not None:
1868
b.sign_revision(revision_id, gpg_strategy)
1869
elif revision is not None:
1870
if len(revision) == 1:
1871
revno, rev_id = revision[0].in_history(b)
1872
b.sign_revision(rev_id, gpg_strategy)
1873
elif len(revision) == 2:
1874
# are they both on rh- if so we can walk between them
1875
# might be nice to have a range helper for arbitrary
1876
# revision paths. hmm.
1877
from_revno, from_revid = revision[0].in_history(b)
1878
to_revno, to_revid = revision[1].in_history(b)
1879
if to_revid is None:
1880
to_revno = b.revno()
1881
if from_revno is None or to_revno is None:
1882
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1883
for revno in range(from_revno, to_revno + 1):
1884
b.sign_revision(b.get_rev_id(revno), gpg_strategy)
1886
raise BzrCommandError('Please supply either one revision, or a range.')
1889
class cmd_uncommit(bzrlib.commands.Command):
1890
"""Remove the last committed revision.
1892
By supplying the --all flag, it will not only remove the entry
1893
from revision_history, but also remove all of the entries in the
1896
--verbose will print out what is being removed.
1897
--dry-run will go through all the motions, but not actually
1900
In the future, uncommit will create a changeset, which can then
1903
takes_options = ['all', 'verbose', 'revision',
1904
Option('dry-run', help='Don\'t actually make changes'),
1905
Option('force', help='Say yes to all questions.')]
1906
takes_args = ['location?']
1909
def run(self, location=None, all=False,
1910
dry_run=False, verbose=False,
1911
revision=None, force=False):
1912
from bzrlib.branch import Branch
1913
from bzrlib.log import log_formatter
1915
from bzrlib.uncommit import uncommit
1917
if location is None:
1919
b, relpath = Branch.open_containing(location)
1921
if revision is None:
1923
rev_id = b.last_revision()
1925
revno, rev_id = revision[0].in_history(b)
1927
print 'No revisions to uncommit.'
1929
for r in range(revno, b.revno()+1):
1930
rev_id = b.get_rev_id(r)
1931
lf = log_formatter('short', to_file=sys.stdout,show_timezone='original')
1932
lf.show(r, b.get_revision(rev_id), None)
1935
print 'Dry-run, pretending to remove the above revisions.'
1937
val = raw_input('Press <enter> to continue')
1939
print 'The above revision(s) will be removed.'
1941
val = raw_input('Are you sure [y/N]? ')
1942
if val.lower() not in ('y', 'yes'):
1946
uncommit(b, remove_files=all,
1947
dry_run=dry_run, verbose=verbose,
1951
# these get imported and then picked up by the scan for cmd_*
1952
# TODO: Some more consistent way to split command definitions across files;
1953
# we do need to load at least some information about them to know of
1955
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore