174
261
lf = LogCatcher()
175
262
lf.supports_merge_revisions = True
176
show_log(b, lf, verbose=True)
178
logentry = lf.logs[0]
179
eq(logentry.revno, '2')
180
eq(logentry.rev.message, 'merge child branch')
182
self.checkDelta(d, removed=['file1'], modified=['file2'])
183
logentry = lf.logs[1]
184
eq(logentry.revno, '1.1.1')
185
eq(logentry.rev.message, 'remove file1 and modify file2')
187
self.checkDelta(d, removed=['file1'], modified=['file2'])
188
logentry = lf.logs[2]
189
eq(logentry.revno, '1')
190
eq(logentry.rev.message, 'add file1 and file2')
192
self.checkDelta(d, added=['file1', 'file2'])
195
def make_commits_with_trailing_newlines(wt):
196
"""Helper method for LogFormatter tests"""
199
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>')
204
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>')
210
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>')
218
class TestShortLogFormatter(TestCaseWithTransport):
263
log.show_log(b, lf, verbose=True)
266
self.assertEqual(3, len(revs))
269
self.assertEqual('2', logentry.revno)
270
self.assertEqual('merge child branch', logentry.rev.message)
271
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
274
self.assertEqual('1.1.1', logentry.revno)
275
self.assertEqual('remove file1 and modify file2', logentry.rev.message)
276
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
279
self.assertEqual('1', logentry.revno)
280
self.assertEqual('add file1 and file2', logentry.rev.message)
281
self.checkDelta(logentry.delta, added=['file1', 'file2'])
284
class TestShortLogFormatter(TestCaseForLogFormatter):
220
286
def test_trailing_newlines(self):
221
287
wt = self.make_branch_and_tree('.')
222
b = make_commits_with_trailing_newlines(wt)
224
lf = ShortLogFormatter(to_file=sio)
226
self.assertEquals(sio.getvalue(), """\
227
3 Joe Foo\t2005-11-21
288
b = self.make_commits_with_trailing_newlines(wt)
289
self.assertFormatterResult("""\
290
3 Joe Foo\t2005-11-22
228
291
single line with trailing newline
230
2 Joe Foo\t2005-11-21
293
2 Joe Foo\t2005-11-22
235
1 Joe Foo\t2005-11-21
298
1 Joe Foo\t2005-11-22
236
299
simple log message
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)
302
b, log.ShortLogFormatter)
304
def test_short_log_with_merges(self):
305
wt = self._prepare_tree_with_merges()
306
self.assertFormatterResult("""\
307
2 Joe Foo\t2005-11-22 [merge]
310
1 Joe Foo\t2005-11-22
314
wt.branch, log.ShortLogFormatter)
316
def test_short_log_with_merges_and_advice(self):
317
wt = self._prepare_tree_with_merges()
318
self.assertFormatterResult("""\
319
2 Joe Foo\t2005-11-22 [merge]
322
1 Joe Foo\t2005-11-22
325
Use --include-merged or -n0 to see merged revisions.
327
wt.branch, log.ShortLogFormatter,
328
formatter_kwargs=dict(show_advice=True))
330
def test_short_log_with_merges_and_range(self):
331
wt = self._prepare_tree_with_merges()
332
self.wt_commit(wt, 'rev-3a', rev_id='rev-3a')
333
wt.branch.set_last_revision_info(2, 'rev-2b')
334
wt.set_parent_ids(['rev-2b', 'rev-3a'])
335
self.wt_commit(wt, 'rev-3b', rev_id='rev-3b')
336
self.assertFormatterResult("""\
337
3 Joe Foo\t2005-11-22 [merge]
340
2 Joe Foo\t2005-11-22 [merge]
344
wt.branch, log.ShortLogFormatter,
345
show_log_kwargs=dict(start_revision=2, end_revision=3))
347
def test_short_log_with_tags(self):
348
wt = self._prepare_tree_with_merges(with_tags=True)
349
self.assertFormatterResult("""\
350
3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
353
2 Joe Foo\t2005-11-22 {v0.2} [merge]
356
1 Joe Foo\t2005-11-22
360
wt.branch, log.ShortLogFormatter)
362
def test_short_log_single_merge_revision(self):
363
wt = self._prepare_tree_with_merges()
364
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
365
rev = revspec.in_history(wt.branch)
366
self.assertFormatterResult("""\
367
1.1.1 Joe Foo\t2005-11-22
371
wt.branch, log.ShortLogFormatter,
372
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
374
def test_show_ids(self):
375
wt = self.make_branch_and_tree('parent')
376
self.build_tree(['parent/f1', 'parent/f2'])
378
self.wt_commit(wt, 'first post', rev_id='a')
379
child_wt = wt.bzrdir.sprout('child').open_workingtree()
380
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
381
wt.merge_from_branch(child_wt.branch)
382
self.wt_commit(wt, 'merge branch 1', rev_id='c')
383
self.assertFormatterResult("""\
384
2 Joe Foo\t2005-11-22 [merge]
388
1.1.1 Joe Foo\t2005-11-22
392
1 Joe Foo\t2005-11-22
397
wt.branch, log.ShortLogFormatter,
398
formatter_kwargs=dict(levels=0,show_ids=True))
401
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
403
def test_short_merge_revs_log_with_merges(self):
404
wt = self._prepare_tree_with_merges()
405
# Note that the 1.1.1 indenting is in fact correct given that
406
# the revision numbers are right justified within 5 characters
407
# for mainline revnos and 9 characters for dotted revnos.
408
self.assertFormatterResult("""\
409
2 Joe Foo\t2005-11-22 [merge]
412
1.1.1 Joe Foo\t2005-11-22
415
1 Joe Foo\t2005-11-22
419
wt.branch, log.ShortLogFormatter,
420
formatter_kwargs=dict(levels=0))
422
def test_short_merge_revs_log_single_merge_revision(self):
423
wt = self._prepare_tree_with_merges()
424
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
425
rev = revspec.in_history(wt.branch)
426
self.assertFormatterResult("""\
427
1.1.1 Joe Foo\t2005-11-22
431
wt.branch, log.ShortLogFormatter,
432
formatter_kwargs=dict(levels=0),
433
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
436
class TestLongLogFormatter(TestCaseForLogFormatter):
256
438
def test_verbose_log(self):
257
439
"""Verbose log includes changed files
261
wt = self.make_branch_and_tree('.')
263
self.build_tree(['a'])
265
# XXX: why does a longer nick show up?
266
b.nick = 'test_verbose_log'
267
wt.commit(message='add a',
268
timestamp=1132711707,
270
committer='Lorem Ipsum <test@example.com>')
271
logfile = file('out.tmp', 'w+')
272
formatter = LongLogFormatter(to_file=logfile)
273
show_log(b, formatter, verbose=True)
276
log_contents = logfile.read()
277
self.assertEqualDiff(log_contents, '''\
443
wt = self.make_standard_commit('test_verbose_log', authors=[])
444
self.assertFormatterResult('''\
278
445
------------------------------------------------------------
280
447
committer: Lorem Ipsum <test@example.com>
281
448
branch nick: test_verbose_log
282
timestamp: Wed 2005-11-23 12:08:27 +1000
449
timestamp: Tue 2005-11-22 00:00:00 +0000
455
wt.branch, log.LongLogFormatter,
456
show_log_kwargs=dict(verbose=True))
289
458
def test_merges_are_indented_by_level(self):
290
459
wt = self.make_branch_and_tree('parent')
291
wt.commit('first post')
292
self.run_bzr('branch parent child')
293
self.run_bzr(['commit', '-m', 'branch 1', '--unchanged', 'child'])
294
self.run_bzr('branch child smallerchild')
295
self.run_bzr(['commit', '-m', 'branch 2', '--unchanged',
298
self.run_bzr('merge ../smallerchild')
299
self.run_bzr(['commit', '-m', 'merge branch 2'])
300
os.chdir('../parent')
301
self.run_bzr('merge ../child')
302
wt.commit('merge branch 1')
305
lf = LongLogFormatter(to_file=sio)
306
show_log(b, lf, verbose=True)
307
log = self.normalize_log(sio.getvalue())
308
self.assertEqualDiff("""\
460
self.wt_commit(wt, 'first post')
461
child_wt = wt.bzrdir.sprout('child').open_workingtree()
462
self.wt_commit(child_wt, 'branch 1')
463
smallerchild_wt = wt.bzrdir.sprout('smallerchild').open_workingtree()
464
self.wt_commit(smallerchild_wt, 'branch 2')
465
child_wt.merge_from_branch(smallerchild_wt.branch)
466
self.wt_commit(child_wt, 'merge branch 2')
467
wt.merge_from_branch(child_wt.branch)
468
self.wt_commit(wt, 'merge branch 1')
469
self.assertFormatterResult("""\
309
470
------------------------------------------------------------
311
committer: Lorem Ipsum <test@example.com>
472
committer: Joe Foo <joe@foo.com>
312
473
branch nick: parent
474
timestamp: Tue 2005-11-22 00:00:04 +0000
316
477
------------------------------------------------------------
318
committer: Lorem Ipsum <test@example.com>
479
committer: Joe Foo <joe@foo.com>
319
480
branch nick: child
481
timestamp: Tue 2005-11-22 00:00:03 +0000
323
484
------------------------------------------------------------
325
committer: Lorem Ipsum <test@example.com>
486
committer: Joe Foo <joe@foo.com>
326
487
branch nick: smallerchild
488
timestamp: Tue 2005-11-22 00:00:02 +0000
330
491
------------------------------------------------------------
332
committer: Lorem Ipsum <test@example.com>
493
committer: Joe Foo <joe@foo.com>
333
494
branch nick: child
495
timestamp: Tue 2005-11-22 00:00:01 +0000
337
498
------------------------------------------------------------
339
committer: Lorem Ipsum <test@example.com>
500
committer: Joe Foo <joe@foo.com>
340
501
branch nick: parent
502
timestamp: Tue 2005-11-22 00:00:00 +0000
506
wt.branch, log.LongLogFormatter,
507
formatter_kwargs=dict(levels=0),
508
show_log_kwargs=dict(verbose=True))
346
510
def test_verbose_merge_revisions_contain_deltas(self):
347
511
wt = self.make_branch_and_tree('parent')
348
512
self.build_tree(['parent/f1', 'parent/f2'])
349
513
wt.add(['f1','f2'])
350
wt.commit('first post')
351
self.run_bzr('branch parent child')
514
self.wt_commit(wt, 'first post')
515
child_wt = wt.bzrdir.sprout('child').open_workingtree()
352
516
os.unlink('child/f1')
353
print >> file('child/f2', 'wb'), 'hello'
354
self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
357
self.run_bzr('merge ../child')
358
wt.commit('merge branch 1')
361
lf = LongLogFormatter(to_file=sio)
362
show_log(b, lf, verbose=True)
363
log = self.normalize_log(sio.getvalue())
364
self.assertEqualDiff("""\
517
self.build_tree_contents([('child/f2', 'hello\n')])
518
self.wt_commit(child_wt, 'removed f1 and modified f2')
519
wt.merge_from_branch(child_wt.branch)
520
self.wt_commit(wt, 'merge branch 1')
521
self.assertFormatterResult("""\
365
522
------------------------------------------------------------
367
committer: Lorem Ipsum <test@example.com>
524
committer: Joe Foo <joe@foo.com>
368
525
branch nick: parent
526
timestamp: Tue 2005-11-22 00:00:02 +0000
387
544
------------------------------------------------------------
389
committer: Lorem Ipsum <test@example.com>
546
committer: Joe Foo <joe@foo.com>
390
547
branch nick: parent
548
timestamp: Tue 2005-11-22 00:00:00 +0000
555
wt.branch, log.LongLogFormatter,
556
formatter_kwargs=dict(levels=0),
557
show_log_kwargs=dict(verbose=True))
399
559
def test_trailing_newlines(self):
400
560
wt = self.make_branch_and_tree('.')
401
b = make_commits_with_trailing_newlines(wt)
403
lf = LongLogFormatter(to_file=sio)
405
self.assertEqualDiff(sio.getvalue(), """\
406
------------------------------------------------------------
408
committer: Joe Foo <joe@foo.com>
410
timestamp: Mon 2005-11-21 09:32:56 -0600
412
single line with trailing newline
413
------------------------------------------------------------
415
committer: Joe Foo <joe@foo.com>
417
timestamp: Mon 2005-11-21 09:27:22 -0600
422
------------------------------------------------------------
424
committer: Joe Foo <joe@foo.com>
426
timestamp: Mon 2005-11-21 09:24:15 -0600
432
class TestLineLogFormatter(TestCaseWithTransport):
561
b = self.make_commits_with_trailing_newlines(wt)
562
self.assertFormatterResult("""\
563
------------------------------------------------------------
565
committer: Joe Foo <joe@foo.com>
567
timestamp: Tue 2005-11-22 00:00:02 +0000
569
single line with trailing newline
570
------------------------------------------------------------
572
committer: Joe Foo <joe@foo.com>
574
timestamp: Tue 2005-11-22 00:00:01 +0000
579
------------------------------------------------------------
581
committer: Joe Foo <joe@foo.com>
583
timestamp: Tue 2005-11-22 00:00:00 +0000
587
b, log.LongLogFormatter)
589
def test_author_in_log(self):
590
"""Log includes the author name if it's set in
591
the revision properties
593
wt = self.make_standard_commit('test_author_log',
594
authors=['John Doe <jdoe@example.com>',
595
'Jane Rey <jrey@example.com>'])
596
self.assertFormatterResult("""\
597
------------------------------------------------------------
599
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
600
committer: Lorem Ipsum <test@example.com>
601
branch nick: test_author_log
602
timestamp: Tue 2005-11-22 00:00:00 +0000
606
wt.branch, log.LongLogFormatter)
608
def test_properties_in_log(self):
609
"""Log includes the custom properties returned by the registered
612
wt = self.make_standard_commit('test_properties_in_log')
613
def trivial_custom_prop_handler(revision):
614
return {'test_prop':'test_value'}
616
# Cleaned up in setUp()
617
log.properties_handler_registry.register(
618
'trivial_custom_prop_handler',
619
trivial_custom_prop_handler)
620
self.assertFormatterResult("""\
621
------------------------------------------------------------
623
test_prop: test_value
624
author: John Doe <jdoe@example.com>
625
committer: Lorem Ipsum <test@example.com>
626
branch nick: test_properties_in_log
627
timestamp: Tue 2005-11-22 00:00:00 +0000
631
wt.branch, log.LongLogFormatter)
633
def test_properties_in_short_log(self):
634
"""Log includes the custom properties returned by the registered
637
wt = self.make_standard_commit('test_properties_in_short_log')
638
def trivial_custom_prop_handler(revision):
639
return {'test_prop':'test_value'}
641
log.properties_handler_registry.register(
642
'trivial_custom_prop_handler',
643
trivial_custom_prop_handler)
644
self.assertFormatterResult("""\
645
1 John Doe\t2005-11-22
646
test_prop: test_value
650
wt.branch, log.ShortLogFormatter)
652
def test_error_in_properties_handler(self):
653
"""Log includes the custom properties returned by the registered
656
wt = self.make_standard_commit('error_in_properties_handler',
657
revprops={'first_prop':'first_value'})
658
sio = self.make_utf8_encoded_stringio()
659
formatter = log.LongLogFormatter(to_file=sio)
660
def trivial_custom_prop_handler(revision):
661
raise StandardError("a test error")
663
log.properties_handler_registry.register(
664
'trivial_custom_prop_handler',
665
trivial_custom_prop_handler)
666
self.assertRaises(StandardError, log.show_log, wt.branch, formatter,)
668
def test_properties_handler_bad_argument(self):
669
wt = self.make_standard_commit('bad_argument',
670
revprops={'a_prop':'test_value'})
671
sio = self.make_utf8_encoded_stringio()
672
formatter = log.LongLogFormatter(to_file=sio)
673
def bad_argument_prop_handler(revision):
674
return {'custom_prop_name':revision.properties['a_prop']}
676
log.properties_handler_registry.register(
677
'bad_argument_prop_handler',
678
bad_argument_prop_handler)
680
self.assertRaises(AttributeError, formatter.show_properties,
683
revision = wt.branch.repository.get_revision(wt.branch.last_revision())
684
formatter.show_properties(revision, '')
685
self.assertEqualDiff('''custom_prop_name: test_value\n''',
688
def test_show_ids(self):
689
wt = self.make_branch_and_tree('parent')
690
self.build_tree(['parent/f1', 'parent/f2'])
692
self.wt_commit(wt, 'first post', rev_id='a')
693
child_wt = wt.bzrdir.sprout('child').open_workingtree()
694
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
695
wt.merge_from_branch(child_wt.branch)
696
self.wt_commit(wt, 'merge branch 1', rev_id='c')
697
self.assertFormatterResult("""\
698
------------------------------------------------------------
703
committer: Joe Foo <joe@foo.com>
705
timestamp: Tue 2005-11-22 00:00:02 +0000
708
------------------------------------------------------------
712
committer: Joe Foo <joe@foo.com>
714
timestamp: Tue 2005-11-22 00:00:01 +0000
717
------------------------------------------------------------
720
committer: Joe Foo <joe@foo.com>
722
timestamp: Tue 2005-11-22 00:00:00 +0000
726
wt.branch, log.LongLogFormatter,
727
formatter_kwargs=dict(levels=0,show_ids=True))
730
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
732
def test_long_verbose_log(self):
733
"""Verbose log includes changed files
737
wt = self.make_standard_commit('test_long_verbose_log', authors=[])
738
self.assertFormatterResult("""\
739
------------------------------------------------------------
741
committer: Lorem Ipsum <test@example.com>
742
branch nick: test_long_verbose_log
743
timestamp: Tue 2005-11-22 00:00:00 +0000
749
wt.branch, log.LongLogFormatter,
750
formatter_kwargs=dict(levels=1),
751
show_log_kwargs=dict(verbose=True))
753
def test_long_verbose_contain_deltas(self):
754
wt = self.make_branch_and_tree('parent')
755
self.build_tree(['parent/f1', 'parent/f2'])
757
self.wt_commit(wt, 'first post')
758
child_wt = wt.bzrdir.sprout('child').open_workingtree()
759
os.unlink('child/f1')
760
self.build_tree_contents([('child/f2', 'hello\n')])
761
self.wt_commit(child_wt, 'removed f1 and modified f2')
762
wt.merge_from_branch(child_wt.branch)
763
self.wt_commit(wt, 'merge branch 1')
764
self.assertFormatterResult("""\
765
------------------------------------------------------------
767
committer: Joe Foo <joe@foo.com>
769
timestamp: Tue 2005-11-22 00:00:02 +0000
776
------------------------------------------------------------
778
committer: Joe Foo <joe@foo.com>
780
timestamp: Tue 2005-11-22 00:00:00 +0000
787
wt.branch, log.LongLogFormatter,
788
formatter_kwargs=dict(levels=1),
789
show_log_kwargs=dict(verbose=True))
791
def test_long_trailing_newlines(self):
792
wt = self.make_branch_and_tree('.')
793
b = self.make_commits_with_trailing_newlines(wt)
794
self.assertFormatterResult("""\
795
------------------------------------------------------------
797
committer: Joe Foo <joe@foo.com>
799
timestamp: Tue 2005-11-22 00:00:02 +0000
801
single line with trailing newline
802
------------------------------------------------------------
804
committer: Joe Foo <joe@foo.com>
806
timestamp: Tue 2005-11-22 00:00:01 +0000
811
------------------------------------------------------------
813
committer: Joe Foo <joe@foo.com>
815
timestamp: Tue 2005-11-22 00:00:00 +0000
819
b, log.LongLogFormatter,
820
formatter_kwargs=dict(levels=1))
822
def test_long_author_in_log(self):
823
"""Log includes the author name if it's set in
824
the revision properties
826
wt = self.make_standard_commit('test_author_log')
827
self.assertFormatterResult("""\
828
------------------------------------------------------------
830
author: John Doe <jdoe@example.com>
831
committer: Lorem Ipsum <test@example.com>
832
branch nick: test_author_log
833
timestamp: Tue 2005-11-22 00:00:00 +0000
837
wt.branch, log.LongLogFormatter,
838
formatter_kwargs=dict(levels=1))
840
def test_long_properties_in_log(self):
841
"""Log includes the custom properties returned by the registered
844
wt = self.make_standard_commit('test_properties_in_log')
845
def trivial_custom_prop_handler(revision):
846
return {'test_prop':'test_value'}
848
log.properties_handler_registry.register(
849
'trivial_custom_prop_handler',
850
trivial_custom_prop_handler)
851
self.assertFormatterResult("""\
852
------------------------------------------------------------
854
test_prop: test_value
855
author: John Doe <jdoe@example.com>
856
committer: Lorem Ipsum <test@example.com>
857
branch nick: test_properties_in_log
858
timestamp: Tue 2005-11-22 00:00:00 +0000
862
wt.branch, log.LongLogFormatter,
863
formatter_kwargs=dict(levels=1))
866
class TestLineLogFormatter(TestCaseForLogFormatter):
434
868
def test_line_log(self):
435
869
"""Line log should show revno
873
wt = self.make_standard_commit('test-line-log',
874
committer='Line-Log-Formatter Tester <test@line.log>',
876
self.assertFormatterResult("""\
877
1: Line-Log-Formatte... 2005-11-22 add a
879
wt.branch, log.LineLogFormatter)
881
def test_trailing_newlines(self):
439
882
wt = self.make_branch_and_tree('.')
883
b = self.make_commits_with_trailing_newlines(wt)
884
self.assertFormatterResult("""\
885
3: Joe Foo 2005-11-22 single line with trailing newline
886
2: Joe Foo 2005-11-22 multiline
887
1: Joe Foo 2005-11-22 simple log message
889
b, log.LineLogFormatter)
891
def test_line_log_single_merge_revision(self):
892
wt = self._prepare_tree_with_merges()
893
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
894
rev = revspec.in_history(wt.branch)
895
self.assertFormatterResult("""\
896
1.1.1: Joe Foo 2005-11-22 rev-merged
898
wt.branch, log.LineLogFormatter,
899
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
901
def test_line_log_with_tags(self):
902
wt = self._prepare_tree_with_merges(with_tags=True)
903
self.assertFormatterResult("""\
904
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
905
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
906
1: Joe Foo 2005-11-22 rev-1
908
wt.branch, log.LineLogFormatter)
911
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
913
def test_line_merge_revs_log(self):
914
"""Line log should show revno
918
wt = self.make_standard_commit('test-line-log',
919
committer='Line-Log-Formatter Tester <test@line.log>',
921
self.assertFormatterResult("""\
922
1: Line-Log-Formatte... 2005-11-22 add a
924
wt.branch, log.LineLogFormatter)
926
def test_line_merge_revs_log_single_merge_revision(self):
927
wt = self._prepare_tree_with_merges()
928
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
929
rev = revspec.in_history(wt.branch)
930
self.assertFormatterResult("""\
931
1.1.1: Joe Foo 2005-11-22 rev-merged
933
wt.branch, log.LineLogFormatter,
934
formatter_kwargs=dict(levels=0),
935
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
937
def test_line_merge_revs_log_with_merges(self):
938
wt = self._prepare_tree_with_merges()
939
self.assertFormatterResult("""\
940
2: Joe Foo 2005-11-22 [merge] rev-2
941
1.1.1: Joe Foo 2005-11-22 rev-merged
942
1: Joe Foo 2005-11-22 rev-1
944
wt.branch, log.LineLogFormatter,
945
formatter_kwargs=dict(levels=0))
948
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
950
def test_gnu_changelog(self):
951
wt = self.make_standard_commit('nicky', authors=[])
952
self.assertFormatterResult('''\
953
2005-11-22 Lorem Ipsum <test@example.com>
958
wt.branch, log.GnuChangelogLogFormatter)
960
def test_with_authors(self):
961
wt = self.make_standard_commit('nicky',
962
authors=['Fooa Fooz <foo@example.com>',
963
'Bari Baro <bar@example.com>'])
964
self.assertFormatterResult('''\
965
2005-11-22 Fooa Fooz <foo@example.com>
970
wt.branch, log.GnuChangelogLogFormatter)
972
def test_verbose(self):
973
wt = self.make_standard_commit('nicky')
974
self.assertFormatterResult('''\
975
2005-11-22 John Doe <jdoe@example.com>
982
wt.branch, log.GnuChangelogLogFormatter,
983
show_log_kwargs=dict(verbose=True))
986
class TestShowChangedRevisions(tests.TestCaseWithTransport):
988
def test_show_changed_revisions_verbose(self):
989
tree = self.make_branch_and_tree('tree_a')
990
self.build_tree(['tree_a/foo'])
992
tree.commit('bar', rev_id='bar-id')
993
s = self.make_utf8_encoded_stringio()
994
log.show_changed_revisions(tree.branch, [], ['bar-id'], s)
995
self.assertContainsRe(s.getvalue(), 'bar')
996
self.assertNotContainsRe(s.getvalue(), 'foo')
999
class TestLogFormatter(tests.TestCase):
1002
super(TestLogFormatter, self).setUp()
1003
self.rev = revision.Revision('a-id')
1004
self.lf = log.LogFormatter(None)
1006
def test_short_committer(self):
1007
def assertCommitter(expected, committer):
1008
self.rev.committer = committer
1009
self.assertEqual(expected, self.lf.short_committer(self.rev))
1011
assertCommitter('John Doe', 'John Doe <jdoe@example.com>')
1012
assertCommitter('John Smith', 'John Smith <jsmith@example.com>')
1013
assertCommitter('John Smith', 'John Smith')
1014
assertCommitter('jsmith@example.com', 'jsmith@example.com')
1015
assertCommitter('jsmith@example.com', '<jsmith@example.com>')
1016
assertCommitter('John Smith', 'John Smith jsmith@example.com')
1018
def test_short_author(self):
1019
def assertAuthor(expected, author):
1020
self.rev.properties['author'] = author
1021
self.assertEqual(expected, self.lf.short_author(self.rev))
1023
assertAuthor('John Smith', 'John Smith <jsmith@example.com>')
1024
assertAuthor('John Smith', 'John Smith')
1025
assertAuthor('jsmith@example.com', 'jsmith@example.com')
1026
assertAuthor('jsmith@example.com', '<jsmith@example.com>')
1027
assertAuthor('John Smith', 'John Smith jsmith@example.com')
1029
def test_short_author_from_committer(self):
1030
self.rev.committer = 'John Doe <jdoe@example.com>'
1031
self.assertEqual('John Doe', self.lf.short_author(self.rev))
1033
def test_short_author_from_authors(self):
1034
self.rev.properties['authors'] = ('John Smith <jsmith@example.com>\n'
1035
'Jane Rey <jrey@example.com>')
1036
self.assertEqual('John Smith', self.lf.short_author(self.rev))
1039
class TestReverseByDepth(tests.TestCase):
1040
"""Test reverse_by_depth behavior.
1042
This is used to present revisions in forward (oldest first) order in a nice
1045
The tests use lighter revision description to ease reading.
1048
def assertReversed(self, forward, backward):
1049
# Transform the descriptions to suit the API: tests use (revno, depth),
1050
# while the API expects (revid, revno, depth)
1051
def complete_revisions(l):
1052
"""Transform the description to suit the API.
1054
Tests use (revno, depth) whil the API expects (revid, revno, depth).
1055
Since the revid is arbitrary, we just duplicate revno
1057
return [ (r, r, d) for r, d in l]
1058
forward = complete_revisions(forward)
1059
backward= complete_revisions(backward)
1060
self.assertEqual(forward, log.reverse_by_depth(backward))
1063
def test_mainline_revisions(self):
1064
self.assertReversed([( '1', 0), ('2', 0)],
1065
[('2', 0), ('1', 0)])
1067
def test_merged_revisions(self):
1068
self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1069
[('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
1070
def test_shifted_merged_revisions(self):
1071
"""Test irregular layout.
1073
Requesting revisions touching a file can produce "holes" in the depths.
1075
self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1076
[('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
1078
def test_merged_without_child_revisions(self):
1079
"""Test irregular layout.
1081
Revision ranges can produce "holes" in the depths.
1083
# When a revision of higher depth doesn't follow one of lower depth, we
1084
# assume a lower depth one is virtually there
1085
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1086
[('4', 4), ('3', 3), ('2', 2), ('1', 2),])
1087
# So we get the same order after reversing below even if the original
1088
# revisions are not in the same order.
1089
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1090
[('3', 3), ('4', 4), ('2', 2), ('1', 2),])
1093
class TestHistoryChange(tests.TestCaseWithTransport):
1095
def setup_a_tree(self):
1096
tree = self.make_branch_and_tree('tree')
1098
self.addCleanup(tree.unlock)
1099
tree.commit('1a', rev_id='1a')
1100
tree.commit('2a', rev_id='2a')
1101
tree.commit('3a', rev_id='3a')
1104
def setup_ab_tree(self):
1105
tree = self.setup_a_tree()
1106
tree.set_last_revision('1a')
1107
tree.branch.set_last_revision_info(1, '1a')
1108
tree.commit('2b', rev_id='2b')
1109
tree.commit('3b', rev_id='3b')
1112
def setup_ac_tree(self):
1113
tree = self.setup_a_tree()
1114
tree.set_last_revision(revision.NULL_REVISION)
1115
tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1116
tree.commit('1c', rev_id='1c')
1117
tree.commit('2c', rev_id='2c')
1118
tree.commit('3c', rev_id='3c')
1121
def test_all_new(self):
1122
tree = self.setup_ab_tree()
1123
old, new = log.get_history_change('1a', '3a', tree.branch.repository)
1124
self.assertEqual([], old)
1125
self.assertEqual(['2a', '3a'], new)
1127
def test_all_old(self):
1128
tree = self.setup_ab_tree()
1129
old, new = log.get_history_change('3a', '1a', tree.branch.repository)
1130
self.assertEqual([], new)
1131
self.assertEqual(['2a', '3a'], old)
1133
def test_null_old(self):
1134
tree = self.setup_ab_tree()
1135
old, new = log.get_history_change(revision.NULL_REVISION,
1136
'3a', tree.branch.repository)
1137
self.assertEqual([], old)
1138
self.assertEqual(['1a', '2a', '3a'], new)
1140
def test_null_new(self):
1141
tree = self.setup_ab_tree()
1142
old, new = log.get_history_change('3a', revision.NULL_REVISION,
1143
tree.branch.repository)
1144
self.assertEqual([], new)
1145
self.assertEqual(['1a', '2a', '3a'], old)
1147
def test_diverged(self):
1148
tree = self.setup_ab_tree()
1149
old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1150
self.assertEqual(old, ['2a', '3a'])
1151
self.assertEqual(new, ['2b', '3b'])
1153
def test_unrelated(self):
1154
tree = self.setup_ac_tree()
1155
old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1156
self.assertEqual(old, ['1a', '2a', '3a'])
1157
self.assertEqual(new, ['1c', '2c', '3c'])
1159
def test_show_branch_change(self):
1160
tree = self.setup_ab_tree()
1162
log.show_branch_change(tree.branch, s, 3, '3a')
1163
self.assertContainsRe(s.getvalue(),
1164
'[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1165
'[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1167
def test_show_branch_change_no_change(self):
1168
tree = self.setup_ab_tree()
1170
log.show_branch_change(tree.branch, s, 3, '3b')
1171
self.assertEqual(s.getvalue(),
1172
'Nothing seems to have changed\n')
1174
def test_show_branch_change_no_old(self):
1175
tree = self.setup_ab_tree()
1177
log.show_branch_change(tree.branch, s, 2, '2b')
1178
self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1179
self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1181
def test_show_branch_change_no_new(self):
1182
tree = self.setup_ab_tree()
1183
tree.branch.set_last_revision_info(2, '2b')
1185
log.show_branch_change(tree.branch, s, 3, '3b')
1186
self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1187
self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1190
class TestRevisionNotInBranch(TestCaseForLogFormatter):
1192
def setup_a_tree(self):
1193
tree = self.make_branch_and_tree('tree')
1195
self.addCleanup(tree.unlock)
1197
'committer': 'Joe Foo <joe@foo.com>',
1198
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1199
'timezone': 0, # UTC
1201
tree.commit('commit 1a', rev_id='1a', **kwargs)
1202
tree.commit('commit 2a', rev_id='2a', **kwargs)
1203
tree.commit('commit 3a', rev_id='3a', **kwargs)
1206
def setup_ab_tree(self):
1207
tree = self.setup_a_tree()
1208
tree.set_last_revision('1a')
1209
tree.branch.set_last_revision_info(1, '1a')
1211
'committer': 'Joe Foo <joe@foo.com>',
1212
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1213
'timezone': 0, # UTC
1215
tree.commit('commit 2b', rev_id='2b', **kwargs)
1216
tree.commit('commit 3b', rev_id='3b', **kwargs)
1219
def test_one_revision(self):
1220
tree = self.setup_ab_tree()
1222
rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1223
log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
1225
self.assertEqual(1, len(lf.revisions))
1226
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1227
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1229
def test_many_revisions(self):
1230
tree = self.setup_ab_tree()
1232
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1233
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1234
log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
1235
end_revision=end_rev)
1236
self.assertEqual(3, len(lf.revisions))
1237
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1238
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1239
self.assertEqual(None, lf.revisions[1].revno) # Out-of-branch
1240
self.assertEqual('2a', lf.revisions[1].rev.revision_id)
1241
self.assertEqual('1', lf.revisions[2].revno) # In-branch
1243
def test_long_format(self):
1244
tree = self.setup_ab_tree()
1245
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1246
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1247
self.assertFormatterResult("""\
1248
------------------------------------------------------------
1250
committer: Joe Foo <joe@foo.com>
1252
timestamp: Tue 2005-11-22 00:00:00 +0000
1255
------------------------------------------------------------
1257
committer: Joe Foo <joe@foo.com>
1259
timestamp: Tue 2005-11-22 00:00:00 +0000
1262
------------------------------------------------------------
1264
committer: Joe Foo <joe@foo.com>
1266
timestamp: Tue 2005-11-22 00:00:00 +0000
1270
tree.branch, log.LongLogFormatter, show_log_kwargs={
1271
'start_revision': start_rev, 'end_revision': end_rev
1274
def test_short_format(self):
1275
tree = self.setup_ab_tree()
1276
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1277
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1278
self.assertFormatterResult("""\
1287
1 Joe Foo\t2005-11-22
1291
tree.branch, log.ShortLogFormatter, show_log_kwargs={
1292
'start_revision': start_rev, 'end_revision': end_rev
1295
def test_line_format(self):
1296
tree = self.setup_ab_tree()
1297
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1298
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1299
self.assertFormatterResult("""\
1300
Joe Foo 2005-11-22 commit 3a
1301
Joe Foo 2005-11-22 commit 2a
1302
1: Joe Foo 2005-11-22 commit 1a
1304
tree.branch, log.LineLogFormatter, show_log_kwargs={
1305
'start_revision': start_rev, 'end_revision': end_rev
1309
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1312
TestCaseForLogFormatter.setUp(self)
1313
log.properties_handler_registry.register(
1314
'bugs_properties_handler',
1315
log._bugs_properties_handler)
1317
def make_commits_with_bugs(self):
1318
"""Helper method for LogFormatter tests"""
1319
tree = self.make_branch_and_tree(u'.')
1320
self.build_tree(['a', 'b'])
1322
self.wt_commit(tree, 'simple log message', rev_id='a1',
1323
revprops={'bugs': 'test://bug/id fixed'})
1325
self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id='a2',
1326
authors=['Joe Bar <joe@bar.com>'],
1327
revprops={'bugs': 'test://bug/id fixed\n'
1328
'test://bug/2 fixed'})
1332
def test_long_bugs(self):
1333
tree = self.make_commits_with_bugs()
1334
self.assertFormatterResult("""\
1335
------------------------------------------------------------
1337
fixes bugs: test://bug/id test://bug/2
1338
author: Joe Bar <joe@bar.com>
1339
committer: Joe Foo <joe@foo.com>
1341
timestamp: Tue 2005-11-22 00:00:01 +0000
1346
------------------------------------------------------------
1348
fixes bug: test://bug/id
1349
committer: Joe Foo <joe@foo.com>
1351
timestamp: Tue 2005-11-22 00:00:00 +0000
1355
tree.branch, log.LongLogFormatter)
1357
def test_short_bugs(self):
1358
tree = self.make_commits_with_bugs()
1359
self.assertFormatterResult("""\
1360
2 Joe Bar\t2005-11-22
1361
fixes bugs: test://bug/id test://bug/2
1366
1 Joe Foo\t2005-11-22
1367
fixes bug: test://bug/id
1371
tree.branch, log.ShortLogFormatter)
1373
def test_wrong_bugs_property(self):
1374
tree = self.make_branch_and_tree(u'.')
1375
self.build_tree(['foo'])
1376
self.wt_commit(tree, 'simple log message', rev_id='a1',
1377
revprops={'bugs': 'test://bug/id invalid_value'})
1378
self.assertFormatterResult("""\
1379
1 Joe Foo\t2005-11-22
1383
tree.branch, log.ShortLogFormatter)
1385
def test_bugs_handler_present(self):
1386
self.properties_handler_registry.get('bugs_properties_handler')
1389
class TestLogForAuthors(TestCaseForLogFormatter):
1392
TestCaseForLogFormatter.setUp(self)
1393
self.wt = self.make_standard_commit('nicky',
1394
authors=['John Doe <jdoe@example.com>',
1395
'Jane Rey <jrey@example.com>'])
1397
def assertFormatterResult(self, formatter, who, result):
1398
formatter_kwargs = dict()
1400
author_list_handler = log.author_list_registry.get(who)
1401
formatter_kwargs['author_list_handler'] = author_list_handler
1402
TestCaseForLogFormatter.assertFormatterResult(self, result,
1403
self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
1405
def test_line_default(self):
1406
self.assertFormatterResult(log.LineLogFormatter, None, """\
1407
1: John Doe 2005-11-22 add a
1410
def test_line_committer(self):
1411
self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
1412
1: Lorem Ipsum 2005-11-22 add a
1415
def test_line_first(self):
1416
self.assertFormatterResult(log.LineLogFormatter, 'first', """\
1417
1: John Doe 2005-11-22 add a
1420
def test_line_all(self):
1421
self.assertFormatterResult(log.LineLogFormatter, 'all', """\
1422
1: John Doe, Jane Rey 2005-11-22 add a
1426
def test_short_default(self):
1427
self.assertFormatterResult(log.ShortLogFormatter, None, """\
1428
1 John Doe\t2005-11-22
1433
def test_short_committer(self):
1434
self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
1435
1 Lorem Ipsum\t2005-11-22
1440
def test_short_first(self):
1441
self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
1442
1 John Doe\t2005-11-22
1447
def test_short_all(self):
1448
self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
1449
1 John Doe, Jane Rey\t2005-11-22
1454
def test_long_default(self):
1455
self.assertFormatterResult(log.LongLogFormatter, None, """\
1456
------------------------------------------------------------
1458
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1459
committer: Lorem Ipsum <test@example.com>
1461
timestamp: Tue 2005-11-22 00:00:00 +0000
1466
def test_long_committer(self):
1467
self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
1468
------------------------------------------------------------
1470
committer: Lorem Ipsum <test@example.com>
1472
timestamp: Tue 2005-11-22 00:00:00 +0000
1477
def test_long_first(self):
1478
self.assertFormatterResult(log.LongLogFormatter, 'first', """\
1479
------------------------------------------------------------
1481
author: John Doe <jdoe@example.com>
1482
committer: Lorem Ipsum <test@example.com>
1484
timestamp: Tue 2005-11-22 00:00:00 +0000
1489
def test_long_all(self):
1490
self.assertFormatterResult(log.LongLogFormatter, 'all', """\
1491
------------------------------------------------------------
1493
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1494
committer: Lorem Ipsum <test@example.com>
1496
timestamp: Tue 2005-11-22 00:00:00 +0000
1501
def test_gnu_changelog_default(self):
1502
self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
1503
2005-11-22 John Doe <jdoe@example.com>
1509
def test_gnu_changelog_committer(self):
1510
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
1511
2005-11-22 Lorem Ipsum <test@example.com>
1517
def test_gnu_changelog_first(self):
1518
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
1519
2005-11-22 John Doe <jdoe@example.com>
1525
def test_gnu_changelog_all(self):
1526
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
1527
2005-11-22 John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1534
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1536
def make_branch_with_alternate_ancestries(self, relpath='.'):
1537
# See test_merge_sorted_exclude_ancestry below for the difference with
1538
# bt.per_branch.test_iter_merge_sorted_revision.
1539
# TestIterMergeSortedRevisionsBushyGraph.
1540
# make_branch_with_alternate_ancestries
1541
# and test_merge_sorted_exclude_ancestry
1542
# See the FIXME in assertLogRevnos too.
1543
builder = branchbuilder.BranchBuilder(self.get_transport(relpath))
1555
builder.start_series()
1556
builder.build_snapshot('1', None, [
1557
('add', ('', 'TREE_ROOT', 'directory', '')),])
1558
builder.build_snapshot('1.1.1', ['1'], [])
1559
builder.build_snapshot('2', ['1'], [])
1560
builder.build_snapshot('1.2.1', ['1.1.1'], [])
1561
builder.build_snapshot('1.1.2', ['1.1.1', '1.2.1'], [])
1562
builder.build_snapshot('3', ['2', '1.1.2'], [])
1563
builder.finish_series()
1564
br = builder.get_branch()
1566
self.addCleanup(br.unlock)
1569
def assertLogRevnos(self, expected_revnos, b, start, end,
1570
exclude_common_ancestry, generate_merge_revisions=True):
1571
# FIXME: the layering in log makes it hard to test intermediate levels,
1572
# I wish adding filters with their parameters was easier...
1574
iter_revs = log._calc_view_revisions(
1575
b, start, end, direction='reverse',
1576
generate_merge_revisions=generate_merge_revisions,
1577
exclude_common_ancestry=exclude_common_ancestry)
1578
self.assertEqual(expected_revnos,
1579
[revid for revid, revno, depth in iter_revs])
1581
def test_merge_sorted_exclude_ancestry(self):
1582
b = self.make_branch_with_alternate_ancestries()
1583
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1584
b, '1', '3', exclude_common_ancestry=False)
1585
# '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1586
# it should be mentioned even if merge_sort order will make it appear
1588
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1589
b, '1.1.1', '3', exclude_common_ancestry=True)
1591
def test_merge_sorted_simple_revnos_exclude_ancestry(self):
1592
b = self.make_branch_with_alternate_ancestries()
1593
self.assertLogRevnos(['3', '2'],
1594
b, '1', '3', exclude_common_ancestry=True,
1595
generate_merge_revisions=False)
1596
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
1597
b, '1', '3', exclude_common_ancestry=True,
1598
generate_merge_revisions=True)
1601
class TestLogDefaults(TestCaseForLogFormatter):
1602
def test_default_log_level(self):
1604
Test to ensure that specifying 'levels=1' to make_log_request_dict
1605
doesn't get overwritten when using a LogFormatter that supports more
1609
wt = self._prepare_tree_with_merges()
441
self.build_tree(['a'])
443
b.nick = 'test-line-log'
444
wt.commit(message='add a',
445
timestamp=1132711707,
447
committer='Line-Log-Formatter Tester <test@line.log>')
448
logfile = file('out.tmp', 'w+')
449
formatter = LineLogFormatter(to_file=logfile)
450
show_log(b, formatter)
453
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())
487
def test_trailing_newlines(self):
488
wt = self.make_branch_and_tree('.')
489
b = make_commits_with_trailing_newlines(wt)
491
lf = LineLogFormatter(to_file=sio)
493
self.assertEqualDiff(sio.getvalue(), """\
494
3: Joe Foo 2005-11-21 single line with trailing newline
495
2: Joe Foo 2005-11-21 multiline
496
1: Joe Foo 2005-11-21 simple log message
500
class TestGetViewRevisions(TestCaseWithTransport):
502
def make_tree_with_commits(self):
503
"""Create a tree with well-known revision ids"""
504
wt = self.make_branch_and_tree('tree1')
505
wt.commit('commit one', rev_id='1')
506
wt.commit('commit two', rev_id='2')
507
wt.commit('commit three', rev_id='3')
508
mainline_revs = [None, '1', '2', '3']
509
rev_nos = {'1': 1, '2': 2, '3': 3}
510
return mainline_revs, rev_nos, wt
512
def make_tree_with_merges(self):
513
"""Create a tree with well-known revision ids and a merge"""
514
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
515
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
516
tree2.commit('four-a', rev_id='4a')
517
wt.merge_from_branch(tree2.branch)
518
wt.commit('four-b', rev_id='4b')
519
mainline_revs.append('4b')
522
return mainline_revs, rev_nos, wt
524
def make_tree_with_many_merges(self):
525
"""Create a tree with well-known revision ids"""
526
wt = self.make_branch_and_tree('tree1')
527
wt.commit('commit one', rev_id='1')
528
wt.commit('commit two', rev_id='2')
529
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
530
tree3.commit('commit three a', rev_id='3a')
531
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
532
tree2.merge_from_branch(tree3.branch)
533
tree2.commit('commit three b', rev_id='3b')
534
wt.merge_from_branch(tree2.branch)
535
wt.commit('commit three c', rev_id='3c')
536
tree2.commit('four-a', rev_id='4a')
537
wt.merge_from_branch(tree2.branch)
538
wt.commit('four-b', rev_id='4b')
539
mainline_revs = [None, '1', '2', '3c', '4b']
540
rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
541
full_rev_nos_for_reference = {
544
'3a': '2.2.1', #first commit tree 3
545
'3b': '2.1.1', # first commit tree 2
546
'3c': '3', #merges 3b to main
547
'4a': '2.1.2', # second commit tree 2
548
'4b': '4', # merges 4a to main
550
return mainline_revs, rev_nos, wt
552
def test_get_view_revisions_forward(self):
553
"""Test the get_view_revisions method"""
554
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
555
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
557
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
559
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
560
'forward', include_merges=False))
561
self.assertEqual(revisions, revisions2)
563
def test_get_view_revisions_reverse(self):
564
"""Test the get_view_revisions with reverse"""
565
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
566
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
568
self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
570
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
571
'reverse', include_merges=False))
572
self.assertEqual(revisions, revisions2)
574
def test_get_view_revisions_merge(self):
575
"""Test get_view_revisions when there are merges"""
576
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
577
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
579
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
580
('4b', '4', 0), ('4a', '3.1.1', 1)],
582
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
583
'forward', include_merges=False))
584
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
588
def test_get_view_revisions_merge_reverse(self):
589
"""Test get_view_revisions in reverse when there are merges"""
590
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
591
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
593
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
594
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
596
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
597
'reverse', include_merges=False))
598
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
602
def test_get_view_revisions_merge2(self):
603
"""Test get_view_revisions when there are merges"""
604
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
605
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
607
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
608
('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
610
self.assertEqual(expected, revisions)
611
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
612
'forward', include_merges=False))
613
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
618
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
620
def create_tree_with_single_merge(self):
621
"""Create a branch with a moderate layout.
623
The revision graph looks like:
631
In this graph, A introduced files f1 and f2 and f3.
632
B modifies f1 and f3, and C modifies f2 and f3.
633
D merges the changes from B and C and resolves the conflict for f3.
635
# TODO: jam 20070218 This seems like it could really be done
636
# with make_branch_and_memory_tree() if we could just
637
# create the content of those files.
638
# TODO: jam 20070218 Another alternative is that we would really
639
# like to only create this tree 1 time for all tests that
640
# use it. Since 'log' only uses the tree in a readonly
641
# fashion, it seems a shame to regenerate an identical
642
# tree for each test.
643
tree = self.make_branch_and_tree('tree')
645
self.addCleanup(tree.unlock)
647
self.build_tree_contents([('tree/f1', 'A\n'),
651
tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
652
tree.commit('A', rev_id='A')
654
self.build_tree_contents([('tree/f2', 'A\nC\n'),
655
('tree/f3', 'A\nC\n'),
657
tree.commit('C', rev_id='C')
658
# Revert back to A to build the other history.
659
tree.set_last_revision('A')
660
tree.branch.set_last_revision_info(1, 'A')
661
self.build_tree_contents([('tree/f1', 'A\nB\n'),
663
('tree/f3', 'A\nB\n'),
665
tree.commit('B', rev_id='B')
666
tree.set_parent_ids(['B', 'C'])
667
self.build_tree_contents([('tree/f1', 'A\nB\n'),
668
('tree/f2', 'A\nC\n'),
669
('tree/f3', 'A\nB\nC\n'),
671
tree.commit('D', rev_id='D')
673
# Switch to a read lock for this tree.
674
# We still have addCleanup(unlock)
679
def test_tree_with_single_merge(self):
680
"""Make sure the tree layout is correct."""
681
tree = self.create_tree_with_single_merge()
682
rev_A_tree = tree.branch.repository.revision_tree('A')
683
rev_B_tree = tree.branch.repository.revision_tree('B')
685
f1_changed = (u'f1', 'f1-id', 'file', True, False)
686
f2_changed = (u'f2', 'f2-id', 'file', True, False)
687
f3_changed = (u'f3', 'f3-id', 'file', True, False)
689
delta = rev_B_tree.changes_from(rev_A_tree)
690
self.assertEqual([f1_changed, f3_changed], delta.modified)
691
self.assertEqual([], delta.renamed)
692
self.assertEqual([], delta.added)
693
self.assertEqual([], delta.removed)
695
rev_C_tree = tree.branch.repository.revision_tree('C')
696
delta = rev_C_tree.changes_from(rev_A_tree)
697
self.assertEqual([f2_changed, f3_changed], delta.modified)
698
self.assertEqual([], delta.renamed)
699
self.assertEqual([], delta.added)
700
self.assertEqual([], delta.removed)
702
rev_D_tree = tree.branch.repository.revision_tree('D')
703
delta = rev_D_tree.changes_from(rev_B_tree)
704
self.assertEqual([f2_changed, f3_changed], delta.modified)
705
self.assertEqual([], delta.renamed)
706
self.assertEqual([], delta.added)
707
self.assertEqual([], delta.removed)
709
delta = rev_D_tree.changes_from(rev_C_tree)
710
self.assertEqual([f1_changed, f3_changed], delta.modified)
711
self.assertEqual([], delta.renamed)
712
self.assertEqual([], delta.added)
713
self.assertEqual([], delta.removed)
715
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
716
"""Make sure _filter_revisions_touching_file_id returns the right values.
718
Get the return value from _filter_revisions_touching_file_id and make
719
sure they are correct.
721
# The api for _get_revisions_touching_file_id is a little crazy,
722
# So we do the setup here.
723
mainline = tree.branch.revision_history()
724
mainline.insert(0, None)
725
revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
726
view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
728
actual_revs = log._filter_revisions_touching_file_id(
732
list(view_revs_iter))
733
self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
735
def test_file_id_f1(self):
736
tree = self.create_tree_with_single_merge()
737
# f1 should be marked as modified by revisions A and B
738
self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
740
def test_file_id_f2(self):
741
tree = self.create_tree_with_single_merge()
742
# f2 should be marked as modified by revisions A, C, and D
743
# because D merged the changes from C.
744
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
746
def test_file_id_f3(self):
747
tree = self.create_tree_with_single_merge()
748
# f3 should be marked as modified by revisions A, B, C, and D
749
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1612
class CustomLogFormatter(log.LogFormatter):
1613
def __init__(self, *args, **kwargs):
1614
super(CustomLogFormatter, self).__init__(*args, **kwargs)
1616
def get_levels(self):
1617
# log formatter supports all levels:
1619
def log_revision(self, revision):
1620
self.revisions.append(revision)
1622
log_formatter = LogCatcher()
1623
# First request we don't specify number of levels, we should get a
1624
# sensible default (whatever the LogFormatter handles - which in this
1625
# case is 0/everything):
1626
request = log.make_log_request_dict(limit=10)
1627
log.Logger(b, request).show(log_formatter)
1628
# should have all three revisions:
1629
self.assertEquals(len(log_formatter.revisions), 3)
1632
log_formatter = LogCatcher()
1633
# now explicitly request mainline revisions only:
1634
request = log.make_log_request_dict(limit=10, levels=1)
1635
log.Logger(b, request).show(log_formatter)
1636
# should now only have 2 revisions:
1637
self.assertEquals(len(log_formatter.revisions), 2)