~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

(gz) Fix test failure on alpha by correcting format string for
 gc_chk_sha1_record (Martin [gz])

Show diffs side-by-side

added added

removed removed

Lines of Context:
117
117
            branch.tags.set_tag('v1.0', 'rev-3')
118
118
        return wt
119
119
 
120
 
 
121
120
class LogCatcher(log.LogFormatter):
122
121
    """Pull log messages into a list rather than displaying them.
123
122
 
323
322
    1 Joe Foo\t2005-11-22
324
323
      rev-1
325
324
 
326
 
Use --include-merged or -n0 to see merged revisions.
 
325
Use --include-merges or -n0 to see merged revisions.
327
326
""",
328
327
            wt.branch, log.ShortLogFormatter,
329
328
            formatter_kwargs=dict(show_advice=True))
372
371
            wt.branch, log.ShortLogFormatter,
373
372
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
374
373
 
375
 
    def test_show_ids(self):
376
 
        wt = self.make_branch_and_tree('parent')
377
 
        self.build_tree(['parent/f1', 'parent/f2'])
378
 
        wt.add(['f1','f2'])
379
 
        self.wt_commit(wt, 'first post', rev_id='a')
380
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
381
 
        self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
382
 
        wt.merge_from_branch(child_wt.branch)
383
 
        self.wt_commit(wt, 'merge branch 1', rev_id='c')
384
 
        self.assertFormatterResult("""\
385
 
    2 Joe Foo\t2005-11-22 [merge]
386
 
      revision-id:c
387
 
      merge branch 1
388
 
 
389
 
          1.1.1 Joe Foo\t2005-11-22
390
 
                revision-id:b
391
 
                branch 1 changes
392
 
 
393
 
    1 Joe Foo\t2005-11-22
394
 
      revision-id:a
395
 
      first post
396
 
 
397
 
""",
398
 
            wt.branch, log.ShortLogFormatter,
399
 
            formatter_kwargs=dict(levels=0,show_ids=True))
400
 
 
401
374
 
402
375
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
403
376
 
686
659
        self.assertEqualDiff('''custom_prop_name: test_value\n''',
687
660
                             sio.getvalue())
688
661
 
689
 
    def test_show_ids(self):
690
 
        wt = self.make_branch_and_tree('parent')
691
 
        self.build_tree(['parent/f1', 'parent/f2'])
692
 
        wt.add(['f1','f2'])
693
 
        self.wt_commit(wt, 'first post', rev_id='a')
694
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
695
 
        self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
696
 
        wt.merge_from_branch(child_wt.branch)
697
 
        self.wt_commit(wt, 'merge branch 1', rev_id='c')
698
 
        self.assertFormatterResult("""\
699
 
------------------------------------------------------------
700
 
revno: 2 [merge]
701
 
revision-id: c
702
 
parent: a
703
 
parent: b
704
 
committer: Joe Foo <joe@foo.com>
705
 
branch nick: parent
706
 
timestamp: Tue 2005-11-22 00:00:02 +0000
707
 
message:
708
 
  merge branch 1
709
 
    ------------------------------------------------------------
710
 
    revno: 1.1.1
711
 
    revision-id: b
712
 
    parent: a
713
 
    committer: Joe Foo <joe@foo.com>
714
 
    branch nick: child
715
 
    timestamp: Tue 2005-11-22 00:00:01 +0000
716
 
    message:
717
 
      branch 1 changes
718
 
------------------------------------------------------------
719
 
revno: 1
720
 
revision-id: a
721
 
committer: Joe Foo <joe@foo.com>
722
 
branch nick: parent
723
 
timestamp: Tue 2005-11-22 00:00:00 +0000
724
 
message:
725
 
  first post
726
 
""",
727
 
            wt.branch, log.LongLogFormatter,
728
 
            formatter_kwargs=dict(levels=0,show_ids=True))
729
 
 
730
662
 
731
663
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
732
664
 
983
915
            wt.branch, log.GnuChangelogLogFormatter,
984
916
            show_log_kwargs=dict(verbose=True))
985
917
 
 
918
class TestGetViewRevisions(tests.TestCaseWithTransport, TestLogMixin):
 
919
 
 
920
    def _get_view_revisions(self, *args, **kwargs):
 
