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
from bzrlib import BZRDIR
27
from bzrlib.commands import Command, display_command
28
from bzrlib.branch import Branch
29
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
30
from bzrlib.errors import DivergedBranches
31
from bzrlib.option import Option
32
from bzrlib.revisionspec import RevisionSpec
33
22
import bzrlib.trace
34
23
from bzrlib.trace import mutter, note, log_error, warning
35
from bzrlib.workingtree import WorkingTree
38
def branch_files(file_list, default_branch='.'):
40
Return a branch and list of branch-relative paths.
41
If supplied file_list is empty or None, the branch default will be used,
42
and returned file_list will match the original.
44
if file_list is None or len(file_list) == 0:
45
return Branch.open_containing(default_branch)[0], file_list
46
b = Branch.open_containing(file_list[0])[0]
48
# note that if this is a remote branch, we would want
49
# relpath against the transport. RBC 20051018
50
# Most branch ops can't meaningfully operate on files in remote branches;
51
# the above comment was in cmd_status. ADHB 20051026
52
tree = WorkingTree(b.base, b)
54
for filename in file_list:
56
new_list.append(tree.relpath(filename))
57
except NotBranchError:
58
raise BzrCommandError("%s is not in the same branch as %s" %
59
(filename, file_list[0]))
63
# TODO: Make sure no commands unconditionally use the working directory as a
64
# branch. If a filename argument is used, the first of them should be used to
65
# specify the branch. (Perhaps this can be factored out into some kind of
66
# Argument class, representing a file in a branch, where the first occurrence
24
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
25
from bzrlib.branch import Branch
26
from bzrlib import BZRDIR
27
from bzrlib.commands import Command
69
30
class cmd_status(Command):
70
31
"""Display status summary.
105
66
If a revision argument is given, the status is calculated against
106
67
that revision, or between two revisions if two are provided.
109
69
# XXX: FIXME: bzr status should accept a -r option to show changes
110
70
# relative to a revision, or between revisions
112
# TODO: --no-recurse, --recurse options
114
72
takes_args = ['file*']
115
takes_options = ['all', 'show-ids']
73
takes_options = ['all', 'show-ids', 'revision']
116
74
aliases = ['st', 'stat']
119
76
def run(self, all=False, show_ids=False, file_list=None, revision=None):
120
b, file_list = branch_files(file_list)
78
b = Branch.open_containing(file_list[0])
79
file_list = [b.relpath(x) for x in file_list]
80
# special case: only one path was given and it's the root
85
b = Branch.open_containing('.')
122
87
from bzrlib.status import show_status
123
88
show_status(b, show_unchanged=all, show_ids=show_ids,
214
178
get added when you add a file in the directory.
216
180
takes_args = ['file*']
217
takes_options = ['no-recurse', 'quiet']
181
takes_options = ['verbose', 'no-recurse']
219
def run(self, file_list, no_recurse=False, quiet=False):
220
from bzrlib.add import smart_add, add_reporter_print, add_reporter_null
222
reporter = add_reporter_null
224
reporter = add_reporter_print
225
smart_add(file_list, not no_recurse, reporter)
183
def run(self, file_list, verbose=False, no_recurse=False):
184
# verbose currently has no effect
185
from bzrlib.add import smart_add, add_reporter_print
186
smart_add(file_list, not no_recurse, add_reporter_print)
228
190
class cmd_mkdir(Command):
345
307
print "%s => %s" % (rel_names[0], rel_names[1])
348
312
class cmd_pull(Command):
349
313
"""Pull any changes from another branch into the current one.
351
If there is no default location set, the first pull will set it. After
352
that, you can omit the location to use the default. To change the
353
default, use --remember.
315
If the location is omitted, the last-used location will be used.
316
Both the revision history and the working directory will be
355
319
This command only works on branches that have not diverged. Branches are
356
320
considered diverged if both branches have had commits without first
357
321
pulling from the other.
359
323
If branches have diverged, you can use 'bzr merge' to pull the text changes
360
from one into the other. Once one branch has merged, the other should
361
be able to pull it again.
363
If you want to forget your local changes and just update your branch to
364
match the remote one, use --overwrite.
324
from one into the other.
366
takes_options = ['remember', 'overwrite']
367
326
takes_args = ['location?']
369
def run(self, location=None, remember=False, overwrite=False):
328
def run(self, location=None):
370
329
from bzrlib.merge import merge
371
331
from shutil import rmtree
374
br_to = Branch.open_containing('.')[0]
334
br_to = Branch.open_containing('.')
375
335
stored_loc = br_to.get_parent()
376
336
if location is None:
377
337
if stored_loc is None:
378
338
raise BzrCommandError("No pull location known or specified.")
380
print "Using saved location: %s" % stored_loc
381
location = stored_loc
382
if br_to.get_parent() is None or remember:
383
br_to.set_parent(location)
384
br_from = Branch.open(location)
386
br_to.working_tree().pull(br_from, overwrite)
387
except DivergedBranches:
388
raise BzrCommandError("These branches have diverged."
392
class cmd_push(Command):
393
"""Push this branch into another branch.
395
The remote branch will not have its working tree populated because this
396
is both expensive, and may not be supported on the remote file system.
398
Some smart servers or protocols *may* put the working tree in place.
400
If there is no default push location set, the first push will set it.
401
After that, you can omit the location to use the default. To change the
402
default, use --remember.
404
This command only works on branches that have not diverged. Branches are
405
considered diverged if the branch being pushed to is not an older version
408
If branches have diverged, you can use 'bzr push --overwrite' to replace
409
the other branch completely.
411
If you want to ensure you have the different changes in the other branch,
412
do a merge (see bzr help merge) from the other branch, and commit that
413
before doing a 'push --overwrite'.
415
takes_options = ['remember', 'overwrite']
416
takes_args = ['location?']
418
def run(self, location=None, remember=False, overwrite=False):
420
from shutil import rmtree
421
from bzrlib.transport import get_transport
423
br_from = Branch.open_containing('.')[0]
424
stored_loc = br_from.get_push_location()
426
if stored_loc is None:
427
raise BzrCommandError("No push location known or specified.")
429
print "Using saved location: %s" % stored_loc
430
location = stored_loc
431
if br_from.get_push_location() is None or remember:
432
br_from.set_push_location(location)
434
br_to = Branch.open(location)
435
except NotBranchError:
437
transport = get_transport(location).clone('..')
438
transport.mkdir(transport.relpath(location))
439
br_to = Branch.initialize(location)
441
br_to.pull(br_from, overwrite)
442
except DivergedBranches:
443
raise BzrCommandError("These branches have diverged."
444
" Try a merge then push with overwrite.")
340
print "Using last location: %s" % stored_loc
341
location = stored_loc
342
cache_root = tempfile.mkdtemp()
343
from bzrlib.errors import DivergedBranches
344
br_from = Branch.open_containing(location)
345
location = br_from.base
346
old_revno = br_to.revno()
348
from bzrlib.errors import DivergedBranches
349
br_from = Branch.open(location)
350
br_from.setup_caching(cache_root)
351
location = br_from.base
352
old_revno = br_to.revno()
354
br_to.update_revisions(br_from)
355
except DivergedBranches:
356
raise BzrCommandError("These branches have diverged."
359
merge(('.', -1), ('.', old_revno), check_clean=False)
360
if location != stored_loc:
361
br_to.set_parent(location)
447
367
class cmd_branch(Command):
464
384
def run(self, from_location, to_location=None, revision=None, basis=None):
465
385
from bzrlib.clone import copy_branch
467
388
from shutil import rmtree
470
elif len(revision) > 1:
471
raise BzrCommandError(
472
'bzr branch --revision takes exactly 1 revision value')
474
br_from = Branch.open(from_location)
476
if e.errno == errno.ENOENT:
477
raise BzrCommandError('Source location "%s" does not'
478
' exist.' % to_location)
389
cache_root = tempfile.mkdtemp()
393
elif len(revision) > 1:
394
raise BzrCommandError(
395
'bzr branch --revision takes exactly 1 revision value')
397
br_from = Branch.open(from_location)
399
if e.errno == errno.ENOENT:
400
raise BzrCommandError('Source location "%s" does not'
401
' exist.' % to_location)
404
br_from.setup_caching(cache_root)
483
405
if basis is not None:
484
basis_branch = Branch.open_containing(basis)[0]
406
basis_branch = Branch.open_containing(basis)
486
408
basis_branch = None
487
409
if len(revision) == 1 and revision[0] is not None:
508
427
copy_branch(br_from, to_location, revision_id, basis_branch)
509
428
except bzrlib.errors.NoSuchRevision:
510
429
rmtree(to_location)
511
msg = "The branch %s has no revision %s." % (from_location, revision[0])
430
msg = "The branch %s has no revision %d." % (from_location, revision[0])
512
431
raise BzrCommandError(msg)
513
432
except bzrlib.errors.UnlistableBranch:
515
433
msg = "The branch %s cannot be used as a --basis"
516
raise BzrCommandError(msg)
518
branch = Branch.open(to_location)
519
name = StringIO(name)
520
branch.put_controlfile('branch-name', name)
525
438
class cmd_renames(Command):
526
439
"""Show list of renamed files.
441
TODO: Option to show renames between two historical versions.
443
TODO: Only show renames under dir, rather than in the whole branch.
528
# TODO: Option to show renames between two historical versions.
530
# TODO: Only show renames under dir, rather than in the whole branch.
531
445
takes_args = ['dir?']
534
447
def run(self, dir='.'):
535
b = Branch.open_containing(dir)[0]
448
b = Branch.open_containing(dir)
536
449
old_inv = b.basis_tree().inventory
537
450
new_inv = b.read_working_inventory()
659
566
If files are listed, only the changes in those files are listed.
660
567
Otherwise, all changes for the tree are listed.
569
TODO: Allow diff across branches.
571
TODO: Option to use external diff command; could be GNU diff, wdiff,
574
TODO: Python difflib is not exactly the same as unidiff; should
575
either fix it up or prefer to use an external diff.
577
TODO: If a directory is given, diff everything under that.
579
TODO: Selected-file diff is inefficient and doesn't show you
582
TODO: This probably handles non-Unix newlines poorly.
667
# TODO: Allow diff across branches.
668
# TODO: Option to use external diff command; could be GNU diff, wdiff,
669
# or a graphical diff.
671
# TODO: Python difflib is not exactly the same as unidiff; should
672
# either fix it up or prefer to use an external diff.
674
# TODO: If a directory is given, diff everything under that.
676
# TODO: Selected-file diff is inefficient and doesn't show you
679
# TODO: This probably handles non-Unix newlines poorly.
681
590
takes_args = ['file*']
682
591
takes_options = ['revision', 'diff-options']
683
592
aliases = ['di', 'dif']
686
594
def run(self, revision=None, file_list=None, diff_options=None):
687
595
from bzrlib.diff import show_diff
689
b, file_list = branch_files(file_list)
598
b = Branch.open_containing(file_list[0])
599
file_list = [b.relpath(f) for f in file_list]
600
if file_list == ['']:
601
# just pointing to top-of-tree
604
b = Branch.open_containing('.')
690
606
if revision is not None:
691
607
if len(revision) == 1:
692
return show_diff(b, revision[0], specific_files=file_list,
693
external_diff_options=diff_options)
608
show_diff(b, revision[0], specific_files=file_list,
609
external_diff_options=diff_options)
694
610
elif len(revision) == 2:
695
return show_diff(b, revision[0], specific_files=file_list,
696
external_diff_options=diff_options,
697
revision2=revision[1])
611
show_diff(b, revision[0], specific_files=file_list,
612
external_diff_options=diff_options,
613
revision2=revision[1])
699
615
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
701
return show_diff(b, None, specific_files=file_list,
702
external_diff_options=diff_options)
617
show_diff(b, None, specific_files=file_list,
618
external_diff_options=diff_options)
705
623
class cmd_deleted(Command):
706
624
"""List files deleted in the working tree.
626
TODO: Show files deleted since a previous revision, or between two revisions.
708
# TODO: Show files deleted since a previous revision, or
709
# between two revisions.
710
# TODO: Much more efficient way to do this: read in new
711
# directories with readdir, rather than stating each one. Same
712
# level of effort but possibly much less IO. (Or possibly not,
713
# if the directories are very large...)
715
628
def run(self, show_ids=False):
716
b = Branch.open_containing('.')[0]
629
b = Branch.open_containing('.')
717
630
old = b.basis_tree()
718
631
new = b.working_tree()
633
## TODO: Much more efficient way to do this: read in new
634
## directories with readdir, rather than stating each one. Same
635
## level of effort but possibly much less IO. (Or possibly not,
636
## if the directories are very large...)
719
638
for path, ie in old.inventory.iter_entries():
720
639
if not new.has_id(ie.file_id):
777
693
To request a range of logs, you can use the command -r begin:end
778
694
-r revision requests a specific revision, -r :end or -r begin: are
697
--message allows you to give a regular expression, which will be evaluated
698
so that only matching entries will be displayed.
700
TODO: Make --revision support uuid: and hash: [future tag:] notation.
782
# TODO: Make --revision support uuid: and hash: [future tag:] notation.
784
704
takes_args = ['filename?']
785
takes_options = [Option('forward',
786
help='show from oldest to newest'),
787
'timezone', 'verbose',
788
'show-ids', 'revision',
789
Option('line', help='format with one line per revision'),
792
help='show revisions whose message matches this regexp',
794
Option('short', help='use moderately short format'),
705
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
706
'long', 'message', 'short',]
797
708
def run(self, filename=None, timezone='original',
868
777
A more user-friendly interface is "bzr log FILE"."""
870
779
takes_args = ["filename"]
872
780
def run(self, filename):
873
b, relpath = Branch.open_containing(filename)[0]
781
b = Branch.open_containing(filename)
874
782
inv = b.read_working_inventory()
875
file_id = inv.path2id(relpath)
783
file_id = inv.path2id(b.relpath(filename))
876
784
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
877
785
print "%6d %s" % (revno, what)
880
788
class cmd_ls(Command):
881
789
"""List files in a tree.
791
TODO: Take a revision or remote path and list that tree instead.
883
# TODO: Take a revision or remote path and list that tree instead.
885
takes_options = ['verbose', 'revision',
886
Option('non-recursive',
887
help='don\'t recurse into sub-directories'),
889
help='Print all paths from the root of the branch.'),
890
Option('unknown', help='Print unknown files'),
891
Option('versioned', help='Print versioned files'),
892
Option('ignored', help='Print ignored files'),
894
Option('null', help='Null separate the files'),
897
def run(self, revision=None, verbose=False,
898
non_recursive=False, from_root=False,
899
unknown=False, versioned=False, ignored=False,
903
raise BzrCommandError('Cannot set both --verbose and --null')
904
all = not (unknown or versioned or ignored)
906
selection = {'I':ignored, '?':unknown, 'V':versioned}
908
b, relpath = Branch.open_containing('.')
794
def run(self, revision=None, verbose=False):
795
b = Branch.open_containing('.')
913
796
if revision == None:
914
797
tree = b.working_tree()
916
tree = b.revision_tree(revision[0].in_history(b).rev_id)
917
for fp, fc, kind, fid, entry in tree.list_files():
918
if fp.startswith(relpath):
919
fp = fp[len(relpath):]
920
if non_recursive and '/' in fp:
922
if not all and not selection[fc]:
925
kindch = entry.kind_character()
926
print '%-8s %s%s' % (fc, fp, kindch)
929
sys.stdout.write('\0')
799
tree = b.revision_tree(revision.in_history(b).rev_id)
801
for fp, fc, kind, fid in tree.list_files():
803
if kind == 'directory':
810
print '%-8s %s%s' % (fc, fp, kindch)
936
816
class cmd_unknowns(Command):
937
817
"""List unknown files."""
940
819
from bzrlib.osutils import quotefn
941
for f in Branch.open_containing('.')[0].unknowns():
820
for f in Branch.open_containing('.').unknowns():
1118
991
A selected-file commit may fail in some cases where the committed
1119
992
tree would be invalid, such as trying to commit a file in a
1120
993
newly-added directory that is not itself committed.
995
TODO: Run hooks on tree to-be-committed, and after commit.
997
TODO: Strict commit that fails if there are unknown or deleted files.
1122
# TODO: Run hooks on tree to-be-committed, and after commit.
1124
# TODO: Strict commit that fails if there are deleted files.
1125
# (what does "deleted files" mean ??)
999
takes_args = ['selected*']
1000
takes_options = ['message', 'file', 'verbose', 'unchanged']
1001
aliases = ['ci', 'checkin']
1127
1003
# TODO: Give better message for -s, --summary, used by tla people
1129
1005
# XXX: verbose currently does nothing
1131
takes_args = ['selected*']
1132
takes_options = ['message', 'verbose',
1134
help='commit even if nothing has changed'),
1135
Option('file', type=str,
1137
help='file containing commit message'),
1139
help="refuse to commit if there are unknown "
1140
"files in the working tree."),
1142
aliases = ['ci', 'checkin']
1144
1007
def run(self, message=None, file=None, verbose=True, selected_list=None,
1145
unchanged=False, strict=False):
1146
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1009
from bzrlib.errors import PointlessCommit
1148
1010
from bzrlib.msgeditor import edit_commit_message
1149
1011
from bzrlib.status import show_status
1150
1012
from cStringIO import StringIO
1152
b, selected_list = branch_files(selected_list)
1153
if message is None and not file:
1014
b = Branch.open_containing('.')
1016
selected_list = [b.relpath(s) for s in selected_list]
1018
if not message and not file:
1154
1019
catcher = StringIO()
1155
1020
show_status(b, specific_files=selected_list,
1156
1021
to_file=catcher)
1157
1022
message = edit_commit_message(catcher.getvalue())
1159
1024
if message is None:
1160
1025
raise BzrCommandError("please specify a commit message"
1161
1026
" with either --message or --file")
1167
1032
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1170
raise BzrCommandError("empty commit message specified")
1173
b.commit(message, specific_files=selected_list,
1174
allow_pointless=unchanged, strict=strict)
1036
specific_files=selected_list,
1037
allow_pointless=unchanged)
1175
1038
except PointlessCommit:
1176
1039
# FIXME: This should really happen before the file is read in;
1177
1040
# perhaps prepare the commit; get the message; then actually commit
1178
1041
raise BzrCommandError("no changes to commit",
1179
1042
["use --unchanged to commit anyhow"])
1180
except ConflictsInTree:
1181
raise BzrCommandError("Conflicts detected in working tree. "
1182
'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
1183
except StrictCommitFailed:
1184
raise BzrCommandError("Commit refused because there are unknown "
1185
"files in the working tree.")
1188
1045
class cmd_check(Command):
1239
1096
"""Show bzr user id."""
1240
1097
takes_options = ['email']
1243
1099
def run(self, email=False):
1245
b = bzrlib.branch.Branch.open_containing('.')[0]
1246
config = bzrlib.config.BranchConfig(b)
1101
b = bzrlib.branch.Branch.open_containing('.')
1247
1102
except NotBranchError:
1248
config = bzrlib.config.GlobalConfig()
1251
print config.user_email()
1106
print bzrlib.osutils.user_email(b)
1253
print config.username()
1108
print bzrlib.osutils.username(b)
1256
1111
class cmd_selftest(Command):
1257
"""Run internal test suite.
1259
This creates temporary test directories in the working directory,
1260
but not existing data is affected. These directories are deleted
1261
if the tests pass, or left behind to help in debugging if they
1264
If arguments are given, they are regular expressions that say
1265
which tests should run.
1267
# TODO: --list should give a list of all available tests
1112
"""Run internal test suite"""
1269
takes_args = ['testspecs*']
1270
takes_options = ['verbose',
1271
Option('one', help='stop when one test fails'),
1274
def run(self, testspecs_list=None, verbose=False, one=False):
1114
takes_options = ['verbose', 'pattern']
1115
def run(self, verbose=False, pattern=".*"):
1275
1116
import bzrlib.ui
1276
1117
from bzrlib.selftest import selftest
1277
1118
# we don't want progress meters from the tests to go to the
1314
1149
class cmd_version(Command):
1315
1150
"""Show version of bzr."""
1320
1154
class cmd_rocks(Command):
1321
1155
"""Statement of optimism."""
1325
1158
print "it sure does!"
1328
1161
class cmd_find_merge_base(Command):
1329
1162
"""Find and print a base revision for merging two branches.
1164
TODO: Options to specify revisions on either side, as if
1165
merging only part of the history.
1331
# TODO: Options to specify revisions on either side, as if
1332
# merging only part of the history.
1333
1167
takes_args = ['branch', 'other']
1337
1170
def run(self, branch, other):
1338
1171
from bzrlib.revision import common_ancestor, MultipleRevisionSources
1340
branch1 = Branch.open_containing(branch)[0]
1341
branch2 = Branch.open_containing(other)[0]
1173
branch1 = Branch.open_containing(branch)
1174
branch2 = Branch.open_containing(other)
1343
1176
history_1 = branch1.revision_history()
1344
1177
history_2 = branch2.revision_history()
1393
1226
--force is given.
1395
1228
takes_args = ['branch?']
1396
takes_options = ['revision', 'force', 'merge-type', 'reprocess',
1397
Option('show-base', help="Show base revision text in "
1229
takes_options = ['revision', 'force', 'merge-type']
1400
def run(self, branch=None, revision=None, force=False, merge_type=None,
1401
show_base=False, reprocess=False):
1231
def run(self, branch='.', revision=None, force=False,
1402
1233
from bzrlib.merge import merge
1403
1234
from bzrlib.merge_core import ApplyMerge3
1404
1235
if merge_type is None:
1405
1236
merge_type = ApplyMerge3
1407
branch = Branch.open_containing('.')[0].get_parent()
1409
raise BzrCommandError("No merge location known or specified.")
1411
print "Using saved location: %s" % branch
1412
1238
if revision is None or len(revision) < 1:
1413
1239
base = [None, None]
1414
1240
other = [branch, -1]
1416
1242
if len(revision) == 1:
1417
1243
base = [None, None]
1418
other_branch = Branch.open_containing(branch)[0]
1419
revno = revision[0].in_history(other_branch).revno
1420
other = [branch, revno]
1244
other = [branch, revision[0].in_history(branch).revno]
1422
1246
assert len(revision) == 2
1423
1247
if None in revision:
1424
1248
raise BzrCommandError(
1425
1249
"Merge doesn't permit that revision specifier.")
1426
b = Branch.open_containing(branch)[0]
1250
from bzrlib.branch import Branch
1251
b = Branch.open(branch)
1428
1253
base = [branch, revision[0].in_history(b).revno]
1429
1254
other = [branch, revision[1].in_history(b).revno]
1432
conflict_count = merge(other, base, check_clean=(not force),
1433
merge_type=merge_type, reprocess=reprocess,
1434
show_base=show_base)
1435
if conflict_count != 0:
1257
merge(other, base, check_clean=(not force), merge_type=merge_type)
1439
1258
except bzrlib.errors.AmbiguousBase, e:
1440
1259
m = ("sorry, bzr can't determine the right merge base yet\n"
1441
1260
"candidates are:\n "
1596
1405
print '\t', d.split('\n')[0]
1599
class cmd_testament(Command):
1600
"""Show testament (signing-form) of a revision."""
1601
takes_options = ['revision', 'long']
1602
takes_args = ['branch?']
1604
def run(self, branch='.', revision=None, long=False):
1605
from bzrlib.testament import Testament
1606
b = Branch.open_containing(branch)[0]
1609
if revision is None:
1610
rev_id = b.last_revision()
1612
rev_id = revision[0].in_history(b).rev_id
1613
t = Testament.from_revision(b, rev_id)
1615
sys.stdout.writelines(t.as_text_lines())
1617
sys.stdout.write(t.as_short_text())
1622
class cmd_annotate(Command):
1623
"""Show the origin of each line in a file.
1625
This prints out the given file with an annotation on the left side
1626
indicating which revision, author and date introduced the change.
1628
If the origin is the same for a run of consecutive lines, it is
1629
shown only at the top, unless the --all option is given.
1631
# TODO: annotate directories; showing when each file was last changed
1632
# TODO: annotate a previous version of a file
1633
# TODO: if the working copy is modified, show annotations on that
1634
# with new uncommitted lines marked
1635
aliases = ['blame', 'praise']
1636
takes_args = ['filename']
1637
takes_options = [Option('all', help='show annotations on all lines'),
1638
Option('long', help='show date in annotations'),
1642
def run(self, filename, all=False, long=False):
1643
from bzrlib.annotate import annotate_file
1644
b, relpath = Branch.open_containing(filename)
1647
tree = WorkingTree(b.base, b)
1648
tree = b.revision_tree(b.last_revision())
1649
file_id = tree.inventory.path2id(relpath)
1650
file_version = tree.inventory[file_id].revision
1651
annotate_file(b, file_version, file_id, long, all, sys.stdout)
1656
class cmd_re_sign(Command):
1657
"""Create a digital signature for an existing revision."""
1658
# TODO be able to replace existing ones.
1660
hidden = True # is this right ?
1661
takes_args = ['revision_id?']
1662
takes_options = ['revision']
1664
def run(self, revision_id=None, revision=None):
1665
import bzrlib.config as config
1666
import bzrlib.gpg as gpg
1667
if revision_id is not None and revision is not None:
1668
raise BzrCommandError('You can only supply one of revision_id or --revision')
1669
if revision_id is None and revision is None:
1670
raise BzrCommandError('You must supply either --revision or a revision_id')
1671
b = Branch.open_containing('.')[0]
1672
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1673
if revision_id is not None:
1674
b.sign_revision(revision_id, gpg_strategy)
1675
elif revision is not None:
1676
if len(revision) == 1:
1677
revno, rev_id = revision[0].in_history(b)
1678
b.sign_revision(rev_id, gpg_strategy)
1679
elif len(revision) == 2:
1680
# are they both on rh- if so we can walk between them
1681
# might be nice to have a range helper for arbitrary
1682
# revision paths. hmm.
1683
from_revno, from_revid = revision[0].in_history(b)
1684
to_revno, to_revid = revision[1].in_history(b)
1685
if to_revid is None:
1686
to_revno = b.revno()
1687
if from_revno is None or to_revno is None:
1688
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1689
for revno in range(from_revno, to_revno + 1):
1690
b.sign_revision(b.get_rev_id(revno), gpg_strategy)
1692
raise BzrCommandError('Please supply either one revision, or a range.')
1695
# these get imported and then picked up by the scan for cmd_*
1696
# TODO: Some more consistent way to split command definitions across files;
1697
# we do need to load at least some information about them to know of
1699
from bzrlib.conflicts import cmd_resolve, cmd_conflicts