29
29
from bzrlib.branch import Branch
30
from bzrlib.errors import InvalidRevisionNumber
30
from bzrlib.errors import (
32
InvalidRevisionNumber,
34
from bzrlib.revision import Revision
35
from bzrlib.revisionspec import (
41
class TestCaseWithoutPropsHandler(TestCaseWithTransport):
44
super(TestCaseWithoutPropsHandler, self).setUp()
45
# keep a reference to the "current" custom prop. handler registry
46
self.properties_handler_registry = \
47
log.properties_handler_registry
48
# clean up the registry in log
49
log.properties_handler_registry = registry.Registry()
52
super(TestCaseWithoutPropsHandler, self)._cleanup()
53
# restore the custom properties handler registry
54
log.properties_handler_registry = \
55
self.properties_handler_registry
33
58
class LogCatcher(LogFormatter):
191
218
d = logentry.delta
192
219
self.checkDelta(d, added=['file1', 'file2'])
221
def test_merges_nonsupporting_formatter(self):
222
"""Tests that show_log will raise if the formatter doesn't
223
support merge revisions."""
224
wt = self.make_branch_and_memory_tree('.')
228
wt.commit('rev-1', rev_id='rev-1',
229
timestamp=1132586655, timezone=36000,
230
committer='Joe Foo <joe@foo.com>')
231
wt.commit('rev-merged', rev_id='rev-2a',
232
timestamp=1132586700, timezone=36000,
233
committer='Joe Foo <joe@foo.com>')
234
wt.set_parent_ids(['rev-1', 'rev-2a'])
235
wt.branch.set_last_revision_info(1, 'rev-1')
236
wt.commit('rev-2', rev_id='rev-2b',
237
timestamp=1132586800, timezone=36000,
238
committer='Joe Foo <joe@foo.com>')
239
logfile = self.make_utf8_encoded_stringio()
240
formatter = ShortLogFormatter(to_file=logfile)
243
revspec = RevisionSpec.from_string('1.1.1')
244
rev = revspec.in_history(wtb)
245
self.assertRaises(BzrCommandError, show_log, wtb, lf,
246
start_revision=rev, end_revision=rev)
195
251
def make_commits_with_trailing_newlines(wt):
196
252
"""Helper method for LogFormatter tests"""
199
255
open('a', 'wb').write('hello moto\n')
201
wt.commit('simple log message', rev_id='a1'
202
, timestamp=1132586655.459960938, timezone=-6*3600
203
, committer='Joe Foo <joe@foo.com>')
257
wt.commit('simple log message', rev_id='a1',
258
timestamp=1132586655.459960938, timezone=-6*3600,
259
committer='Joe Foo <joe@foo.com>')
204
260
open('b', 'wb').write('goodbye\n')
206
wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
207
, timestamp=1132586842.411175966, timezone=-6*3600
208
, committer='Joe Foo <joe@foo.com>')
262
wt.commit('multiline\nlog\nmessage\n', rev_id='a2',
263
timestamp=1132586842.411175966, timezone=-6*3600,
264
committer='Joe Foo <joe@foo.com>',
265
author='Joe Bar <joe@bar.com>')
210
267
open('c', 'wb').write('just another manic monday\n')
212
wt.commit('single line with trailing newline\n', rev_id='a3'
213
, timestamp=1132587176.835228920, timezone=-6*3600
214
, committer = 'Joe Foo <joe@foo.com>')
269
wt.commit('single line with trailing newline\n', rev_id='a3',
270
timestamp=1132587176.835228920, timezone=-6*3600,
271
committer = 'Joe Foo <joe@foo.com>')
275
def normalize_log(log):
276
"""Replaces the variable lines of logs with fixed lines"""
277
author = 'author: Dolor Sit <test@example.com>'
278
committer = 'committer: Lorem Ipsum <test@example.com>'
279
lines = log.splitlines(True)
280
for idx,line in enumerate(lines):
281
stripped_line = line.lstrip()
282
indent = ' ' * (len(line) - len(stripped_line))
283
if stripped_line.startswith('author:'):
284
lines[idx] = indent + author + '\n'
285
elif stripped_line.startswith('committer:'):
286
lines[idx] = indent + committer + '\n'
287
elif stripped_line.startswith('timestamp:'):
288
lines[idx] = indent + 'timestamp: Just now\n'
289
return ''.join(lines)
218
292
class TestShortLogFormatter(TestCaseWithTransport):
220
294
def test_trailing_newlines(self):
221
295
wt = self.make_branch_and_tree('.')
222
296
b = make_commits_with_trailing_newlines(wt)
297
sio = self.make_utf8_encoded_stringio()
224
298
lf = ShortLogFormatter(to_file=sio)
226
self.assertEquals(sio.getvalue(), """\
300
self.assertEqualDiff(sio.getvalue(), """\
227
301
3 Joe Foo\t2005-11-21
228
302
single line with trailing newline
230
2 Joe Foo\t2005-11-21
304
2 Joe Bar\t2005-11-21
241
class TestLongLogFormatter(TestCaseWithTransport):
243
def normalize_log(self,log):
244
"""Replaces the variable lines of logs with fixed lines"""
245
committer = 'committer: Lorem Ipsum <test@example.com>'
246
lines = log.splitlines(True)
247
for idx,line in enumerate(lines):
248
stripped_line = line.lstrip()
249
indent = ' ' * (len(line) - len(stripped_line))
250
if stripped_line.startswith('committer:'):
251
lines[idx] = indent + committer + '\n'
252
if stripped_line.startswith('timestamp:'):
253
lines[idx] = indent + 'timestamp: Just now\n'
254
return ''.join(lines)
314
def test_short_log_with_merges(self):
315
wt = self.make_branch_and_memory_tree('.')
319
wt.commit('rev-1', rev_id='rev-1',
320
timestamp=1132586655, timezone=36000,
321
committer='Joe Foo <joe@foo.com>')
322
wt.commit('rev-merged', rev_id='rev-2a',
323
timestamp=1132586700, timezone=36000,
324
committer='Joe Foo <joe@foo.com>')
325
wt.set_parent_ids(['rev-1', 'rev-2a'])
326
wt.branch.set_last_revision_info(1, 'rev-1')
327
wt.commit('rev-2', rev_id='rev-2b',
328
timestamp=1132586800, timezone=36000,
329
committer='Joe Foo <joe@foo.com>')
330
logfile = self.make_utf8_encoded_stringio()
331
formatter = ShortLogFormatter(to_file=logfile)
332
show_log(wt.branch, formatter)
333
self.assertEqualDiff(logfile.getvalue(), """\
334
2 Joe Foo\t2005-11-22 [merge]
337
1 Joe Foo\t2005-11-22
344
def test_short_log_single_merge_revision(self):
345
wt = self.make_branch_and_memory_tree('.')
349
wt.commit('rev-1', rev_id='rev-1',
350
timestamp=1132586655, timezone=36000,
351
committer='Joe Foo <joe@foo.com>')
352
wt.commit('rev-merged', rev_id='rev-2a',
353
timestamp=1132586700, timezone=36000,
354
committer='Joe Foo <joe@foo.com>')
355
wt.set_parent_ids(['rev-1', 'rev-2a'])
356
wt.branch.set_last_revision_info(1, 'rev-1')
357
wt.commit('rev-2', rev_id='rev-2b',
358
timestamp=1132586800, timezone=36000,
359
committer='Joe Foo <joe@foo.com>')
360
logfile = self.make_utf8_encoded_stringio()
361
formatter = ShortLogFormatter(to_file=logfile)
362
revspec = RevisionSpec.from_string('1.1.1')
364
rev = revspec.in_history(wtb)
365
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
366
self.assertEqualDiff(logfile.getvalue(), """\
367
1.1.1 Joe Foo\t2005-11-22
375
class TestLongLogFormatter(TestCaseWithoutPropsHandler):
256
377
def test_verbose_log(self):
257
378
"""Verbose log includes changed files
350
471
wt.commit('first post')
351
472
self.run_bzr('branch parent child')
352
473
os.unlink('child/f1')
353
print >> file('child/f2', 'wb'), 'hello'
474
file('child/f2', 'wb').write('hello\n')
354
475
self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
356
477
os.chdir('parent')
357
478
self.run_bzr('merge ../child')
358
479
wt.commit('merge branch 1')
481
sio = self.make_utf8_encoded_stringio()
361
482
lf = LongLogFormatter(to_file=sio)
362
483
show_log(b, lf, verbose=True)
363
log = self.normalize_log(sio.getvalue())
364
self.assertEqualDiff("""\
484
log = normalize_log(sio.getvalue())
485
self.assertEqualDiff(log, """\
365
486
------------------------------------------------------------
367
488
committer: Lorem Ipsum <test@example.com>
428
550
simple log message
553
def test_author_in_log(self):
554
"""Log includes the author name if it's set in
555
the revision properties
557
wt = self.make_branch_and_tree('.')
559
self.build_tree(['a'])
561
b.nick = 'test_author_log'
562
wt.commit(message='add a',
563
timestamp=1132711707,
565
committer='Lorem Ipsum <test@example.com>',
566
author='John Doe <jdoe@example.com>')
568
formatter = LongLogFormatter(to_file=sio)
569
show_log(b, formatter)
570
self.assertEqualDiff(sio.getvalue(), '''\
571
------------------------------------------------------------
573
author: John Doe <jdoe@example.com>
574
committer: Lorem Ipsum <test@example.com>
575
branch nick: test_author_log
576
timestamp: Wed 2005-11-23 12:08:27 +1000
581
def test_properties_in_log(self):
582
"""Log includes the custom properties returned by the registered
585
wt = self.make_branch_and_tree('.')
587
self.build_tree(['a'])
589
b.nick = 'test_properties_in_log'
590
wt.commit(message='add a',
591
timestamp=1132711707,
593
committer='Lorem Ipsum <test@example.com>',
594
author='John Doe <jdoe@example.com>')
596
formatter = LongLogFormatter(to_file=sio)
598
def trivial_custom_prop_handler(revision):
599
return {'test_prop':'test_value'}
601
log.properties_handler_registry.register(
602
'trivial_custom_prop_handler',
603
trivial_custom_prop_handler)
604
show_log(b, formatter)
606
log.properties_handler_registry.remove(
607
'trivial_custom_prop_handler')
608
self.assertEqualDiff(sio.getvalue(), '''\
609
------------------------------------------------------------
611
test_prop: test_value
612
author: John Doe <jdoe@example.com>
613
committer: Lorem Ipsum <test@example.com>
614
branch nick: test_properties_in_log
615
timestamp: Wed 2005-11-23 12:08:27 +1000
620
def test_error_in_properties_handler(self):
621
"""Log includes the custom properties returned by the registered
624
wt = self.make_branch_and_tree('.')
626
self.build_tree(['a'])
628
b.nick = 'test_author_log'
629
wt.commit(message='add a',
630
timestamp=1132711707,
632
committer='Lorem Ipsum <test@example.com>',
633
author='John Doe <jdoe@example.com>',
634
revprops={'first_prop':'first_value'})
636
formatter = LongLogFormatter(to_file=sio)
638
def trivial_custom_prop_handler(revision):
639
raise StandardError("a test error")
641
log.properties_handler_registry.register(
642
'trivial_custom_prop_handler',
643
trivial_custom_prop_handler)
644
self.assertRaises(StandardError, show_log, b, formatter,)
646
log.properties_handler_registry.remove(
647
'trivial_custom_prop_handler')
649
def test_properties_handler_bad_argument(self):
650
wt = self.make_branch_and_tree('.')
652
self.build_tree(['a'])
654
b.nick = 'test_author_log'
655
wt.commit(message='add a',
656
timestamp=1132711707,
658
committer='Lorem Ipsum <test@example.com>',
659
author='John Doe <jdoe@example.com>',
660
revprops={'a_prop':'test_value'})
662
formatter = LongLogFormatter(to_file=sio)
664
def bad_argument_prop_handler(revision):
665
return {'custom_prop_name':revision.properties['a_prop']}
667
log.properties_handler_registry.register(
668
'bad_argument_prop_handler',
669
bad_argument_prop_handler)
671
self.assertRaises(AttributeError, formatter.show_properties,
674
revision = b.repository.get_revision(b.last_revision())
675
formatter.show_properties(revision, '')
676
self.assertEqualDiff(sio.getvalue(),
677
'''custom_prop_name: test_value\n''')
679
log.properties_handler_registry.remove(
680
'bad_argument_prop_handler')
432
683
class TestLineLogFormatter(TestCaseWithTransport):
453
704
log_contents = logfile.read()
454
self.assertEqualDiff(log_contents, '1: Line-Log-Formatte... 2005-11-23 add a\n')
456
def test_short_log_with_merges(self):
457
wt = self.make_branch_and_memory_tree('.')
461
wt.commit('rev-1', rev_id='rev-1',
462
timestamp=1132586655, timezone=36000,
463
committer='Joe Foo <joe@foo.com>')
464
wt.commit('rev-merged', rev_id='rev-2a',
465
timestamp=1132586700, timezone=36000,
466
committer='Joe Foo <joe@foo.com>')
467
wt.set_parent_ids(['rev-1', 'rev-2a'])
468
wt.branch.set_last_revision_info(1, 'rev-1')
469
wt.commit('rev-2', rev_id='rev-2b',
470
timestamp=1132586800, timezone=36000,
471
committer='Joe Foo <joe@foo.com>')
473
formatter = ShortLogFormatter(to_file=logfile)
474
show_log(wt.branch, formatter)
476
self.assertEqualDiff("""\
477
2 Joe Foo\t2005-11-22 [merge]
480
1 Joe Foo\t2005-11-22
483
""", logfile.getvalue())
705
self.assertEqualDiff(log_contents,
706
'1: Line-Log-Formatte... 2005-11-23 add a\n')
487
708
def test_trailing_newlines(self):
488
709
wt = self.make_branch_and_tree('.')
489
710
b = make_commits_with_trailing_newlines(wt)
711
sio = self.make_utf8_encoded_stringio()
491
712
lf = LineLogFormatter(to_file=sio)
493
714
self.assertEqualDiff(sio.getvalue(), """\
494
715
3: Joe Foo 2005-11-21 single line with trailing newline
495
2: Joe Foo 2005-11-21 multiline
716
2: Joe Bar 2005-11-21 multiline
496
717
1: Joe Foo 2005-11-21 simple log message
720
def test_line_log_single_merge_revision(self):
721
wt = self.make_branch_and_memory_tree('.')
725
wt.commit('rev-1', rev_id='rev-1',
726
timestamp=1132586655, timezone=36000,
727
committer='Joe Foo <joe@foo.com>')
728
wt.commit('rev-merged', rev_id='rev-2a',
729
timestamp=1132586700, timezone=36000,
730
committer='Joe Foo <joe@foo.com>')
731
wt.set_parent_ids(['rev-1', 'rev-2a'])
732
wt.branch.set_last_revision_info(1, 'rev-1')
733
wt.commit('rev-2', rev_id='rev-2b',
734
timestamp=1132586800, timezone=36000,
735
committer='Joe Foo <joe@foo.com>')
736
logfile = self.make_utf8_encoded_stringio()
737
formatter = LineLogFormatter(to_file=logfile)
738
revspec = RevisionSpec.from_string('1.1.1')
740
rev = revspec.in_history(wtb)
741
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
742
self.assertEqualDiff(logfile.getvalue(), """\
743
1.1.1: Joe Foo 2005-11-22 rev-merged
500
750
class TestGetViewRevisions(TestCaseWithTransport):
541
791
full_rev_nos_for_reference = {
544
'3a': '2.2.1', #first commit tree 3
545
'3b': '2.1.1', # first commit tree 2
794
'3a': '2.1.1', #first commit tree 3
795
'3b': '2.2.1', # first commit tree 2
546
796
'3c': '3', #merges 3b to main
547
'4a': '2.1.2', # second commit tree 2
797
'4a': '2.2.2', # second commit tree 2
548
798
'4b': '4', # merges 4a to main
550
800
return mainline_revs, rev_nos, wt
602
860
def test_get_view_revisions_merge2(self):
603
861
"""Test get_view_revisions when there are merges"""
604
862
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
864
self.addCleanup(wt.unlock)
605
865
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
607
867
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
608
('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
868
('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
610
870
self.assertEqual(expected, revisions)
611
871
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
612
872
'forward', include_merges=False))
747
1007
tree = self.create_tree_with_single_merge()
748
1008
# f3 should be marked as modified by revisions A, B, C, and D
749
1009
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1011
def test_file_id_with_ghosts(self):
1012
# This is testing bug #209948, where having a ghost would cause
1013
# _filter_revisions_touching_file_id() to fail.
1014
tree = self.create_tree_with_single_merge()
1015
# We need to add a revision, so switch back to a write-locked tree
1018
first_parent = tree.last_revision()
1019
tree.set_parent_ids([first_parent, 'ghost-revision-id'])
1020
self.build_tree_contents([('tree/f1', 'A\nB\nXX\n')])
1021
tree.commit('commit with a ghost', rev_id='XX')
1022
self.assertAllRevisionsForFileID(tree, 'f1-id', ['XX', 'B', 'A'])
1023
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1026
class TestShowChangedRevisions(TestCaseWithTransport):
1028
def test_show_changed_revisions_verbose(self):
1029
tree = self.make_branch_and_tree('tree_a')
1030
self.build_tree(['tree_a/foo'])
1032
tree.commit('bar', rev_id='bar-id')
1033
s = self.make_utf8_encoded_stringio()
1034
log.show_changed_revisions(tree.branch, [], ['bar-id'], s)
1035
self.assertContainsRe(s.getvalue(), 'bar')
1036
self.assertNotContainsRe(s.getvalue(), 'foo')
1039
class TestLogFormatter(TestCase):
1041
def test_short_committer(self):
1042
rev = Revision('a-id')
1043
rev.committer = 'John Doe <jdoe@example.com>'
1044
lf = LogFormatter(None)
1045
self.assertEqual('John Doe', lf.short_committer(rev))
1046
rev.committer = 'John Smith <jsmith@example.com>'
1047
self.assertEqual('John Smith', lf.short_committer(rev))
1048
rev.committer = 'John Smith'
1049
self.assertEqual('John Smith', lf.short_committer(rev))
1050
rev.committer = 'jsmith@example.com'
1051
self.assertEqual('jsmith@example.com', lf.short_committer(rev))
1052
rev.committer = '<jsmith@example.com>'
1053
self.assertEqual('jsmith@example.com', lf.short_committer(rev))
1054
rev.committer = 'John Smith jsmith@example.com'
1055
self.assertEqual('John Smith', lf.short_committer(rev))
1057
def test_short_author(self):
1058
rev = Revision('a-id')
1059
rev.committer = 'John Doe <jdoe@example.com>'
1060
lf = LogFormatter(None)
1061
self.assertEqual('John Doe', lf.short_author(rev))
1062
rev.properties['author'] = 'John Smith <jsmith@example.com>'
1063
self.assertEqual('John Smith', lf.short_author(rev))
1064
rev.properties['author'] = 'John Smith'
1065
self.assertEqual('John Smith', lf.short_author(rev))
1066
rev.properties['author'] = 'jsmith@example.com'
1067
self.assertEqual('jsmith@example.com', lf.short_author(rev))
1068
rev.properties['author'] = '<jsmith@example.com>'
1069
self.assertEqual('jsmith@example.com', lf.short_author(rev))
1070
rev.properties['author'] = 'John Smith jsmith@example.com'
1071
self.assertEqual('John Smith', lf.short_author(rev))