14
14
# along with this program; if not, write to the Free Software
15
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
22
from bzrlib import BZRDIR
27
from bzrlib.commands import Command, display_command
23
from bzrlib.commands import Command
28
24
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)
25
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
26
from bzrlib.errors import DivergedBranches
34
27
from bzrlib.option import Option
35
28
from bzrlib.revisionspec import RevisionSpec
36
29
import bzrlib.trace
37
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
30
from bzrlib.trace import mutter, note, log_error, warning
38
31
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
34
class cmd_status(Command):
73
35
"""Display status summary.
109
71
that revision, or between two revisions if two are provided.
74
# XXX: FIXME: bzr status should accept a -r option to show changes
75
# relative to a revision, or between revisions
112
77
# TODO: --no-recurse, --recurse options
114
79
takes_args = ['file*']
115
takes_options = ['all', 'show-ids', 'revision']
80
takes_options = ['all', 'show-ids']
116
81
aliases = ['st', 'stat']
119
83
def run(self, all=False, show_ids=False, file_list=None, revision=None):
120
tree, file_list = tree_files(file_list)
85
b, relpath = Branch.open_containing(file_list[0])
86
if relpath == '' and len(file_list) == 1:
89
# generate relative paths.
90
# note that if this is a remote branch, we would want
91
# relpath against the transport. RBC 20051018
92
tree = WorkingTree(b.base, b)
93
file_list = [tree.relpath(x) for x in file_list]
95
b = Branch.open_containing('.')[0]
122
97
from bzrlib.status import show_status
123
show_status(tree.branch, show_unchanged=all, show_ids=show_ids,
98
show_status(b, show_unchanged=all, show_ids=show_ids,
124
99
specific_files=file_list, revision=revision)
135
110
takes_args = ['revision_id?']
136
111
takes_options = ['revision']
139
113
def run(self, revision_id=None, revision=None):
141
115
if revision_id is not None and revision is not None:
142
116
raise BzrCommandError('You can only supply one of revision_id or --revision')
143
117
if revision_id is None and revision is None:
144
118
raise BzrCommandError('You must supply either --revision or a revision_id')
145
b = WorkingTree.open_containing(u'.')[0].branch
119
b = Branch.open_containing('.')[0]
146
120
if revision_id is not None:
147
sys.stdout.write(b.get_revision_xml(revision_id))
121
sys.stdout.write(b.get_revision_xml_file(revision_id).read())
148
122
elif revision is not None:
149
123
for rev in revision:
151
125
raise BzrCommandError('You cannot specify a NULL revision.')
152
126
revno, rev_id = rev.in_history(b)
153
sys.stdout.write(b.get_revision_xml(rev_id))
127
sys.stdout.write(b.get_revision_xml_file(rev_id).read())
156
130
class cmd_revno(Command):
157
131
"""Show current revision number.
159
133
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()
135
print Branch.open_containing('.')[0].revno()
166
138
class cmd_revision_info(Command):
246
220
takes_args = ['filename']
250
223
def run(self, filename):
251
tree, relpath = WorkingTree.open_containing(filename)
224
branch, relpath = Branch.open_containing(filename)
255
228
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']
229
"""Show inventory of the current working copy or a revision."""
230
takes_options = ['revision', 'show-ids']
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]
232
def run(self, revision=None, show_ids=False):
233
b = Branch.open_containing('.')[0]
268
234
if revision is None:
269
inv = tree.read_working_inventory()
235
inv = b.read_working_inventory()
271
237
if len(revision) > 1:
272
238
raise BzrCommandError('bzr inventory --revision takes'
273
239
' exactly one revision identifier')
274
inv = tree.branch.get_revision_inventory(
275
revision[0].in_history(tree.branch).rev_id)
240
inv = b.get_revision_inventory(revision[0].in_history(b).rev_id)
277
242
for path, entry in inv.entries():
278
if kind and kind != entry.kind:
281
244
print '%-50s %s' % (path, entry.file_id)
336
302
def run(self, names_list):
337
303
if len(names_list) < 2:
338
304
raise BzrCommandError("missing file argument")
339
tree, rel_names = tree_files(names_list)
305
b = Branch.open_containing(names_list[0])[0]
306
tree = WorkingTree(b.base, b)
307
rel_names = [tree.relpath(x) for x in names_list]
341
309
if os.path.isdir(names_list[-1]):
342
310
# move into existing directory
343
for pair in tree.move(rel_names[:-1], rel_names[-1]):
311
for pair in b.move(rel_names[:-1], rel_names[-1]):
344
312
print "%s => %s" % pair
346
314
if len(names_list) != 2:
347
315
raise BzrCommandError('to mv multiple files the destination '
348
316
'must be a versioned directory')
349
tree.rename_one(rel_names[0], rel_names[1])
317
b.rename_one(rel_names[0], rel_names[1])
350
318
print "%s => %s" % (rel_names[0], rel_names[1])
353
323
class cmd_pull(Command):
354
324
"""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.
326
If the location is omitted, the last-used location will be used.
327
Both the revision history and the working directory will be
360
330
This command only works on branches that have not diverged. Branches are
361
331
considered diverged if both branches have had commits without first
362
332
pulling from the other.
364
334
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.
335
from one into the other.
371
takes_options = ['remember', 'overwrite', 'verbose']
337
takes_options = ['remember']
372
338
takes_args = ['location?']
374
def run(self, location=None, remember=False, overwrite=False, verbose=False):
340
def run(self, location=None, remember=False):
375
341
from bzrlib.merge import merge
376
343
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()
346
br_to = Branch.open_containing('.')[0]
347
stored_loc = br_to.get_parent()
381
348
if location is None:
382
349
if stored_loc is None:
383
350
raise BzrCommandError("No pull location known or specified.")
385
352
print "Using saved location: %s" % stored_loc
386
353
location = stored_loc
354
cache_root = tempfile.mkdtemp()
388
355
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)
394
if br_to.get_parent() is None or remember:
395
br_to.set_parent(location)
396
note('%d revision(s) pulled.' % (count,))
399
new_rh = tree_to.branch.revision_history()
402
from bzrlib.log import show_changed_revisions
403
show_changed_revisions(tree_to.branch, old_rh, new_rh)
406
class cmd_push(Command):
407
"""Push this branch into another branch.
409
The remote branch will not have its working tree populated because this
410
is both expensive, and may not be supported on the remote file system.
412
Some smart servers or protocols *may* put the working tree in place.
414
If there is no default push location set, the first push will set it.
415
After that, you can omit the location to use the default. To change the
416
default, use --remember.
418
This command only works on branches that have not diverged. Branches are
419
considered diverged if the branch being pushed to is not an older version
422
If branches have diverged, you can use 'bzr push --overwrite' to replace
423
the other branch completely.
425
If you want to ensure you have the different changes in the other branch,
426
do a merge (see bzr help merge) from the other branch, and commit that
427
before doing a 'push --overwrite'.
429
takes_options = ['remember', 'overwrite',
430
Option('create-prefix',
431
help='Create the path leading up to the branch '
432
'if it does not already exist')]
433
takes_args = ['location?']
435
def run(self, location=None, remember=False, overwrite=False,
436
create_prefix=False, verbose=False):
437
# FIXME: Way too big! Put this into a function called from the
440
from shutil import rmtree
441
from bzrlib.transport import get_transport
443
tree_from = WorkingTree.open_containing(u'.')[0]
444
br_from = tree_from.branch
445
stored_loc = tree_from.branch.get_push_location()
447
if stored_loc is None:
448
raise BzrCommandError("No push location known or specified.")
450
print "Using saved location: %s" % stored_loc
451
location = stored_loc
453
br_to = Branch.open(location)
454
except NotBranchError:
456
transport = get_transport(location).clone('..')
457
if not create_prefix:
459
transport.mkdir(transport.relpath(location))
461
raise BzrCommandError("Parent directory of %s "
462
"does not exist." % location)
464
current = transport.base
465
needed = [(transport, transport.relpath(location))]
468
transport, relpath = needed[-1]
469
transport.mkdir(relpath)
472
new_transport = transport.clone('..')
473
needed.append((new_transport,
474
new_transport.relpath(transport.base)))
475
if new_transport.base == transport.base:
476
raise BzrCommandError("Could not creeate "
478
br_to = Branch.initialize(location)
479
old_rh = br_to.revision_history()
358
br_from.setup_caching(cache_root)
359
location = br_from.base
360
old_revno = br_to.revno()
361
old_revision_history = br_to.revision_history()
482
tree_to = br_to.working_tree()
483
except NoWorkingTree:
484
# TODO: This should be updated for branches which don't have a
485
# working tree, as opposed to ones where we just couldn't
487
warning('Unable to update the working tree of: %s' % (br_to.base,))
488
count = br_to.pull(br_from, overwrite)
490
count = tree_to.pull(br_from, overwrite)
491
except DivergedBranches:
492
raise BzrCommandError("These branches have diverged."
493
" Try a merge then push with overwrite.")
494
if br_from.get_push_location() is None or remember:
495
br_from.set_push_location(location)
496
note('%d revision(s) pushed.' % (count,))
363
br_to.update_revisions(br_from)
364
except DivergedBranches:
365
raise BzrCommandError("These branches have diverged."
367
new_revision_history = br_to.revision_history()
368
if new_revision_history != old_revision_history:
369
merge(('.', -1), ('.', old_revno), check_clean=False)
370
if stored_loc is None or remember:
371
br_to.set_parent(location)
499
new_rh = br_to.revision_history()
502
from bzrlib.log import show_changed_revisions
503
show_changed_revisions(br_to, old_rh, new_rh)
506
378
class cmd_branch(Command):
745
601
takes_options = ['revision', 'diff-options']
746
602
aliases = ['di', 'dif']
749
604
def run(self, revision=None, file_list=None, diff_options=None):
750
605
from bzrlib.diff import show_diff
752
tree, file_list = internal_tree_files(file_list)
755
except FileInWrongBranch:
756
if len(file_list) != 2:
757
raise BzrCommandError("Files are in different branches")
759
b, file1 = Branch.open_containing(file_list[0])
760
b2, file2 = Branch.open_containing(file_list[1])
761
if file1 != "" or file2 != "":
762
# FIXME diff those two files. rbc 20051123
763
raise BzrCommandError("Files are in different branches")
608
b = Branch.open_containing(file_list[0])[0]
609
tree = WorkingTree(b.base, b)
610
file_list = [tree.relpath(f) for f in file_list]
611
if file_list == ['']:
612
# just pointing to top-of-tree
615
b = Branch.open_containing('.')[0]
765
617
if revision is not None:
767
raise BzrCommandError("Can't specify -r with two branches")
768
618
if len(revision) == 1:
769
return show_diff(tree.branch, revision[0], specific_files=file_list,
770
external_diff_options=diff_options)
619
show_diff(b, revision[0], specific_files=file_list,
620
external_diff_options=diff_options)
771
621
elif len(revision) == 2:
772
return show_diff(tree.branch, revision[0], specific_files=file_list,
773
external_diff_options=diff_options,
774
revision2=revision[1])
622
show_diff(b, revision[0], specific_files=file_list,
623
external_diff_options=diff_options,
624
revision2=revision[1])
776
626
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
779
return show_diff(b, None, specific_files=file_list,
780
external_diff_options=diff_options, b2=b2)
782
return show_diff(tree.branch, None, specific_files=file_list,
783
external_diff_options=diff_options)
628
show_diff(b, None, specific_files=file_list,
629
external_diff_options=diff_options)
786
634
class cmd_deleted(Command):
977
807
# TODO: Take a revision or remote path and list that tree instead.
979
takes_options = ['verbose', 'revision',
980
Option('non-recursive',
981
help='don\'t recurse into sub-directories'),
983
help='Print all paths from the root of the branch.'),
984
Option('unknown', help='Print unknown files'),
985
Option('versioned', help='Print versioned files'),
986
Option('ignored', help='Print ignored files'),
988
Option('null', help='Null separate the files'),
991
def run(self, revision=None, verbose=False,
992
non_recursive=False, from_root=False,
993
unknown=False, versioned=False, ignored=False,
997
raise BzrCommandError('Cannot set both --verbose and --null')
998
all = not (unknown or versioned or ignored)
1000
selection = {'I':ignored, '?':unknown, 'V':versioned}
1002
tree, relpath = WorkingTree.open_containing(u'.')
1007
if revision is not None:
1008
tree = tree.branch.revision_tree(
1009
revision[0].in_history(tree.branch).rev_id)
809
def run(self, revision=None, verbose=False):
810
b, relpath = Branch.open_containing('.')[0]
812
tree = b.working_tree()
814
tree = b.revision_tree(revision.in_history(b).rev_id)
1010
815
for fp, fc, kind, fid, entry in tree.list_files():
1011
if fp.startswith(relpath):
1012
fp = fp[len(relpath):]
1013
if non_recursive and '/' in fp:
1015
if not all and not selection[fc]:
1018
kindch = entry.kind_character()
1019
print '%-8s %s%s' % (fc, fp, kindch)
1021
sys.stdout.write(fp)
1022
sys.stdout.write('\0')
817
kindch = entry.kind_character()
818
print '%-8s %s%s' % (fc, fp, kindch)
1028
824
class cmd_unknowns(Command):
1029
825
"""List unknown files."""
1032
827
from bzrlib.osutils import quotefn
1033
for f in WorkingTree.open_containing(u'.')[0].unknowns():
828
for f in Branch.open_containing('.')[0].unknowns():
1034
829
print quotefn(f)
1037
833
class cmd_ignore(Command):
1038
834
"""Ignore a command or pattern.
1137
932
is found exports to a directory (equivalent to --format=dir).
1139
934
Root may be the top directory for tar, tgz and tbz2 formats. If none
1140
is given, the top directory will be the root name of the file.
1142
Note: export of tree with non-ascii filenames to zip is not supported.
1144
Supported formats Autodetected by extension
1145
----------------- -------------------------
1148
tbz2 .tar.bz2, .tbz2
935
is given, the top directory will be the root name of the file."""
936
# TODO: list known exporters
1152
937
takes_args = ['dest']
1153
938
takes_options = ['revision', 'format', 'root']
1154
939
def run(self, dest, revision=None, format=None, root=None):
1156
from bzrlib.export import export
1157
tree = WorkingTree.open_containing(u'.')[0]
941
b = Branch.open_containing('.')[0]
1159
942
if revision is None:
1160
# should be tree.last_revision FIXME
1161
943
rev_id = b.last_revision()
1163
945
if len(revision) != 1:
1164
946
raise BzrError('bzr export --revision takes exactly 1 argument')
1165
947
rev_id = revision[0].in_history(b).rev_id
1166
948
t = b.revision_tree(rev_id)
1168
export(t, dest, format, root)
1169
except errors.NoSuchExportFormat, e:
1170
raise BzrCommandError('Unsupported export format: %s' % e.format)
949
arg_root, ext = os.path.splitext(os.path.basename(dest))
950
if ext in ('.gz', '.bz2'):
951
new_root, new_ext = os.path.splitext(arg_root)
952
if new_ext == '.tar':
960
elif ext in (".tar.gz", ".tgz"):
962
elif ext in (".tar.bz2", ".tbz2"):
966
t.export(dest, format, root)
1173
969
class cmd_cat(Command):
1244
1028
unchanged=False, strict=False):
1245
1029
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1246
1030
StrictCommitFailed)
1247
from bzrlib.msgeditor import edit_commit_message, \
1248
make_commit_message_template
1031
from bzrlib.msgeditor import edit_commit_message
1249
1032
from bzrlib.status import show_status
1250
from tempfile import TemporaryFile
1253
# TODO: Need a blackbox test for invoking the external editor; may be
1254
# slightly problematic to run this cross-platform.
1256
# TODO: do more checks that the commit will succeed before
1257
# spending the user's valuable time typing a commit message.
1259
# TODO: if the commit *does* happen to fail, then save the commit
1260
# message to a temporary file where it can be recovered
1261
tree, selected_list = tree_files(selected_list)
1033
from cStringIO import StringIO
1035
b = Branch.open_containing('.')[0]
1036
tree = WorkingTree(b.base, b)
1038
selected_list = [tree.relpath(s) for s in selected_list]
1262
1039
if message is None and not file:
1263
template = make_commit_message_template(tree, selected_list)
1264
message = edit_commit_message(template)
1040
catcher = StringIO()
1041
show_status(b, specific_files=selected_list,
1043
message = edit_commit_message(catcher.getvalue())
1265
1045
if message is None:
1266
1046
raise BzrCommandError("please specify a commit message"
1267
1047
" with either --message or --file")
1582
class cmd_remerge(Command):
1585
takes_args = ['file*']
1586
takes_options = ['merge-type', 'reprocess',
1587
Option('show-base', help="Show base revision text in "
1590
def run(self, file_list=None, merge_type=None, show_base=False,
1592
from bzrlib.merge import merge_inner, transform_tree
1593
from bzrlib.merge_core import ApplyMerge3
1594
if merge_type is None:
1595
merge_type = ApplyMerge3
1596
tree, file_list = tree_files(file_list)
1599
pending_merges = tree.pending_merges()
1600
if len(pending_merges) != 1:
1601
raise BzrCommandError("Sorry, remerge only works after normal"
1602
+ " merges. Not cherrypicking or"
1604
base_revision = common_ancestor(tree.branch.last_revision(),
1605
pending_merges[0], tree.branch)
1606
base_tree = tree.branch.revision_tree(base_revision)
1607
other_tree = tree.branch.revision_tree(pending_merges[0])
1608
interesting_ids = None
1609
if file_list is not None:
1610
interesting_ids = set()
1611
for filename in file_list:
1612
file_id = tree.path2id(filename)
1613
interesting_ids.add(file_id)
1614
if tree.kind(file_id) != "directory":
1617
for name, ie in tree.inventory.iter_entries(file_id):
1618
interesting_ids.add(ie.file_id)
1619
transform_tree(tree, tree.branch.basis_tree(), interesting_ids)
1620
if file_list is None:
1621
restore_files = list(tree.iter_conflicts())
1623
restore_files = file_list
1624
for filename in restore_files:
1626
restore(tree.abspath(filename))
1627
except NotConflicted:
1629
conflicts = merge_inner(tree.branch, other_tree, base_tree,
1630
interesting_ids = interesting_ids,
1631
other_rev_id=pending_merges[0],
1632
merge_type=merge_type,
1633
show_base=show_base,
1634
reprocess=reprocess)
1642
1321
class cmd_revert(Command):
1643
1322
"""Reverse all changes since the last commit.
1740
1411
takes_args = ['remote?']
1741
1412
aliases = ['mis', 'miss']
1742
takes_options = ['verbose']
1413
# We don't have to add quiet to the list, because
1414
# unknown options are parsed as booleans
1415
takes_options = ['verbose', 'quiet']
1745
def run(self, remote=None, verbose=False):
1417
def run(self, remote=None, verbose=False, quiet=False):
1746
1418
from bzrlib.errors import BzrCommandError
1747
1419
from bzrlib.missing import show_missing
1749
if verbose and is_quiet():
1421
if verbose and quiet:
1750
1422
raise BzrCommandError('Cannot pass both quiet and verbose')
1752
tree = WorkingTree.open_containing(u'.')[0]
1753
parent = tree.branch.get_parent()
1424
b = Branch.open_containing('.')[0]
1425
parent = b.get_parent()
1754
1426
if remote is None:
1755
1427
if parent is None:
1756
1428
raise BzrCommandError("No missing location known or specified.")
1759
1431
print "Using last location: %s" % parent
1760
1432
remote = parent
1761
1433
elif parent is None:
1762
1434
# We only update parent if it did not exist, missing
1763
1435
# should not change the parent
1764
tree.branch.set_parent(remote)
1436
b.set_parent(remote)
1765
1437
br_remote = Branch.open_containing(remote)[0]
1766
return show_missing(tree.branch, br_remote, verbose=verbose,
1438
return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1770
1441
class cmd_plugins(Command):
1771
1442
"""List plugins"""
1775
1445
import bzrlib.plugin
1776
1446
from inspect import getdoc
1777
for name, plugin in bzrlib.plugin.all_plugins().items():
1447
for plugin in bzrlib.plugin.all_plugins:
1778
1448
if hasattr(plugin, '__path__'):
1779
1449
print plugin.__path__[0]
1780
1450
elif hasattr(plugin, '__file__'):
1859
1527
raise BzrCommandError('You can only supply one of revision_id or --revision')
1860
1528
if revision_id is None and revision is None:
1861
1529
raise BzrCommandError('You must supply either --revision or a revision_id')
1862
b = WorkingTree.open_containing(u'.')[0].branch
1530
b = Branch.open_containing('.')[0]
1863
1531
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1864
1532
if revision_id is not None:
1865
1533
b.sign_revision(revision_id, gpg_strategy)
1866
1534
elif revision is not None:
1867
if len(revision) == 1:
1868
revno, rev_id = revision[0].in_history(b)
1535
for rev in revision:
1537
raise BzrCommandError('You cannot specify a NULL revision.')
1538
revno, rev_id = rev.in_history(b)
1869
1539
b.sign_revision(rev_id, gpg_strategy)
1870
elif len(revision) == 2:
1871
# are they both on rh- if so we can walk between them
1872
# might be nice to have a range helper for arbitrary
1873
# revision paths. hmm.
1874
from_revno, from_revid = revision[0].in_history(b)
1875
to_revno, to_revid = revision[1].in_history(b)
1876
if to_revid is None:
1877
to_revno = b.revno()
1878
if from_revno is None or to_revno is None:
1879
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1880
for revno in range(from_revno, to_revno + 1):
1881
b.sign_revision(b.get_rev_id(revno), gpg_strategy)
1883
raise BzrCommandError('Please supply either one revision, or a range.')
1886
class cmd_uncommit(bzrlib.commands.Command):
1887
"""Remove the last committed revision.
1889
By supplying the --all flag, it will not only remove the entry
1890
from revision_history, but also remove all of the entries in the
1893
--verbose will print out what is being removed.
1894
--dry-run will go through all the motions, but not actually
1897
In the future, uncommit will create a changeset, which can then
1900
takes_options = ['all', 'verbose', 'revision',
1901
Option('dry-run', help='Don\'t actually make changes'),
1902
Option('force', help='Say yes to all questions.')]
1903
takes_args = ['location?']
1906
def run(self, location=None, all=False,
1907
dry_run=False, verbose=False,
1908
revision=None, force=False):
1909
from bzrlib.branch import Branch
1910
from bzrlib.log import log_formatter
1912
from bzrlib.uncommit import uncommit
1914
if location is None:
1916
b, relpath = Branch.open_containing(location)
1918
if revision is None:
1920
rev_id = b.last_revision()
1922
revno, rev_id = revision[0].in_history(b)
1924
print 'No revisions to uncommit.'
1926
for r in range(revno, b.revno()+1):
1927
rev_id = b.get_rev_id(r)
1928
lf = log_formatter('short', to_file=sys.stdout,show_timezone='original')
1929
lf.show(r, b.get_revision(rev_id), None)
1932
print 'Dry-run, pretending to remove the above revisions.'
1934
val = raw_input('Press <enter> to continue')
1936
print 'The above revision(s) will be removed.'
1938
val = raw_input('Are you sure [y/N]? ')
1939
if val.lower() not in ('y', 'yes'):
1943
uncommit(b, remove_files=all,
1944
dry_run=dry_run, verbose=verbose,
1948
1542
# these get imported and then picked up by the scan for cmd_*
1949
1543
# TODO: Some more consistent way to split command definitions across files;
1950
1544
# we do need to load at least some information about them to know of
1952
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore
1546
from bzrlib.conflicts import cmd_resolve, cmd_conflicts