168
73
wt.commit('empty commit')
169
log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
171
# Since there is a single revision in the branch all the combinations
173
self.assertInvalidRevisonNumber(b, 2, 1)
174
self.assertInvalidRevisonNumber(b, 1, 2)
175
self.assertInvalidRevisonNumber(b, 0, 2)
176
self.assertInvalidRevisonNumber(b, 1, 0)
177
self.assertInvalidRevisonNumber(b, -1, 1)
178
self.assertInvalidRevisonNumber(b, 1, -1)
180
def test_empty_branch(self):
74
show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
75
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
76
start_revision=2, end_revision=1)
77
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
78
start_revision=1, end_revision=2)
79
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
80
start_revision=0, end_revision=2)
81
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
82
start_revision=1, end_revision=0)
83
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
84
start_revision=-1, end_revision=1)
85
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
86
start_revision=1, end_revision=-1)
88
def test_simple_log(self):
89
eq = self.assertEquals
181
91
wt = self.make_branch_and_tree('.')
184
log.show_log(wt.branch, lf)
186
self.assertEqual([], lf.revisions)
188
def test_empty_commit(self):
189
wt = self.make_branch_and_tree('.')
191
99
wt.commit('empty commit')
192
100
lf = LogCatcher()
193
log.show_log(wt.branch, lf, verbose=True)
195
self.assertEqual(1, len(revs))
196
self.assertEqual('1', revs[0].revno)
197
self.assertEqual('empty commit', revs[0].rev.message)
198
self.checkDelta(revs[0].delta)
101
show_log(b, lf, verbose=True)
103
eq(lf.logs[0].revno, '1')
104
eq(lf.logs[0].rev.message, 'empty commit')
106
self.log('log delta: %r' % d)
200
def test_simple_commit(self):
201
wt = self.make_branch_and_tree('.')
202
wt.commit('empty commit')
203
109
self.build_tree(['hello'])
205
wt.commit('add one file',
206
committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
207
u'<test@example.com>')
111
wt.commit('add one file')
114
# log using regular thing
115
show_log(b, LongLogFormatter(lf))
117
for l in lf.readlines():
120
# get log as data structure
208
121
lf = LogCatcher()
209
log.show_log(wt.branch, lf, verbose=True)
210
self.assertEqual(2, len(lf.revisions))
122
show_log(b, lf, verbose=True)
124
self.log('log entries:')
125
for logentry in lf.logs:
126
self.log('%4s %s' % (logentry.revno, logentry.rev.message))
211
128
# first one is most recent
212
log_entry = lf.revisions[0]
213
self.assertEqual('2', log_entry.revno)
214
self.assertEqual('add one file', log_entry.rev.message)
215
self.checkDelta(log_entry.delta, added=['hello'])
217
def test_commit_message_with_control_chars(self):
218
wt = self.make_branch_and_tree('.')
219
msg = u"All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
220
msg = msg.replace(u'\r', u'\n')
129
logentry = lf.logs[0]
130
eq(logentry.revno, '2')
131
eq(logentry.rev.message, 'add one file')
133
self.log('log 2 delta: %r' % d)
134
# self.checkDelta(d, added=['hello'])
136
# commit a log message with control characters
137
msg = "All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
138
self.log("original commit message: %r", msg)
222
140
lf = LogCatcher()
223
log.show_log(wt.branch, lf, verbose=True)
224
committed_msg = lf.revisions[0].rev.message
225
if wt.branch.repository._serializer.squashes_xml_invalid_characters:
226
self.assertNotEqual(msg, committed_msg)
227
self.assertTrue(len(committed_msg) > len(msg))
229
self.assertEqual(msg, committed_msg)
141
show_log(b, lf, verbose=True)
142
committed_msg = lf.logs[0].rev.message
143
self.log("escaped commit message: %r", committed_msg)
144
self.assert_(msg != committed_msg)
145
self.assert_(len(committed_msg) > len(msg))
231
def test_commit_message_without_control_chars(self):
232
wt = self.make_branch_and_tree('.')
147
# Check that log message with only XML-valid characters isn't
233
148
# escaped. As ElementTree apparently does some kind of
234
149
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
235
150
# included in the test commit message, even though they are
236
151
# valid XML 1.0 characters.
237
152
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
153
self.log("original commit message: %r", msg)
239
155
lf = LogCatcher()
240
log.show_log(wt.branch, lf, verbose=True)
241
committed_msg = lf.revisions[0].rev.message
242
self.assertEqual(msg, committed_msg)
244
def test_deltas_in_merge_revisions(self):
245
"""Check deltas created for both mainline and merge revisions"""
246
wt = self.make_branch_and_tree('parent')
247
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
250
wt.commit(message='add file1 and file2')
251
self.run_bzr('branch parent child')
252
os.unlink('child/file1')
253
file('child/file2', 'wb').write('hello\n')
254
self.run_bzr(['commit', '-m', 'remove file1 and modify file2',
257
self.run_bzr('merge ../child')
258
wt.commit('merge child branch')
262
lf.supports_merge_revisions = True
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):
156
show_log(b, lf, verbose=True)
157
committed_msg = lf.logs[0].rev.message
158
self.log("escaped commit message: %r", committed_msg)
159
self.assert_(msg == committed_msg)
286
161
def test_trailing_newlines(self):
287
162
wt = self.make_branch_and_tree('.')
288
b = self.make_commits_with_trailing_newlines(wt)
289
self.assertFormatterResult("""\
290
3 Joe Foo\t2005-11-22
165
open('a', 'wb').write('hello moto\n')
167
wt.commit('simple log message', rev_id='a1'
168
, timestamp=1132586655.459960938, timezone=-6*3600
169
, committer='Joe Foo <joe@foo.com>')
170
open('b', 'wb').write('goodbye\n')
172
wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
173
, timestamp=1132586842.411175966, timezone=-6*3600
174
, committer='Joe Foo <joe@foo.com>')
176
open('c', 'wb').write('just another manic monday\n')
178
wt.commit('single line with trailing newline\n', rev_id='a3'
179
, timestamp=1132587176.835228920, timezone=-6*3600
180
, committer = 'Joe Foo <joe@foo.com>')
183
lf = ShortLogFormatter(to_file=sio)
185
self.assertEquals(sio.getvalue(), """\
186
3 Joe Foo\t2005-11-21
291
187
single line with trailing newline
293
2 Joe Foo\t2005-11-22
189
2 Joe Foo\t2005-11-21
298
1 Joe Foo\t2005-11-22
194
1 Joe Foo\t2005-11-21
299
195
simple log message
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):
200
lf = LongLogFormatter(to_file=sio)
202
self.assertEquals(sio.getvalue(), """\
203
------------------------------------------------------------
205
committer: Joe Foo <joe@foo.com>
207
timestamp: Mon 2005-11-21 09:32:56 -0600
209
single line with trailing newline
210
------------------------------------------------------------
212
committer: Joe Foo <joe@foo.com>
214
timestamp: Mon 2005-11-21 09:27:22 -0600
219
------------------------------------------------------------
221
committer: Joe Foo <joe@foo.com>
223
timestamp: Mon 2005-11-21 09:24:15 -0600
438
228
def test_verbose_log(self):
439
229
"""Verbose log includes changed files
443
wt = self.make_standard_commit('test_verbose_log', authors=[])
444
self.assertFormatterResult('''\
233
wt = self.make_branch_and_tree('.')
235
self.build_tree(['a'])
237
# XXX: why does a longer nick show up?
238
b.nick = 'test_verbose_log'
239
wt.commit(message='add a',
240
timestamp=1132711707,
242
committer='Lorem Ipsum <test@example.com>')
243
logfile = file('out.tmp', 'w+')
244
formatter = LongLogFormatter(to_file=logfile)
245
show_log(b, formatter, verbose=True)
248
log_contents = logfile.read()
249
self.assertEqualDiff(log_contents, '''\
445
250
------------------------------------------------------------
447
252
committer: Lorem Ipsum <test@example.com>
448
253
branch nick: test_verbose_log
449
timestamp: Tue 2005-11-22 00:00:00 +0000
455
wt.branch, log.LongLogFormatter,
456
show_log_kwargs=dict(verbose=True))
458
def test_merges_are_indented_by_level(self):
459
wt = self.make_branch_and_tree('parent')
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("""\
470
------------------------------------------------------------
472
committer: Joe Foo <joe@foo.com>
474
timestamp: Tue 2005-11-22 00:00:04 +0000
477
------------------------------------------------------------
479
committer: Joe Foo <joe@foo.com>
481
timestamp: Tue 2005-11-22 00:00:03 +0000
484
------------------------------------------------------------
486
committer: Joe Foo <joe@foo.com>
487
branch nick: smallerchild
488
timestamp: Tue 2005-11-22 00:00:02 +0000
491
------------------------------------------------------------
493
committer: Joe Foo <joe@foo.com>
495
timestamp: Tue 2005-11-22 00:00:01 +0000
498
------------------------------------------------------------
500
committer: Joe Foo <joe@foo.com>
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))
510
def test_verbose_merge_revisions_contain_deltas(self):
511
wt = self.make_branch_and_tree('parent')
512
self.build_tree(['parent/f1', 'parent/f2'])
514
self.wt_commit(wt, 'first post')
515
child_wt = wt.bzrdir.sprout('child').open_workingtree()
516
os.unlink('child/f1')
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("""\
522
------------------------------------------------------------
524
committer: Joe Foo <joe@foo.com>
526
timestamp: Tue 2005-11-22 00:00:02 +0000
533
------------------------------------------------------------
535
committer: Joe Foo <joe@foo.com>
537
timestamp: Tue 2005-11-22 00:00:01 +0000
539
removed f1 and modified f2
544
------------------------------------------------------------
546
committer: Joe Foo <joe@foo.com>
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))
559
def test_trailing_newlines(self):
560
wt = self.make_branch_and_tree('.')
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):
254
timestamp: Wed 2005-11-23 12:08:27 +1000
868
261
def test_line_log(self):
869
262
"""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):
882
266
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()
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)
268
self.build_tree(['a'])
270
b.nick = 'test-line-log'
271
wt.commit(message='add a',
272
timestamp=1132711707,
274
committer='Line-Log-Formatter Tester <test@line.log>')
275
logfile = file('out.tmp', 'w+')
276
formatter = LineLogFormatter(to_file=logfile)
277
show_log(b, formatter)
280
log_contents = logfile.read()
281
self.assertEqualDiff(log_contents, '1: Line-Log-Formatte... 2005-11-23 add a\n')
283
def test_short_log_with_merges(self):
284
wt = self.make_branch_and_memory_tree('.')
288
wt.commit('rev-1', rev_id='rev-1',
289
timestamp=1132586655, timezone=36000,
290
committer='Joe Foo <joe@foo.com>')
291
wt.commit('rev-merged', rev_id='rev-2a',
292
timestamp=1132586700, timezone=36000,
293
committer='Joe Foo <joe@foo.com>')
294
wt.set_parent_ids(['rev-1', 'rev-2a'])
295
wt.branch.set_last_revision_info(1, 'rev-1')
296
wt.commit('rev-2', rev_id='rev-2b',
297
timestamp=1132586800, timezone=36000,
298
committer='Joe Foo <joe@foo.com>')
300
formatter = ShortLogFormatter(to_file=logfile)
301
show_log(wt.branch, formatter)
303
self.assertEqualDiff("""\
304
2 Joe Foo\t2005-11-22 [merge]
307
1 Joe Foo\t2005-11-22
310
""", logfile.getvalue())
314
def make_tree_with_commits(self):
315
"""Create a tree with well-known revision ids"""
316
wt = self.make_branch_and_tree('tree1')
317
wt.commit('commit one', rev_id='1')
318
wt.commit('commit two', rev_id='2')
319
wt.commit('commit three', rev_id='3')
320
mainline_revs = [None, '1', '2', '3']
321
rev_nos = {'1': 1, '2': 2, '3': 3}
322
return mainline_revs, rev_nos, wt
324
def make_tree_with_merges(self):
325
"""Create a tree with well-known revision ids and a merge"""
326
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
327
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
328
tree2.commit('four-a', rev_id='4a')
329
wt.merge_from_branch(tree2.branch)
330
wt.commit('four-b', rev_id='4b')
331
mainline_revs.append('4b')
334
return mainline_revs, rev_nos, wt
336
def make_tree_with_many_merges(self):
337
"""Create a tree with well-known revision ids"""
338
wt = self.make_branch_and_tree('tree1')
339
wt.commit('commit one', rev_id='1')
340
wt.commit('commit two', rev_id='2')
341
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
342
tree3.commit('commit three a', rev_id='3a')
343
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
344
tree2.merge_from_branch(tree3.branch)
345
tree2.commit('commit three b', rev_id='3b')
346
wt.merge_from_branch(tree2.branch)
347
wt.commit('commit three c', rev_id='3c')
348
tree2.commit('four-a', rev_id='4a')
349
wt.merge_from_branch(tree2.branch)
350
wt.commit('four-b', rev_id='4b')
351
mainline_revs = [None, '1', '2', '3c', '4b']
352
rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
353
full_rev_nos_for_reference = {
356
'3a': '2.2.1', #first commit tree 3
357
'3b': '2.1.1', # first commit tree 2
358
'3c': '3', #merges 3b to main
359
'4a': '2.1.2', # second commit tree 2
360
'4b': '4', # merges 4a to main
362
return mainline_revs, rev_nos, wt
364
def test_get_view_revisions_forward(self):
365
"""Test the get_view_revisions method"""
366
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
367
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
369
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
371
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
372
'forward', include_merges=False))
373
self.assertEqual(revisions, revisions2)
375
def test_get_view_revisions_reverse(self):
376
"""Test the get_view_revisions with reverse"""
377
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
378
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
380
self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
382
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
383
'reverse', include_merges=False))
384
self.assertEqual(revisions, revisions2)
386
def test_get_view_revisions_merge(self):
387
"""Test get_view_revisions when there are merges"""
388
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
389
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
391
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
392
('4b', '4', 0), ('4a', '3.1.1', 1)],
394
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
395
'forward', include_merges=False))
396
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
400
def test_get_view_revisions_merge_reverse(self):
401
"""Test get_view_revisions in reverse when there are merges"""
402
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
403
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
405
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
406
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
408
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
409
'reverse', include_merges=False))
410
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
414
def test_get_view_revisions_merge2(self):
415
"""Test get_view_revisions when there are merges"""
416
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
417
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
419
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
420
('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
422
self.assertEqual(expected, revisions)
423
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
424
'forward', include_merges=False))
425
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
430
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
432
def create_tree_with_single_merge(self):
433
"""Create a branch with a moderate layout.
435
The revision graph looks like:
443
In this graph, A introduced files f1 and f2 and f3.
444
B modifies f1 and f3, and C modifies f2 and f3.
445
D merges the changes from B and C and resolves the conflict for f3.
447
# TODO: jam 20070218 This seems like it could really be done
448
# with make_branch_and_memory_tree() if we could just
449
# create the content of those files.
450
# TODO: jam 20070218 Another alternative is that we would really
451
# like to only create this tree 1 time for all tests that
452
# use it. Since 'log' only uses the tree in a readonly
453
# fashion, it seems a shame to regenerate an identical
454
# tree for each test.
455
tree = self.make_branch_and_tree('tree')
457
self.addCleanup(tree.unlock)
459
self.build_tree_contents([('tree/f1', 'A\n'),
463
tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
464
tree.commit('A', rev_id='A')
466
self.build_tree_contents([('tree/f2', 'A\nC\n'),
467
('tree/f3', 'A\nC\n'),
469
tree.commit('C', rev_id='C')
470
# Revert back to A to build the other history.
471
tree.set_last_revision('A')
472
tree.branch.set_last_revision_info(1, 'A')
473
self.build_tree_contents([('tree/f1', 'A\nB\n'),
475
('tree/f3', 'A\nB\n'),
477
tree.commit('B', rev_id='B')
478
tree.set_parent_ids(['B', 'C'])
479
self.build_tree_contents([('tree/f1', 'A\nB\n'),
480
('tree/f2', 'A\nC\n'),
481
('tree/f3', 'A\nB\nC\n'),
483
tree.commit('D', rev_id='D')
485
# Switch to a read lock for this tree.
486
# We still have addCleanup(unlock)
491
def test_tree_with_single_merge(self):
492
"""Make sure the tree layout is correct."""
493
tree = self.create_tree_with_single_merge()
494
rev_A_tree = tree.branch.repository.revision_tree('A')
495
rev_B_tree = tree.branch.repository.revision_tree('B')
497
f1_changed = (u'f1', 'f1-id', 'file', True, False)
498
f2_changed = (u'f2', 'f2-id', 'file', True, False)
499
f3_changed = (u'f3', 'f3-id', 'file', True, False)
501
delta = rev_B_tree.changes_from(rev_A_tree)
502
self.assertEqual([f1_changed, f3_changed], delta.modified)
503
self.assertEqual([], delta.renamed)
504
self.assertEqual([], delta.added)
505
self.assertEqual([], delta.removed)
507
rev_C_tree = tree.branch.repository.revision_tree('C')
508
delta = rev_C_tree.changes_from(rev_A_tree)
509
self.assertEqual([f2_changed, f3_changed], delta.modified)
510
self.assertEqual([], delta.renamed)
511
self.assertEqual([], delta.added)
512
self.assertEqual([], delta.removed)
514
rev_D_tree = tree.branch.repository.revision_tree('D')
515
delta = rev_D_tree.changes_from(rev_B_tree)
516
self.assertEqual([f2_changed, f3_changed], delta.modified)
517
self.assertEqual([], delta.renamed)
518
self.assertEqual([], delta.added)
519
self.assertEqual([], delta.removed)
521
delta = rev_D_tree.changes_from(rev_C_tree)
522
self.assertEqual([f1_changed, f3_changed], delta.modified)
523
self.assertEqual([], delta.renamed)
524
self.assertEqual([], delta.added)
525
self.assertEqual([], delta.removed)
527
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
528
"""Make sure _get_revisions_touching_file_id returns the right values.
530
Get the return value from _get_revisions_touching_file_id and make
531
sure they are correct.
533
# The api for _get_revisions_touching_file_id is a little crazy,
534
# So we do the setup here.
535
mainline = tree.branch.revision_history()
536
mainline.insert(0, None)
537
revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
538
view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
540
actual_revs = log._get_revisions_touching_file_id(tree.branch, file_id,
543
self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
545
def test_file_id_f1(self):
546
tree = self.create_tree_with_single_merge()
547
# f1 should be marked as modified by revisions A and B
548
self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
550
def test_file_id_f2(self):
551
tree = self.create_tree_with_single_merge()
552
# f2 should be marked as modified by revisions A, C, and D
553
# because D merged the changes from C.
554
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
556
def test_file_id_f3(self):
557
tree = self.create_tree_with_single_merge()
558
# f3 should be marked as modified by revisions A, B, C, and D
559
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])