263
181
lf.supports_merge_revisions = True
264
182
log.show_log(b, lf, verbose=True)
267
self.assertEqual(3, len(revs))
184
self.assertEqual(3, len(lf.logs))
186
logentry = lf.logs[0]
270
187
self.assertEqual('2', logentry.revno)
271
188
self.assertEqual('merge child branch', logentry.rev.message)
272
189
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
191
logentry = lf.logs[1]
275
192
self.assertEqual('1.1.1', logentry.revno)
276
193
self.assertEqual('remove file1 and modify file2', logentry.rev.message)
277
194
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
196
logentry = lf.logs[2]
280
197
self.assertEqual('1', logentry.revno)
281
198
self.assertEqual('add file1 and file2', logentry.rev.message)
282
199
self.checkDelta(logentry.delta, added=['file1', 'file2'])
285
class TestShortLogFormatter(TestCaseForLogFormatter):
202
def make_commits_with_trailing_newlines(wt):
203
"""Helper method for LogFormatter tests"""
206
open('a', 'wb').write('hello moto\n')
208
wt.commit('simple log message', rev_id='a1',
209
timestamp=1132586655.459960938, timezone=-6*3600,
210
committer='Joe Foo <joe@foo.com>')
211
open('b', 'wb').write('goodbye\n')
213
wt.commit('multiline\nlog\nmessage\n', rev_id='a2',
214
timestamp=1132586842.411175966, timezone=-6*3600,
215
committer='Joe Foo <joe@foo.com>',
216
authors=['Joe Bar <joe@bar.com>'])
218
open('c', 'wb').write('just another manic monday\n')
220
wt.commit('single line with trailing newline\n', rev_id='a3',
221
timestamp=1132587176.835228920, timezone=-6*3600,
222
committer = 'Joe Foo <joe@foo.com>')
226
def normalize_log(log):
227
"""Replaces the variable lines of logs with fixed lines"""
228
author = 'author: Dolor Sit <test@example.com>'
229
committer = 'committer: Lorem Ipsum <test@example.com>'
230
lines = log.splitlines(True)
231
for idx,line in enumerate(lines):
232
stripped_line = line.lstrip()
233
indent = ' ' * (len(line) - len(stripped_line))
234
if stripped_line.startswith('author:'):
235
lines[idx] = indent + author + '\n'
236
elif stripped_line.startswith('committer:'):
237
lines[idx] = indent + committer + '\n'
238
elif stripped_line.startswith('timestamp:'):
239
lines[idx] = indent + 'timestamp: Just now\n'
240
return ''.join(lines)
243
class TestShortLogFormatter(tests.TestCaseWithTransport):
287
245
def test_trailing_newlines(self):
288
246
wt = self.make_branch_and_tree('.')
289
b = self.make_commits_with_trailing_newlines(wt)
290
self.assertFormatterResult("""\
291
3 Joe Foo\t2005-11-22
247
b = make_commits_with_trailing_newlines(wt)
248
sio = self.make_utf8_encoded_stringio()
249
lf = log.ShortLogFormatter(to_file=sio)
251
self.assertEqualDiff("""\
252
3 Joe Foo\t2005-11-21
292
253
single line with trailing newline
294
2 Joe Foo\t2005-11-22
255
2 Joe Bar\t2005-11-21
299
1 Joe Foo\t2005-11-22
260
1 Joe Foo\t2005-11-21
300
261
simple log message
303
b, log.ShortLogFormatter)
266
def _prepare_tree_with_merges(self, with_tags=False):
267
wt = self.make_branch_and_memory_tree('.')
269
self.addCleanup(wt.unlock)
271
wt.commit('rev-1', rev_id='rev-1',
272
timestamp=1132586655, timezone=36000,
273
committer='Joe Foo <joe@foo.com>')
274
wt.commit('rev-merged', rev_id='rev-2a',
275
timestamp=1132586700, timezone=36000,
276
committer='Joe Foo <joe@foo.com>')
277
wt.set_parent_ids(['rev-1', 'rev-2a'])
278
wt.branch.set_last_revision_info(1, 'rev-1')
279
wt.commit('rev-2', rev_id='rev-2b',
280
timestamp=1132586800, timezone=36000,
281
committer='Joe Foo <joe@foo.com>')
284
branch.tags.set_tag('v0.2', 'rev-2b')
285
wt.commit('rev-3', rev_id='rev-3',
286
timestamp=1132586900, timezone=36000,
287
committer='Jane Foo <jane@foo.com>')
288
branch.tags.set_tag('v1.0rc1', 'rev-3')
289
branch.tags.set_tag('v1.0', 'rev-3')
305
292
def test_short_log_with_merges(self):
306
293
wt = self._prepare_tree_with_merges()
307
self.assertFormatterResult("""\
308
2 Joe Foo\t2005-11-22 [merge]
311
1 Joe Foo\t2005-11-22
315
wt.branch, log.ShortLogFormatter)
317
def test_short_log_with_merges_and_advice(self):
318
wt = self._prepare_tree_with_merges()
319
self.assertFormatterResult("""\
320
2 Joe Foo\t2005-11-22 [merge]
323
1 Joe Foo\t2005-11-22
326
Use --include-merges or -n0 to see merged revisions.
328
wt.branch, log.ShortLogFormatter,
329
formatter_kwargs=dict(show_advice=True))
294
logfile = self.make_utf8_encoded_stringio()
295
formatter = log.ShortLogFormatter(to_file=logfile)
296
log.show_log(wt.branch, formatter)
297
self.assertEqualDiff("""\
298
2 Joe Foo\t2005-11-22 [merge]
301
1 Joe Foo\t2005-11-22
304
Use --levels 0 (or -n0) to see merged revisions.
331
308
def test_short_log_with_merges_and_range(self):
332
wt = self._prepare_tree_with_merges()
333
self.wt_commit(wt, 'rev-3a', rev_id='rev-3a')
309
wt = self.make_branch_and_memory_tree('.')
311
self.addCleanup(wt.unlock)
313
wt.commit('rev-1', rev_id='rev-1',
314
timestamp=1132586655, timezone=36000,
315
committer='Joe Foo <joe@foo.com>')
316
wt.commit('rev-merged', rev_id='rev-2a',
317
timestamp=1132586700, timezone=36000,
318
committer='Joe Foo <joe@foo.com>')
319
wt.branch.set_last_revision_info(1, 'rev-1')
320
wt.set_parent_ids(['rev-1', 'rev-2a'])
321
wt.commit('rev-2b', rev_id='rev-2b',
322
timestamp=1132586800, timezone=36000,
323
committer='Joe Foo <joe@foo.com>')
324
wt.commit('rev-3a', rev_id='rev-3a',
325
timestamp=1132586800, timezone=36000,
326
committer='Joe Foo <joe@foo.com>')
334
327
wt.branch.set_last_revision_info(2, 'rev-2b')
335
328
wt.set_parent_ids(['rev-2b', 'rev-3a'])
336
self.wt_commit(wt, 'rev-3b', rev_id='rev-3b')
337
self.assertFormatterResult("""\
329
wt.commit('rev-3b', rev_id='rev-3b',
330
timestamp=1132586800, timezone=36000,
331
committer='Joe Foo <joe@foo.com>')
332
logfile = self.make_utf8_encoded_stringio()
333
formatter = log.ShortLogFormatter(to_file=logfile)
334
log.show_log(wt.branch, formatter,
335
start_revision=2, end_revision=3)
336
self.assertEqualDiff("""\
338
337
3 Joe Foo\t2005-11-22 [merge]
341
340
2 Joe Foo\t2005-11-22 [merge]
343
Use --levels 0 (or -n0) to see merged revisions.
345
wt.branch, log.ShortLogFormatter,
346
show_log_kwargs=dict(start_revision=2, end_revision=3))
348
347
def test_short_log_with_tags(self):
349
348
wt = self._prepare_tree_with_merges(with_tags=True)
350
self.assertFormatterResult("""\
351
3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
349
logfile = self.make_utf8_encoded_stringio()
350
formatter = log.ShortLogFormatter(to_file=logfile)
351
log.show_log(wt.branch, formatter)
352
self.assertEqualDiff("""\
353
3 Jane Foo\t2005-11-22 {v1.0, v1.0rc1}
354
356
2 Joe Foo\t2005-11-22 {v0.2} [merge]
420
wt.branch, log.ShortLogFormatter,
421
formatter_kwargs=dict(levels=0))
423
433
def test_short_merge_revs_log_single_merge_revision(self):
424
wt = self._prepare_tree_with_merges()
434
wt = self.make_branch_and_memory_tree('.')
436
self.addCleanup(wt.unlock)
438
wt.commit('rev-1', rev_id='rev-1',
439
timestamp=1132586655, timezone=36000,
440
committer='Joe Foo <joe@foo.com>')
441
wt.commit('rev-merged', rev_id='rev-2a',
442
timestamp=1132586700, timezone=36000,
443
committer='Joe Foo <joe@foo.com>')
444
wt.set_parent_ids(['rev-1', 'rev-2a'])
445
wt.branch.set_last_revision_info(1, 'rev-1')
446
wt.commit('rev-2', rev_id='rev-2b',
447
timestamp=1132586800, timezone=36000,
448
committer='Joe Foo <joe@foo.com>')
449
logfile = self.make_utf8_encoded_stringio()
450
formatter = log.ShortLogFormatter(to_file=logfile, levels=0)
425
451
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
426
rev = revspec.in_history(wt.branch)
427
self.assertFormatterResult("""\
453
rev = revspec.in_history(wtb)
454
log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
455
self.assertEqualDiff("""\
428
456
1.1.1 Joe Foo\t2005-11-22
432
wt.branch, log.ShortLogFormatter,
433
formatter_kwargs=dict(levels=0),
434
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
437
class TestLongLogFormatter(TestCaseForLogFormatter):
463
class TestLongLogFormatter(TestCaseWithoutPropsHandler):
439
465
def test_verbose_log(self):
440
466
"""Verbose log includes changed files
444
wt = self.make_standard_commit('test_verbose_log', authors=[])
445
self.assertFormatterResult('''\
470
wt = self.make_branch_and_tree('.')
472
self.build_tree(['a'])
474
# XXX: why does a longer nick show up?
475
b.nick = 'test_verbose_log'
476
wt.commit(message='add a',
477
timestamp=1132711707,
479
committer='Lorem Ipsum <test@example.com>')
480
logfile = file('out.tmp', 'w+')
481
formatter = log.LongLogFormatter(to_file=logfile)
482
log.show_log(b, formatter, verbose=True)
485
log_contents = logfile.read()
486
self.assertEqualDiff('''\
446
487
------------------------------------------------------------
448
489
committer: Lorem Ipsum <test@example.com>
449
490
branch nick: test_verbose_log
450
timestamp: Tue 2005-11-22 00:00:00 +0000
491
timestamp: Wed 2005-11-23 12:08:27 +1000
456
wt.branch, log.LongLogFormatter,
457
show_log_kwargs=dict(verbose=True))
459
499
def test_merges_are_indented_by_level(self):
460
500
wt = self.make_branch_and_tree('parent')
461
self.wt_commit(wt, 'first post')
462
child_wt = wt.bzrdir.sprout('child').open_workingtree()
463
self.wt_commit(child_wt, 'branch 1')
464
smallerchild_wt = wt.bzrdir.sprout('smallerchild').open_workingtree()
465
self.wt_commit(smallerchild_wt, 'branch 2')
466
child_wt.merge_from_branch(smallerchild_wt.branch)
467
self.wt_commit(child_wt, 'merge branch 2')
468
wt.merge_from_branch(child_wt.branch)
469
self.wt_commit(wt, 'merge branch 1')
470
self.assertFormatterResult("""\
501
wt.commit('first post')
502
self.run_bzr('branch parent child')
503
self.run_bzr(['commit', '-m', 'branch 1', '--unchanged', 'child'])
504
self.run_bzr('branch child smallerchild')
505
self.run_bzr(['commit', '-m', 'branch 2', '--unchanged',
508
self.run_bzr('merge ../smallerchild')
509
self.run_bzr(['commit', '-m', 'merge branch 2'])
510
os.chdir('../parent')
511
self.run_bzr('merge ../child')
512
wt.commit('merge branch 1')
514
sio = self.make_utf8_encoded_stringio()
515
lf = log.LongLogFormatter(to_file=sio, levels=0)
516
log.show_log(b, lf, verbose=True)
517
the_log = normalize_log(sio.getvalue())
518
self.assertEqualDiff("""\
471
519
------------------------------------------------------------
473
committer: Joe Foo <joe@foo.com>
521
committer: Lorem Ipsum <test@example.com>
474
522
branch nick: parent
475
timestamp: Tue 2005-11-22 00:00:04 +0000
478
526
------------------------------------------------------------
479
527
revno: 1.1.2 [merge]
480
committer: Joe Foo <joe@foo.com>
528
committer: Lorem Ipsum <test@example.com>
481
529
branch nick: child
482
timestamp: Tue 2005-11-22 00:00:03 +0000
485
533
------------------------------------------------------------
487
committer: Joe Foo <joe@foo.com>
535
committer: Lorem Ipsum <test@example.com>
488
536
branch nick: smallerchild
489
timestamp: Tue 2005-11-22 00:00:02 +0000
492
540
------------------------------------------------------------
494
committer: Joe Foo <joe@foo.com>
542
committer: Lorem Ipsum <test@example.com>
495
543
branch nick: child
496
timestamp: Tue 2005-11-22 00:00:01 +0000
499
547
------------------------------------------------------------
501
committer: Joe Foo <joe@foo.com>
549
committer: Lorem Ipsum <test@example.com>
502
550
branch nick: parent
503
timestamp: Tue 2005-11-22 00:00:00 +0000
507
wt.branch, log.LongLogFormatter,
508
formatter_kwargs=dict(levels=0),
509
show_log_kwargs=dict(verbose=True))
511
557
def test_verbose_merge_revisions_contain_deltas(self):
512
558
wt = self.make_branch_and_tree('parent')
513
559
self.build_tree(['parent/f1', 'parent/f2'])
514
560
wt.add(['f1','f2'])
515
self.wt_commit(wt, 'first post')
516
child_wt = wt.bzrdir.sprout('child').open_workingtree()
561
wt.commit('first post')
562
self.run_bzr('branch parent child')
517
563
os.unlink('child/f1')
518
self.build_tree_contents([('child/f2', 'hello\n')])
519
self.wt_commit(child_wt, 'removed f1 and modified f2')
520
wt.merge_from_branch(child_wt.branch)
521
self.wt_commit(wt, 'merge branch 1')
522
self.assertFormatterResult("""\
564
file('child/f2', 'wb').write('hello\n')
565
self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
568
self.run_bzr('merge ../child')
569
wt.commit('merge branch 1')
571
sio = self.make_utf8_encoded_stringio()
572
lf = log.LongLogFormatter(to_file=sio, levels=0)
573
log.show_log(b, lf, verbose=True)
574
the_log = normalize_log(sio.getvalue())
575
self.assertEqualDiff("""\
523
576
------------------------------------------------------------
525
committer: Joe Foo <joe@foo.com>
578
committer: Lorem Ipsum <test@example.com>
526
579
branch nick: parent
527
timestamp: Tue 2005-11-22 00:00:02 +0000
582
637
committer: Joe Foo <joe@foo.com>
583
638
branch nick: test
584
timestamp: Tue 2005-11-22 00:00:00 +0000
639
timestamp: Mon 2005-11-21 09:24:15 -0600
586
641
simple log message
588
b, log.LongLogFormatter)
590
645
def test_author_in_log(self):
591
646
"""Log includes the author name if it's set in
592
647
the revision properties
594
wt = self.make_standard_commit('test_author_log',
595
authors=['John Doe <jdoe@example.com>',
596
'Jane Rey <jrey@example.com>'])
597
self.assertFormatterResult("""\
649
wt = self.make_branch_and_tree('.')
651
self.build_tree(['a'])
653
b.nick = 'test_author_log'
654
wt.commit(message='add a',
655
timestamp=1132711707,
657
committer='Lorem Ipsum <test@example.com>',
658
authors=['John Doe <jdoe@example.com>',
659
'Jane Rey <jrey@example.com>'])
661
formatter = log.LongLogFormatter(to_file=sio)
662
log.show_log(b, formatter)
663
self.assertEqualDiff('''\
598
664
------------------------------------------------------------
600
666
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
601
667
committer: Lorem Ipsum <test@example.com>
602
668
branch nick: test_author_log
603
timestamp: Tue 2005-11-22 00:00:00 +0000
669
timestamp: Wed 2005-11-23 12:08:27 +1000
607
wt.branch, log.LongLogFormatter)
609
675
def test_properties_in_log(self):
610
676
"""Log includes the custom properties returned by the registered
613
wt = self.make_standard_commit('test_properties_in_log')
614
def trivial_custom_prop_handler(revision):
615
return {'test_prop':'test_value'}
679
wt = self.make_branch_and_tree('.')
681
self.build_tree(['a'])
683
b.nick = 'test_properties_in_log'
684
wt.commit(message='add a',
685
timestamp=1132711707,
687
committer='Lorem Ipsum <test@example.com>',
688
authors=['John Doe <jdoe@example.com>'])
690
formatter = log.LongLogFormatter(to_file=sio)
692
def trivial_custom_prop_handler(revision):
693
return {'test_prop':'test_value'}
617
# Cleaned up in setUp()
618
log.properties_handler_registry.register(
619
'trivial_custom_prop_handler',
620
trivial_custom_prop_handler)
621
self.assertFormatterResult("""\
695
log.properties_handler_registry.register(
696
'trivial_custom_prop_handler',
697
trivial_custom_prop_handler)
698
log.show_log(b, formatter)
700
log.properties_handler_registry.remove(
701
'trivial_custom_prop_handler')
702
self.assertEqualDiff('''\
622
703
------------------------------------------------------------
624
705
test_prop: test_value
625
706
author: John Doe <jdoe@example.com>
626
707
committer: Lorem Ipsum <test@example.com>
627
708
branch nick: test_properties_in_log
628
timestamp: Tue 2005-11-22 00:00:00 +0000
709
timestamp: Wed 2005-11-23 12:08:27 +1000
632
wt.branch, log.LongLogFormatter)
634
715
def test_properties_in_short_log(self):
635
716
"""Log includes the custom properties returned by the registered
638
wt = self.make_standard_commit('test_properties_in_short_log')
639
def trivial_custom_prop_handler(revision):
640
return {'test_prop':'test_value'}
719
wt = self.make_branch_and_tree('.')
721
self.build_tree(['a'])
723
b.nick = 'test_properties_in_short_log'
724
wt.commit(message='add a',
725
timestamp=1132711707,
727
committer='Lorem Ipsum <test@example.com>',
728
authors=['John Doe <jdoe@example.com>'])
730
formatter = log.ShortLogFormatter(to_file=sio)
732
def trivial_custom_prop_handler(revision):
733
return {'test_prop':'test_value'}
642
log.properties_handler_registry.register(
643
'trivial_custom_prop_handler',
644
trivial_custom_prop_handler)
645
self.assertFormatterResult("""\
646
1 John Doe\t2005-11-22
735
log.properties_handler_registry.register(
736
'trivial_custom_prop_handler',
737
trivial_custom_prop_handler)
738
log.show_log(b, formatter)
740
log.properties_handler_registry.remove(
741
'trivial_custom_prop_handler')
742
self.assertEqualDiff('''\
743
1 John Doe\t2005-11-23
647
744
test_prop: test_value
651
wt.branch, log.ShortLogFormatter)
653
750
def test_error_in_properties_handler(self):
654
751
"""Log includes the custom properties returned by the registered
657
wt = self.make_standard_commit('error_in_properties_handler',
658
revprops={'first_prop':'first_value'})
659
sio = self.make_utf8_encoded_stringio()
754
wt = self.make_branch_and_tree('.')
756
self.build_tree(['a'])
758
b.nick = 'test_author_log'
759
wt.commit(message='add a',
760
timestamp=1132711707,
762
committer='Lorem Ipsum <test@example.com>',
763
authors=['John Doe <jdoe@example.com>'],
764
revprops={'first_prop':'first_value'})
660
766
formatter = log.LongLogFormatter(to_file=sio)
661
def trivial_custom_prop_handler(revision):
662
raise StandardError("a test error")
768
def trivial_custom_prop_handler(revision):
769
raise StandardError("a test error")
664
log.properties_handler_registry.register(
665
'trivial_custom_prop_handler',
666
trivial_custom_prop_handler)
667
self.assertRaises(StandardError, log.show_log, wt.branch, formatter,)
771
log.properties_handler_registry.register(
772
'trivial_custom_prop_handler',
773
trivial_custom_prop_handler)
774
self.assertRaises(StandardError, log.show_log, b, formatter,)
776
log.properties_handler_registry.remove(
777
'trivial_custom_prop_handler')
669
779
def test_properties_handler_bad_argument(self):
670
wt = self.make_standard_commit('bad_argument',
671
revprops={'a_prop':'test_value'})
672
sio = self.make_utf8_encoded_stringio()
780
wt = self.make_branch_and_tree('.')
782
self.build_tree(['a'])
784
b.nick = 'test_author_log'
785
wt.commit(message='add a',
786
timestamp=1132711707,
788
committer='Lorem Ipsum <test@example.com>',
789
authors=['John Doe <jdoe@example.com>'],
790
revprops={'a_prop':'test_value'})
673
792
formatter = log.LongLogFormatter(to_file=sio)
674
def bad_argument_prop_handler(revision):
675
return {'custom_prop_name':revision.properties['a_prop']}
677
log.properties_handler_registry.register(
678
'bad_argument_prop_handler',
679
bad_argument_prop_handler)
681
self.assertRaises(AttributeError, formatter.show_properties,
684
revision = wt.branch.repository.get_revision(wt.branch.last_revision())
685
formatter.show_properties(revision, '')
686
self.assertEqualDiff('''custom_prop_name: test_value\n''',
689
def test_show_ids(self):
690
wt = self.make_branch_and_tree('parent')
691
self.build_tree(['parent/f1', 'parent/f2'])
693
self.wt_commit(wt, 'first post', rev_id='a')
694
child_wt = wt.bzrdir.sprout('child').open_workingtree()
695
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
696
wt.merge_from_branch(child_wt.branch)
697
self.wt_commit(wt, 'merge branch 1', rev_id='c')
698
self.assertFormatterResult("""\
699
------------------------------------------------------------
704
committer: Joe Foo <joe@foo.com>
706
timestamp: Tue 2005-11-22 00:00:02 +0000
709
------------------------------------------------------------
713
committer: Joe Foo <joe@foo.com>
715
timestamp: Tue 2005-11-22 00:00:01 +0000
718
------------------------------------------------------------
721
committer: Joe Foo <joe@foo.com>
723
timestamp: Tue 2005-11-22 00:00:00 +0000
727
wt.branch, log.LongLogFormatter,
728
formatter_kwargs=dict(levels=0,show_ids=True))
731
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
794
def bad_argument_prop_handler(revision):
795
return {'custom_prop_name':revision.properties['a_prop']}
797
log.properties_handler_registry.register(
798
'bad_argument_prop_handler',
799
bad_argument_prop_handler)
801
self.assertRaises(AttributeError, formatter.show_properties,
804
revision = b.repository.get_revision(b.last_revision())
805
formatter.show_properties(revision, '')
806
self.assertEqualDiff('''custom_prop_name: test_value\n''',
809
log.properties_handler_registry.remove(
810
'bad_argument_prop_handler')
813
class TestLongLogFormatterWithoutMergeRevisions(TestCaseWithoutPropsHandler):
733
815
def test_long_verbose_log(self):
734
816
"""Verbose log includes changed files
738
wt = self.make_standard_commit('test_long_verbose_log', authors=[])
739
self.assertFormatterResult("""\
820
wt = self.make_branch_and_tree('.')
822
self.build_tree(['a'])
824
# XXX: why does a longer nick show up?
825
b.nick = 'test_verbose_log'
826
wt.commit(message='add a',
827
timestamp=1132711707,
829
committer='Lorem Ipsum <test@example.com>')
830
logfile = file('out.tmp', 'w+')
831
formatter = log.LongLogFormatter(to_file=logfile, levels=1)
832
log.show_log(b, formatter, verbose=True)
835
log_contents = logfile.read()
836
self.assertEqualDiff('''\
740
837
------------------------------------------------------------
742
839
committer: Lorem Ipsum <test@example.com>
743
branch nick: test_long_verbose_log
744
timestamp: Tue 2005-11-22 00:00:00 +0000
840
branch nick: test_verbose_log
841
timestamp: Wed 2005-11-23 12:08:27 +1000
750
wt.branch, log.LongLogFormatter,
751
formatter_kwargs=dict(levels=1),
752
show_log_kwargs=dict(verbose=True))
754
849
def test_long_verbose_contain_deltas(self):
755
850
wt = self.make_branch_and_tree('parent')
756
851
self.build_tree(['parent/f1', 'parent/f2'])
757
852
wt.add(['f1','f2'])
758
self.wt_commit(wt, 'first post')
759
child_wt = wt.bzrdir.sprout('child').open_workingtree()
853
wt.commit('first post')
854
self.run_bzr('branch parent child')
760
855
os.unlink('child/f1')
761
self.build_tree_contents([('child/f2', 'hello\n')])
762
self.wt_commit(child_wt, 'removed f1 and modified f2')
763
wt.merge_from_branch(child_wt.branch)
764
self.wt_commit(wt, 'merge branch 1')
765
self.assertFormatterResult("""\
856
file('child/f2', 'wb').write('hello\n')
857
self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
860
self.run_bzr('merge ../child')
861
wt.commit('merge branch 1')
863
sio = self.make_utf8_encoded_stringio()
864
lf = log.LongLogFormatter(to_file=sio, levels=1)
865
log.show_log(b, lf, verbose=True)
866
the_log = normalize_log(sio.getvalue())
867
self.assertEqualDiff("""\
766
868
------------------------------------------------------------
768
committer: Joe Foo <joe@foo.com>
870
committer: Lorem Ipsum <test@example.com>
769
871
branch nick: parent
770
timestamp: Tue 2005-11-22 00:00:02 +0000
814
920
committer: Joe Foo <joe@foo.com>
815
921
branch nick: test
816
timestamp: Tue 2005-11-22 00:00:00 +0000
922
timestamp: Mon 2005-11-21 09:24:15 -0600
818
924
simple log message
820
b, log.LongLogFormatter,
821
formatter_kwargs=dict(levels=1))
823
928
def test_long_author_in_log(self):
824
929
"""Log includes the author name if it's set in
825
930
the revision properties
827
wt = self.make_standard_commit('test_author_log')
828
self.assertFormatterResult("""\
932
wt = self.make_branch_and_tree('.')
934
self.build_tree(['a'])
936
b.nick = 'test_author_log'
937
wt.commit(message='add a',
938
timestamp=1132711707,
940
committer='Lorem Ipsum <test@example.com>',
941
authors=['John Doe <jdoe@example.com>'])
943
formatter = log.LongLogFormatter(to_file=sio, levels=1)
944
log.show_log(b, formatter)
945
self.assertEqualDiff('''\
829
946
------------------------------------------------------------
831
948
author: John Doe <jdoe@example.com>
832
949
committer: Lorem Ipsum <test@example.com>
833
950
branch nick: test_author_log
834
timestamp: Tue 2005-11-22 00:00:00 +0000
951
timestamp: Wed 2005-11-23 12:08:27 +1000
838
wt.branch, log.LongLogFormatter,
839
formatter_kwargs=dict(levels=1))
841
957
def test_long_properties_in_log(self):
842
958
"""Log includes the custom properties returned by the registered
845
wt = self.make_standard_commit('test_properties_in_log')
846
def trivial_custom_prop_handler(revision):
847
return {'test_prop':'test_value'}
961
wt = self.make_branch_and_tree('.')
963
self.build_tree(['a'])
965
b.nick = 'test_properties_in_log'
966
wt.commit(message='add a',
967
timestamp=1132711707,
969
committer='Lorem Ipsum <test@example.com>',
970
authors=['John Doe <jdoe@example.com>'])
972
formatter = log.LongLogFormatter(to_file=sio, levels=1)
974
def trivial_custom_prop_handler(revision):
975
return {'test_prop':'test_value'}
849
log.properties_handler_registry.register(
850
'trivial_custom_prop_handler',
851
trivial_custom_prop_handler)
852
self.assertFormatterResult("""\
977
log.properties_handler_registry.register(
978
'trivial_custom_prop_handler',
979
trivial_custom_prop_handler)
980
log.show_log(b, formatter)
982
log.properties_handler_registry.remove(
983
'trivial_custom_prop_handler')
984
self.assertEqualDiff('''\
853
985
------------------------------------------------------------
855
987
test_prop: test_value
856
988
author: John Doe <jdoe@example.com>
857
989
committer: Lorem Ipsum <test@example.com>
858
990
branch nick: test_properties_in_log
859
timestamp: Tue 2005-11-22 00:00:00 +0000
991
timestamp: Wed 2005-11-23 12:08:27 +1000
863
wt.branch, log.LongLogFormatter,
864
formatter_kwargs=dict(levels=1))
867
class TestLineLogFormatter(TestCaseForLogFormatter):
998
class TestLineLogFormatter(tests.TestCaseWithTransport):
869
1000
def test_line_log(self):
870
1001
"""Line log should show revno
874
wt = self.make_standard_commit('test-line-log',
875
committer='Line-Log-Formatter Tester <test@line.log>',
877
self.assertFormatterResult("""\
878
1: Line-Log-Formatte... 2005-11-22 add a
880
wt.branch, log.LineLogFormatter)
1005
wt = self.make_branch_and_tree('.')
1007
self.build_tree(['a'])
1009
b.nick = 'test-line-log'
1010
wt.commit(message='add a',
1011
timestamp=1132711707,
1013
committer='Line-Log-Formatter Tester <test@line.log>')
1014
logfile = file('out.tmp', 'w+')
1015
formatter = log.LineLogFormatter(to_file=logfile)
1016
log.show_log(b, formatter)
1019
log_contents = logfile.read()
1020
self.assertEqualDiff('1: Line-Log-Formatte... 2005-11-23 add a\n',
882
1023
def test_trailing_newlines(self):
883
1024
wt = self.make_branch_and_tree('.')
884
b = self.make_commits_with_trailing_newlines(wt)
885
self.assertFormatterResult("""\
886
3: Joe Foo 2005-11-22 single line with trailing newline
887
2: Joe Foo 2005-11-22 multiline
888
1: Joe Foo 2005-11-22 simple log message
1025
b = make_commits_with_trailing_newlines(wt)
1026
sio = self.make_utf8_encoded_stringio()
1027
lf = log.LineLogFormatter(to_file=sio)
1029
self.assertEqualDiff("""\
1030
3: Joe Foo 2005-11-21 single line with trailing newline
1031
2: Joe Bar 2005-11-21 multiline
1032
1: Joe Foo 2005-11-21 simple log message
890
b, log.LineLogFormatter)
1036
def _prepare_tree_with_merges(self, with_tags=False):
1037
wt = self.make_branch_and_memory_tree('.')
1039
self.addCleanup(wt.unlock)
1041
wt.commit('rev-1', rev_id='rev-1',
1042
timestamp=1132586655, timezone=36000,
1043
committer='Joe Foo <joe@foo.com>')
1044
wt.commit('rev-merged', rev_id='rev-2a',
1045
timestamp=1132586700, timezone=36000,
1046
committer='Joe Foo <joe@foo.com>')
1047
wt.set_parent_ids(['rev-1', 'rev-2a'])
1048
wt.branch.set_last_revision_info(1, 'rev-1')
1049
wt.commit('rev-2', rev_id='rev-2b',
1050
timestamp=1132586800, timezone=36000,
1051
committer='Joe Foo <joe@foo.com>')
1054
branch.tags.set_tag('v0.2', 'rev-2b')
1055
wt.commit('rev-3', rev_id='rev-3',
1056
timestamp=1132586900, timezone=36000,
1057
committer='Jane Foo <jane@foo.com>')
1058
branch.tags.set_tag('v1.0rc1', 'rev-3')
1059
branch.tags.set_tag('v1.0', 'rev-3')
892
1062
def test_line_log_single_merge_revision(self):
893
1063
wt = self._prepare_tree_with_merges()
1064
logfile = self.make_utf8_encoded_stringio()
1065
formatter = log.LineLogFormatter(to_file=logfile)
894
1066
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
895
rev = revspec.in_history(wt.branch)
896
self.assertFormatterResult("""\
1068
rev = revspec.in_history(wtb)
1069
log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
1070
self.assertEqualDiff("""\
897
1071
1.1.1: Joe Foo 2005-11-22 rev-merged
899
wt.branch, log.LineLogFormatter,
900
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
902
1075
def test_line_log_with_tags(self):
903
1076
wt = self._prepare_tree_with_merges(with_tags=True)
904
self.assertFormatterResult("""\
905
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
1077
logfile = self.make_utf8_encoded_stringio()
1078
formatter = log.LineLogFormatter(to_file=logfile)
1079
log.show_log(wt.branch, formatter)
1080
self.assertEqualDiff("""\
1081
3: Jane Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
906
1082
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
907
1083
1: Joe Foo 2005-11-22 rev-1
909
wt.branch, log.LineLogFormatter)
912
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
1087
class TestLineLogFormatterWithMergeRevisions(tests.TestCaseWithTransport):
914
1089
def test_line_merge_revs_log(self):
915
1090
"""Line log should show revno
919
wt = self.make_standard_commit('test-line-log',
920
committer='Line-Log-Formatter Tester <test@line.log>',
922
self.assertFormatterResult("""\
923
1: Line-Log-Formatte... 2005-11-22 add a
925
wt.branch, log.LineLogFormatter)
1094
wt = self.make_branch_and_tree('.')
1096
self.build_tree(['a'])
1098
b.nick = 'test-line-log'
1099
wt.commit(message='add a',
1100
timestamp=1132711707,
1102
committer='Line-Log-Formatter Tester <test@line.log>')
1103
logfile = file('out.tmp', 'w+')
1104
formatter = log.LineLogFormatter(to_file=logfile, levels=0)
1105
log.show_log(b, formatter)
1108
log_contents = logfile.read()
1109
self.assertEqualDiff('1: Line-Log-Formatte... 2005-11-23 add a\n',
927
1112
def test_line_merge_revs_log_single_merge_revision(self):
928
wt = self._prepare_tree_with_merges()
1113
wt = self.make_branch_and_memory_tree('.')
1115
self.addCleanup(wt.unlock)
1117
wt.commit('rev-1', rev_id='rev-1',
1118
timestamp=1132586655, timezone=36000,
1119
committer='Joe Foo <joe@foo.com>')
1120
wt.commit('rev-merged', rev_id='rev-2a',
1121
timestamp=1132586700, timezone=36000,
1122
committer='Joe Foo <joe@foo.com>')
1123
wt.set_parent_ids(['rev-1', 'rev-2a'])
1124
wt.branch.set_last_revision_info(1, 'rev-1')
1125
wt.commit('rev-2', rev_id='rev-2b',
1126
timestamp=1132586800, timezone=36000,
1127
committer='Joe Foo <joe@foo.com>')
1128
logfile = self.make_utf8_encoded_stringio()
1129
formatter = log.LineLogFormatter(to_file=logfile, levels=0)
929
1130
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
930
rev = revspec.in_history(wt.branch)
931
self.assertFormatterResult("""\
1132
rev = revspec.in_history(wtb)
1133
log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
1134
self.assertEqualDiff("""\
932
1135
1.1.1: Joe Foo 2005-11-22 rev-merged
934
wt.branch, log.LineLogFormatter,
935
formatter_kwargs=dict(levels=0),
936
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
938
1139
def test_line_merge_revs_log_with_merges(self):
939
wt = self._prepare_tree_with_merges()
940
self.assertFormatterResult("""\
1140
wt = self.make_branch_and_memory_tree('.')
1142
self.addCleanup(wt.unlock)
1144
wt.commit('rev-1', rev_id='rev-1',
1145
timestamp=1132586655, timezone=36000,
1146
committer='Joe Foo <joe@foo.com>')
1147
wt.commit('rev-merged', rev_id='rev-2a',
1148
timestamp=1132586700, timezone=36000,
1149
committer='Joe Foo <joe@foo.com>')
1150
wt.set_parent_ids(['rev-1', 'rev-2a'])
1151
wt.branch.set_last_revision_info(1, 'rev-1')
1152
wt.commit('rev-2', rev_id='rev-2b',
1153
timestamp=1132586800, timezone=36000,
1154
committer='Joe Foo <joe@foo.com>')
1155
logfile = self.make_utf8_encoded_stringio()
1156
formatter = log.LineLogFormatter(to_file=logfile, levels=0)
1157
log.show_log(wt.branch, formatter)
1158
self.assertEqualDiff("""\
941
1159
2: Joe Foo 2005-11-22 [merge] rev-2
942
1160
1.1.1: Joe Foo 2005-11-22 rev-merged
943
1161
1: Joe Foo 2005-11-22 rev-1
945
wt.branch, log.LineLogFormatter,
946
formatter_kwargs=dict(levels=0))
949
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
951
def test_gnu_changelog(self):
952
wt = self.make_standard_commit('nicky', authors=[])
953
self.assertFormatterResult('''\
954
2005-11-22 Lorem Ipsum <test@example.com>
959
wt.branch, log.GnuChangelogLogFormatter)
961
def test_with_authors(self):
962
wt = self.make_standard_commit('nicky',
963
authors=['Fooa Fooz <foo@example.com>',
964
'Bari Baro <bar@example.com>'])
965
self.assertFormatterResult('''\
966
2005-11-22 Fooa Fooz <foo@example.com>
971
wt.branch, log.GnuChangelogLogFormatter)
973
def test_verbose(self):
974
wt = self.make_standard_commit('nicky')
975
self.assertFormatterResult('''\
976
2005-11-22 John Doe <jdoe@example.com>
983
wt.branch, log.GnuChangelogLogFormatter,
984
show_log_kwargs=dict(verbose=True))
1165
class TestGetViewRevisions(tests.TestCaseWithTransport):
1167
def make_tree_with_commits(self):
1168
"""Create a tree with well-known revision ids"""
1169
wt = self.make_branch_and_tree('tree1')
1170
wt.commit('commit one', rev_id='1')
1171
wt.commit('commit two', rev_id='2')
1172
wt.commit('commit three', rev_id='3')
1173
mainline_revs = [None, '1', '2', '3']
1174
rev_nos = {'1': 1, '2': 2, '3': 3}
1175
return mainline_revs, rev_nos, wt
1177
def make_tree_with_merges(self):
1178
"""Create a tree with well-known revision ids and a merge"""
1179
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1180
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
1181
tree2.commit('four-a', rev_id='4a')
1182
wt.merge_from_branch(tree2.branch)
1183
wt.commit('four-b', rev_id='4b')
1184
mainline_revs.append('4b')
1187
return mainline_revs, rev_nos, wt
1189
def make_tree_with_many_merges(self):
1190
"""Create a tree with well-known revision ids"""
1191
wt = self.make_branch_and_tree('tree1')
1192
self.build_tree_contents([('tree1/f', '1\n')])
1193
wt.add(['f'], ['f-id'])
1194
wt.commit('commit one', rev_id='1')
1195
wt.commit('commit two', rev_id='2')
1197
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
1198
self.build_tree_contents([('tree3/f', '1\n2\n3a\n')])
1199
tree3.commit('commit three a', rev_id='3a')
1201
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
1202
tree2.merge_from_branch(tree3.branch)
1203
tree2.commit('commit three b', rev_id='3b')
1205
wt.merge_from_branch(tree2.branch)
1206
wt.commit('commit three c', rev_id='3c')
1207
tree2.commit('four-a', rev_id='4a')
1209
wt.merge_from_branch(tree2.branch)
1210
wt.commit('four-b', rev_id='4b')
1212
mainline_revs = [None, '1', '2', '3c', '4b']
1213
rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
1214
full_rev_nos_for_reference = {
1217
'3a': '2.1.1', #first commit tree 3
1218
'3b': '2.2.1', # first commit tree 2
1219
'3c': '3', #merges 3b to main
1220
'4a': '2.2.2', # second commit tree 2
1221
'4b': '4', # merges 4a to main
1223
return mainline_revs, rev_nos, wt
1225
def test_get_view_revisions_forward(self):
1226
"""Test the get_view_revisions method"""
1227
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1229
self.addCleanup(wt.unlock)
1230
revisions = list(log.get_view_revisions(
1231
mainline_revs, rev_nos, wt.branch, 'forward'))
1232
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
1234
revisions2 = list(log.get_view_revisions(
1235
mainline_revs, rev_nos, wt.branch, 'forward',
1236
include_merges=False))
1237
self.assertEqual(revisions, revisions2)
1239
def test_get_view_revisions_reverse(self):
1240
"""Test the get_view_revisions with reverse"""
1241
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1243
self.addCleanup(wt.unlock)
1244
revisions = list(log.get_view_revisions(
1245
mainline_revs, rev_nos, wt.branch, 'reverse'))
1246
self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
1248
revisions2 = list(log.get_view_revisions(
1249
mainline_revs, rev_nos, wt.branch, 'reverse',
1250
include_merges=False))
1251
self.assertEqual(revisions, revisions2)
1253
def test_get_view_revisions_merge(self):
1254
"""Test get_view_revisions when there are merges"""
1255
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1257
self.addCleanup(wt.unlock)
1258
revisions = list(log.get_view_revisions(
1259
mainline_revs, rev_nos, wt.branch, 'forward'))
1260
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1261
('4b', '4', 0), ('4a', '3.1.1', 1)],
1263
revisions = list(log.get_view_revisions(
1264
mainline_revs, rev_nos, wt.branch, 'forward',
1265
include_merges=False))
1266
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1270
def test_get_view_revisions_merge_reverse(self):
1271
"""Test get_view_revisions in reverse when there are merges"""
1272
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1274
self.addCleanup(wt.unlock)
1275
revisions = list(log.get_view_revisions(
1276
mainline_revs, rev_nos, wt.branch, 'reverse'))
1277
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
1278
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
1280
revisions = list(log.get_view_revisions(
1281
mainline_revs, rev_nos, wt.branch, 'reverse',
1282
include_merges=False))
1283
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
1287
def test_get_view_revisions_merge2(self):
1288
"""Test get_view_revisions when there are merges"""
1289
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
1291
self.addCleanup(wt.unlock)
1292
revisions = list(log.get_view_revisions(
1293
mainline_revs, rev_nos, wt.branch, 'forward'))
1294
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1295
('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
1297
self.assertEqual(expected, revisions)
1298
revisions = list(log.get_view_revisions(
1299
mainline_revs, rev_nos, wt.branch, 'forward',
1300
include_merges=False))
1301
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1306
def test_file_id_for_range(self):
1307
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
1309
self.addCleanup(wt.unlock)
1311
def rev_from_rev_id(revid, branch):
1312
revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
1313
return revspec.in_history(branch)
1315
def view_revs(start_rev, end_rev, file_id, direction):
1316
revs = log.calculate_view_revisions(
1318
start_rev, # start_revision
1319
end_rev, # end_revision
1320
direction, # direction
1321
file_id, # specific_fileid
1322
True, # generate_merge_revisions
1326
rev_3a = rev_from_rev_id('3a', wt.branch)
1327
rev_4b = rev_from_rev_id('4b', wt.branch)
1328
self.assertEqual([('3c', '3', 0), ('3a', '2.1.1', 1)],
1329
view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
1330
# Note: 3c still appears before 3a here because of depth-based sorting
1331
self.assertEqual([('3c', '3', 0), ('3a', '2.1.1', 1)],
1332
view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
1335
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
1337
def create_tree_with_single_merge(self):
1338
"""Create a branch with a moderate layout.
1340
The revision graph looks like:
1348
In this graph, A introduced files f1 and f2 and f3.
1349
B modifies f1 and f3, and C modifies f2 and f3.
1350
D merges the changes from B and C and resolves the conflict for f3.
1352
# TODO: jam 20070218 This seems like it could really be done
1353
# with make_branch_and_memory_tree() if we could just
1354
# create the content of those files.
1355
# TODO: jam 20070218 Another alternative is that we would really
1356
# like to only create this tree 1 time for all tests that
1357
# use it. Since 'log' only uses the tree in a readonly
1358
# fashion, it seems a shame to regenerate an identical
1359
# tree for each test.
1360
tree = self.make_branch_and_tree('tree')
1362
self.addCleanup(tree.unlock)
1364
self.build_tree_contents([('tree/f1', 'A\n'),
1368
tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
1369
tree.commit('A', rev_id='A')
1371
self.build_tree_contents([('tree/f2', 'A\nC\n'),
1372
('tree/f3', 'A\nC\n'),
1374
tree.commit('C', rev_id='C')
1375
# Revert back to A to build the other history.
1376
tree.set_last_revision('A')
1377
tree.branch.set_last_revision_info(1, 'A')
1378
self.build_tree_contents([('tree/f1', 'A\nB\n'),
1380
('tree/f3', 'A\nB\n'),
1382
tree.commit('B', rev_id='B')
1383
tree.set_parent_ids(['B', 'C'])
1384
self.build_tree_contents([('tree/f1', 'A\nB\n'),
1385
('tree/f2', 'A\nC\n'),
1386
('tree/f3', 'A\nB\nC\n'),
1388
tree.commit('D', rev_id='D')
1390
# Switch to a read lock for this tree.
1391
# We still have an addCleanup(tree.unlock) pending
1396
def check_delta(self, delta, **kw):
1397
"""Check the filenames touched by a delta are as expected.
1399
Caller only have to pass in the list of files for each part, all
1400
unspecified parts are considered empty (and checked as such).
1402
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
1403
# By default we expect an empty list
1404
expected = kw.get(n, [])
1405
# strip out only the path components
1406
got = [x[0] for x in getattr(delta, n)]
1407
self.assertEqual(expected, got)
1409
def test_tree_with_single_merge(self):
1410
"""Make sure the tree layout is correct."""
1411
tree = self.create_tree_with_single_merge()
1412
rev_A_tree = tree.branch.repository.revision_tree('A')
1413
rev_B_tree = tree.branch.repository.revision_tree('B')
1414
rev_C_tree = tree.branch.repository.revision_tree('C')
1415
rev_D_tree = tree.branch.repository.revision_tree('D')
1417
self.check_delta(rev_B_tree.changes_from(rev_A_tree),
1418
modified=['f1', 'f3'])
1420
self.check_delta(rev_C_tree.changes_from(rev_A_tree),
1421
modified=['f2', 'f3'])
1423
self.check_delta(rev_D_tree.changes_from(rev_B_tree),
1424
modified=['f2', 'f3'])
1426
self.check_delta(rev_D_tree.changes_from(rev_C_tree),
1427
modified=['f1', 'f3'])
1429
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
1430
"""Ensure _filter_revisions_touching_file_id returns the right values.
1432
Get the return value from _filter_revisions_touching_file_id and make
1433
sure they are correct.
1435
# The api for _filter_revisions_touching_file_id is a little crazy.
1436
# So we do the setup here.
1437
mainline = tree.branch.revision_history()
1438
mainline.insert(0, None)
1439
revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
1440
view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
1442
actual_revs = log._filter_revisions_touching_file_id(
1445
list(view_revs_iter))
1446
self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
1448
def test_file_id_f1(self):
1449
tree = self.create_tree_with_single_merge()
1450
# f1 should be marked as modified by revisions A and B
1451
self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
1453
def test_file_id_f2(self):
1454
tree = self.create_tree_with_single_merge()
1455
# f2 should be marked as modified by revisions A, C, and D
1456
# because D merged the changes from C.
1457
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1459
def test_file_id_f3(self):
1460
tree = self.create_tree_with_single_merge()
1461
# f3 should be marked as modified by revisions A, B, C, and D
1462
self.assertAllRevisionsForFileID(tree, 'f3-id', ['D', 'C', 'B', 'A'])
1464
def test_file_id_with_ghosts(self):
1465
# This is testing bug #209948, where having a ghost would cause
1466
# _filter_revisions_touching_file_id() to fail.
1467
tree = self.create_tree_with_single_merge()
1468
# We need to add a revision, so switch back to a write-locked tree
1469
# (still a single addCleanup(tree.unlock) pending).
1472
first_parent = tree.last_revision()
1473
tree.set_parent_ids([first_parent, 'ghost-revision-id'])
1474
self.build_tree_contents([('tree/f1', 'A\nB\nXX\n')])
1475
tree.commit('commit with a ghost', rev_id='XX')
1476
self.assertAllRevisionsForFileID(tree, 'f1-id', ['XX', 'B', 'A'])
1477
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1479
def test_unknown_file_id(self):
1480
tree = self.create_tree_with_single_merge()
1481
self.assertAllRevisionsForFileID(tree, 'unknown', [])
1483
def test_empty_branch_unknown_file_id(self):
1484
tree = self.make_branch_and_tree('tree')
1485
self.assertAllRevisionsForFileID(tree, 'unknown', [])
987
1488
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1186
1686
log.show_branch_change(tree.branch, s, 3, '3b')
1187
1687
self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1188
1688
self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1191
class TestRevisionNotInBranch(TestCaseForLogFormatter):
1193
def setup_a_tree(self):
1194
tree = self.make_branch_and_tree('tree')
1196
self.addCleanup(tree.unlock)
1198
'committer': 'Joe Foo <joe@foo.com>',
1199
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1200
'timezone': 0, # UTC
1202
tree.commit('commit 1a', rev_id='1a', **kwargs)
1203
tree.commit('commit 2a', rev_id='2a', **kwargs)
1204
tree.commit('commit 3a', rev_id='3a', **kwargs)
1207
def setup_ab_tree(self):
1208
tree = self.setup_a_tree()
1209
tree.set_last_revision('1a')
1210
tree.branch.set_last_revision_info(1, '1a')
1212
'committer': 'Joe Foo <joe@foo.com>',
1213
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1214
'timezone': 0, # UTC
1216
tree.commit('commit 2b', rev_id='2b', **kwargs)
1217
tree.commit('commit 3b', rev_id='3b', **kwargs)
1220
def test_one_revision(self):
1221
tree = self.setup_ab_tree()
1223
rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1224
log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
1226
self.assertEqual(1, len(lf.revisions))
1227
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1228
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1230
def test_many_revisions(self):
1231
tree = self.setup_ab_tree()
1233
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1234
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1235
log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
1236
end_revision=end_rev)
1237
self.assertEqual(3, len(lf.revisions))
1238
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1239
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1240
self.assertEqual(None, lf.revisions[1].revno) # Out-of-branch
1241
self.assertEqual('2a', lf.revisions[1].rev.revision_id)
1242
self.assertEqual('1', lf.revisions[2].revno) # In-branch
1244
def test_long_format(self):
1245
tree = self.setup_ab_tree()
1246
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1247
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1248
self.assertFormatterResult("""\
1249
------------------------------------------------------------
1251
committer: Joe Foo <joe@foo.com>
1253
timestamp: Tue 2005-11-22 00:00:00 +0000
1256
------------------------------------------------------------
1258
committer: Joe Foo <joe@foo.com>
1260
timestamp: Tue 2005-11-22 00:00:00 +0000
1263
------------------------------------------------------------
1265
committer: Joe Foo <joe@foo.com>
1267
timestamp: Tue 2005-11-22 00:00:00 +0000
1271
tree.branch, log.LongLogFormatter, show_log_kwargs={
1272
'start_revision': start_rev, 'end_revision': end_rev
1275
def test_short_format(self):
1276
tree = self.setup_ab_tree()
1277
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1278
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1279
self.assertFormatterResult("""\
1288
1 Joe Foo\t2005-11-22
1292
tree.branch, log.ShortLogFormatter, show_log_kwargs={
1293
'start_revision': start_rev, 'end_revision': end_rev
1296
def test_line_format(self):
1297
tree = self.setup_ab_tree()
1298
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1299
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1300
self.assertFormatterResult("""\
1301
Joe Foo 2005-11-22 commit 3a
1302
Joe Foo 2005-11-22 commit 2a
1303
1: Joe Foo 2005-11-22 commit 1a
1305
tree.branch, log.LineLogFormatter, show_log_kwargs={
1306
'start_revision': start_rev, 'end_revision': end_rev
1310
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1313
TestCaseForLogFormatter.setUp(self)
1314
log.properties_handler_registry.register(
1315
'bugs_properties_handler',
1316
log._bugs_properties_handler)
1318
def make_commits_with_bugs(self):
1319
"""Helper method for LogFormatter tests"""
1320
tree = self.make_branch_and_tree(u'.')
1321
self.build_tree(['a', 'b'])
1323
self.wt_commit(tree, 'simple log message', rev_id='a1',
1324
revprops={'bugs': 'test://bug/id fixed'})
1326
self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id='a2',
1327
authors=['Joe Bar <joe@bar.com>'],
1328
revprops={'bugs': 'test://bug/id fixed\n'
1329
'test://bug/2 fixed'})
1333
def test_long_bugs(self):
1334
tree = self.make_commits_with_bugs()
1335
self.assertFormatterResult("""\
1336
------------------------------------------------------------
1338
fixes bug(s): test://bug/id test://bug/2
1339
author: Joe Bar <joe@bar.com>
1340
committer: Joe Foo <joe@foo.com>
1342
timestamp: Tue 2005-11-22 00:00:01 +0000
1347
------------------------------------------------------------
1349
fixes bug(s): test://bug/id
1350
committer: Joe Foo <joe@foo.com>
1352
timestamp: Tue 2005-11-22 00:00:00 +0000
1356
tree.branch, log.LongLogFormatter)
1358
def test_short_bugs(self):
1359
tree = self.make_commits_with_bugs()
1360
self.assertFormatterResult("""\
1361
2 Joe Bar\t2005-11-22
1362
fixes bug(s): test://bug/id test://bug/2
1367
1 Joe Foo\t2005-11-22
1368
fixes bug(s): test://bug/id
1372
tree.branch, log.ShortLogFormatter)
1374
def test_wrong_bugs_property(self):
1375
tree = self.make_branch_and_tree(u'.')
1376
self.build_tree(['foo'])
1377
self.wt_commit(tree, 'simple log message', rev_id='a1',
1378
revprops={'bugs': 'test://bug/id invalid_value'})
1379
self.assertFormatterResult("""\
1380
1 Joe Foo\t2005-11-22
1384
tree.branch, log.ShortLogFormatter)
1386
def test_bugs_handler_present(self):
1387
self.properties_handler_registry.get('bugs_properties_handler')
1390
class TestLogForAuthors(TestCaseForLogFormatter):
1393
TestCaseForLogFormatter.setUp(self)
1394
self.wt = self.make_standard_commit('nicky',
1395
authors=['John Doe <jdoe@example.com>',
1396
'Jane Rey <jrey@example.com>'])
1398
def assertFormatterResult(self, formatter, who, result):
1399
formatter_kwargs = dict()
1401
author_list_handler = log.author_list_registry.get(who)
1402
formatter_kwargs['author_list_handler'] = author_list_handler
1403
TestCaseForLogFormatter.assertFormatterResult(self, result,
1404
self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
1406
def test_line_default(self):
1407
self.assertFormatterResult(log.LineLogFormatter, None, """\
1408
1: John Doe 2005-11-22 add a
1411
def test_line_committer(self):
1412
self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
1413
1: Lorem Ipsum 2005-11-22 add a
1416
def test_line_first(self):
1417
self.assertFormatterResult(log.LineLogFormatter, 'first', """\
1418
1: John Doe 2005-11-22 add a
1421
def test_line_all(self):
1422
self.assertFormatterResult(log.LineLogFormatter, 'all', """\
1423
1: John Doe, Jane Rey 2005-11-22 add a
1427
def test_short_default(self):
1428
self.assertFormatterResult(log.ShortLogFormatter, None, """\
1429
1 John Doe\t2005-11-22
1434
def test_short_committer(self):
1435
self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
1436
1 Lorem Ipsum\t2005-11-22
1441
def test_short_first(self):
1442
self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
1443
1 John Doe\t2005-11-22
1448
def test_short_all(self):
1449
self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
1450
1 John Doe, Jane Rey\t2005-11-22
1455
def test_long_default(self):
1456
self.assertFormatterResult(log.LongLogFormatter, None, """\
1457
------------------------------------------------------------
1459
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1460
committer: Lorem Ipsum <test@example.com>
1462
timestamp: Tue 2005-11-22 00:00:00 +0000
1467
def test_long_committer(self):
1468
self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
1469
------------------------------------------------------------
1471
committer: Lorem Ipsum <test@example.com>
1473
timestamp: Tue 2005-11-22 00:00:00 +0000
1478
def test_long_first(self):
1479
self.assertFormatterResult(log.LongLogFormatter, 'first', """\
1480
------------------------------------------------------------
1482
author: John Doe <jdoe@example.com>
1483
committer: Lorem Ipsum <test@example.com>
1485
timestamp: Tue 2005-11-22 00:00:00 +0000
1490
def test_long_all(self):
1491
self.assertFormatterResult(log.LongLogFormatter, 'all', """\
1492
------------------------------------------------------------
1494
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1495
committer: Lorem Ipsum <test@example.com>
1497
timestamp: Tue 2005-11-22 00:00:00 +0000
1502
def test_gnu_changelog_default(self):
1503
self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
1504
2005-11-22 John Doe <jdoe@example.com>
1510
def test_gnu_changelog_committer(self):
1511
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
1512
2005-11-22 Lorem Ipsum <test@example.com>
1518
def test_gnu_changelog_first(self):
1519
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
1520
2005-11-22 John Doe <jdoe@example.com>
1526
def test_gnu_changelog_all(self):
1527
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
1528
2005-11-22 John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1535
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1537
def make_branch_with_alternate_ancestries(self, relpath='.'):
1538
# See test_merge_sorted_exclude_ancestry below for the difference with
1539
# bt.per_branch.test_iter_merge_sorted_revision.
1540
# TestIterMergeSortedRevisionsBushyGraph.
1541
# make_branch_with_alternate_ancestries
1542
# and test_merge_sorted_exclude_ancestry
1543
# See the FIXME in assertLogRevnos too.
1544
builder = branchbuilder.BranchBuilder(self.get_transport(relpath))
1556
builder.start_series()
1557
builder.build_snapshot('1', None, [
1558
('add', ('', 'TREE_ROOT', 'directory', '')),])
1559
builder.build_snapshot('1.1.1', ['1'], [])
1560
builder.build_snapshot('2', ['1'], [])
1561
builder.build_snapshot('1.2.1', ['1.1.1'], [])
1562
builder.build_snapshot('1.1.2', ['1.1.1', '1.2.1'], [])
1563
builder.build_snapshot('3', ['2', '1.1.2'], [])
1564
builder.finish_series()
1565
br = builder.get_branch()
1567
self.addCleanup(br.unlock)
1570
def assertLogRevnos(self, expected_revnos, b, start, end,
1571
exclude_common_ancestry, generate_merge_revisions=True):
1572
# FIXME: the layering in log makes it hard to test intermediate levels,
1573
# I wish adding filters with their parameters were easier...
1575
iter_revs = log._calc_view_revisions(
1576
b, start, end, direction='reverse',
1577
generate_merge_revisions=generate_merge_revisions,
1578
exclude_common_ancestry=exclude_common_ancestry)
1579
self.assertEqual(expected_revnos,
1580
[revid for revid, revno, depth in iter_revs])
1582
def test_merge_sorted_exclude_ancestry(self):
1583
b = self.make_branch_with_alternate_ancestries()
1584
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1585
b, '1', '3', exclude_common_ancestry=False)
1586
# '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1587
# it should be mentioned even if merge_sort order will make it appear
1589
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1590
b, '1.1.1', '3', exclude_common_ancestry=True)
1592
def test_merge_sorted_simple_revnos_exclude_ancestry(self):
1593
b = self.make_branch_with_alternate_ancestries()
1594
self.assertLogRevnos(['3', '2'],
1595
b, '1', '3', exclude_common_ancestry=True,
1596
generate_merge_revisions=False)
1597
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
1598
b, '1', '3', exclude_common_ancestry=True,
1599
generate_merge_revisions=True)
1602
class TestLogDefaults(TestCaseForLogFormatter):
1603
def test_default_log_level(self):
1605
Test to ensure that specifying 'levels=1' to make_log_request_dict
1606
doesn't get overwritten when using a LogFormatter that supports more
1610
wt = self._prepare_tree_with_merges()
1613
class CustomLogFormatter(log.LogFormatter):
1614
def __init__(self, *args, **kwargs):
1615
super(CustomLogFormatter, self).__init__(*args, **kwargs)
1617
def get_levels(self):
1618
# log formatter supports all levels:
1620
def log_revision(self, revision):
1621
self.revisions.append(revision)
1623
log_formatter = LogCatcher()
1624
# First request we don't specify number of levels, we should get a
1625
# sensible default (whatever the LogFormatter handles - which in this
1626
# case is 0/everything):
1627
request = log.make_log_request_dict(limit=10)
1628
log.Logger(b, request).show(log_formatter)
1629
# should have all three revisions:
1630
self.assertEquals(len(log_formatter.revisions), 3)
1633
log_formatter = LogCatcher()
1634
# now explicitly request mainline revisions only:
1635
request = log.make_log_request_dict(limit=10, levels=1)
1636
log.Logger(b, request).show(log_formatter)
1637
# should now only have 2 revisions:
1638
self.assertEquals(len(log_formatter.revisions), 2)