~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Jelmer Vernooij
  • Date: 2011-10-14 12:49:33 UTC
  • mto: This revision was merged to the branch mainline in revision 6216.
  • Revision ID: jelmer@samba.org-20111014124933-732wmdc39ebzga0b
Fix last locking issues.

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
 
120
121
class LogCatcher(log.LogFormatter):
121
122
    """Pull log messages into a list rather than displaying them.
122
123
 
322
323
    1 Joe Foo\t2005-11-22
323
324
      rev-1
324
325
 
325
 
Use --include-merges or -n0 to see merged revisions.
 
326
Use --include-merged or -n0 to see merged revisions.
326
327
""",
327
328
            wt.branch, log.ShortLogFormatter,
328
329
            formatter_kwargs=dict(show_advice=True))
371
372
            wt.branch, log.ShortLogFormatter,
372
373
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
373
374
 
 
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
 
374
401
 
375
402
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
376
403
 
659
686
        self.assertEqualDiff('''custom_prop_name: test_value\n''',
660
687
                             sio.getvalue())
661
688
 
 
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
 
662
730
 
663
731
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
664
732
 
915
983
            wt.branch, log.GnuChangelogLogFormatter,
916
984
            show_log_kwargs=dict(verbose=True))
917
985
 
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
 
 
1261
986
 
1262
987
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1263
988
 
1463
1188
        self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1464
1189
 
1465
1190
 
 
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
 
1466
1309
 
1467
1310
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1468
1311
 
1492
1335
        self.assertFormatterResult("""\
1493
1336
------------------------------------------------------------
1494
1337
revno: 2
1495
 
fixes bug(s): test://bug/id test://bug/2
 
1338
fixes bugs: test://bug/id test://bug/2
1496
1339
author: Joe Bar <joe@bar.com>
1497
1340
committer: Joe Foo <joe@foo.com>
1498
1341
branch nick: work
1503
1346
  message
1504
1347
------------------------------------------------------------
1505
1348
revno: 1
1506
 
fixes bug(s): test://bug/id
 
1349
fixes bug: test://bug/id
1507
1350
committer: Joe Foo <joe@foo.com>
1508
1351
branch nick: work
1509
1352
timestamp: Tue 2005-11-22 00:00:00 +0000
1516
1359
        tree = self.make_commits_with_bugs()
1517
1360
        self.assertFormatterResult("""\
1518
1361
    2 Joe Bar\t2005-11-22
1519
 
      fixes bug(s): test://bug/id test://bug/2
 
1362
      fixes bugs: test://bug/id test://bug/2
1520
1363
      multiline
1521
1364
      log
1522
1365
      message
1523
1366
 
1524
1367
    1 Joe Foo\t2005-11-22
1525
 
      fixes bug(s): test://bug/id
 
1368
      fixes bug: test://bug/id
1526
1369
      simple log message
1527
1370
 
1528
1371
""",
1543
1386
    def test_bugs_handler_present(self):
1544
1387
        self.properties_handler_registry.get('bugs_properties_handler')
1545
1388
 
 
1389
 
 
1390
class TestLogForAuthors(TestCaseForLogFormatter):
 
1391
 
 
1392
    def setUp(self):
 
1393
        TestCaseForLogFormatter.setUp(self)
 
1394
        self.wt = self.make_standard_commit('nicky',
 
1395
            authors=['John Doe <jdoe@example.com>',
 
1396
                     'Jane Rey <jrey@example.com>'])
 
1397
 
 
1398
    def assertFormatterResult(self, formatter, who, result):
 
1399
        formatter_kwargs = dict()
 
1400
        if who is not None:
 
1401
            author_list_handler = log.author_list_registry.get(who)
 
1402
            formatter_kwargs['author_list_handler'] = author_list_handler
 
1403
        TestCaseForLogFormatter.assertFormatterResult(self, result,
 
1404
            self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
 
1405
 
 
1406
    def test_line_default(self):
 
1407
        self.assertFormatterResult(log.LineLogFormatter, None, """\
 
1408
1: John Doe 2005-11-22 add a
 
1409
""")
 
1410
 
 
1411
    def test_line_committer(self):
 
1412
        self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
 
1413
1: Lorem Ipsum 2005-11-22 add a
 
1414
""")
 
1415
 
 
1416
    def test_line_first(self):
 
1417
        self.assertFormatterResult(log.LineLogFormatter, 'first', """\
 
1418
1: John Doe 2005-11-22 add a
 
1419
""")
 
1420
 
 
1421
    def test_line_all(self):
 
1422
        self.assertFormatterResult(log.LineLogFormatter, 'all', """\
 
1423
1: John Doe, Jane Rey 2005-11-22 add a
 
1424
""")
 
1425
 
 
1426
 
 
1427
    def test_short_default(self):
 
1428
        self.assertFormatterResult(log.ShortLogFormatter, None, """\
 
1429
    1 John Doe\t2005-11-22
 
1430
      add a
 
1431
 
 
1432
""")
 
1433
 
 
1434
    def test_short_committer(self):
 
1435
        self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
 
1436
    1 Lorem Ipsum\t2005-11-22
 
1437
      add a
 
1438
 
 
1439
""")
 
1440
 
 
1441
    def test_short_first(self):
 
1442
        self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
 
1443
    1 John Doe\t2005-11-22
 
1444
      add a
 
1445
 
 
1446
""")
 
1447
 
 
1448
    def test_short_all(self):
 
1449
        self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
 
1450
    1 John Doe, Jane Rey\t2005-11-22
 
1451
      add a
 
1452
 
 
1453
""")
 
1454
 
 
1455
    def test_long_default(self):
 
1456
        self.assertFormatterResult(log.LongLogFormatter, None, """\
 
1457
------------------------------------------------------------
 
1458
revno: 1
 
1459
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
1460
committer: Lorem Ipsum <test@example.com>
 
1461
branch nick: nicky
 
1462
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1463
message:
 
1464
  add a
 
1465
""")
 
1466
 
 
1467
    def test_long_committer(self):
 
1468
        self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
 
1469
------------------------------------------------------------
 
1470
revno: 1
 
1471
committer: Lorem Ipsum <test@example.com>
 
1472
branch nick: nicky
 
1473
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1474
message:
 
1475
  add a
 
1476
""")
 
1477
 
 
1478
    def test_long_first(self):
 
1479
        self.assertFormatterResult(log.LongLogFormatter, 'first', """\
 
1480
------------------------------------------------------------
 
1481
revno: 1
 
1482
author: John Doe <jdoe@example.com>
 
1483
committer: Lorem Ipsum <test@example.com>
 
1484
branch nick: nicky
 
1485
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1486
message:
 
1487
  add a
 
1488
""")
 
1489
 
 
1490
    def test_long_all(self):
 
1491
        self.assertFormatterResult(log.LongLogFormatter, 'all', """\
 
1492
------------------------------------------------------------
 
1493
revno: 1
 
1494
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
1495
committer: Lorem Ipsum <test@example.com>
 
1496
branch nick: nicky
 
1497
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1498
message:
 
1499
  add a
 
1500
""")
 
1501
 
 
1502
    def test_gnu_changelog_default(self):
 
1503
        self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
 
1504
2005-11-22  John Doe  <jdoe@example.com>
 
1505
 
 
1506
\tadd a
 
1507
 
 
1508
""")
 
1509
 
 
1510
    def test_gnu_changelog_committer(self):
 
1511
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
 
1512
2005-11-22  Lorem Ipsum  <test@example.com>
 
1513
 
 
1514
\tadd a
 
1515
 
 
1516
""")
 
1517
 
 
1518
    def test_gnu_changelog_first(self):
 
1519
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
 
1520
2005-11-22  John Doe  <jdoe@example.com>
 
1521
 
 
1522
\tadd a
 
1523
 
 
1524
""")
 
1525
 
 
1526
    def test_gnu_changelog_all(self):
 
1527
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
 
1528
2005-11-22  John Doe  <jdoe@example.com>, Jane Rey  <jrey@example.com>
 
1529
 
 
1530
\tadd a
 
1531
 
 
1532
""")
 
1533
 
 
1534
 
1546
1535
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1547
1536
 
1548
1537
    def make_branch_with_alternate_ancestries(self, relpath='.'):
1549
1538
        # See test_merge_sorted_exclude_ancestry below for the difference with
1550
1539
        # bt.per_branch.test_iter_merge_sorted_revision.
1551
 
        # TestIterMergeSortedRevisionsBushyGraph. 
 
1540
        # TestIterMergeSortedRevisionsBushyGraph.
1552
1541
        # make_branch_with_alternate_ancestries
1553
1542
        # and test_merge_sorted_exclude_ancestry
1554
1543
        # See the FIXME in assertLogRevnos too.
1579
1568
        return br
1580
1569
 
1581
1570
    def assertLogRevnos(self, expected_revnos, b, start, end,
1582
 
                        exclude_common_ancestry):
 
1571
                        exclude_common_ancestry, generate_merge_revisions=True):
1583
1572
        # FIXME: the layering in log makes it hard to test intermediate levels,
1584
1573
        # I wish adding filters with their parameters were easier...
1585
1574
        # -- vila 20100413
1586
1575
        iter_revs = log._calc_view_revisions(
1587
1576
            b, start, end, direction='reverse',
1588
 
            generate_merge_revisions=True,
 
1577
            generate_merge_revisions=generate_merge_revisions,
1589
1578
            exclude_common_ancestry=exclude_common_ancestry)
1590
1579
        self.assertEqual(expected_revnos,
1591
1580
                         [revid for revid, revno, depth in iter_revs])
1593
1582
    def test_merge_sorted_exclude_ancestry(self):
1594
1583
        b = self.make_branch_with_alternate_ancestries()
1595
1584
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1596
 
                             b, '1', '3', False)
 
1585
                             b, '1', '3', exclude_common_ancestry=False)
1597
1586
        # '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1598
1587
        # it should be mentioned even if merge_sort order will make it appear
1599
1588
        # after 1.1.1
1600
1589
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1601
 
                             b, '1.1.1', '3', True)
1602
 
 
1603
 
 
 
1590
                             b, '1.1.1', '3', exclude_common_ancestry=True)
 
1591
 
 
1592
    def test_merge_sorted_simple_revnos_exclude_ancestry(self):
 
1593
        b = self.make_branch_with_alternate_ancestries()
 
1594
        self.assertLogRevnos(['3', '2'],
 
1595
                             b, '1', '3', exclude_common_ancestry=True,
 
1596
                             generate_merge_revisions=False)
 
1597
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
 
1598
                             b, '1', '3', exclude_common_ancestry=True,
 
1599
                             generate_merge_revisions=True)
 
1600
 
 
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)