153
180
self.log("escaped commit message: %r", committed_msg)
154
181
self.assert_(msg == committed_msg)
156
def test_deltas_in_merge_revisions(self):
157
"""Check deltas created for both mainline and merge revisions"""
158
eq = self.assertEquals
159
wt = self.make_branch_and_tree('parent')
160
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
163
wt.commit(message='add file1 and file2')
164
self.run_bzr('branch', 'parent', 'child')
165
os.unlink('child/file1')
166
print >> file('child/file2', 'wb'), 'hello'
167
self.run_bzr('commit', '-m', 'remove file1 and modify file2', 'child')
169
self.run_bzr('merge', '../child')
170
wt.commit('merge child branch')
174
lf.supports_merge_revisions = True
175
show_log(b, lf, verbose=True)
177
logentry = lf.logs[0]
178
eq(logentry.revno, '2')
179
eq(logentry.rev.message, 'merge child branch')
181
self.checkDelta(d, removed=['file1'], modified=['file2'])
182
logentry = lf.logs[1]
183
eq(logentry.revno, '1.1.1')
184
eq(logentry.rev.message, 'remove file1 and modify file2')
186
self.checkDelta(d, removed=['file1'], modified=['file2'])
187
logentry = lf.logs[2]
188
eq(logentry.revno, '1')
189
eq(logentry.rev.message, 'add file1 and file2')
191
self.checkDelta(d, added=['file1', 'file2'])
194
def make_commits_with_trailing_newlines(wt):
195
"""Helper method for LogFormatter tests"""
198
open('a', 'wb').write('hello moto\n')
200
wt.commit('simple log message', rev_id='a1'
201
, timestamp=1132586655.459960938, timezone=-6*3600
202
, committer='Joe Foo <joe@foo.com>')
203
open('b', 'wb').write('goodbye\n')
205
wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
206
, timestamp=1132586842.411175966, timezone=-6*3600
207
, committer='Joe Foo <joe@foo.com>')
209
open('c', 'wb').write('just another manic monday\n')
211
wt.commit('single line with trailing newline\n', rev_id='a3'
212
, timestamp=1132587176.835228920, timezone=-6*3600
213
, committer = 'Joe Foo <joe@foo.com>')
217
class TestShortLogFormatter(TestCaseWithTransport):
219
183
def test_trailing_newlines(self):
220
184
wt = self.make_branch_and_tree('.')
221
b = make_commits_with_trailing_newlines(wt)
187
open('a', 'wb').write('hello moto\n')
189
wt.commit('simple log message', rev_id='a1'
190
, timestamp=1132586655.459960938, timezone=-6*3600
191
, committer='Joe Foo <joe@foo.com>')
192
open('b', 'wb').write('goodbye\n')
194
wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
195
, timestamp=1132586842.411175966, timezone=-6*3600
196
, committer='Joe Foo <joe@foo.com>')
198
open('c', 'wb').write('just another manic monday\n')
200
wt.commit('single line with trailing newline\n', rev_id='a3'
201
, timestamp=1132587176.835228920, timezone=-6*3600
202
, committer = 'Joe Foo <joe@foo.com>')
223
205
lf = ShortLogFormatter(to_file=sio)
288
def test_merges_are_indented_by_level(self):
289
wt = self.make_branch_and_tree('parent')
290
wt.commit('first post')
291
self.run_bzr('branch', 'parent', 'child')
292
self.run_bzr('commit', '-m', 'branch 1', '--unchanged', 'child')
293
self.run_bzr('branch', 'child', 'smallerchild')
294
self.run_bzr('commit', '-m', 'branch 2', '--unchanged', 'smallerchild')
296
self.run_bzr('merge', '../smallerchild')
297
self.run_bzr('commit', '-m', 'merge branch 2')
298
os.chdir('../parent')
299
self.run_bzr('merge', '../child')
300
wt.commit('merge branch 1')
303
lf = LongLogFormatter(to_file=sio)
304
show_log(b, lf, verbose=True)
305
log = self.normalize_log(sio.getvalue())
306
self.assertEqualDiff("""\
307
------------------------------------------------------------
309
committer: Lorem Ipsum <test@example.com>
314
------------------------------------------------------------
316
committer: Lorem Ipsum <test@example.com>
321
------------------------------------------------------------
323
committer: Lorem Ipsum <test@example.com>
324
branch nick: smallerchild
328
------------------------------------------------------------
330
committer: Lorem Ipsum <test@example.com>
335
------------------------------------------------------------
337
committer: Lorem Ipsum <test@example.com>
344
def test_verbose_merge_revisions_contain_deltas(self):
345
wt = self.make_branch_and_tree('parent')
346
self.build_tree(['parent/f1', 'parent/f2'])
348
wt.commit('first post')
349
self.run_bzr('branch', 'parent', 'child')
350
os.unlink('child/f1')
351
print >> file('child/f2', 'wb'), 'hello'
352
self.run_bzr('commit', '-m', 'removed f1 and modified f2', 'child')
354
self.run_bzr('merge', '../child')
355
wt.commit('merge branch 1')
358
lf = LongLogFormatter(to_file=sio)
359
show_log(b, lf, verbose=True)
360
log = self.normalize_log(sio.getvalue())
361
self.assertEqualDiff("""\
362
------------------------------------------------------------
364
committer: Lorem Ipsum <test@example.com>
373
------------------------------------------------------------
375
committer: Lorem Ipsum <test@example.com>
379
removed f1 and modified f2
384
------------------------------------------------------------
386
committer: Lorem Ipsum <test@example.com>
396
def test_trailing_newlines(self):
397
wt = self.make_branch_and_tree('.')
398
b = make_commits_with_trailing_newlines(wt)
400
lf = LongLogFormatter(to_file=sio)
402
self.assertEqualDiff(sio.getvalue(), """\
403
------------------------------------------------------------
405
committer: Joe Foo <joe@foo.com>
407
timestamp: Mon 2005-11-21 09:32:56 -0600
409
single line with trailing newline
410
------------------------------------------------------------
412
committer: Joe Foo <joe@foo.com>
414
timestamp: Mon 2005-11-21 09:27:22 -0600
419
------------------------------------------------------------
421
committer: Joe Foo <joe@foo.com>
423
timestamp: Mon 2005-11-21 09:24:15 -0600
429
class TestLineLogFormatter(TestCaseWithTransport):
431
283
def test_line_log(self):
432
284
"""Line log should show revno
450
302
log_contents = logfile.read()
451
303
self.assertEqualDiff(log_contents, '1: Line-Log-Formatte... 2005-11-23 add a\n')
453
def test_short_log_with_merges(self):
454
wt = self.make_branch_and_memory_tree('.')
458
wt.commit('rev-1', rev_id='rev-1',
459
timestamp=1132586655, timezone=36000,
460
committer='Joe Foo <joe@foo.com>')
461
wt.commit('rev-merged', rev_id='rev-2a',
462
timestamp=1132586700, timezone=36000,
463
committer='Joe Foo <joe@foo.com>')
464
wt.set_parent_ids(['rev-1', 'rev-2a'])
465
wt.branch.set_last_revision_info(1, 'rev-1')
466
wt.commit('rev-2', rev_id='rev-2b',
467
timestamp=1132586800, timezone=36000,
468
committer='Joe Foo <joe@foo.com>')
470
formatter = ShortLogFormatter(to_file=logfile)
471
show_log(wt.branch, formatter)
473
self.assertEqualDiff("""\
474
2 Joe Foo\t2005-11-22 [merge]
477
1 Joe Foo\t2005-11-22
480
""", logfile.getvalue())
484
def test_trailing_newlines(self):
485
wt = self.make_branch_and_tree('.')
486
b = make_commits_with_trailing_newlines(wt)
488
lf = LineLogFormatter(to_file=sio)
490
self.assertEqualDiff(sio.getvalue(), """\
491
3: Joe Foo 2005-11-21 single line with trailing newline
492
2: Joe Foo 2005-11-21 multiline
493
1: Joe Foo 2005-11-21 simple log message
497
class TestGetViewRevisions(TestCaseWithTransport):
499
def make_tree_with_commits(self):
500
"""Create a tree with well-known revision ids"""
501
wt = self.make_branch_and_tree('tree1')
502
wt.commit('commit one', rev_id='1')
503
wt.commit('commit two', rev_id='2')
504
wt.commit('commit three', rev_id='3')
505
mainline_revs = [None, '1', '2', '3']
506
rev_nos = {'1': 1, '2': 2, '3': 3}
507
return mainline_revs, rev_nos, wt
509
def make_tree_with_merges(self):
510
"""Create a tree with well-known revision ids and a merge"""
511
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
512
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
513
tree2.commit('four-a', rev_id='4a')
514
wt.merge_from_branch(tree2.branch)
515
wt.commit('four-b', rev_id='4b')
516
mainline_revs.append('4b')
519
return mainline_revs, rev_nos, wt
521
def make_tree_with_many_merges(self):
522
"""Create a tree with well-known revision ids"""
523
wt = self.make_branch_and_tree('tree1')
524
wt.commit('commit one', rev_id='1')
525
wt.commit('commit two', rev_id='2')
526
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
527
tree3.commit('commit three a', rev_id='3a')
528
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
529
tree2.merge_from_branch(tree3.branch)
530
tree2.commit('commit three b', rev_id='3b')
531
wt.merge_from_branch(tree2.branch)
532
wt.commit('commit three c', rev_id='3c')
533
tree2.commit('four-a', rev_id='4a')
534
wt.merge_from_branch(tree2.branch)
535
wt.commit('four-b', rev_id='4b')
536
mainline_revs = [None, '1', '2', '3c', '4b']
537
rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
538
full_rev_nos_for_reference = {
541
'3a': '2.2.1', #first commit tree 3
542
'3b': '2.1.1', # first commit tree 2
543
'3c': '3', #merges 3b to main
544
'4a': '2.1.2', # second commit tree 2
545
'4b': '4', # merges 4a to main
547
return mainline_revs, rev_nos, wt
549
def test_get_view_revisions_forward(self):
550
"""Test the get_view_revisions method"""
551
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
552
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
554
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
556
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
557
'forward', include_merges=False))
558
self.assertEqual(revisions, revisions2)
560
def test_get_view_revisions_reverse(self):
561
"""Test the get_view_revisions with reverse"""
562
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
563
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
565
self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
567
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
568
'reverse', include_merges=False))
569
self.assertEqual(revisions, revisions2)
571
def test_get_view_revisions_merge(self):
572
"""Test get_view_revisions when there are merges"""
573
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
574
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
576
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
577
('4b', '4', 0), ('4a', '3.1.1', 1)],
579
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
580
'forward', include_merges=False))
581
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
585
def test_get_view_revisions_merge_reverse(self):
586
"""Test get_view_revisions in reverse when there are merges"""
587
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
588
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
590
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
591
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
593
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
594
'reverse', include_merges=False))
595
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
599
def test_get_view_revisions_merge2(self):
600
"""Test get_view_revisions when there are merges"""
601
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
602
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
604
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
605
('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
607
self.assertEqual(expected, revisions)
608
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
609
'forward', include_merges=False))
610
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
615
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
617
def create_tree_with_single_merge(self):
618
"""Create a branch with a moderate layout.
620
The revision graph looks like:
628
In this graph, A introduced files f1 and f2 and f3.
629
B modifies f1 and f3, and C modifies f2 and f3.
630
D merges the changes from B and C and resolves the conflict for f3.
632
# TODO: jam 20070218 This seems like it could really be done
633
# with make_branch_and_memory_tree() if we could just
634
# create the content of those files.
635
# TODO: jam 20070218 Another alternative is that we would really
636
# like to only create this tree 1 time for all tests that
637
# use it. Since 'log' only uses the tree in a readonly
638
# fashion, it seems a shame to regenerate an identical
639
# tree for each test.
640
tree = self.make_branch_and_tree('tree')
642
self.addCleanup(tree.unlock)
644
self.build_tree_contents([('tree/f1', 'A\n'),
648
tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
649
tree.commit('A', rev_id='A')
651
self.build_tree_contents([('tree/f2', 'A\nC\n'),
652
('tree/f3', 'A\nC\n'),
654
tree.commit('C', rev_id='C')
655
# Revert back to A to build the other history.
656
tree.set_last_revision('A')
657
tree.branch.set_last_revision_info(1, 'A')
658
self.build_tree_contents([('tree/f1', 'A\nB\n'),
660
('tree/f3', 'A\nB\n'),
662
tree.commit('B', rev_id='B')
663
tree.set_parent_ids(['B', 'C'])
664
self.build_tree_contents([('tree/f1', 'A\nB\n'),
665
('tree/f2', 'A\nC\n'),
666
('tree/f3', 'A\nB\nC\n'),
668
tree.commit('D', rev_id='D')
670
# Switch to a read lock for this tree.
671
# We still have addCleanup(unlock)
676
def test_tree_with_single_merge(self):
677
"""Make sure the tree layout is correct."""
678
tree = self.create_tree_with_single_merge()
679
rev_A_tree = tree.branch.repository.revision_tree('A')
680
rev_B_tree = tree.branch.repository.revision_tree('B')
682
f1_changed = (u'f1', 'f1-id', 'file', True, False)
683
f2_changed = (u'f2', 'f2-id', 'file', True, False)
684
f3_changed = (u'f3', 'f3-id', 'file', True, False)
686
delta = rev_B_tree.changes_from(rev_A_tree)
687
self.assertEqual([f1_changed, f3_changed], delta.modified)
688
self.assertEqual([], delta.renamed)
689
self.assertEqual([], delta.added)
690
self.assertEqual([], delta.removed)
692
rev_C_tree = tree.branch.repository.revision_tree('C')
693
delta = rev_C_tree.changes_from(rev_A_tree)
694
self.assertEqual([f2_changed, f3_changed], delta.modified)
695
self.assertEqual([], delta.renamed)
696
self.assertEqual([], delta.added)
697
self.assertEqual([], delta.removed)
699
rev_D_tree = tree.branch.repository.revision_tree('D')
700
delta = rev_D_tree.changes_from(rev_B_tree)
701
self.assertEqual([f2_changed, f3_changed], delta.modified)
702
self.assertEqual([], delta.renamed)
703
self.assertEqual([], delta.added)
704
self.assertEqual([], delta.removed)
706
delta = rev_D_tree.changes_from(rev_C_tree)
707
self.assertEqual([f1_changed, f3_changed], delta.modified)
708
self.assertEqual([], delta.renamed)
709
self.assertEqual([], delta.added)
710
self.assertEqual([], delta.removed)
712
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
713
"""Make sure _filter_revisions_touching_file_id returns the right values.
715
Get the return value from _filter_revisions_touching_file_id and make
716
sure they are correct.
718
# The api for _get_revisions_touching_file_id is a little crazy,
719
# So we do the setup here.
720
mainline = tree.branch.revision_history()
721
mainline.insert(0, None)
722
revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
723
view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
725
actual_revs = log._filter_revisions_touching_file_id(
729
list(view_revs_iter))
730
self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
732
def test_file_id_f1(self):
733
tree = self.create_tree_with_single_merge()
734
# f1 should be marked as modified by revisions A and B
735
self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
737
def test_file_id_f2(self):
738
tree = self.create_tree_with_single_merge()
739
# f2 should be marked as modified by revisions A, C, and D
740
# because D merged the changes from C.
741
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
743
def test_file_id_f3(self):
744
tree = self.create_tree_with_single_merge()
745
# f3 should be marked as modified by revisions A, B, C, and D
746
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])