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"""
24
from bzrlib import BZRDIR
25
from bzrlib._merge_core import ApplyMerge3
26
from bzrlib.commands import Command, display_command
27
from bzrlib.branch import Branch
28
from bzrlib.revision import common_ancestor
29
import bzrlib.errors as errors
30
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError,
31
NotBranchError, DivergedBranches, NotConflicted,
32
NoSuchFile, NoWorkingTree, FileInWrongBranch)
33
from bzrlib.option import Option
34
from bzrlib.revisionspec import RevisionSpec
22
35
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
36
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
37
from bzrlib.workingtree import WorkingTree
38
from bzrlib.log import show_one_log
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
31
72
class cmd_status(Command):
32
73
"""Display status summary.
177
213
implicitly add the parent, and so on up to the root. This means
178
214
you should never need to explictly add a directory, they'll just
179
215
get added when you add a file in the directory.
217
--dry-run will show which files would be added, but not actually
181
220
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
221
takes_options = ['no-recurse', 'dry-run', 'verbose']
223
def run(self, file_list, no_recurse=False, dry_run=False, verbose=False):
228
# This is pointless, but I'd rather not raise an error
229
action = bzrlib.add.add_action_null
231
action = bzrlib.add.add_action_print
233
action = bzrlib.add.add_action_add
189
reporter = add_reporter_print
190
smart_add(file_list, not no_recurse, reporter)
235
action = bzrlib.add.add_action_add_and_print
237
added, ignored = bzrlib.add.smart_add(file_list, not no_recurse,
240
for glob in sorted(ignored.keys()):
241
match_len = len(ignored[glob])
243
for path in ignored[glob]:
244
print "ignored %s matching \"%s\"" % (path, glob)
246
print "ignored %d file(s) matching \"%s\"" % (match_len,
248
print "If you wish to add some of these files, please add them"\
193
252
class cmd_mkdir(Command):
213
269
takes_args = ['filename']
216
273
def run(self, filename):
217
print Branch.open_containing(filename).relpath(filename)
274
tree, relpath = WorkingTree.open_containing(filename)
221
278
class cmd_inventory(Command):
222
"""Show inventory of the current working copy or a revision."""
223
takes_options = ['revision', 'show-ids']
279
"""Show inventory of the current working copy or a revision.
281
It is possible to limit the output to a particular entry
282
type using the --kind option. For example; --kind file.
284
takes_options = ['revision', 'show-ids', 'kind']
225
def run(self, revision=None, show_ids=False):
226
b = Branch.open_containing('.')
287
def run(self, revision=None, show_ids=False, kind=None):
288
if kind and kind not in ['file', 'directory', 'symlink']:
289
raise BzrCommandError('invalid kind specified')
290
tree = WorkingTree.open_containing(u'.')[0]
227
291
if revision is None:
228
inv = b.read_working_inventory()
292
inv = tree.read_working_inventory()
230
294
if len(revision) > 1:
231
295
raise BzrCommandError('bzr inventory --revision takes'
232
296
' exactly one revision identifier')
233
inv = b.get_revision_inventory(revision[0].in_history(b).rev_id)
297
inv = tree.branch.repository.get_revision_inventory(
298
revision[0].in_history(tree.branch).rev_id)
235
300
for path, entry in inv.entries():
301
if kind and kind != entry.kind:
237
304
print '%-50s %s' % (path, entry.file_id)
294
359
def run(self, names_list):
295
360
if len(names_list) < 2:
296
361
raise BzrCommandError("missing file argument")
297
b = Branch.open_containing(names_list[0])
299
rel_names = [b.relpath(x) for x in names_list]
362
tree, rel_names = tree_files(names_list)
301
364
if os.path.isdir(names_list[-1]):
302
365
# move into existing directory
303
for pair in b.move(rel_names[:-1], rel_names[-1]):
366
for pair in tree.move(rel_names[:-1], rel_names[-1]):
304
367
print "%s => %s" % pair
306
369
if len(names_list) != 2:
307
370
raise BzrCommandError('to mv multiple files the destination '
308
371
'must be a versioned directory')
309
b.rename_one(rel_names[0], rel_names[1])
372
tree.rename_one(rel_names[0], rel_names[1])
310
373
print "%s => %s" % (rel_names[0], rel_names[1])
315
376
class cmd_pull(Command):
316
377
"""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
379
If there is no default location set, the first pull will set it. After
380
that, you can omit the location to use the default. To change the
381
default, use --remember.
322
383
This command only works on branches that have not diverged. Branches are
323
384
considered diverged if both branches have had commits without first
324
385
pulling from the other.
326
387
If branches have diverged, you can use 'bzr merge' to pull the text changes
327
from one into the other.
388
from one into the other. Once one branch has merged, the other should
389
be able to pull it again.
391
If you want to forget your local changes and just update your branch to
392
match the remote one, use --overwrite.
329
takes_options = ['remember']
394
takes_options = ['remember', 'overwrite', 'verbose']
330
395
takes_args = ['location?']
332
def run(self, location=None, remember=False):
333
from bzrlib.merge import merge
397
def run(self, location=None, remember=False, overwrite=False, verbose=False):
335
398
from shutil import rmtree
338
br_to = Branch.open_containing('.')
339
stored_loc = br_to.get_parent()
400
# FIXME: too much stuff is in the command class
401
tree_to = WorkingTree.open_containing(u'.')[0]
402
stored_loc = tree_to.branch.get_parent()
340
403
if location is None:
341
404
if stored_loc is None:
342
405
raise BzrCommandError("No pull location known or specified.")
344
407
print "Using saved location: %s" % stored_loc
345
408
location = stored_loc
346
cache_root = tempfile.mkdtemp()
347
410
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()
411
br_to = tree_to.branch
413
old_rh = br_to.revision_history()
414
count = tree_to.pull(br_from, overwrite)
416
if br_to.get_parent() is None or remember:
417
br_to.set_parent(location)
418
note('%d revision(s) pulled.' % (count,))
421
new_rh = tree_to.branch.revision_history()
424
from bzrlib.log import show_changed_revisions
425
show_changed_revisions(tree_to.branch, old_rh, new_rh)
428
class cmd_push(Command):
429
"""Push this branch into another branch.
431
The remote branch will not have its working tree populated because this
432
is both expensive, and may not be supported on the remote file system.
434
Some smart servers or protocols *may* put the working tree in place.
436
If there is no default push location set, the first push will set it.
437
After that, you can omit the location to use the default. To change the
438
default, use --remember.
440
This command only works on branches that have not diverged. Branches are
441
considered diverged if the branch being pushed to is not an older version
444
If branches have diverged, you can use 'bzr push --overwrite' to replace
445
the other branch completely.
447
If you want to ensure you have the different changes in the other branch,
448
do a merge (see bzr help merge) from the other branch, and commit that
449
before doing a 'push --overwrite'.
451
takes_options = ['remember', 'overwrite',
452
Option('create-prefix',
453
help='Create the path leading up to the branch '
454
'if it does not already exist')]
455
takes_args = ['location?']
457
def run(self, location=None, remember=False, overwrite=False,
458
create_prefix=False, verbose=False):
459
# FIXME: Way too big! Put this into a function called from the
462
from shutil import rmtree
463
from bzrlib.transport import get_transport
465
tree_from = WorkingTree.open_containing(u'.')[0]
466
br_from = tree_from.branch
467
stored_loc = tree_from.branch.get_push_location()
469
if stored_loc is None:
470
raise BzrCommandError("No push location known or specified.")
472
print "Using saved location: %s" % stored_loc
473
location = stored_loc
475
br_to = Branch.open(location)
476
except NotBranchError:
478
transport = get_transport(location).clone('..')
479
if not create_prefix:
481
transport.mkdir(transport.relpath(location))
483
raise BzrCommandError("Parent directory of %s "
484
"does not exist." % location)
486
current = transport.base
487
needed = [(transport, transport.relpath(location))]
490
transport, relpath = needed[-1]
491
transport.mkdir(relpath)
494
new_transport = transport.clone('..')
495
needed.append((new_transport,
496
new_transport.relpath(transport.base)))
497
if new_transport.base == transport.base:
498
raise BzrCommandError("Could not creeate "
500
br_to = Branch.initialize(location)
501
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)
504
tree_to = br_to.working_tree()
505
except NoWorkingTree:
506
# TODO: This should be updated for branches which don't have a
507
# working tree, as opposed to ones where we just couldn't
509
warning('Unable to update the working tree of: %s' % (br_to.base,))
510
count = br_to.pull(br_from, overwrite)
512
count = tree_to.pull(br_from, overwrite)
513
except DivergedBranches:
514
raise BzrCommandError("These branches have diverged."
515
" Try a merge then push with overwrite.")
516
if br_from.get_push_location() is None or remember:
517
br_from.set_push_location(location)
518
note('%d revision(s) pushed.' % (count,))
521
new_rh = br_to.revision_history()
524
from bzrlib.log import show_changed_revisions
525
show_changed_revisions(br_to, old_rh, new_rh)
370
528
class cmd_branch(Command):
592
766
takes_options = ['revision', 'diff-options']
593
767
aliases = ['di', 'dif']
595
770
def run(self, revision=None, file_list=None, diff_options=None):
596
771
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('.')
773
tree, file_list = internal_tree_files(file_list)
776
except FileInWrongBranch:
777
if len(file_list) != 2:
778
raise BzrCommandError("Files are in different branches")
780
b, file1 = Branch.open_containing(file_list[0])
781
b2, file2 = Branch.open_containing(file_list[1])
782
if file1 != "" or file2 != "":
783
# FIXME diff those two files. rbc 20051123
784
raise BzrCommandError("Files are in different branches")
607
786
if revision is not None:
608
if len(revision) == 1:
609
show_diff(b, revision[0], specific_files=file_list,
610
external_diff_options=diff_options)
788
raise BzrCommandError("Can't specify -r with two branches")
789
if (len(revision) == 1) or (revision[1].spec is None):
790
return show_diff(tree.branch, revision[0], specific_files=file_list,
791
external_diff_options=diff_options)
611
792
elif len(revision) == 2:
612
show_diff(b, revision[0], specific_files=file_list,
613
external_diff_options=diff_options,
614
revision2=revision[1])
793
return show_diff(tree.branch, revision[0], specific_files=file_list,
794
external_diff_options=diff_options,
795
revision2=revision[1])
616
797
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)
800
return show_diff(b, None, specific_files=file_list,
801
external_diff_options=diff_options, b2=b2)
803
return show_diff(tree.branch, None, specific_files=file_list,
804
external_diff_options=diff_options)
624
807
class cmd_deleted(Command):
789
1002
# 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)
1004
takes_options = ['verbose', 'revision',
1005
Option('non-recursive',
1006
help='don\'t recurse into sub-directories'),
1008
help='Print all paths from the root of the branch.'),
1009
Option('unknown', help='Print unknown files'),
1010
Option('versioned', help='Print versioned files'),
1011
Option('ignored', help='Print ignored files'),
1013
Option('null', help='Null separate the files'),
1016
def run(self, revision=None, verbose=False,
1017
non_recursive=False, from_root=False,
1018
unknown=False, versioned=False, ignored=False,
1021
if verbose and null:
1022
raise BzrCommandError('Cannot set both --verbose and --null')
1023
all = not (unknown or versioned or ignored)
1025
selection = {'I':ignored, '?':unknown, 'V':versioned}
1027
tree, relpath = WorkingTree.open_containing(u'.')
1032
if revision is not None:
1033
tree = tree.branch.repository.revision_tree(
1034
revision[0].in_history(tree.branch).rev_id)
797
1035
for fp, fc, kind, fid, entry in tree.list_files():
799
kindch = entry.kind_character()
800
print '%-8s %s%s' % (fc, fp, kindch)
1036
if fp.startswith(relpath):
1037
fp = fp[len(relpath):]
1038
if non_recursive and '/' in fp:
1040
if not all and not selection[fc]:
1043
kindch = entry.kind_character()
1044
print '%-8s %s%s' % (fc, fp, kindch)
1046
sys.stdout.write(fp)
1047
sys.stdout.write('\0')
806
1053
class cmd_unknowns(Command):
807
1054
"""List unknown files."""
809
1057
from bzrlib.osutils import quotefn
810
for f in Branch.open_containing('.').unknowns():
1058
for f in WorkingTree.open_containing(u'.')[0].unknowns():
811
1059
print quotefn(f)
815
1062
class cmd_ignore(Command):
816
1063
"""Ignore a command or pattern.
914
1162
is found exports to a directory (equivalent to --format=dir).
916
1164
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
1165
is given, the top directory will be the root name of the file.
1167
Note: export of tree with non-ascii filenames to zip is not supported.
1169
Supported formats Autodetected by extension
1170
----------------- -------------------------
1173
tbz2 .tar.bz2, .tbz2
919
1177
takes_args = ['dest']
920
1178
takes_options = ['revision', 'format', 'root']
921
1179
def run(self, dest, revision=None, format=None, root=None):
923
b = Branch.open_containing('.')
1181
from bzrlib.export import export
1182
tree = WorkingTree.open_containing(u'.')[0]
924
1184
if revision is None:
1185
# should be tree.last_revision FIXME
925
1186
rev_id = b.last_revision()
927
1188
if len(revision) != 1:
928
1189
raise BzrError('bzr export --revision takes exactly 1 argument')
929
1190
rev_id = revision[0].in_history(b).rev_id
930
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)
1191
t = b.repository.revision_tree(rev_id)
1193
export(t, dest, format, root)
1194
except errors.NoSuchExportFormat, e:
1195
raise BzrCommandError('Unsupported export format: %s' % e.format)
951
1198
class cmd_cat(Command):
987
1246
# TODO: Run hooks on tree to-be-committed, and after commit.
989
# TODO: Strict commit that fails if there are unknown or deleted files.
1248
# TODO: Strict commit that fails if there are deleted files.
1249
# (what does "deleted files" mean ??)
990
1251
# TODO: Give better message for -s, --summary, used by tla people
992
1253
# XXX: verbose currently does nothing
994
1255
takes_args = ['selected*']
995
takes_options = ['message', 'file', 'verbose', 'unchanged']
1256
takes_options = ['message', 'verbose',
1258
help='commit even if nothing has changed'),
1259
Option('file', type=str,
1261
help='file containing commit message'),
1263
help="refuse to commit if there are unknown "
1264
"files in the working tree."),
996
1266
aliases = ['ci', 'checkin']
998
1268
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
1269
unchanged=False, strict=False):
1270
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1272
from bzrlib.msgeditor import edit_commit_message, \
1273
make_commit_message_template
1002
1274
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]
1275
from tempfile import TemporaryFile
1278
# TODO: Need a blackbox test for invoking the external editor; may be
1279
# slightly problematic to run this cross-platform.
1281
# TODO: do more checks that the commit will succeed before
1282
# spending the user's valuable time typing a commit message.
1284
# TODO: if the commit *does* happen to fail, then save the commit
1285
# message to a temporary file where it can be recovered
1286
tree, selected_list = tree_files(selected_list)
1010
1287
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())
1288
template = make_commit_message_template(tree, selected_list)
1289
message = edit_commit_message(template)
1016
1290
if message is None:
1017
1291
raise BzrCommandError("please specify a commit message"
1018
1292
" with either --message or --file")
1625
class cmd_remerge(Command):
1628
takes_args = ['file*']
1629
takes_options = ['merge-type', 'reprocess',
1630
Option('show-base', help="Show base revision text in "
1633
def run(self, file_list=None, merge_type=None, show_base=False,
1635
from bzrlib.merge import merge_inner, transform_tree
1636
from bzrlib._merge_core import ApplyMerge3
1637
if merge_type is None:
1638
merge_type = ApplyMerge3
1639
tree, file_list = tree_files(file_list)
1642
pending_merges = tree.pending_merges()
1643
if len(pending_merges) != 1:
1644
raise BzrCommandError("Sorry, remerge only works after normal"
1645
+ " merges. Not cherrypicking or"
1647
repository = tree.branch.repository
1648
base_revision = common_ancestor(tree.branch.last_revision(),
1649
pending_merges[0], repository)
1650
base_tree = repository.revision_tree(base_revision)
1651
other_tree = repository.revision_tree(pending_merges[0])
1652
interesting_ids = None
1653
if file_list is not None:
1654
interesting_ids = set()
1655
for filename in file_list:
1656
file_id = tree.path2id(filename)
1657
interesting_ids.add(file_id)
1658
if tree.kind(file_id) != "directory":
1661
for name, ie in tree.inventory.iter_entries(file_id):
1662
interesting_ids.add(ie.file_id)
1663
transform_tree(tree, tree.branch.basis_tree(), interesting_ids)
1664
if file_list is None:
1665
restore_files = list(tree.iter_conflicts())
1667
restore_files = file_list
1668
for filename in restore_files:
1670
restore(tree.abspath(filename))
1671
except NotConflicted:
1673
conflicts = merge_inner(tree.branch, other_tree, base_tree,
1674
interesting_ids = interesting_ids,
1675
other_rev_id=pending_merges[0],
1676
merge_type=merge_type,
1677
show_base=show_base,
1678
reprocess=reprocess)
1284
1686
class cmd_revert(Command):
1285
1687
"""Reverse all changes since the last commit.
1359
1760
def run(self, from_branch, to_branch):
1360
1761
from bzrlib.fetch import Fetcher
1361
1762
from bzrlib.branch import Branch
1362
from_b = Branch(from_branch)
1363
to_b = Branch(to_branch)
1763
from_b = Branch.open(from_branch)
1764
to_b = Branch.open(to_branch)
1364
1765
Fetcher(to_b, from_b)
1368
1768
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()
1769
"""Show unmerged/unpulled revisions between two branches.
1771
OTHER_BRANCH may be local or remote."""
1772
takes_args = ['other_branch?']
1773
takes_options = [Option('reverse', 'Reverse the order of revisions'),
1775
'Display changes in the local branch only'),
1776
Option('theirs-only',
1777
'Display changes in the remote branch only'),
1785
def run(self, other_branch=None, reverse=False, mine_only=False,
1786
theirs_only=False, long=True, short=False, line=False,
1787
show_ids=False, verbose=False):
1788
from bzrlib.missing import find_unmerged, iter_log_data
1789
from bzrlib.log import log_formatter
1790
local_branch = bzrlib.branch.Branch.open_containing(u".")[0]
1791
parent = local_branch.get_parent()
1792
if other_branch is None:
1793
other_branch = parent
1794
if other_branch is None:
1391
1795
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)
1796
print "Using last location: " + local_branch.get_parent()
1797
remote_branch = bzrlib.branch.Branch.open(other_branch)
1798
local_extra, remote_extra = find_unmerged(local_branch, remote_branch)
1799
log_format = get_log_format(long=long, short=short, line=line)
1800
lf = log_formatter(log_format, sys.stdout,
1802
show_timezone='original')
1803
if reverse is False:
1804
local_extra.reverse()
1805
remote_extra.reverse()
1806
if local_extra and not theirs_only:
1807
print "You have %d extra revision(s):" % len(local_extra)
1808
for data in iter_log_data(local_extra, local_branch.repository,
1811
printed_local = True
1813
printed_local = False
1814
if remote_extra and not mine_only:
1815
if printed_local is True:
1817
print "You are missing %d revision(s):" % len(remote_extra)
1818
for data in iter_log_data(remote_extra, remote_branch.repository,
1821
if not remote_extra and not local_extra:
1823
print "Branches are up to date."
1826
if parent is None and other_branch is not None:
1827
local_branch.set_parent(other_branch)
1404
1831
class cmd_plugins(Command):
1405
1832
"""List plugins"""
1408
1836
import bzrlib.plugin
1409
1837
from inspect import getdoc
1410
for plugin in bzrlib.plugin.all_plugins:
1838
for name, plugin in bzrlib.plugin.all_plugins().items():
1411
1839
if hasattr(plugin, '__path__'):
1412
1840
print plugin.__path__[0]
1413
1841
elif hasattr(plugin, '__file__'):
1445
1874
class cmd_annotate(Command):
1446
1875
"""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
1877
This prints out the given file with an annotation on the left side
1878
indicating which revision, author and date introduced the change.
1880
If the origin is the same for a run of consecutive lines, it is
1881
shown only at the top, unless the --all option is given.
1452
1883
# TODO: annotate directories; showing when each file was last changed
1453
1884
# TODO: annotate a previous version of a file
1885
# TODO: if the working copy is modified, show annotations on that
1886
# with new uncommitted lines marked
1454
1887
aliases = ['blame', 'praise']
1455
1888
takes_args = ['filename']
1889
takes_options = [Option('all', help='show annotations on all lines'),
1890
Option('long', help='show date in annotations'),
1457
def run(self, filename):
1894
def run(self, filename, all=False, long=False):
1458
1895
from bzrlib.annotate import annotate_file
1459
b = Branch.open_containing(filename)
1896
tree, relpath = WorkingTree.open_containing(filename)
1897
branch = tree.branch
1462
rp = b.relpath(filename)
1463
tree = b.revision_tree(b.last_revision())
1464
file_id = tree.inventory.path2id(rp)
1900
file_id = tree.inventory.path2id(relpath)
1901
tree = branch.repository.revision_tree(branch.last_revision())
1465
1902
file_version = tree.inventory[file_id].revision
1466
annotate_file(b, file_version, file_id, sys.stdout)
1903
annotate_file(branch, file_version, file_id, long, all, sys.stdout)
1908
class cmd_re_sign(Command):
1909
"""Create a digital signature for an existing revision."""
1910
# TODO be able to replace existing ones.
1912
hidden = True # is this right ?
1913
takes_args = ['revision_id?']
1914
takes_options = ['revision']
1916
def run(self, revision_id=None, revision=None):
1917
import bzrlib.config as config
1918
import bzrlib.gpg as gpg
1919
if revision_id is not None and revision is not None:
1920
raise BzrCommandError('You can only supply one of revision_id or --revision')
1921
if revision_id is None and revision is None:
1922
raise BzrCommandError('You must supply either --revision or a revision_id')
1923
b = WorkingTree.open_containing(u'.')[0].branch
1924
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1925
if revision_id is not None:
1926
b.repository.sign_revision(revision_id, gpg_strategy)
1927
elif revision is not None:
1928
if len(revision) == 1:
1929
revno, rev_id = revision[0].in_history(b)
1930
b.repository.sign_revision(rev_id, gpg_strategy)
1931
elif len(revision) == 2:
1932
# are they both on rh- if so we can walk between them
1933
# might be nice to have a range helper for arbitrary
1934
# revision paths. hmm.
1935
from_revno, from_revid = revision[0].in_history(b)
1936
to_revno, to_revid = revision[1].in_history(b)
1937
if to_revid is None:
1938
to_revno = b.revno()
1939
if from_revno is None or to_revno is None:
1940
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1941
for revno in range(from_revno, to_revno + 1):
1942
b.repository.sign_revision(b.get_rev_id(revno),
1945
raise BzrCommandError('Please supply either one revision, or a range.')
1948
class cmd_uncommit(bzrlib.commands.Command):
1949
"""Remove the last committed revision.
1951
By supplying the --all flag, it will not only remove the entry
1952
from revision_history, but also remove all of the entries in the
1955
--verbose will print out what is being removed.
1956
--dry-run will go through all the motions, but not actually
1959
In the future, uncommit will create a changeset, which can then
1962
TODO: jam 20060108 Add an option to allow uncommit to remove unreferenced
1963
information in 'branch-as-repostory' branches.
1964
TODO: jam 20060108 Add the ability for uncommit to remove unreferenced
1965
information in shared branches as well.
1967
takes_options = ['verbose', 'revision',
1968
Option('dry-run', help='Don\'t actually make changes'),
1969
Option('force', help='Say yes to all questions.')]
1970
takes_args = ['location?']
1973
def run(self, location=None,
1974
dry_run=False, verbose=False,
1975
revision=None, force=False):
1976
from bzrlib.branch import Branch
1977
from bzrlib.log import log_formatter
1979
from bzrlib.uncommit import uncommit
1981
if location is None:
1983
b, relpath = Branch.open_containing(location)
1985
if revision is None:
1987
rev_id = b.last_revision()
1989
revno, rev_id = revision[0].in_history(b)
1991
print 'No revisions to uncommit.'
1993
for r in range(revno, b.revno()+1):
1994
rev_id = b.get_rev_id(r)
1995
lf = log_formatter('short', to_file=sys.stdout,show_timezone='original')
1996
lf.show(r, b.repository.get_revision(rev_id), None)
1999
print 'Dry-run, pretending to remove the above revisions.'
2001
val = raw_input('Press <enter> to continue')
2003
print 'The above revision(s) will be removed.'
2005
val = raw_input('Are you sure [y/N]? ')
2006
if val.lower() not in ('y', 'yes'):
2010
uncommit(b, dry_run=dry_run, verbose=verbose,
2014
def merge(other_revision, base_revision,
2015
check_clean=True, ignore_zero=False,
2016
this_dir=None, backup_files=False, merge_type=ApplyMerge3,
2017
file_list=None, show_base=False, reprocess=False):
2018
"""Merge changes into a tree.
2021
list(path, revno) Base for three-way merge.
2022
If [None, None] then a base will be automatically determined.
2024
list(path, revno) Other revision for three-way merge.
2026
Directory to merge changes into; '.' by default.
2028
If true, this_dir must have no uncommitted changes before the
2030
ignore_zero - If true, suppress the "zero conflicts" message when
2031
there are no conflicts; should be set when doing something we expect
2032
to complete perfectly.
2033
file_list - If supplied, merge only changes to selected files.
2035
All available ancestors of other_revision and base_revision are
2036
automatically pulled into the branch.
2038
The revno may be -1 to indicate the last revision on the branch, which is
2041
This function is intended for use from the command line; programmatic
2042
clients might prefer to call merge.merge_inner(), which has less magic
2045
from bzrlib.merge import Merger, _MergeConflictHandler
2046
if this_dir is None:
2048
this_branch = Branch.open_containing(this_dir)[0]
2049
if show_base and not merge_type is ApplyMerge3:
2050
raise BzrCommandError("Show-base is not supported for this merge"
2051
" type. %s" % merge_type)
2052
if reprocess and not merge_type is ApplyMerge3:
2053
raise BzrCommandError("Reprocess is not supported for this merge"
2054
" type. %s" % merge_type)
2055
if reprocess and show_base:
2056
raise BzrCommandError("Cannot reprocess and show base.")
2057
merger = Merger(this_branch)
2058
merger.check_basis(check_clean)
2059
merger.set_other(other_revision)
2060
merger.set_base(base_revision)
2061
if merger.base_rev_id == merger.other_rev_id:
2062
note('Nothing to do.')
2064
merger.backup_files = backup_files
2065
merger.merge_type = merge_type
2066
merger.set_interesting_files(file_list)
2067
merger.show_base = show_base
2068
merger.reprocess = reprocess
2069
merger.conflict_handler = _MergeConflictHandler(merger.this_tree,
2072
ignore_zero=ignore_zero)
2073
conflicts = merger.do_merge()
2074
merger.set_pending()
1470
2078
# these get imported and then picked up by the scan for cmd_*
1471
2079
# TODO: Some more consistent way to split command definitions across files;
1472
2080
# we do need to load at least some information about them to know of
1474
from bzrlib.conflicts import cmd_resolve, cmd_conflicts
2082
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore