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
"""builtin bzr commands"""
19
# DO NOT change this to cStringIO - it results in control files
21
# FIXIT! (Only deal with byte streams OR unicode at any one layer.)
24
from StringIO import StringIO
29
from bzrlib import BZRDIR
30
from bzrlib.commands import Command, display_command
31
from bzrlib.branch import Branch
32
from bzrlib.revision import common_ancestor
33
import bzrlib.errors as errors
34
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError,
35
NotBranchError, DivergedBranches, NotConflicted,
36
NoSuchFile, NoWorkingTree, FileInWrongBranch)
37
from bzrlib.option import Option
38
from bzrlib.revisionspec import RevisionSpec
22
39
import bzrlib.trace
23
from bzrlib.trace import mutter, note, log_error, warning
24
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
25
from bzrlib.errors import DivergedBranches
26
from bzrlib.branch import Branch
27
from bzrlib import BZRDIR
28
from bzrlib.commands import Command
40
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
41
from bzrlib.workingtree import WorkingTree
42
from bzrlib.log import show_one_log
45
def tree_files(file_list, default_branch=u'.'):
47
return internal_tree_files(file_list, default_branch)
48
except FileInWrongBranch, e:
49
raise BzrCommandError("%s is not in the same branch as %s" %
50
(e.path, file_list[0]))
52
def internal_tree_files(file_list, default_branch=u'.'):
54
Return a branch and list of branch-relative paths.
55
If supplied file_list is empty or None, the branch default will be used,
56
and returned file_list will match the original.
58
if file_list is None or len(file_list) == 0:
59
return WorkingTree.open_containing(default_branch)[0], file_list
60
tree = WorkingTree.open_containing(file_list[0])[0]
62
for filename in file_list:
64
new_list.append(tree.relpath(filename))
65
except errors.PathNotChild:
66
raise FileInWrongBranch(tree.branch, filename)
70
# TODO: Make sure no commands unconditionally use the working directory as a
71
# branch. If a filename argument is used, the first of them should be used to
72
# specify the branch. (Perhaps this can be factored out into some kind of
73
# Argument class, representing a file in a branch, where the first occurrence
31
76
class cmd_status(Command):
32
77
"""Display status summary.
67
112
If a revision argument is given, the status is calculated against
68
113
that revision, or between two revisions if two are provided.
70
# XXX: FIXME: bzr status should accept a -r option to show changes
71
# relative to a revision, or between revisions
116
# TODO: --no-recurse, --recurse options
73
118
takes_args = ['file*']
74
takes_options = ['all', 'show-ids']
119
takes_options = ['all', 'show-ids', 'revision']
75
120
aliases = ['st', 'stat']
77
123
def run(self, all=False, show_ids=False, file_list=None, revision=None):
79
b = Branch.open_containing(file_list[0])
80
file_list = [b.relpath(x) for x in file_list]
81
# special case: only one path was given and it's the root
86
b = Branch.open_containing('.')
124
tree, file_list = tree_files(file_list)
88
126
from bzrlib.status import show_status
89
show_status(b, show_unchanged=all, show_ids=show_ids,
127
show_status(tree.branch, show_unchanged=all, show_ids=show_ids,
90
128
specific_files=file_list, revision=revision)
101
139
takes_args = ['revision_id?']
102
140
takes_options = ['revision']
104
143
def run(self, revision_id=None, revision=None):
105
from bzrlib.revisionspec import RevisionSpec
107
145
if revision_id is not None and revision is not None:
108
146
raise BzrCommandError('You can only supply one of revision_id or --revision')
109
147
if revision_id is None and revision is None:
110
148
raise BzrCommandError('You must supply either --revision or a revision_id')
111
b = Branch.open_containing('.')
149
b = WorkingTree.open_containing(u'.')[0].branch
112
150
if revision_id is not None:
113
sys.stdout.write(b.get_revision_xml_file(revision_id).read())
151
sys.stdout.write(b.get_revision_xml(revision_id))
114
152
elif revision is not None:
115
153
for rev in revision:
117
155
raise BzrCommandError('You cannot specify a NULL revision.')
118
156
revno, rev_id = rev.in_history(b)
119
sys.stdout.write(b.get_revision_xml_file(rev_id).read())
157
sys.stdout.write(b.get_revision_xml(rev_id))
122
160
class cmd_revno(Command):
123
161
"""Show current revision number.
125
163
This is equal to the number of revisions on this branch."""
127
print Branch.open_containing('.').revno()
164
takes_args = ['location?']
166
def run(self, location=u'.'):
167
print Branch.open_containing(location)[0].revno()
130
170
class cmd_revision_info(Command):
177
217
implicitly add the parent, and so on up to the root. This means
178
218
you should never need to explictly add a directory, they'll just
179
219
get added when you add a file in the directory.
221
--dry-run will show which files would be added, but not actually
181
224
takes_args = ['file*']
182
takes_options = ['no-recurse', 'quiet']
184
def run(self, file_list, no_recurse=False, quiet=False):
185
from bzrlib.add import smart_add, add_reporter_print, add_reporter_null
187
reporter = add_reporter_null
225
takes_options = ['no-recurse', 'dry-run', 'verbose']
227
def run(self, file_list, no_recurse=False, dry_run=False, verbose=False):
232
# This is pointless, but I'd rather not raise an error
233
action = bzrlib.add.add_action_null
235
action = bzrlib.add.add_action_print
237
action = bzrlib.add.add_action_add
189
reporter = add_reporter_print
190
smart_add(file_list, not no_recurse, reporter)
239
action = bzrlib.add.add_action_add_and_print
241
added, ignored = bzrlib.add.smart_add(file_list, not no_recurse,
244
for glob in sorted(ignored.keys()):
245
match_len = len(ignored[glob])
247
for path in ignored[glob]:
248
print "ignored %s matching \"%s\"" % (path, glob)
250
print "ignored %d file(s) matching \"%s\"" % (match_len,
252
print "If you wish to add some of these files, please add them"\
193
256
class cmd_mkdir(Command):
213
273
takes_args = ['filename']
216
277
def run(self, filename):
217
print Branch.open_containing(filename).relpath(filename)
278
tree, relpath = WorkingTree.open_containing(filename)
221
282
class cmd_inventory(Command):
222
"""Show inventory of the current working copy or a revision."""
223
takes_options = ['revision', 'show-ids']
283
"""Show inventory of the current working copy or a revision.
285
It is possible to limit the output to a particular entry
286
type using the --kind option. For example; --kind file.
288
takes_options = ['revision', 'show-ids', 'kind']
225
def run(self, revision=None, show_ids=False):
226
b = Branch.open_containing('.')
291
def run(self, revision=None, show_ids=False, kind=None):
292
if kind and kind not in ['file', 'directory', 'symlink']:
293
raise BzrCommandError('invalid kind specified')
294
tree = WorkingTree.open_containing(u'.')[0]
227
295
if revision is None:
228
inv = b.read_working_inventory()
296
inv = tree.read_working_inventory()
230
298
if len(revision) > 1:
231
299
raise BzrCommandError('bzr inventory --revision takes'
232
300
' exactly one revision identifier')
233
inv = b.get_revision_inventory(revision[0].in_history(b).rev_id)
301
inv = tree.branch.get_revision_inventory(
302
revision[0].in_history(tree.branch).rev_id)
235
304
for path, entry in inv.entries():
305
if kind and kind != entry.kind:
237
308
print '%-50s %s' % (path, entry.file_id)
294
363
def run(self, names_list):
295
364
if len(names_list) < 2:
296
365
raise BzrCommandError("missing file argument")
297
b = Branch.open_containing(names_list[0])
299
rel_names = [b.relpath(x) for x in names_list]
366
tree, rel_names = tree_files(names_list)
301
368
if os.path.isdir(names_list[-1]):
302
369
# move into existing directory
303
for pair in b.move(rel_names[:-1], rel_names[-1]):
370
for pair in tree.move(rel_names[:-1], rel_names[-1]):
304
371
print "%s => %s" % pair
306
373
if len(names_list) != 2:
307
374
raise BzrCommandError('to mv multiple files the destination '
308
375
'must be a versioned directory')
309
b.rename_one(rel_names[0], rel_names[1])
376
tree.rename_one(rel_names[0], rel_names[1])
310
377
print "%s => %s" % (rel_names[0], rel_names[1])
315
380
class cmd_pull(Command):
316
381
"""Pull any changes from another branch into the current one.
318
If the location is omitted, the last-used location will be used.
319
Both the revision history and the working directory will be
383
If there is no default location set, the first pull will set it. After
384
that, you can omit the location to use the default. To change the
385
default, use --remember.
322
387
This command only works on branches that have not diverged. Branches are
323
388
considered diverged if both branches have had commits without first
324
389
pulling from the other.
326
391
If branches have diverged, you can use 'bzr merge' to pull the text changes
327
from one into the other.
392
from one into the other. Once one branch has merged, the other should
393
be able to pull it again.
395
If you want to forget your local changes and just update your branch to
396
match the remote one, use --overwrite.
329
takes_options = ['remember']
398
takes_options = ['remember', 'overwrite', 'verbose']
330
399
takes_args = ['location?']
332
def run(self, location=None, remember=False):
401
def run(self, location=None, remember=False, overwrite=False, verbose=False):
333
402
from bzrlib.merge import merge
335
403
from shutil import rmtree
338
br_to = Branch.open_containing('.')
339
stored_loc = br_to.get_parent()
405
# FIXME: too much stuff is in the command class
406
tree_to = WorkingTree.open_containing(u'.')[0]
407
stored_loc = tree_to.branch.get_parent()
340
408
if location is None:
341
409
if stored_loc is None:
342
410
raise BzrCommandError("No pull location known or specified.")
344
412
print "Using saved location: %s" % stored_loc
345
413
location = stored_loc
346
cache_root = tempfile.mkdtemp()
347
415
br_from = Branch.open(location)
350
br_from.setup_caching(cache_root)
351
location = br_from.base
352
old_revno = br_to.revno()
353
old_revision_history = br_to.revision_history()
416
br_to = tree_to.branch
418
old_rh = br_to.revision_history()
419
count = tree_to.pull(br_from, overwrite)
421
if br_to.get_parent() is None or remember:
422
br_to.set_parent(location)
423
note('%d revision(s) pulled.' % (count,))
426
new_rh = tree_to.branch.revision_history()
429
from bzrlib.log import show_changed_revisions
430
show_changed_revisions(tree_to.branch, old_rh, new_rh)
433
class cmd_push(Command):
434
"""Push this branch into another branch.
436
The remote branch will not have its working tree populated because this
437
is both expensive, and may not be supported on the remote file system.
439
Some smart servers or protocols *may* put the working tree in place.
441
If there is no default push location set, the first push will set it.
442
After that, you can omit the location to use the default. To change the
443
default, use --remember.
445
This command only works on branches that have not diverged. Branches are
446
considered diverged if the branch being pushed to is not an older version
449
If branches have diverged, you can use 'bzr push --overwrite' to replace
450
the other branch completely.
452
If you want to ensure you have the different changes in the other branch,
453
do a merge (see bzr help merge) from the other branch, and commit that
454
before doing a 'push --overwrite'.
456
takes_options = ['remember', 'overwrite',
457
Option('create-prefix',
458
help='Create the path leading up to the branch '
459
'if it does not already exist')]
460
takes_args = ['location?']
462
def run(self, location=None, remember=False, overwrite=False,
463
create_prefix=False, verbose=False):
464
# FIXME: Way too big! Put this into a function called from the
467
from shutil import rmtree
468
from bzrlib.transport import get_transport
470
tree_from = WorkingTree.open_containing(u'.')[0]
471
br_from = tree_from.branch
472
stored_loc = tree_from.branch.get_push_location()
474
if stored_loc is None:
475
raise BzrCommandError("No push location known or specified.")
477
print "Using saved location: %s" % stored_loc
478
location = stored_loc
480
br_to = Branch.open(location)
481
except NotBranchError:
483
transport = get_transport(location).clone('..')
484
if not create_prefix:
486
transport.mkdir(transport.relpath(location))
488
raise BzrCommandError("Parent directory of %s "
489
"does not exist." % location)
491
current = transport.base
492
needed = [(transport, transport.relpath(location))]
495
transport, relpath = needed[-1]
496
transport.mkdir(relpath)
499
new_transport = transport.clone('..')
500
needed.append((new_transport,
501
new_transport.relpath(transport.base)))
502
if new_transport.base == transport.base:
503
raise BzrCommandError("Could not creeate "
505
br_to = Branch.initialize(location)
506
old_rh = br_to.revision_history()
355
br_to.update_revisions(br_from)
356
except DivergedBranches:
357
raise BzrCommandError("These branches have diverged."
359
new_revision_history = br_to.revision_history()
360
if new_revision_history != old_revision_history:
361
merge(('.', -1), ('.', old_revno), check_clean=False)
362
if stored_loc is None or remember:
363
br_to.set_parent(location)
509
tree_to = br_to.working_tree()
510
except NoWorkingTree:
511
# TODO: This should be updated for branches which don't have a
512
# working tree, as opposed to ones where we just couldn't
514
warning('Unable to update the working tree of: %s' % (br_to.base,))
515
count = br_to.pull(br_from, overwrite)
517
count = tree_to.pull(br_from, overwrite)
518
except DivergedBranches:
519
raise BzrCommandError("These branches have diverged."
520
" Try a merge then push with overwrite.")
521
if br_from.get_push_location() is None or remember:
522
br_from.set_push_location(location)
523
note('%d revision(s) pushed.' % (count,))
526
new_rh = br_to.revision_history()
529
from bzrlib.log import show_changed_revisions
530
show_changed_revisions(br_to, old_rh, new_rh)
370
533
class cmd_branch(Command):
592
772
takes_options = ['revision', 'diff-options']
593
773
aliases = ['di', 'dif']
595
776
def run(self, revision=None, file_list=None, diff_options=None):
596
777
from bzrlib.diff import show_diff
599
b = Branch.open_containing(file_list[0])
600
file_list = [b.relpath(f) for f in file_list]
601
if file_list == ['']:
602
# just pointing to top-of-tree
605
b = Branch.open_containing('.')
779
tree, file_list = internal_tree_files(file_list)
782
except FileInWrongBranch:
783
if len(file_list) != 2:
784
raise BzrCommandError("Files are in different branches")
786
b, file1 = Branch.open_containing(file_list[0])
787
b2, file2 = Branch.open_containing(file_list[1])
788
if file1 != "" or file2 != "":
789
# FIXME diff those two files. rbc 20051123
790
raise BzrCommandError("Files are in different branches")
607
792
if revision is not None:
794
raise BzrCommandError("Can't specify -r with two branches")
608
795
if len(revision) == 1:
609
show_diff(b, revision[0], specific_files=file_list,
610
external_diff_options=diff_options)
796
return show_diff(tree.branch, revision[0], specific_files=file_list,
797
external_diff_options=diff_options)
611
798
elif len(revision) == 2:
612
show_diff(b, revision[0], specific_files=file_list,
613
external_diff_options=diff_options,
614
revision2=revision[1])
799
return show_diff(tree.branch, revision[0], specific_files=file_list,
800
external_diff_options=diff_options,
801
revision2=revision[1])
616
803
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
618
show_diff(b, None, specific_files=file_list,
619
external_diff_options=diff_options)
806
return show_diff(b, None, specific_files=file_list,
807
external_diff_options=diff_options, b2=b2)
809
return show_diff(tree.branch, None, specific_files=file_list,
810
external_diff_options=diff_options)
624
813
class cmd_deleted(Command):
680
870
The root is the nearest enclosing directory with a .bzr control
682
872
takes_args = ['filename?']
683
874
def run(self, filename=None):
684
875
"""Print the branch root."""
685
b = Branch.open_containing(filename)
876
tree = WorkingTree.open_containing(filename)[0]
689
880
class cmd_log(Command):
690
881
"""Show log of this branch.
692
To request a range of logs, you can use the command -r begin:end
693
-r revision requests a specific revision, -r :end or -r begin: are
883
To request a range of logs, you can use the command -r begin..end
884
-r revision requests a specific revision, -r ..end or -r begin.. are
696
--message allows you to give a regular expression, which will be evaluated
697
so that only matching entries will be displayed.
700
888
# TODO: Make --revision support uuid: and hash: [future tag:] notation.
702
890
takes_args = ['filename?']
703
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
704
'long', 'message', 'short',]
891
takes_options = [Option('forward',
892
help='show from oldest to newest'),
893
'timezone', 'verbose',
894
'show-ids', 'revision',
897
help='show revisions whose message matches this regexp',
706
902
def run(self, filename=None, timezone='original',
789
1008
# TODO: Take a revision or remote path and list that tree instead.
791
def run(self, revision=None, verbose=False):
792
b = Branch.open_containing('.')
794
tree = b.working_tree()
796
tree = b.revision_tree(revision.in_history(b).rev_id)
1010
takes_options = ['verbose', 'revision',
1011
Option('non-recursive',
1012
help='don\'t recurse into sub-directories'),
1014
help='Print all paths from the root of the branch.'),
1015
Option('unknown', help='Print unknown files'),
1016
Option('versioned', help='Print versioned files'),
1017
Option('ignored', help='Print ignored files'),
1019
Option('null', help='Null separate the files'),
1022
def run(self, revision=None, verbose=False,
1023
non_recursive=False, from_root=False,
1024
unknown=False, versioned=False, ignored=False,
1027
if verbose and null:
1028
raise BzrCommandError('Cannot set both --verbose and --null')
1029
all = not (unknown or versioned or ignored)
1031
selection = {'I':ignored, '?':unknown, 'V':versioned}
1033
tree, relpath = WorkingTree.open_containing(u'.')
1038
if revision is not None:
1039
tree = tree.branch.revision_tree(
1040
revision[0].in_history(tree.branch).rev_id)
797
1041
for fp, fc, kind, fid, entry in tree.list_files():
799
kindch = entry.kind_character()
800
print '%-8s %s%s' % (fc, fp, kindch)
1042
if fp.startswith(relpath):
1043
fp = fp[len(relpath):]
1044
if non_recursive and '/' in fp:
1046
if not all and not selection[fc]:
1049
kindch = entry.kind_character()
1050
print '%-8s %s%s' % (fc, fp, kindch)
1052
sys.stdout.write(fp)
1053
sys.stdout.write('\0')
806
1059
class cmd_unknowns(Command):
807
1060
"""List unknown files."""
809
1063
from bzrlib.osutils import quotefn
810
for f in Branch.open_containing('.').unknowns():
1064
for f in WorkingTree.open_containing(u'.')[0].unknowns():
811
1065
print quotefn(f)
815
1068
class cmd_ignore(Command):
816
1069
"""Ignore a command or pattern.
914
1168
is found exports to a directory (equivalent to --format=dir).
916
1170
Root may be the top directory for tar, tgz and tbz2 formats. If none
917
is given, the top directory will be the root name of the file."""
918
# TODO: list known exporters
1171
is given, the top directory will be the root name of the file.
1173
Note: export of tree with non-ascii filenames to zip is not supported.
1175
Supported formats Autodetected by extension
1176
----------------- -------------------------
1179
tbz2 .tar.bz2, .tbz2
919
1183
takes_args = ['dest']
920
1184
takes_options = ['revision', 'format', 'root']
921
1185
def run(self, dest, revision=None, format=None, root=None):
923
b = Branch.open_containing('.')
1187
from bzrlib.export import export
1188
tree = WorkingTree.open_containing(u'.')[0]
924
1190
if revision is None:
1191
# should be tree.last_revision FIXME
925
1192
rev_id = b.last_revision()
927
1194
if len(revision) != 1:
928
1195
raise BzrError('bzr export --revision takes exactly 1 argument')
929
1196
rev_id = revision[0].in_history(b).rev_id
930
1197
t = b.revision_tree(rev_id)
931
arg_root, ext = os.path.splitext(os.path.basename(dest))
932
if ext in ('.gz', '.bz2'):
933
new_root, new_ext = os.path.splitext(arg_root)
934
if new_ext == '.tar':
942
elif ext in (".tar.gz", ".tgz"):
944
elif ext in (".tar.bz2", ".tbz2"):
948
t.export(dest, format, root)
1199
export(t, dest, format, root)
1200
except errors.NoSuchExportFormat, e:
1201
raise BzrCommandError('Unsupported export format: %s' % e.format)
951
1204
class cmd_cat(Command):
987
1252
# TODO: Run hooks on tree to-be-committed, and after commit.
989
# TODO: Strict commit that fails if there are unknown or deleted files.
1254
# TODO: Strict commit that fails if there are deleted files.
1255
# (what does "deleted files" mean ??)
990
1257
# TODO: Give better message for -s, --summary, used by tla people
992
1259
# XXX: verbose currently does nothing
994
1261
takes_args = ['selected*']
995
takes_options = ['message', 'file', 'verbose', 'unchanged']
1262
takes_options = ['message', 'verbose',
1264
help='commit even if nothing has changed'),
1265
Option('file', type=str,
1267
help='file containing commit message'),
1269
help="refuse to commit if there are unknown "
1270
"files in the working tree."),
996
1272
aliases = ['ci', 'checkin']
998
1274
def run(self, message=None, file=None, verbose=True, selected_list=None,
1000
from bzrlib.errors import PointlessCommit, ConflictsInTree
1001
from bzrlib.msgeditor import edit_commit_message
1275
unchanged=False, strict=False):
1276
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1278
from bzrlib.msgeditor import edit_commit_message, \
1279
make_commit_message_template
1002
1280
from bzrlib.status import show_status
1003
from cStringIO import StringIO
1005
b = Branch.open_containing('.')
1007
selected_list = [b.relpath(s) for s in selected_list]
1281
from tempfile import TemporaryFile
1284
# TODO: Need a blackbox test for invoking the external editor; may be
1285
# slightly problematic to run this cross-platform.
1287
# TODO: do more checks that the commit will succeed before
1288
# spending the user's valuable time typing a commit message.
1290
# TODO: if the commit *does* happen to fail, then save the commit
1291
# message to a temporary file where it can be recovered
1292
tree, selected_list = tree_files(selected_list)
1010
1293
if message is None and not file:
1011
catcher = StringIO()
1012
show_status(b, specific_files=selected_list,
1014
message = edit_commit_message(catcher.getvalue())
1294
template = make_commit_message_template(tree, selected_list)
1295
message = edit_commit_message(template)
1016
1296
if message is None:
1017
1297
raise BzrCommandError("please specify a commit message"
1018
1298
" with either --message or --file")
1632
class cmd_remerge(Command):
1635
takes_args = ['file*']
1636
takes_options = ['merge-type', 'reprocess',
1637
Option('show-base', help="Show base revision text in "
1640
def run(self, file_list=None, merge_type=None, show_base=False,
1642
from bzrlib.merge import merge_inner, transform_tree
1643
from bzrlib.merge_core import ApplyMerge3
1644
if merge_type is None:
1645
merge_type = ApplyMerge3
1646
tree, file_list = tree_files(file_list)
1649
pending_merges = tree.pending_merges()
1650
if len(pending_merges) != 1:
1651
raise BzrCommandError("Sorry, remerge only works after normal"
1652
+ " merges. Not cherrypicking or"
1654
base_revision = common_ancestor(tree.branch.last_revision(),
1655
pending_merges[0], tree.branch)
1656
base_tree = tree.branch.revision_tree(base_revision)
1657
other_tree = tree.branch.revision_tree(pending_merges[0])
1658
interesting_ids = None
1659
if file_list is not None:
1660
interesting_ids = set()
1661
for filename in file_list:
1662
file_id = tree.path2id(filename)
1663
interesting_ids.add(file_id)
1664
if tree.kind(file_id) != "directory":
1667
for name, ie in tree.inventory.iter_entries(file_id):
1668
interesting_ids.add(ie.file_id)
1669
transform_tree(tree, tree.branch.basis_tree(), interesting_ids)
1670
if file_list is None:
1671
restore_files = list(tree.iter_conflicts())
1673
restore_files = file_list
1674
for filename in restore_files:
1676
restore(tree.abspath(filename))
1677
except NotConflicted:
1679
conflicts = merge_inner(tree.branch, other_tree, base_tree,
1680
interesting_ids = interesting_ids,
1681
other_rev_id=pending_merges[0],
1682
merge_type=merge_type,
1683
show_base=show_base,
1684
reprocess=reprocess)
1284
1692
class cmd_revert(Command):
1285
1693
"""Reverse all changes since the last commit.
1359
1768
def run(self, from_branch, to_branch):
1360
1769
from bzrlib.fetch import Fetcher
1361
1770
from bzrlib.branch import Branch
1362
from_b = Branch(from_branch)
1363
to_b = Branch(to_branch)
1364
Fetcher(to_b, from_b)
1771
from_b = Branch.open(from_branch)
1772
to_b = Branch.open(to_branch)
1777
Fetcher(to_b, from_b)
1368
1784
class cmd_missing(Command):
1369
"""What is missing in this branch relative to other branch.
1371
# TODO: rewrite this in terms of ancestry so that it shows only
1374
takes_args = ['remote?']
1375
aliases = ['mis', 'miss']
1376
# We don't have to add quiet to the list, because
1377
# unknown options are parsed as booleans
1378
takes_options = ['verbose', 'quiet']
1380
def run(self, remote=None, verbose=False, quiet=False):
1381
from bzrlib.errors import BzrCommandError
1382
from bzrlib.missing import show_missing
1384
if verbose and quiet:
1385
raise BzrCommandError('Cannot pass both quiet and verbose')
1387
b = Branch.open_containing('.')
1388
parent = b.get_parent()
1785
"""Show unmerged/unpulled revisions between two branches.
1787
OTHER_BRANCH may be local or remote."""
1788
takes_args = ['other_branch?']
1789
takes_options = [Option('reverse', 'Reverse the order of revisions'),
1791
'Display changes in the local branch only'),
1792
Option('theirs-only',
1793
'Display changes in the remote branch only'),
1801
def run(self, other_branch=None, reverse=False, mine_only=False,
1802
theirs_only=False, long=True, short=False, line=False,
1803
show_ids=False, verbose=False):
1804
from bzrlib.missing import find_unmerged, iter_log_data
1805
from bzrlib.log import log_formatter
1806
local_branch = bzrlib.branch.Branch.open_containing(u".")[0]
1807
parent = local_branch.get_parent()
1808
if other_branch is None:
1809
other_branch = parent
1810
if other_branch is None:
1391
1811
raise BzrCommandError("No missing location known or specified.")
1394
print "Using last location: %s" % parent
1396
elif parent is None:
1397
# We only update parent if it did not exist, missing
1398
# should not change the parent
1399
b.set_parent(remote)
1400
br_remote = Branch.open_containing(remote)
1401
return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1812
print "Using last location: " + local_branch.get_parent()
1813
remote_branch = bzrlib.branch.Branch.open(other_branch)
1814
local_extra, remote_extra = find_unmerged(local_branch, remote_branch)
1815
log_format = get_log_format(long=long, short=short, line=line)
1816
lf = log_formatter(log_format, sys.stdout,
1818
show_timezone='original')
1819
if reverse is False:
1820
local_extra.reverse()
1821
remote_extra.reverse()
1822
if local_extra and not theirs_only:
1823
print "You have %d extra revision(s):" % len(local_extra)
1824
for data in iter_log_data(local_extra, local_branch, verbose):
1826
printed_local = True
1828
printed_local = False
1829
if remote_extra and not mine_only:
1830
if printed_local is True:
1832
print "You are missing %d revision(s):" % len(remote_extra)
1833
for data in iter_log_data(remote_extra, remote_branch, verbose):
1835
if not remote_extra and not local_extra:
1837
print "Branches are up to date."
1840
if parent is None and other_branch is not None:
1841
local_branch.set_parent(other_branch)
1404
1845
class cmd_plugins(Command):
1405
1846
"""List plugins"""
1408
1850
import bzrlib.plugin
1409
1851
from inspect import getdoc
1410
for plugin in bzrlib.plugin.all_plugins:
1852
for name, plugin in bzrlib.plugin.all_plugins().items():
1411
1853
if hasattr(plugin, '__path__'):
1412
1854
print plugin.__path__[0]
1413
1855
elif hasattr(plugin, '__file__'):
1445
1888
class cmd_annotate(Command):
1446
1889
"""Show the origin of each line in a file.
1448
This prints out the given file with an annotation on the
1449
left side indicating which revision, author and date introduced the
1891
This prints out the given file with an annotation on the left side
1892
indicating which revision, author and date introduced the change.
1894
If the origin is the same for a run of consecutive lines, it is
1895
shown only at the top, unless the --all option is given.
1452
1897
# TODO: annotate directories; showing when each file was last changed
1453
1898
# TODO: annotate a previous version of a file
1899
# TODO: if the working copy is modified, show annotations on that
1900
# with new uncommitted lines marked
1454
1901
aliases = ['blame', 'praise']
1455
1902
takes_args = ['filename']
1903
takes_options = [Option('all', help='show annotations on all lines'),
1904
Option('long', help='show date in annotations'),
1457
def run(self, filename):
1908
def run(self, filename, all=False, long=False):
1458
1909
from bzrlib.annotate import annotate_file
1459
b = Branch.open_containing(filename)
1910
tree, relpath = WorkingTree.open_containing(filename)
1911
branch = tree.branch
1462
rp = b.relpath(filename)
1463
tree = b.revision_tree(b.last_revision())
1464
file_id = tree.inventory.path2id(rp)
1914
file_id = tree.inventory.path2id(relpath)
1915
tree = branch.revision_tree(branch.last_revision())
1465
1916
file_version = tree.inventory[file_id].revision
1466
annotate_file(b, file_version, file_id, sys.stdout)
1917
annotate_file(branch, file_version, file_id, long, all, sys.stdout)
1922
class cmd_re_sign(Command):
1923
"""Create a digital signature for an existing revision."""
1924
# TODO be able to replace existing ones.
1926
hidden = True # is this right ?
1927
takes_args = ['revision_id?']
1928
takes_options = ['revision']
1930
def run(self, revision_id=None, revision=None):
1931
import bzrlib.config as config
1932
import bzrlib.gpg as gpg
1933
if revision_id is not None and revision is not None:
1934
raise BzrCommandError('You can only supply one of revision_id or --revision')
1935
if revision_id is None and revision is None:
1936
raise BzrCommandError('You must supply either --revision or a revision_id')
1937
b = WorkingTree.open_containing(u'.')[0].branch
1938
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1939
if revision_id is not None:
1940
b.sign_revision(revision_id, gpg_strategy)
1941
elif revision is not None:
1942
if len(revision) == 1:
1943
revno, rev_id = revision[0].in_history(b)
1944
b.sign_revision(rev_id, gpg_strategy)
1945
elif len(revision) == 2:
1946
# are they both on rh- if so we can walk between them
1947
# might be nice to have a range helper for arbitrary
1948
# revision paths. hmm.
1949
from_revno, from_revid = revision[0].in_history(b)
1950
to_revno, to_revid = revision[1].in_history(b)
1951
if to_revid is None:
1952
to_revno = b.revno()
1953
if from_revno is None or to_revno is None:
1954
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1955
for revno in range(from_revno, to_revno + 1):
1956
b.sign_revision(b.get_rev_id(revno), gpg_strategy)
1958
raise BzrCommandError('Please supply either one revision, or a range.')
1961
class cmd_uncommit(bzrlib.commands.Command):
1962
"""Remove the last committed revision.
1964
By supplying the --all flag, it will not only remove the entry
1965
from revision_history, but also remove all of the entries in the
1968
--verbose will print out what is being removed.
1969
--dry-run will go through all the motions, but not actually
1972
In the future, uncommit will create a changeset, which can then
1975
takes_options = ['all', 'verbose', 'revision',
1976
Option('dry-run', help='Don\'t actually make changes'),
1977
Option('force', help='Say yes to all questions.')]
1978
takes_args = ['location?']
1981
def run(self, location=None, all=False,
1982
dry_run=False, verbose=False,
1983
revision=None, force=False):
1984
from bzrlib.branch import Branch
1985
from bzrlib.log import log_formatter
1987
from bzrlib.uncommit import uncommit
1989
if location is None:
1991
b, relpath = Branch.open_containing(location)
1993
if revision is None:
1995
rev_id = b.last_revision()
1997
revno, rev_id = revision[0].in_history(b)
1999
print 'No revisions to uncommit.'
2001
for r in range(revno, b.revno()+1):
2002
rev_id = b.get_rev_id(r)
2003
lf = log_formatter('short', to_file=sys.stdout,show_timezone='original')
2004
lf.show(r, b.get_revision(rev_id), None)
2007
print 'Dry-run, pretending to remove the above revisions.'
2009
val = raw_input('Press <enter> to continue')
2011
print 'The above revision(s) will be removed.'
2013
val = raw_input('Are you sure [y/N]? ')
2014
if val.lower() not in ('y', 'yes'):
2018
uncommit(b, remove_files=all,
2019
dry_run=dry_run, verbose=verbose,
1470
2023
# these get imported and then picked up by the scan for cmd_*
1471
2024
# TODO: Some more consistent way to split command definitions across files;
1472
2025
# we do need to load at least some information about them to know of
1474
from bzrlib.conflicts import cmd_resolve, cmd_conflicts
2027
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore