141
128
eq(len(lf.logs), 2)
142
129
self.log('log entries:')
143
130
for logentry in lf.logs:
144
self.log('%4d %s' % (logentry.revno, logentry.rev.message))
131
self.log('%4s %s' % (logentry.revno, logentry.rev.message))
146
133
# first one is most recent
147
134
logentry = lf.logs[0]
148
eq(logentry.revno, 2)
135
eq(logentry.revno, '2')
149
136
eq(logentry.rev.message, 'add one file')
150
137
d = logentry.delta
151
138
self.log('log 2 delta: %r' % d)
152
# self.checkDelta(d, added=['hello'])
139
self.checkDelta(d, added=['hello'])
154
141
# commit a log message with control characters
155
142
msg = "All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
143
self.log("original commit message: %r", msg)
157
145
lf = LogCatcher()
158
146
show_log(b, lf, verbose=True)
159
147
committed_msg = lf.logs[0].rev.message
160
148
self.log("escaped commit message: %r", committed_msg)
161
149
self.assert_(msg != committed_msg)
162
150
self.assert_(len(committed_msg) > len(msg))
152
# Check that log message with only XML-valid characters isn't
153
# escaped. As ElementTree apparently does some kind of
154
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
155
# included in the test commit message, even though they are
156
# valid XML 1.0 characters.
157
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
158
self.log("original commit message: %r", msg)
161
show_log(b, lf, verbose=True)
162
committed_msg = lf.logs[0].rev.message
163
self.log("escaped commit message: %r", committed_msg)
164
self.assert_(msg == committed_msg)
166
def test_deltas_in_merge_revisions(self):
167
"""Check deltas created for both mainline and merge revisions"""
168
eq = self.assertEquals
169
wt = self.make_branch_and_tree('parent')
170
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
173
wt.commit(message='add file1 and file2')
174
self.run_bzr('branch parent child')
175
os.unlink('child/file1')
176
file('child/file2', 'wb').write('hello\n')
177
self.run_bzr(['commit', '-m', 'remove file1 and modify file2',
180
self.run_bzr('merge ../child')
181
wt.commit('merge child branch')
185
lf.supports_merge_revisions = True
186
show_log(b, lf, verbose=True)
188
logentry = lf.logs[0]
189
eq(logentry.revno, '2')
190
eq(logentry.rev.message, 'merge child branch')
192
self.checkDelta(d, removed=['file1'], modified=['file2'])
193
logentry = lf.logs[1]
194
eq(logentry.revno, '1.1.1')
195
eq(logentry.rev.message, 'remove file1 and modify file2')
197
self.checkDelta(d, removed=['file1'], modified=['file2'])
198
logentry = lf.logs[2]
199
eq(logentry.revno, '1')
200
eq(logentry.rev.message, 'add file1 and file2')
202
self.checkDelta(d, added=['file1', 'file2'])
204
def test_merges_nonsupporting_formatter(self):
205
"""Tests that show_log will raise if the formatter doesn't
206
support merge revisions."""
207
wt = self.make_branch_and_memory_tree('.')
211
wt.commit('rev-1', rev_id='rev-1',
212
timestamp=1132586655, timezone=36000,
213
committer='Joe Foo <joe@foo.com>')
214
wt.commit('rev-merged', rev_id='rev-2a',
215
timestamp=1132586700, timezone=36000,
216
committer='Joe Foo <joe@foo.com>')
217
wt.set_parent_ids(['rev-1', 'rev-2a'])
218
wt.branch.set_last_revision_info(1, 'rev-1')
219
wt.commit('rev-2', rev_id='rev-2b',
220
timestamp=1132586800, timezone=36000,
221
committer='Joe Foo <joe@foo.com>')
222
logfile = self.make_utf8_encoded_stringio()
223
formatter = ShortLogFormatter(to_file=logfile)
226
revspec = RevisionSpec.from_string('1.1.1')
227
rev = revspec.in_history(wtb)
228
self.assertRaises(BzrCommandError, show_log, wtb, lf,
229
start_revision=rev, end_revision=rev)
234
def make_commits_with_trailing_newlines(wt):
235
"""Helper method for LogFormatter tests"""
238
open('a', 'wb').write('hello moto\n')
240
wt.commit('simple log message', rev_id='a1',
241
timestamp=1132586655.459960938, timezone=-6*3600,
242
committer='Joe Foo <joe@foo.com>')
243
open('b', 'wb').write('goodbye\n')
245
wt.commit('multiline\nlog\nmessage\n', rev_id='a2',
246
timestamp=1132586842.411175966, timezone=-6*3600,
247
committer='Joe Foo <joe@foo.com>',
248
author='Joe Bar <joe@bar.com>')
250
open('c', 'wb').write('just another manic monday\n')
252
wt.commit('single line with trailing newline\n', rev_id='a3',
253
timestamp=1132587176.835228920, timezone=-6*3600,
254
committer = 'Joe Foo <joe@foo.com>')
258
def normalize_log(log):
259
"""Replaces the variable lines of logs with fixed lines"""
260
author = 'author: Dolor Sit <test@example.com>'
261
committer = 'committer: Lorem Ipsum <test@example.com>'
262
lines = log.splitlines(True)
263
for idx,line in enumerate(lines):
264
stripped_line = line.lstrip()
265
indent = ' ' * (len(line) - len(stripped_line))
266
if stripped_line.startswith('author:'):
267
lines[idx] = indent + author + '\n'
268
elif stripped_line.startswith('committer:'):
269
lines[idx] = indent + committer + '\n'
270
elif stripped_line.startswith('timestamp:'):
271
lines[idx] = indent + 'timestamp: Just now\n'
272
return ''.join(lines)
275
class TestShortLogFormatter(TestCaseWithTransport):
277
def test_trailing_newlines(self):
278
wt = self.make_branch_and_tree('.')
279
b = make_commits_with_trailing_newlines(wt)
280
sio = self.make_utf8_encoded_stringio()
281
lf = ShortLogFormatter(to_file=sio)
283
self.assertEqualDiff(sio.getvalue(), """\
284
3 Joe Foo\t2005-11-21
285
single line with trailing newline
287
2 Joe Bar\t2005-11-21
292
1 Joe Foo\t2005-11-21
297
def test_short_log_with_merges(self):
298
wt = self.make_branch_and_memory_tree('.')
302
wt.commit('rev-1', rev_id='rev-1',
303
timestamp=1132586655, timezone=36000,
304
committer='Joe Foo <joe@foo.com>')
305
wt.commit('rev-merged', rev_id='rev-2a',
306
timestamp=1132586700, timezone=36000,
307
committer='Joe Foo <joe@foo.com>')
308
wt.set_parent_ids(['rev-1', 'rev-2a'])
309
wt.branch.set_last_revision_info(1, 'rev-1')
310
wt.commit('rev-2', rev_id='rev-2b',
311
timestamp=1132586800, timezone=36000,
312
committer='Joe Foo <joe@foo.com>')
313
logfile = self.make_utf8_encoded_stringio()
314
formatter = ShortLogFormatter(to_file=logfile)
315
show_log(wt.branch, formatter)
316
self.assertEqualDiff(logfile.getvalue(), """\
317
2 Joe Foo\t2005-11-22 [merge]
320
1 Joe Foo\t2005-11-22
327
def test_short_log_single_merge_revision(self):
328
wt = self.make_branch_and_memory_tree('.')
332
wt.commit('rev-1', rev_id='rev-1',
333
timestamp=1132586655, timezone=36000,
334
committer='Joe Foo <joe@foo.com>')
335
wt.commit('rev-merged', rev_id='rev-2a',
336
timestamp=1132586700, timezone=36000,
337
committer='Joe Foo <joe@foo.com>')
338
wt.set_parent_ids(['rev-1', 'rev-2a'])
339
wt.branch.set_last_revision_info(1, 'rev-1')
340
wt.commit('rev-2', rev_id='rev-2b',
341
timestamp=1132586800, timezone=36000,
342
committer='Joe Foo <joe@foo.com>')
343
logfile = self.make_utf8_encoded_stringio()
344
formatter = ShortLogFormatter(to_file=logfile)
345
revspec = RevisionSpec.from_string('1.1.1')
347
rev = revspec.in_history(wtb)
348
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
349
self.assertEqualDiff(logfile.getvalue(), """\
350
1.1.1 Joe Foo\t2005-11-22
358
class TestLongLogFormatter(TestCaseWithTransport):
360
def test_verbose_log(self):
361
"""Verbose log includes changed files
365
wt = self.make_branch_and_tree('.')
367
self.build_tree(['a'])
369
# XXX: why does a longer nick show up?
370
b.nick = 'test_verbose_log'
371
wt.commit(message='add a',
372
timestamp=1132711707,
374
committer='Lorem Ipsum <test@example.com>')
375
logfile = file('out.tmp', 'w+')
376
formatter = LongLogFormatter(to_file=logfile)
377
show_log(b, formatter, verbose=True)
380
log_contents = logfile.read()
381
self.assertEqualDiff(log_contents, '''\
382
------------------------------------------------------------
384
committer: Lorem Ipsum <test@example.com>
385
branch nick: test_verbose_log
386
timestamp: Wed 2005-11-23 12:08:27 +1000
393
def test_merges_are_indented_by_level(self):
394
wt = self.make_branch_and_tree('parent')
395
wt.commit('first post')
396
self.run_bzr('branch parent child')
397
self.run_bzr(['commit', '-m', 'branch 1', '--unchanged', 'child'])
398
self.run_bzr('branch child smallerchild')
399
self.run_bzr(['commit', '-m', 'branch 2', '--unchanged',
402
self.run_bzr('merge ../smallerchild')
403
self.run_bzr(['commit', '-m', 'merge branch 2'])
404
os.chdir('../parent')
405
self.run_bzr('merge ../child')
406
wt.commit('merge branch 1')
408
sio = self.make_utf8_encoded_stringio()
409
lf = LongLogFormatter(to_file=sio)
410
show_log(b, lf, verbose=True)
411
log = normalize_log(sio.getvalue())
412
self.assertEqualDiff(log, """\
413
------------------------------------------------------------
415
committer: Lorem Ipsum <test@example.com>
420
------------------------------------------------------------
422
committer: Lorem Ipsum <test@example.com>
427
------------------------------------------------------------
429
committer: Lorem Ipsum <test@example.com>
430
branch nick: smallerchild
434
------------------------------------------------------------
436
committer: Lorem Ipsum <test@example.com>
441
------------------------------------------------------------
443
committer: Lorem Ipsum <test@example.com>
450
def test_verbose_merge_revisions_contain_deltas(self):
451
wt = self.make_branch_and_tree('parent')
452
self.build_tree(['parent/f1', 'parent/f2'])
454
wt.commit('first post')
455
self.run_bzr('branch parent child')
456
os.unlink('child/f1')
457
file('child/f2', 'wb').write('hello\n')
458
self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
461
self.run_bzr('merge ../child')
462
wt.commit('merge branch 1')
464
sio = self.make_utf8_encoded_stringio()
465
lf = LongLogFormatter(to_file=sio)
466
show_log(b, lf, verbose=True)
467
log = normalize_log(sio.getvalue())
468
self.assertEqualDiff(log, """\
469
------------------------------------------------------------
471
committer: Lorem Ipsum <test@example.com>
480
------------------------------------------------------------
482
committer: Lorem Ipsum <test@example.com>
486
removed f1 and modified f2
491
------------------------------------------------------------
493
committer: Lorem Ipsum <test@example.com>
503
def test_trailing_newlines(self):
504
wt = self.make_branch_and_tree('.')
505
b = make_commits_with_trailing_newlines(wt)
506
sio = self.make_utf8_encoded_stringio()
507
lf = LongLogFormatter(to_file=sio)
509
self.assertEqualDiff(sio.getvalue(), """\
510
------------------------------------------------------------
512
committer: Joe Foo <joe@foo.com>
514
timestamp: Mon 2005-11-21 09:32:56 -0600
516
single line with trailing newline
517
------------------------------------------------------------
519
author: Joe Bar <joe@bar.com>
520
committer: Joe Foo <joe@foo.com>
522
timestamp: Mon 2005-11-21 09:27:22 -0600
527
------------------------------------------------------------
529
committer: Joe Foo <joe@foo.com>
531
timestamp: Mon 2005-11-21 09:24:15 -0600
536
def test_author_in_log(self):
537
"""Log includes the author name if it's set in
538
the revision properties
540
wt = self.make_branch_and_tree('.')
542
self.build_tree(['a'])
544
b.nick = 'test_author_log'
545
wt.commit(message='add a',
546
timestamp=1132711707,
548
committer='Lorem Ipsum <test@example.com>',
549
author='John Doe <jdoe@example.com>')
551
formatter = LongLogFormatter(to_file=sio)
552
show_log(b, formatter)
553
self.assertEqualDiff(sio.getvalue(), '''\
554
------------------------------------------------------------
556
author: John Doe <jdoe@example.com>
557
committer: Lorem Ipsum <test@example.com>
558
branch nick: test_author_log
559
timestamp: Wed 2005-11-23 12:08:27 +1000
566
class TestLineLogFormatter(TestCaseWithTransport):
568
def test_line_log(self):
569
"""Line log should show revno
573
wt = self.make_branch_and_tree('.')
575
self.build_tree(['a'])
577
b.nick = 'test-line-log'
578
wt.commit(message='add a',
579
timestamp=1132711707,
581
committer='Line-Log-Formatter Tester <test@line.log>')
582
logfile = file('out.tmp', 'w+')
583
formatter = LineLogFormatter(to_file=logfile)
584
show_log(b, formatter)
587
log_contents = logfile.read()
588
self.assertEqualDiff(log_contents,
589
'1: Line-Log-Formatte... 2005-11-23 add a\n')
591
def test_trailing_newlines(self):
592
wt = self.make_branch_and_tree('.')
593
b = make_commits_with_trailing_newlines(wt)
594
sio = self.make_utf8_encoded_stringio()
595
lf = LineLogFormatter(to_file=sio)
597
self.assertEqualDiff(sio.getvalue(), """\
598
3: Joe Foo 2005-11-21 single line with trailing newline
599
2: Joe Bar 2005-11-21 multiline
600
1: Joe Foo 2005-11-21 simple log message
603
def test_line_log_single_merge_revision(self):
604
wt = self.make_branch_and_memory_tree('.')
608
wt.commit('rev-1', rev_id='rev-1',
609
timestamp=1132586655, timezone=36000,
610
committer='Joe Foo <joe@foo.com>')
611
wt.commit('rev-merged', rev_id='rev-2a',
612
timestamp=1132586700, timezone=36000,
613
committer='Joe Foo <joe@foo.com>')
614
wt.set_parent_ids(['rev-1', 'rev-2a'])
615
wt.branch.set_last_revision_info(1, 'rev-1')
616
wt.commit('rev-2', rev_id='rev-2b',
617
timestamp=1132586800, timezone=36000,
618
committer='Joe Foo <joe@foo.com>')
619
logfile = self.make_utf8_encoded_stringio()
620
formatter = LineLogFormatter(to_file=logfile)
621
revspec = RevisionSpec.from_string('1.1.1')
623
rev = revspec.in_history(wtb)
624
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
625
self.assertEqualDiff(logfile.getvalue(), """\
626
1.1.1: Joe Foo 2005-11-22 rev-merged
633
class TestGetViewRevisions(TestCaseWithTransport):
635
def make_tree_with_commits(self):
636
"""Create a tree with well-known revision ids"""
637
wt = self.make_branch_and_tree('tree1')
638
wt.commit('commit one', rev_id='1')
639
wt.commit('commit two', rev_id='2')
640
wt.commit('commit three', rev_id='3')
641
mainline_revs = [None, '1', '2', '3']
642
rev_nos = {'1': 1, '2': 2, '3': 3}
643
return mainline_revs, rev_nos, wt
645
def make_tree_with_merges(self):
646
"""Create a tree with well-known revision ids and a merge"""
647
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
648
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
649
tree2.commit('four-a', rev_id='4a')
650
wt.merge_from_branch(tree2.branch)
651
wt.commit('four-b', rev_id='4b')
652
mainline_revs.append('4b')
655
return mainline_revs, rev_nos, wt
657
def make_tree_with_many_merges(self):
658
"""Create a tree with well-known revision ids"""
659
wt = self.make_branch_and_tree('tree1')
660
wt.commit('commit one', rev_id='1')
661
wt.commit('commit two', rev_id='2')
662
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
663
tree3.commit('commit three a', rev_id='3a')
664
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
665
tree2.merge_from_branch(tree3.branch)
666
tree2.commit('commit three b', rev_id='3b')
667
wt.merge_from_branch(tree2.branch)
668
wt.commit('commit three c', rev_id='3c')
669
tree2.commit('four-a', rev_id='4a')
670
wt.merge_from_branch(tree2.branch)
671
wt.commit('four-b', rev_id='4b')
672
mainline_revs = [None, '1', '2', '3c', '4b']
673
rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
674
full_rev_nos_for_reference = {
677
'3a': '2.1.1', #first commit tree 3
678
'3b': '2.2.1', # first commit tree 2
679
'3c': '3', #merges 3b to main
680
'4a': '2.2.2', # second commit tree 2
681
'4b': '4', # merges 4a to main
683
return mainline_revs, rev_nos, wt
685
def test_get_view_revisions_forward(self):
686
"""Test the get_view_revisions method"""
687
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
689
self.addCleanup(wt.unlock)
690
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
692
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
694
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
695
'forward', include_merges=False))
696
self.assertEqual(revisions, revisions2)
698
def test_get_view_revisions_reverse(self):
699
"""Test the get_view_revisions with reverse"""
700
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
702
self.addCleanup(wt.unlock)
703
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
705
self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
707
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
708
'reverse', include_merges=False))
709
self.assertEqual(revisions, revisions2)
711
def test_get_view_revisions_merge(self):
712
"""Test get_view_revisions when there are merges"""
713
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
715
self.addCleanup(wt.unlock)
716
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
718
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
719
('4b', '4', 0), ('4a', '3.1.1', 1)],
721
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
722
'forward', include_merges=False))
723
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
727
def test_get_view_revisions_merge_reverse(self):
728
"""Test get_view_revisions in reverse when there are merges"""
729
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
731
self.addCleanup(wt.unlock)
732
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
734
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
735
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
737
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
738
'reverse', include_merges=False))
739
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
743
def test_get_view_revisions_merge2(self):
744
"""Test get_view_revisions when there are merges"""
745
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
747
self.addCleanup(wt.unlock)
748
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
750
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
751
('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
753
self.assertEqual(expected, revisions)
754
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
755
'forward', include_merges=False))
756
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
761
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
763
def create_tree_with_single_merge(self):
764
"""Create a branch with a moderate layout.
766
The revision graph looks like:
774
In this graph, A introduced files f1 and f2 and f3.
775
B modifies f1 and f3, and C modifies f2 and f3.
776
D merges the changes from B and C and resolves the conflict for f3.
778
# TODO: jam 20070218 This seems like it could really be done
779
# with make_branch_and_memory_tree() if we could just
780
# create the content of those files.
781
# TODO: jam 20070218 Another alternative is that we would really
782
# like to only create this tree 1 time for all tests that
783
# use it. Since 'log' only uses the tree in a readonly
784
# fashion, it seems a shame to regenerate an identical
785
# tree for each test.
786
tree = self.make_branch_and_tree('tree')
788
self.addCleanup(tree.unlock)
790
self.build_tree_contents([('tree/f1', 'A\n'),
794
tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
795
tree.commit('A', rev_id='A')
797
self.build_tree_contents([('tree/f2', 'A\nC\n'),
798
('tree/f3', 'A\nC\n'),
800
tree.commit('C', rev_id='C')
801
# Revert back to A to build the other history.
802
tree.set_last_revision('A')
803
tree.branch.set_last_revision_info(1, 'A')
804
self.build_tree_contents([('tree/f1', 'A\nB\n'),
806
('tree/f3', 'A\nB\n'),
808
tree.commit('B', rev_id='B')
809
tree.set_parent_ids(['B', 'C'])
810
self.build_tree_contents([('tree/f1', 'A\nB\n'),
811
('tree/f2', 'A\nC\n'),
812
('tree/f3', 'A\nB\nC\n'),
814
tree.commit('D', rev_id='D')
816
# Switch to a read lock for this tree.
817
# We still have addCleanup(unlock)
822
def test_tree_with_single_merge(self):
823
"""Make sure the tree layout is correct."""
824
tree = self.create_tree_with_single_merge()
825
rev_A_tree = tree.branch.repository.revision_tree('A')
826
rev_B_tree = tree.branch.repository.revision_tree('B')
828
f1_changed = (u'f1', 'f1-id', 'file', True, False)
829
f2_changed = (u'f2', 'f2-id', 'file', True, False)
830
f3_changed = (u'f3', 'f3-id', 'file', True, False)
832
delta = rev_B_tree.changes_from(rev_A_tree)
833
self.assertEqual([f1_changed, f3_changed], delta.modified)
834
self.assertEqual([], delta.renamed)
835
self.assertEqual([], delta.added)
836
self.assertEqual([], delta.removed)
838
rev_C_tree = tree.branch.repository.revision_tree('C')
839
delta = rev_C_tree.changes_from(rev_A_tree)
840
self.assertEqual([f2_changed, f3_changed], delta.modified)
841
self.assertEqual([], delta.renamed)
842
self.assertEqual([], delta.added)
843
self.assertEqual([], delta.removed)
845
rev_D_tree = tree.branch.repository.revision_tree('D')
846
delta = rev_D_tree.changes_from(rev_B_tree)
847
self.assertEqual([f2_changed, f3_changed], delta.modified)
848
self.assertEqual([], delta.renamed)
849
self.assertEqual([], delta.added)
850
self.assertEqual([], delta.removed)
852
delta = rev_D_tree.changes_from(rev_C_tree)
853
self.assertEqual([f1_changed, f3_changed], delta.modified)
854
self.assertEqual([], delta.renamed)
855
self.assertEqual([], delta.added)
856
self.assertEqual([], delta.removed)
858
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
859
"""Make sure _filter_revisions_touching_file_id returns the right values.
861
Get the return value from _filter_revisions_touching_file_id and make
862
sure they are correct.
864
# The api for _get_revisions_touching_file_id is a little crazy,
865
# So we do the setup here.
866
mainline = tree.branch.revision_history()
867
mainline.insert(0, None)
868
revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
869
view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
871
actual_revs = log._filter_revisions_touching_file_id(
875
list(view_revs_iter))
876
self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
878
def test_file_id_f1(self):
879
tree = self.create_tree_with_single_merge()
880
# f1 should be marked as modified by revisions A and B
881
self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
883
def test_file_id_f2(self):
884
tree = self.create_tree_with_single_merge()
885
# f2 should be marked as modified by revisions A, C, and D
886
# because D merged the changes from C.
887
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
889
def test_file_id_f3(self):
890
tree = self.create_tree_with_single_merge()
891
# f3 should be marked as modified by revisions A, B, C, and D
892
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
894
def test_file_id_with_ghosts(self):
895
# This is testing bug #209948, where having a ghost would cause
896
# _filter_revisions_touching_file_id() to fail.
897
tree = self.create_tree_with_single_merge()
898
# We need to add a revision, so switch back to a write-locked tree
901
first_parent = tree.last_revision()
902
tree.set_parent_ids([first_parent, 'ghost-revision-id'])
903
self.build_tree_contents([('tree/f1', 'A\nB\nXX\n')])
904
tree.commit('commit with a ghost', rev_id='XX')
905
self.assertAllRevisionsForFileID(tree, 'f1-id', ['XX', 'B', 'A'])
906
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
909
class TestShowChangedRevisions(TestCaseWithTransport):
911
def test_show_changed_revisions_verbose(self):
912
tree = self.make_branch_and_tree('tree_a')
913
self.build_tree(['tree_a/foo'])
915
tree.commit('bar', rev_id='bar-id')
916
s = self.make_utf8_encoded_stringio()
917
log.show_changed_revisions(tree.branch, [], ['bar-id'], s)
918
self.assertContainsRe(s.getvalue(), 'bar')
919
self.assertNotContainsRe(s.getvalue(), 'foo')
922
class TestLogFormatter(TestCase):
924
def test_short_committer(self):
925
rev = Revision('a-id')
926
rev.committer = 'John Doe <jdoe@example.com>'
927
lf = LogFormatter(None)
928
self.assertEqual('John Doe', lf.short_committer(rev))
929
rev.committer = 'John Smith <jsmith@example.com>'
930
self.assertEqual('John Smith', lf.short_committer(rev))
931
rev.committer = 'John Smith'
932
self.assertEqual('John Smith', lf.short_committer(rev))
933
rev.committer = 'jsmith@example.com'
934
self.assertEqual('jsmith@example.com', lf.short_committer(rev))
935
rev.committer = '<jsmith@example.com>'
936
self.assertEqual('jsmith@example.com', lf.short_committer(rev))
937
rev.committer = 'John Smith jsmith@example.com'
938
self.assertEqual('John Smith', lf.short_committer(rev))
940
def test_short_author(self):
941
rev = Revision('a-id')
942
rev.committer = 'John Doe <jdoe@example.com>'
943
lf = LogFormatter(None)
944
self.assertEqual('John Doe', lf.short_author(rev))
945
rev.properties['author'] = 'John Smith <jsmith@example.com>'
946
self.assertEqual('John Smith', lf.short_author(rev))
947
rev.properties['author'] = 'John Smith'
948
self.assertEqual('John Smith', lf.short_author(rev))
949
rev.properties['author'] = 'jsmith@example.com'
950
self.assertEqual('jsmith@example.com', lf.short_author(rev))
951
rev.properties['author'] = '<jsmith@example.com>'
952
self.assertEqual('jsmith@example.com', lf.short_author(rev))
953
rev.properties['author'] = 'John Smith jsmith@example.com'
954
self.assertEqual('John Smith', lf.short_author(rev))