921
        return self.applyDeprecated(symbol_versioning.deprecated_in((2, 2, 0)),
 
922
                                    log.get_view_revisions, *args, **kwargs)
 
923
 
 
924
    def make_tree_with_commits(self):
 
925
        """Create a tree with well-known revision ids"""
 
926
        wt = self.make_branch_and_tree('tree1')
 
927
        self.wt_commit(wt, 'commit one', rev_id='1')
 
928
        self.wt_commit(wt, 'commit two', rev_id='2')
 
929
        self.wt_commit(wt, 'commit three', rev_id='3')
 
930
        mainline_revs = [None, '1', '2', '3']
 
931
        rev_nos = {'1': 1, '2': 2, '3': 3}
 
932
        return mainline_revs, rev_nos, wt
 
933
 
 
934
    def make_tree_with_merges(self):
 
935
        """Create a tree with well-known revision ids and a merge"""
 
936
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
937
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
 
938
        self.wt_commit(tree2, 'four-a', rev_id='4a')
 
939
        wt.merge_from_branch(tree2.branch)
 
940
        self.wt_commit(wt, 'four-b', rev_id='4b')
 
941
        mainline_revs.append('4b')
 
942
        rev_nos['4b'] = 4
 
943
        # 4a: 3.1.1
 
944
        return mainline_revs, rev_nos, wt
 
945
 
 
946
    def make_branch_with_many_merges(self):
 
947
        """Create a tree with well-known revision ids"""
 
948
        builder = self.make_branch_builder('tree1')
 
949
        builder.start_series()
 
950
        builder.build_snapshot('1', None, [
 
951
            ('add', ('', 'TREE_ROOT', 'directory', '')),
 
952
            ('add', ('f', 'f-id', 'file', '1\n'))])
 
953
        builder.build_snapshot('2', ['1'], [])
 
954
        builder.build_snapshot('3a', ['2'], [
 
955
            ('modify', ('f-id', '1\n2\n3a\n'))])
 
956
        builder.build_snapshot('3b', ['2', '3a'], [
 
957
            ('modify', ('f-id', '1\n2\n3a\n'))])
 
958
        builder.build_snapshot('3c', ['2', '3b'], [
 
959
            ('modify', ('f-id', '1\n2\n3a\n'))])
 
960
        builder.build_snapshot('4a', ['3b'], [])
 
961
        builder.build_snapshot('4b', ['3c', '4a'], [])
 
962
        builder.finish_series()
 
963
 
 
964
        # 1
 
965
        # |
 
966
        # 2-.
 
967
        # |\ \
 
968
        # | | 3a
 
969
        # | |/
 
970
        # | 3b
 
971
        # |/|
 
972
        # 3c4a
 
973
        # |/
 
974
        # 4b
 
975
 
 
976
        mainline_revs = [None, '1', '2', '3c', '4b']
 
977
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
 
978
        full_rev_nos_for_reference = {
 
979
            '1': '1',
 
980
            '2': '2',
 
981
            '3a': '2.1.1', #first commit tree 3
 
982
            '3b': '2.2.1', # first commit tree 2
 
983
            '3c': '3', #merges 3b to main
 
984
            '4a': '2.2.2', # second commit tree 2
 
985
            '4b': '4', # merges 4a to main
 
986
            }
 
987
        return mainline_revs, rev_nos, builder.get_branch()
 
988
 
 
989
    def test_get_view_revisions_forward(self):
 
990
        """Test the get_view_revisions method"""
 
991
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
992
        wt.lock_read()
 
993
        self.addCleanup(wt.unlock)
 
994
        revisions = list(self._get_view_revisions(
 
995
                mainline_revs, rev_nos, wt.branch, 'forward'))
 
996
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
 
997
                         revisions)
 
998
        revisions2 = list(self._get_view_revisions(
 
999
                mainline_revs, rev_nos, wt.branch, 'forward',
 
1000
                include_merges=False))
 
1001
        self.assertEqual(revisions, revisions2)
 
1002
 
 
1003
    def test_get_view_revisions_reverse(self):
 
1004
        """Test the get_view_revisions with reverse"""
 
1005
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
1006
        wt.lock_read()
 
1007
        self.addCleanup(wt.unlock)
 
1008
        revisions = list(self._get_view_revisions(
 
1009
                mainline_revs, rev_nos, wt.branch, 'reverse'))
 
