153
184
self.log("escaped commit message: %r", committed_msg)
154
185
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',
170
self.run_bzr('merge ../child')
171
wt.commit('merge child branch')
175
lf.supports_merge_revisions = True
176
show_log(b, lf, verbose=True)
178
logentry = lf.logs[0]
179
eq(logentry.revno, '2')
180
eq(logentry.rev.message, 'merge child branch')
182
self.checkDelta(d, removed=['file1'], modified=['file2'])
183
logentry = lf.logs[1]
184
eq(logentry.revno, '1.1.1')
185
eq(logentry.rev.message, 'remove file1 and modify file2')
187
self.checkDelta(d, removed=['file1'], modified=['file2'])
188
logentry = lf.logs[2]
189
eq(logentry.revno, '1')
190
eq(logentry.rev.message, 'add file1 and file2')
192
self.checkDelta(d, added=['file1', 'file2'])
195
def make_commits_with_trailing_newlines(wt):
196
"""Helper method for LogFormatter tests"""
199
open('a', 'wb').write('hello moto\n')
201
wt.commit('simple log message', rev_id='a1'
202
, timestamp=1132586655.459960938, timezone=-6*3600
203
, committer='Joe Foo <joe@foo.com>')
204
open('b', 'wb').write('goodbye\n')
206
wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
207
, timestamp=1132586842.411175966, timezone=-6*3600
208
, committer='Joe Foo <joe@foo.com>')
210
open('c', 'wb').write('just another manic monday\n')
212
wt.commit('single line with trailing newline\n', rev_id='a3'
213
, timestamp=1132587176.835228920, timezone=-6*3600
214
, committer = 'Joe Foo <joe@foo.com>')
218
class TestShortLogFormatter(TestCaseWithTransport):
220
187
def test_trailing_newlines(self):
221
188
wt = self.make_branch_and_tree('.')
222
b = make_commits_with_trailing_newlines(wt)
191
open('a', 'wb').write('hello moto\n')
193
wt.commit('simple log message', rev_id='a1'
194
, timestamp=1132586655.459960938, timezone=-6*3600
195
, committer='Joe Foo <joe@foo.com>')
196
open('b', 'wb').write('goodbye\n')
198
wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
199
, timestamp=1132586842.411175966, timezone=-6*3600
200
, committer='Joe Foo <joe@foo.com>')
202
open('c', 'wb').write('just another manic monday\n')
204
wt.commit('single line with trailing newline\n', rev_id='a3'
205
, timestamp=1132587176.835228920, timezone=-6*3600
206
, committer = 'Joe Foo <joe@foo.com>')
224
209
lf = ShortLogFormatter(to_file=sio)
289
def test_merges_are_indented_by_level(self):
290
wt = self.make_branch_and_tree('parent')
291
wt.commit('first post')
292
self.run_bzr('branch parent child')
293
self.run_bzr(['commit', '-m', 'branch 1', '--unchanged', 'child'])
294
self.run_bzr('branch child smallerchild')
295
self.run_bzr(['commit', '-m', 'branch 2', '--unchanged',
298
self.run_bzr('merge ../smallerchild')
299
self.run_bzr(['commit', '-m', 'merge branch 2'])
300
os.chdir('../parent')
301
self.run_bzr('merge ../child')
302
wt.commit('merge branch 1')
305
lf = LongLogFormatter(to_file=sio)
306
show_log(b, lf, verbose=True)
307
log = self.normalize_log(sio.getvalue())
308
self.assertEqualDiff("""\
309
------------------------------------------------------------
311
committer: Lorem Ipsum <test@example.com>
316
------------------------------------------------------------
318
committer: Lorem Ipsum <test@example.com>
323
------------------------------------------------------------
325
committer: Lorem Ipsum <test@example.com>
326
branch nick: smallerchild
330
------------------------------------------------------------
332
committer: Lorem Ipsum <test@example.com>
337
------------------------------------------------------------
339
committer: Lorem Ipsum <test@example.com>
346
def test_verbose_merge_revisions_contain_deltas(self):
347
wt = self.make_branch_and_tree('parent')
348
self.build_tree(['parent/f1', 'parent/f2'])
350
wt.commit('first post')
351
self.run_bzr('branch parent child')
352
os.unlink('child/f1')
353
print >> file('child/f2', 'wb'), 'hello'
354
self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
357
self.run_bzr('merge ../child')
358
wt.commit('merge branch 1')
361
lf = LongLogFormatter(to_file=sio)
362
show_log(b, lf, verbose=True)
363
log = self.normalize_log(sio.getvalue())
364
self.assertEqualDiff("""\
365
------------------------------------------------------------
367
committer: Lorem Ipsum <test@example.com>
376
------------------------------------------------------------
378
committer: Lorem Ipsum <test@example.com>
382
removed f1 and modified f2
387
------------------------------------------------------------
389
committer: Lorem Ipsum <test@example.com>
399
def test_trailing_newlines(self):
400
wt = self.make_branch_and_tree('.')
401
b = make_commits_with_trailing_newlines(wt)
403
lf = LongLogFormatter(to_file=sio)
405
self.assertEqualDiff(sio.getvalue(), """\
406
------------------------------------------------------------
408
committer: Joe Foo <joe@foo.com>
410
timestamp: Mon 2005-11-21 09:32:56 -0600
412
single line with trailing newline
413
------------------------------------------------------------
415
committer: Joe Foo <joe@foo.com>
417
timestamp: Mon 2005-11-21 09:27:22 -0600
422
------------------------------------------------------------
424
committer: Joe Foo <joe@foo.com>
426
timestamp: Mon 2005-11-21 09:24:15 -0600
432
class TestLineLogFormatter(TestCaseWithTransport):
434
287
def test_line_log(self):
435
288
"""Line log should show revno
576
376
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
577
377
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
579
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
580
('4b', '4', 0), ('4a', '3.1.1', 1)],
379
self.assertEqual(revisions, [('1', 1, 0), ('2', 2, 0), ('3', 3, 0),
380
('4b', 4, 0), ('4a', None, 1)])
582
381
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
583
382
'forward', include_merges=False))
584
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
383
self.assertEqual(revisions, [('1', 1, 0), ('2', 2, 0), ('3', 3, 0),
588
386
def test_get_view_revisions_merge_reverse(self):
589
387
"""Test get_view_revisions in reverse when there are merges"""
590
388
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
591
389
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
593
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
594
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
391
self.assertEqual(revisions, [('4b', 4, 0), ('4a', None, 1),
392
('3', 3, 0), ('2', 2, 0), ('1', 1, 0)])
596
393
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
597
394
'reverse', include_merges=False))
598
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
395
self.assertEqual(revisions, [('4b', 4, 0), ('3', 3, 0), ('2', 2, 0),
602
398
def test_get_view_revisions_merge2(self):
603
399
"""Test get_view_revisions when there are merges"""
604
400
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
605
401
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
607
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
608
('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
610
self.assertEqual(expected, revisions)
403
expected = [('1', 1, 0), ('2', 2, 0), ('3c', 3, 0), ('3a', None, 1),
404
('3b', None, 1), ('4b', 4, 0), ('4a', None, 1)]
405
self.assertEqual(revisions, expected)
611
406
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
612
407
'forward', include_merges=False))
613
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
618
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
620
def create_tree_with_single_merge(self):
621
"""Create a branch with a moderate layout.
623
The revision graph looks like:
631
In this graph, A introduced files f1 and f2 and f3.
632
B modifies f1 and f3, and C modifies f2 and f3.
633
D merges the changes from B and C and resolves the conflict for f3.
635
# TODO: jam 20070218 This seems like it could really be done
636
# with make_branch_and_memory_tree() if we could just
637
# create the content of those files.
638
# TODO: jam 20070218 Another alternative is that we would really
639
# like to only create this tree 1 time for all tests that
640
# use it. Since 'log' only uses the tree in a readonly
641
# fashion, it seems a shame to regenerate an identical
642
# tree for each test.
643
tree = self.make_branch_and_tree('tree')
645
self.addCleanup(tree.unlock)
647
self.build_tree_contents([('tree/f1', 'A\n'),
651
tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
652
tree.commit('A', rev_id='A')
654
self.build_tree_contents([('tree/f2', 'A\nC\n'),
655
('tree/f3', 'A\nC\n'),
657
tree.commit('C', rev_id='C')
658
# Revert back to A to build the other history.
659
tree.set_last_revision('A')
660
tree.branch.set_last_revision_info(1, 'A')
661
self.build_tree_contents([('tree/f1', 'A\nB\n'),
663
('tree/f3', 'A\nB\n'),
665
tree.commit('B', rev_id='B')
666
tree.set_parent_ids(['B', 'C'])
667
self.build_tree_contents([('tree/f1', 'A\nB\n'),
668
('tree/f2', 'A\nC\n'),
669
('tree/f3', 'A\nB\nC\n'),
671
tree.commit('D', rev_id='D')
673
# Switch to a read lock for this tree.
674
# We still have addCleanup(unlock)
679
def test_tree_with_single_merge(self):
680
"""Make sure the tree layout is correct."""
681
tree = self.create_tree_with_single_merge()
682
rev_A_tree = tree.branch.repository.revision_tree('A')
683
rev_B_tree = tree.branch.repository.revision_tree('B')
685
f1_changed = (u'f1', 'f1-id', 'file', True, False)
686
f2_changed = (u'f2', 'f2-id', 'file', True, False)
687
f3_changed = (u'f3', 'f3-id', 'file', True, False)
689
delta = rev_B_tree.changes_from(rev_A_tree)
690
self.assertEqual([f1_changed, f3_changed], delta.modified)
691
self.assertEqual([], delta.renamed)
692
self.assertEqual([], delta.added)
693
self.assertEqual([], delta.removed)
695
rev_C_tree = tree.branch.repository.revision_tree('C')
696
delta = rev_C_tree.changes_from(rev_A_tree)
697
self.assertEqual([f2_changed, f3_changed], delta.modified)
698
self.assertEqual([], delta.renamed)
699
self.assertEqual([], delta.added)
700
self.assertEqual([], delta.removed)
702
rev_D_tree = tree.branch.repository.revision_tree('D')
703
delta = rev_D_tree.changes_from(rev_B_tree)
704
self.assertEqual([f2_changed, f3_changed], delta.modified)
705
self.assertEqual([], delta.renamed)
706
self.assertEqual([], delta.added)
707
self.assertEqual([], delta.removed)
709
delta = rev_D_tree.changes_from(rev_C_tree)
710
self.assertEqual([f1_changed, f3_changed], delta.modified)
711
self.assertEqual([], delta.renamed)
712
self.assertEqual([], delta.added)
713
self.assertEqual([], delta.removed)
715
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
716
"""Make sure _filter_revisions_touching_file_id returns the right values.
718
Get the return value from _filter_revisions_touching_file_id and make
719
sure they are correct.
721
# The api for _get_revisions_touching_file_id is a little crazy,
722
# So we do the setup here.
723
mainline = tree.branch.revision_history()
724
mainline.insert(0, None)
725
revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
726
view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
728
actual_revs = log._filter_revisions_touching_file_id(
732
list(view_revs_iter))
733
self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
735
def test_file_id_f1(self):
736
tree = self.create_tree_with_single_merge()
737
# f1 should be marked as modified by revisions A and B
738
self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
740
def test_file_id_f2(self):
741
tree = self.create_tree_with_single_merge()
742
# f2 should be marked as modified by revisions A, C, and D
743
# because D merged the changes from C.
744
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
746
def test_file_id_f3(self):
747
tree = self.create_tree_with_single_merge()
748
# f3 should be marked as modified by revisions A, B, C, and D
749
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
408
self.assertEqual(revisions, [('1', 1, 0), ('2', 2, 0), ('3c', 3, 0),