1010
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
 
1011
                         revisions)
 
1012
        revisions2 = list(self._get_view_revisions(
 
1013
                mainline_revs, rev_nos, wt.branch, 'reverse',
 
1014
                include_merges=False))
 
1015
        self.assertEqual(revisions, revisions2)
 
1016
 
 
1017
    def test_get_view_revisions_merge(self):
 
1018
        """Test get_view_revisions when there are merges"""
 
1019
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
 
1020
        wt.lock_read()
 
1021
        self.addCleanup(wt.unlock)
 
1022
        revisions = list(self._get_view_revisions(
 
1023
                mainline_revs, rev_nos, wt.branch, 'forward'))
 
1024
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
1025
                          ('4b', '4', 0), ('4a', '3.1.1', 1)],
 
1026
                         revisions)
 
1027
        revisions = list(self._get_view_revisions(
 
1028
                mainline_revs, rev_nos, wt.branch, 'forward',
 
1029
                include_merges=False))
 
1030
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
1031
                          ('4b', '4', 0)],
 
1032
                         revisions)
 
1033
 
 
1034
    def test_get_view_revisions_merge_reverse(self):
 
1035
        """Test get_view_revisions in reverse when there are merges"""
 
1036
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
 
1037
        wt.lock_read()
 
1038
        self.addCleanup(wt.unlock)
 
1039
        revisions = list(self._get_view_revisions(
 
1040
                mainline_revs, rev_nos, wt.branch, 'reverse'))
 
1041
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
 
1042
                          ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
 
1043
                         revisions)
 
1044
        revisions = list(self._get_view_revisions(
 
1045
                mainline_revs, rev_nos, wt.branch, 'reverse',
 
1046
                include_merges=False))
 
1047
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
 
1048
                          ('1', '1', 0)],
 
1049
                         revisions)
 
1050
 
 
1051
    def test_get_view_revisions_merge2(self):
 
1052
        """Test get_view_revisions when there are merges"""
 
1053
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
 
1054
        b.lock_read()
 
1055
        self.addCleanup(b.unlock)
 
1056
        revisions = list(self._get_view_revisions(
 
1057
                mainline_revs, rev_nos, b, 'forward'))
 
1058
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
 
1059
                    ('3b', '2.2.1', 1), ('3a', '2.1.1', 2), ('4b', '4', 0),
 
1060
                    ('4a', '2.2.2', 1)]
 
1061
        self.assertEqual(expected, revisions)
 
1062
        revisions = list(self._get_view_revisions(
 
1063
                mainline_revs, rev_nos, b, 'forward',
 
1064
                include_merges=False))
 
1065
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
 
1066
                          ('4b', '4', 0)],
 
1067
                         revisions)
 
1068
 
 
1069
    def test_file_id_for_range(self):
 
1070
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
 
1071
        b.lock_read()
 
1072
        self.addCleanup(b.unlock)
 
1073
 
 
1074
        def rev_from_rev_id(revid, branch):
 
1075
            revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
 
1076
            return revspec.in_history(branch)
 
1077
 
 
1078
        def view_revs(start_rev, end_rev, file_id, direction):
 
1079
            revs = self.applyDeprecated(
 
1080
                symbol_versioning.deprecated_in((2, 2, 0)),
 
1081
                log.calculate_view_revisions,
 
1082
                b,
 
1083
                start_rev, # start_revision
 
1084
                end_rev, # end_revision
 
1085
                direction, # direction
 
1086
                file_id, # specific_fileid
 
1087
                True, # generate_merge_revisions
 
1088
                )
 
1089
            return revs
 
1090
 
 
1091
        rev_3a = rev_from_rev_id('3a', b)
 
1092
        rev_4b = rev_from_rev_id('4b', b)
 
1093
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1),
 
1094
                          ('3a', '2.1.1', 2)],
 
1095
                          view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
 
1096
        # Note: 3c still appears before 3a here because of depth-based sorting
 
1097
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1),
 
1098
                          ('3a', '2.1.1', 2)],
 
1099
                          view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
 
1100
 
 
1101
 
 
1102
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
 
1103
 
 
1104
    def get_view_revisions(self, *args):
 
1105
        return self.applyDeprecated(symbol_versioning.deprecated_in((2, 2, 0)),
 
1106
                                    log.get_view_revisions, *args)
 
1107
 
 
1108
    def create_tree_with_single_merge(self):
 
1109
        """Create a branch with a moderate layout.
 
1110
 
 
1111
        The revision graph looks like:
 
1112
 
 
1113
           A
 
1114
           |\
 
1115
           B C
 
1116
           |/
 
1117
           D
 
1118
 
 
1119
        In this graph, A introduced files f1 and f2 and f3.
 
1120
        B modifies f1 and f3, and C modifies f2 and f3.
 
1121
        D merges the changes from B and C and resolves the conflict for f3.
 
1122
        """
 
1123
        # TODO: jam 20070218 This seems like it could really be done
 
1124
        #       with make_branch_and_memory_tree() if we could just
 
1125
        #       create the content of those files.
 
1126
        # TODO: jam 20070218 Another alternative is that we would really
 
1127
        #       like to only create this tree 1 time for all tests that
 
1128
        #       use it. Since 'log' only uses the tree in a readonly
 
1129
        #       fashion, it seems a shame to regenerate an identical
 
1130
        #       tree for each test.
 
1131
        # TODO: vila 20100122 One way to address the shame above will be to
 
1132
        #       create a memory tree during test parametrization and give a
 
1133
        #       *copy* of this tree to each test. Copying a memory tree ought
 
1134
        #       to be cheap, at least cheaper than creating them with such
 
1135
        #       complex setups.
 
1136
        tree = self.make_branch_and_tree('tree')
 
1137
        tree.lock_write()
 
1138
        self.addCleanup(tree.unlock)
 
1139
 
 
1140
        self.build_tree_contents([('tree/f1', 'A\n'),
 
1141
                                  ('tree/f2', 'A\n'),
 
1142
                                  ('tree/f3', 'A\n'),
 
1143
                                 ])
 
1144
        tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
 
1145
        tree.commit('A', rev_id='A')
 
1146
 
 
1147
        self.build_tree_contents([('tree/f2', 'A\nC\n'),
 
1148
                                  ('tree/f3', 'A\nC\n'),
 
1149
                                 ])
 
1150
        tree.commit('C', rev_id='C')
 
1151
        # Revert back to A to build the other history.
 
1152
        tree.set_last_revision('A')
 
1153
        tree.branch.set_last_revision_info(1, 'A')
 
1154
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
 
1155
                                  ('tree/f2', 'A\n'),
 
1156
                                  ('tree/f3', 'A\nB\n'),
 
1157
                                 ])
 
1158
        tree.commit('B', rev_id='B')
 
1159
        tree.set_parent_ids(['B', 'C'])
 
1160
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
 
1161
                                  ('tree/f2', 'A\nC\n'),
 
1162
                                  ('tree/f3', 'A\nB\nC\n'),
 
1163
                                 ])
 
1164
        tree.commit('D', rev_id='D')
 
1165
 
 
1166
        # Switch to a read lock for this tree.
 
1167
        # We still have an addCleanup(tree.unlock) pending
 
1168
        tree.unlock()
 
1169
        tree.lock_read()
 
1170
        return tree
 
1171
 
 
1172
    def check_delta(self, delta, **kw):
 
1173
        """Check the filenames touched by a delta are as expected.
 
1174
 
 
1175
        Caller only have to pass in the list of files for each part, all
 
1176
        unspecified parts are considered empty (and checked as such).
 
1177
        """
 
1178
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
 
1179
            # By default we expect an empty list
 
1180
            expected = kw.get(n, [])
 
1181
            # strip out only the path components
 
1182
            got = [x[0] for x in getattr(delta, n)]
 
1183
            self.assertEqual(expected, got)
 
1184
 
 
1185
    def test_tree_with_single_merge(self):
 
1186
        """Make sure the tree layout is correct."""
 
1187
        tree = self.create_tree_with_single_merge()
 
1188
        rev_A_tree = tree.branch.repository.revision_tree('A')
 
1189
        rev_B_tree = tree.branch.repository.revision_tree('B')
 
1190
        rev_C_tree = tree.branch.repository.revision_tree('C')
 
1191
        rev_D_tree = tree.branch.repository.revision_tree('D')
 
1192
 
 
1193
        self.check_delta(rev_B_tree.changes_from(rev_A_tree),
 
1194
                         modified=['f1', 'f3'])
 
1195
 
 
1196
        self.check_delta(rev_C_tree.changes_from(rev_A_tree),
 
1197
                         modified=['f2', 'f3'])
 
1198
 
 
1199
        self.check_delta(rev_D_tree.changes_from(rev_B_tree),
 
1200
                         modified=['f2', 'f3'])
 
1201
 
 
1202
        self.check_delta(rev_D_tree.changes_from(rev_C_tree),
 
1203
                         modified=['f1', 'f3'])
 
1204
 
 
1205
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
 
1206
        """Ensure _filter_revisions_touching_file_id returns the right values.
 
1207
 
 
1208
        Get the return value from _filter_revisions_touching_file_id and make
 
1209
        sure they are correct.
 
1210
        """
 
1211
        # The api for _filter_revisions_touching_file_id is a little crazy.
 
1212
        # So we do the setup here.
 
1213
        mainline = tree.branch.revision_history()
 
1214
        mainline.insert(0, None)
 
1215
        revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
 
1216
        view_revs_iter = self.get_view_revisions(
 
1217
            mainline, revnos, tree.branch, 'reverse', True)
 
1218
        actual_revs = log._filter_revisions_touching_file_id(
 
1219
            tree.branch, file_id, list(view_revs_iter))
 
1220
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
 
1221
 
 
1222
    def test_file_id_f1(self):
 
1223
        tree = self.create_tree_with_single_merge()
 
1224
        # f1 should be marked as modified by revisions A and B
 
1225
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
 
1226
 
 
1227
    def test_file_id_f2(self):
 
1228
        tree = self.create_tree_with_single_merge()
 
1229
        # f2 should be marked as modified by revisions A, C, and D
 
1230
        # because D merged the changes from C.
 
1231
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
 
1232
 
 
1233
    def test_file_id_f3(self):
 
1234
        tree = self.create_tree_with_single_merge()
 
1235
        # f3 should be marked as modified by revisions A, B, C, and D
 
1236
        self.assertAllRevisionsForFileID(tree, 'f3-id', ['D', 'C', 'B', 'A'])
 
1237
 
 
1238
    def test_file_id_with_ghosts(self):
 
1239
        # This is testing bug #209948, where having a ghost would cause
 
1240
        # _filter_revisions_touching_file_id() to fail.
 
1241
        tree = self.create_tree_with_single_merge()
 
1242
        # We need to add a revision, so switch back to a write-locked tree
 
1243
        # (still a single addCleanup(tree.unlock) pending).
 
1244
        tree.unlock()
 
1245
        tree.lock_write()
 
1246
        first_parent = tree.last_revision()
 
1247
        tree.set_parent_ids([first_parent, 'ghost-revision-id'])
 
1248
        self.build_tree_contents([('tree/f1', 'A\nB\nXX\n')])
 
1249
        tree.commit('commit with a ghost', rev_id='XX')
 
1250
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['XX', 'B', 'A'])
 
1251
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
 
1252
 
 
1253
    def test_unknown_file_id(self):
 
1254
        tree = self.create_tree_with_single_merge()
 
1255
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
 
1256
 
 
1257
    def test_empty_branch_unknown_file_id(self):
 
1258
        tree = self.make_branch_and_tree('tree')
 
1259
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
 
1260
 
986
1261
 
987
1262
class TestShowChangedRevisions(tests.TestCaseWithTransport):
988
1263
 
1188
1463
        self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1189
1464
 
1190
1465
 
1191
 
class TestRevisionNotInBranch(TestCaseForLogFormatter):
1192
 
 
1193
 
    def setup_a_tree(self):
1194
 
        tree = self.make_branch_and_tree('tree')
1195
 
        tree.lock_write()
1196
 
        self.addCleanup(tree.unlock)
1197
 
        kwargs = {
1198
 
            'committer': 'Joe Foo <joe@foo.com>',
1199
 
            'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1200
 
            'timezone': 0, # UTC
1201
 
        }
1202
 
        tree.commit('commit 1a', rev_id='1a', **kwargs)
1203
 
        tree.commit('commit 2a', rev_id='2a', **kwargs)
1204
 
        tree.commit('commit 3a', rev_id='3a', **kwargs)
1205
 
        return tree
1206
 
 
1207
 
    def setup_ab_tree(self):
1208
 
        tree = self.setup_a_tree()
1209
 
        tree.set_last_revision('1a')
1210
 
        tree.branch.set_last_revision_info(1, '1a')
1211
 
        kwargs = {
1212
 
            'committer': 'Joe Foo <joe@foo.com>',
1213
 
            'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1214
 
            'timezone': 0, # UTC
1215
 
        }
1216
 
        tree.commit('commit 2b', rev_id='2b', **kwargs)
1217
 
        tree.commit('commit 3b', rev_id='3b', **kwargs)
1218
 
        return tree
1219
 
 
1220
 
    def test_one_revision(self):
1221
 
        tree = self.setup_ab_tree()
1222
 
        lf = LogCatcher()
1223
 
        rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1224
 
        log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
1225
 
                     end_revision=rev)
1226
 
        self.assertEqual(1, len(lf.revisions))
1227
 
        self.assertEqual(None, lf.revisions[0].revno)   # Out-of-branch
1228
 
        self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1229
 
 
1230
 
    def test_many_revisions(self):
1231
 
        tree = self.setup_ab_tree()
1232
 
        lf = LogCatcher()
1233
 
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1234
 
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1235
 
        log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
1236
 
                     end_revision=end_rev)
1237
 
        self.assertEqual(3, len(lf.revisions))
1238
 
        self.assertEqual(None, lf.revisions[0].revno)   # Out-of-branch
1239
 
        self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1240
 
        self.assertEqual(None, lf.revisions[1].revno)   # Out-of-branch
1241
 
        self.assertEqual('2a', lf.revisions[1].rev.revision_id)
1242
 
        self.assertEqual('1', lf.revisions[2].revno)    # In-branch
1243
 
 
1244
 
    def test_long_format(self):
1245
 
        tree = self.setup_ab_tree()
1246
 
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1247
 
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1248
 
        self.assertFormatterResult("""\
1249
 
------------------------------------------------------------
1250
 
revision-id: 3a
1251
 
committer: Joe Foo <joe@foo.com>
1252
 
branch nick: tree
1253
 
timestamp: Tue 2005-11-22 00:00:00 +0000
1254
 
message:
1255
 
  commit 3a
1256
 
------------------------------------------------------------
1257
 
revision-id: 2a
1258
 
committer: Joe Foo <joe@foo.com>
1259
 
branch nick: tree
1260
 
timestamp: Tue 2005-11-22 00:00:00 +0000
1261
 
message:
1262
 
  commit 2a
1263
 
------------------------------------------------------------
1264
 
revno: 1
1265
 
committer: Joe Foo <joe@foo.com>
1266
 
branch nick: tree
1267
 
timestamp: Tue 2005-11-22 00:00:00 +0000
1268
 
message:
1269
 
  commit 1a
1270
 
""",
1271
 
            tree.branch, log.LongLogFormatter, show_log_kwargs={
1272
 
                'start_revision': start_rev, 'end_revision': end_rev
1273
 
            })
1274
 
 
1275
 
    def test_short_format(self):
1276
 
        tree = self.setup_ab_tree()
1277
 
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1278
 
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1279
 
        self.assertFormatterResult("""\
1280
 
      Joe Foo\t2005-11-22
1281
 
      revision-id:3a
1282
 
      commit 3a
1283
 
 
1284
 
      Joe Foo\t2005-11-22
1285
 
      revision-id:2a
1286
 
      commit 2a
1287
 
 
1288
 
    1 Joe Foo\t2005-11-22
1289
 
      commit 1a
1290
 
 
1291
 
""",
1292
 
            tree.branch, log.ShortLogFormatter, show_log_kwargs={
1293
 
                'start_revision': start_rev, 'end_revision': end_rev
1294
 
            })
1295
 
 
1296
 
    def test_line_format(self):
1297
 
        tree = self.setup_ab_tree()
1298
 
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1299
 
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1300
 
        self.assertFormatterResult("""\
1301
 
Joe Foo 2005-11-22 commit 3a
1302
 
Joe Foo 2005-11-22 commit 2a
1303
 
1: Joe Foo 2005-11-22 commit 1a
1304
 
""",
1305
 
            tree.branch, log.LineLogFormatter, show_log_kwargs={
1306
 
                'start_revision': start_rev, 'end_revision': end_rev
1307
 
            })
1308
 
 
1309
1466
 
1310
1467
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1311
1468
 
1335
1492
        self.assertFormatterResult("""\
1336
1493
------------------------------------------------------------
1337
1494
revno: 2
1338
 
fixes bugs: test://bug/id test://bug/2
 
1495
fixes bug(s): test://bug/id test://bug/2
1339
1496
author: Joe Bar <joe@bar.com>
1340
1497
committer: Joe Foo <joe@foo.com>
1341
1498
branch nick: work
1346
1503
  message
1347
1504
------------------------------------------------------------
1348
1505
revno: 1
1349
 
fixes bug: test://bug/id
 
1506
fixes bug(s): test://bug/id
1350
1507
committer: Joe Foo <joe@foo.com>
1351
1508
branch nick: work
1352
1509
timestamp: Tue 2005-11-22 00:00:00 +0000
1359
1516
        tree = self.make_commits_with_bugs()
1360
1517
        self.assertFormatterResult("""\
1361
1518
    2 Joe Bar\t2005-11-22
1362
 
      fixes bugs: test://bug/id test://bug/2
 
1519
      fixes bug(s): test://bug/id test://bug/2
1363
1520
      multiline
1364
1521
      log
1365
1522
      message
1366
1523
 
1367
1524
    1 Joe Foo\t2005-11-22
1368
 
      fixes bug: test://bug/id
 
1525
      fixes bug(s): test://bug/id
1369
1526
      simple log message
1370
1527
 
1371
1528
""",
1531
1688
 
1532
1689
""")
1533
1690
 
1534
 
 
1535
1691
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1536
1692
 
1537
1693
    def make_branch_with_alternate_ancestries(self, relpath='.'):
1538
1694
        # See test_merge_sorted_exclude_ancestry below for the difference with
1539
1695
        # bt.per_branch.test_iter_merge_sorted_revision.
1540
 
        # TestIterMergeSortedRevisionsBushyGraph.
 
1696
        # TestIterMergeSortedRevisionsBushyGraph. 
1541
1697
        # make_branch_with_alternate_ancestries
1542
1698
        # and test_merge_sorted_exclude_ancestry
1543
1699
        # See the FIXME in assertLogRevnos too.
1598
1754
                             b, '1', '3', exclude_common_ancestry=True,
1599
1755
                             generate_merge_revisions=True)
1600
1756
 
1601
 
 
1602
 
class TestLogDefaults(TestCaseForLogFormatter):
1603
 
    def test_default_log_level(self):
1604
 
        """
1605
 
        Test to ensure that specifying 'levels=1' to make_log_request_dict
1606
 
        doesn't get overwritten when using a LogFormatter that supports more
1607
 
        detail.
1608
 
        Fixes bug #747958.
1609
 
        """
1610
 
        wt = self._prepare_tree_with_merges()
1611
 
        b = wt.branch
1612
 
 
1613
 
        class CustomLogFormatter(log.LogFormatter):
1614
 
            def __init__(self, *args, **kwargs):
1615
 
                super(CustomLogFormatter, self).__init__(*args, **kwargs)
1616
 
                self.revisions = []
1617
 
            def get_levels(self):
1618
 
                # log formatter supports all levels:
1619
 
                return 0
1620
 
            def log_revision(self, revision):
1621
 
                self.revisions.append(revision)
1622
 
 
1623
 
        log_formatter = LogCatcher()
1624
 
        # First request we don't specify number of levels, we should get a
1625
 
        # sensible default (whatever the LogFormatter handles - which in this
1626
 
        # case is 0/everything):
1627
 
        request = log.make_log_request_dict(limit=10)
1628
 
        log.Logger(b, request).show(log_formatter)
1629
 
        # should have all three revisions:
1630
 
        self.assertEquals(len(log_formatter.revisions), 3)
1631
 
 
1632
 
        del log_formatter
1633
 
        log_formatter = LogCatcher()
1634
 
        # now explicitly request mainline revisions only:
1635
 
        request = log.make_log_request_dict(limit=10, levels=1)
1636
 
        log.Logger(b, request).show(log_formatter)
1637
 
        # should now only have 2 revisions:
1638
 
        self.assertEquals(len(log_formatter.revisions), 2)