~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_log.py

  • Committer: Ian Clatworthy
  • Date: 2009-09-09 15:30:59 UTC
  • mto: (4634.37.2 prepare-2.0)
  • mto: This revision was merged to the branch mainline in revision 4689.
  • Revision ID: ian.clatworthy@canonical.com-20090909153059-sb038agvd38ci2q8
more link fixes in the User Guide

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2012, 2016 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
 
18
18
"""Black-box tests for bzr log."""
19
19
 
20
 
from itertools import izip
21
20
import os
 
21
import re
22
22
 
23
23
from bzrlib import (
24
 
    branchbuilder,
25
 
    errors,
26
 
    log,
27
24
    osutils,
28
25
    tests,
29
26
    )
30
 
from bzrlib.tests import (
31
 
    test_log,
32
 
    features,
33
 
    )
34
 
from bzrlib.tests.matchers import ContainsNoVfsCalls
35
 
 
36
 
 
37
 
class TestLog(tests.TestCaseWithTransport, test_log.TestLogMixin):
 
27
from bzrlib.tests import test_log
 
28
 
 
29
 
 
30
class TestLog(tests.TestCaseWithTransport):
 
31
 
 
32
    def setUp(self):
 
33
        super(TestLog, self).setUp()
 
34
        self.timezone = 0 # UTC
 
35
        self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
38
36
 
39
37
    def make_minimal_branch(self, path='.', format=None):
40
38
        tree = self.make_branch_and_tree(path, format=format)
65
63
        tree.commit(message='merge')
66
64
        return tree
67
65
 
68
 
 
69
 
class TestLogWithLogCatcher(TestLog):
70
 
 
71
 
    def setUp(self):
72
 
        super(TestLogWithLogCatcher, self).setUp()
73
 
        # Capture log formatter creations
74
 
        class MyLogFormatter(test_log.LogCatcher):
75
 
 
76
 
            def __new__(klass, *args, **kwargs):
77
 
                self.log_catcher = test_log.LogCatcher(*args, **kwargs)
78
 
                # Always return our own log formatter
79
 
                return self.log_catcher
80
 
        # Break cycle with closure over self on cleanup by removing method
81
 
        self.addCleanup(setattr, MyLogFormatter, "__new__", None)
82
 
 
83
 
        def getme(branch):
84
 
                # Always return our own log formatter class hijacking the
85
 
                # default behavior (which requires setting up a config
86
 
                # variable)
87
 
            return MyLogFormatter
88
 
        self.overrideAttr(log.log_formatter_registry, 'get_default', getme)
89
 
 
90
 
    def get_captured_revisions(self):
91
 
        return self.log_catcher.revisions
92
 
 
93
 
    def assertLogRevnos(self, args, expected_revnos, working_dir='.',
94
 
                        out='', err=''):
95
 
        actual_out, actual_err = self.run_bzr(['log'] + args,
96
 
                                              working_dir=working_dir)
97
 
        self.assertEqual(out, actual_out)
98
 
        self.assertEqual(err, actual_err)
99
 
        self.assertEqual(expected_revnos,
100
 
                         [r.revno for r in self.get_captured_revisions()])
101
 
 
102
 
    def assertLogRevnosAndDepths(self, args, expected_revnos_and_depths,
103
 
                                working_dir='.'):
104
 
        self.run_bzr(['log'] + args, working_dir=working_dir)
105
 
        self.assertEqual(expected_revnos_and_depths,
106
 
                         [(r.revno, r.merge_depth)
107
 
                           for r in self.get_captured_revisions()])
108
 
 
109
 
 
110
 
class TestLogRevSpecs(TestLogWithLogCatcher):
111
 
 
112
 
    def test_log_no_revspec(self):
113
 
        self.make_linear_branch()
114
 
        self.assertLogRevnos([], ['3', '2', '1'])
 
66
    def assertRevnos(self, log, must_have=(), must_not_have=()):
 
67
        """Check if revnos are in or not in the log output"""
 
68
        for revno in must_have:
 
69
            self.assertTrue(('revno: %s\n' % revno) in log,
 
70
                'Does not contain expected revno %s' % revno)
 
71
        for revno in must_not_have:
 
72
            self.assertFalse(('revno: %s\n' % revno) in log,
 
73
                'Contains unexpected revno %s' % revno)
 
74
 
 
75
    def commit_options(self):
 
76
        """Use some mostly fixed values for commits to simplify tests.
 
77
 
 
78
        Tests can use this function to get some commit attributes. The time
 
79
        stamp is incremented at each commit.
 
80
        """
 
81
        self.timestamp += 1 # 1 second between each commit
 
82
        return dict(committer='Lorem Ipsum <joe@foo.com>',
 
83
                 timezone=self.timezone,
 
84
                 timestamp=self.timestamp,
 
85
                 )
 
86
 
 
87
    def check_log(self, expected, args, working_dir='level0'):
 
88
        out, err = self.run_bzr(['log', '--timezone', 'utc'] + args,
 
89
                                working_dir=working_dir)
 
90
        self.assertEqual('', err)
 
91
        self.assertEqualDiff(expected, test_log.normalize_log(out))
 
92
 
 
93
 
 
94
class TestLogRevSpecs(TestLog):
115
95
 
116
96
    def test_log_null_end_revspec(self):
117
97
        self.make_linear_branch()
118
 
        self.assertLogRevnos(['-r1..'], ['3', '2', '1'])
 
98
        log = self.run_bzr(['log'])[0]
 
99
        self.assertTrue('revno: 1\n' in log)
 
100
        self.assertTrue('revno: 2\n' in log)
 
101
        self.assertTrue('revno: 3\n' in log)
 
102
        self.assertTrue('message:\n  message1\n' in log)
 
103
        self.assertTrue('message:\n  message2\n' in log)
 
104
        self.assertTrue('message:\n  message3\n' in log)
 
105
 
 
106
        full_log = self.run_bzr(['log'])[0]
 
107
        log = self.run_bzr("log -r 1..")[0]
 
108
        self.assertEqualDiff(log, full_log)
119
109
 
120
110
    def test_log_null_begin_revspec(self):
121
111
        self.make_linear_branch()
122
 
        self.assertLogRevnos(['-r..3'], ['3', '2', '1'])
 
112
        full_log = self.run_bzr(['log'])[0]
 
113
        log = self.run_bzr("log -r ..3")[0]
 
114
        self.assertEqualDiff(full_log, log)
123
115
 
124
116
    def test_log_null_both_revspecs(self):
125
117
        self.make_linear_branch()
126
 
        self.assertLogRevnos(['-r..'], ['3', '2', '1'])
 
118
        full_log = self.run_bzr(['log'])[0]
 
119
        log = self.run_bzr("log -r ..")[0]
 
120
        self.assertEqualDiff(full_log, log)
 
121
 
 
122
    def test_log_zero_revspec(self):
 
123
        self.make_minimal_branch()
 
124
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
125
                           ['log', '-r0'])
 
126
 
 
127
    def test_log_zero_begin_revspec(self):
 
128
        self.make_linear_branch()
 
129
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
130
                           ['log', '-r0..2'])
 
131
 
 
132
    def test_log_zero_end_revspec(self):
 
133
        self.make_linear_branch()
 
134
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
135
                           ['log', '-r-2..0'])
127
136
 
128
137
    def test_log_negative_begin_revspec_full_log(self):
129
138
        self.make_linear_branch()
130
 
        self.assertLogRevnos(['-r-3..'], ['3', '2', '1'])
 
139
        full_log = self.run_bzr(['log'])[0]
 
140
        log = self.run_bzr("log -r -3..")[0]
 
141
        self.assertEqualDiff(full_log, log)
131
142
 
132
143
    def test_log_negative_both_revspec_full_log(self):
133
144
        self.make_linear_branch()
134
 
        self.assertLogRevnos(['-r-3..-1'], ['3', '2', '1'])
 
145
        full_log = self.run_bzr(['log'])[0]
 
146
        log = self.run_bzr("log -r -3..-1")[0]
 
147
        self.assertEqualDiff(full_log, log)
135
148
 
136
149
    def test_log_negative_both_revspec_partial(self):
137
150
        self.make_linear_branch()
138
 
        self.assertLogRevnos(['-r-3..-2'], ['2', '1'])
 
151
        log = self.run_bzr("log -r -3..-2")[0]
 
152
        self.assertTrue('revno: 1\n' in log)
 
153
        self.assertTrue('revno: 2\n' in log)
 
154
        self.assertTrue('revno: 3\n' not in log)
139
155
 
140
156
    def test_log_negative_begin_revspec(self):
141
157
        self.make_linear_branch()
142
 
        self.assertLogRevnos(['-r-2..'], ['3', '2'])
 
158
        log = self.run_bzr("log -r -2..")[0]
 
159
        self.assertTrue('revno: 1\n' not in log)
 
160
        self.assertTrue('revno: 2\n' in log)
 
161
        self.assertTrue('revno: 3\n' in log)
143
162
 
144
163
    def test_log_positive_revspecs(self):
145
164
        self.make_linear_branch()
146
 
        self.assertLogRevnos(['-r1..3'], ['3', '2', '1'])
 
165
        full_log = self.run_bzr(['log'])[0]
 
166
        log = self.run_bzr("log -r 1..3")[0]
 
167
        self.assertEqualDiff(full_log, log)
147
168
 
148
169
    def test_log_dotted_revspecs(self):
149
170
        self.make_merged_branch()
150
 
        self.assertLogRevnos(['-n0', '-r1..1.1.1'], ['1.1.1', '1'])
151
 
 
152
 
    def test_log_limit(self):
153
 
        tree = self.make_branch_and_tree('.')
154
 
        # We want more commits than our batch size starts at
155
 
        for pos in range(10):
156
 
            tree.commit("%s" % pos)
157
 
        self.assertLogRevnos(['--limit', '2'], ['10', '9'])
158
 
 
159
 
    def test_log_limit_short(self):
160
 
        self.make_linear_branch()
161
 
        self.assertLogRevnos(['-l', '2'], ['3', '2'])
162
 
 
163
 
    def test_log_change_revno(self):
164
 
        self.make_linear_branch()
165
 
        self.assertLogRevnos(['-c1'], ['1'])
166
 
 
167
 
    def test_branch_revspec(self):
168
 
        foo = self.make_branch_and_tree('foo')
169
 
        bar = self.make_branch_and_tree('bar')
170
 
        self.build_tree(['foo/foo.txt', 'bar/bar.txt'])
171
 
        foo.add('foo.txt')
172
 
        bar.add('bar.txt')
173
 
        foo.commit(message='foo')
174
 
        bar.commit(message='bar')
175
 
        self.run_bzr('log -r branch:../bar', working_dir='foo')
176
 
        self.assertEqual([bar.branch.get_rev_id(1)],
177
 
                         [r.rev.revision_id
178
 
                          for r in self.get_captured_revisions()])
179
 
 
180
 
 
181
 
class TestLogExcludeCommonAncestry(TestLogWithLogCatcher):
182
 
 
183
 
    def test_exclude_common_ancestry_simple_revnos(self):
184
 
        self.make_linear_branch()
185
 
        self.assertLogRevnos(['-r1..3', '--exclude-common-ancestry'],
186
 
                             ['3', '2'])
187
 
 
188
 
 
189
 
class TestLogMergedLinearAncestry(TestLogWithLogCatcher):
190
 
 
191
 
    def setUp(self):
192
 
        super(TestLogMergedLinearAncestry, self).setUp()
193
 
        # FIXME: Using a MemoryTree would be even better here (but until we
194
 
        # stop calling run_bzr, there is no point) --vila 100118.
195
 
        builder = branchbuilder.BranchBuilder(self.get_transport())
196
 
        builder.start_series()
197
 
        # 1
198
 
        # | \
199
 
        # 2  1.1.1
200
 
        # | / |
201
 
        # 3  1.1.2
202
 
        # |   |
203
 
        # |  1.1.3
204
 
        # | / |
205
 
        # 4  1.1.4
206
 
        # | /
207
 
        # 5
208
 
        # | \
209
 
        # | 5.1.1
210
 
        # | /
211
 
        # 6
212
 
 
213
 
        # mainline
214
 
        builder.build_snapshot('1', None, [
215
 
            ('add', ('', 'root-id', 'directory', ''))])
216
 
        builder.build_snapshot('2', ['1'], [])
217
 
        # branch
218
 
        builder.build_snapshot('1.1.1', ['1'], [])
219
 
        # merge branch into mainline
220
 
        builder.build_snapshot('3', ['2', '1.1.1'], [])
221
 
        # new commits in branch
222
 
        builder.build_snapshot('1.1.2', ['1.1.1'], [])
223
 
        builder.build_snapshot('1.1.3', ['1.1.2'], [])
224
 
        # merge branch into mainline
225
 
        builder.build_snapshot('4', ['3', '1.1.3'], [])
226
 
        # merge mainline into branch
227
 
        builder.build_snapshot('1.1.4', ['1.1.3', '4'], [])
228
 
        # merge branch into mainline
229
 
        builder.build_snapshot('5', ['4', '1.1.4'], [])
230
 
        builder.build_snapshot('5.1.1', ['5'], [])
231
 
        builder.build_snapshot('6', ['5', '5.1.1'], [])
232
 
        builder.finish_series()
233
 
 
234
 
    def test_n0(self):
235
 
        self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4'],
236
 
                             ['1.1.4', '4', '1.1.3', '1.1.2', '3', '1.1.1'])
237
 
    def test_n0_forward(self):
238
 
        self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4', '--forward'],
239
 
                             ['3', '1.1.1', '4', '1.1.2', '1.1.3', '1.1.4'])
240
 
 
241
 
    def test_n1(self):
242
 
        # starting from 1.1.4 we follow the left-hand ancestry
243
 
        self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4'],
244
 
                             ['1.1.4', '1.1.3', '1.1.2', '1.1.1'])
245
 
 
246
 
    def test_n1_forward(self):
247
 
        self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4', '--forward'],
248
 
                             ['1.1.1', '1.1.2', '1.1.3', '1.1.4'])
249
 
 
250
 
    def test_fallback_when_end_rev_is_not_on_mainline(self):
251
 
        self.assertLogRevnos(['-n1', '-r1.1.1..5.1.1'],
252
 
                             # We don't get 1.1.1 because we say -n1
253
 
                             ['5.1.1', '5', '4', '3'])
254
 
 
255
 
 
256
 
class Test_GenerateAllRevisions(TestLogWithLogCatcher):
257
 
 
258
 
    def setUp(self):
259
 
        super(Test_GenerateAllRevisions, self).setUp()
260
 
        builder = self.make_branch_with_many_merges()
261
 
        b = builder.get_branch()
262
 
        b.lock_read()
263
 
        self.addCleanup(b.unlock)
264
 
        self.branch = b
265
 
 
266
 
    def make_branch_with_many_merges(self, path='.', format=None):
267
 
        builder = branchbuilder.BranchBuilder(self.get_transport())
268
 
        builder.start_series()
269
 
        # The graph below may look a bit complicated (and it may be but I've
270
 
        # banged my head enough on it) but the bug requires at least dotted
271
 
        # revnos *and* merged revisions below that.
272
 
        # 1
273
 
        # | \
274
 
        # 2  1.1.1
275
 
        # | X
276
 
        # 3  2.1.1
277
 
        # |   |    \
278
 
        # |  2.1.2  2.2.1
279
 
        # |   |    X
280
 
        # |  2.1.3  \
281
 
        # | /       /
282
 
        # 4        /
283
 
        # |       /
284
 
        # 5 -----/
285
 
        builder.build_snapshot('1', None, [
286
 
            ('add', ('', 'root-id', 'directory', ''))])
287
 
        builder.build_snapshot('2', ['1'], [])
288
 
        builder.build_snapshot('1.1.1', ['1'], [])
289
 
        builder.build_snapshot('2.1.1', ['2'], [])
290
 
        builder.build_snapshot('3', ['2', '1.1.1'], [])
291
 
        builder.build_snapshot('2.1.2', ['2.1.1'], [])
292
 
        builder.build_snapshot('2.2.1', ['2.1.1'], [])
293
 
        builder.build_snapshot('2.1.3', ['2.1.2', '2.2.1'], [])
294
 
        builder.build_snapshot('4', ['3', '2.1.3'], [])
295
 
        builder.build_snapshot('5', ['4', '2.1.2'], [])
296
 
        builder.finish_series()
297
 
        return builder
298
 
 
299
 
    def test_not_an_ancestor(self):
300
 
        self.assertRaises(errors.BzrCommandError,
301
 
                          log._generate_all_revisions,
302
 
                          self.branch, '1.1.1', '2.1.3', 'reverse',
303
 
                          delayed_graph_generation=True)
304
 
 
305
 
    def test_wrong_order(self):
306
 
        self.assertRaises(errors.BzrCommandError,
307
 
                          log._generate_all_revisions,
308
 
                          self.branch, '5', '2.1.3', 'reverse',
309
 
                          delayed_graph_generation=True)
310
 
 
311
 
    def test_no_start_rev_id_with_end_rev_id_being_a_merge(self):
312
 
        revs = log._generate_all_revisions(
313
 
            self.branch, None, '2.1.3',
314
 
            'reverse', delayed_graph_generation=True)
315
 
 
316
 
 
317
 
class TestLogRevSpecsWithPaths(TestLogWithLogCatcher):
318
 
 
319
 
    def test_log_revno_n_path_wrong_namespace(self):
 
171
        log = self.run_bzr("log -n0 -r 1..1.1.1")[0]
 
172
        self.assertRevnos(log, (1, '1.1.1'), (2, 3, '1.1.2', 4))
 
173
 
 
174
    def test_log_reversed_revspecs(self):
 
175
        self.make_linear_branch()
 
176
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
 
177
                            'the end revision.\n',),
 
178
                           ['log', '-r3..1'])
 
179
 
 
180
    def test_log_reversed_dotted_revspecs(self):
 
181
        self.make_merged_branch()
 
182
        self.run_bzr_error(('bzr: ERROR: Start revision not found in '
 
183
                            'left-hand history of end revision.\n',),
 
184
                           "log -r 1.1.1..1")
 
185
 
 
186
    def test_log_revno_n_path(self):
320
187
        self.make_linear_branch('branch1')
321
188
        self.make_linear_branch('branch2')
322
 
        # There is no guarantee that a path exist between two arbitrary
323
 
        # revisions.
324
 
        self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)
325
 
 
326
 
    def test_log_revno_n_path_correct_order(self):
327
 
        self.make_linear_branch('branch2')
328
 
        self.assertLogRevnos(['-rrevno:1:branch2..revno:3:branch2'],
329
 
                             ['3', '2','1'])
330
 
 
331
 
    def test_log_revno_n_path(self):
332
 
        self.make_linear_branch('branch2')
333
 
        self.assertLogRevnos(['-rrevno:1:branch2'],
334
 
                             ['1'])
335
 
        rev_props = self.log_catcher.revisions[0].rev.properties
336
 
        self.assertEqual('branch2', rev_props['branch-nick'])
337
 
 
338
 
 
339
 
class TestLogErrors(TestLog):
340
 
 
341
 
    def test_log_zero_revspec(self):
342
 
        self.make_minimal_branch()
343
 
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
344
 
                           ['log', '-r0'])
345
 
 
346
 
    def test_log_zero_begin_revspec(self):
347
 
        self.make_linear_branch()
348
 
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
349
 
                           ['log', '-r0..2'])
350
 
 
351
 
    def test_log_zero_end_revspec(self):
352
 
        self.make_linear_branch()
353
 
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
354
 
                           ['log', '-r-2..0'])
 
189
        # Swapped revisions
 
190
        self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)[0]
 
191
        # Correct order
 
192
        log = self.run_bzr("log -r revno:1:branch2..revno:3:branch2")[0]
 
193
        full_log = self.run_bzr(['log'], working_dir='branch2')[0]
 
194
        self.assertEqualDiff(full_log, log)
 
195
        log = self.run_bzr("log -r revno:1:branch2")[0]
 
196
        self.assertTrue('revno: 1\n' in log)
 
197
        self.assertTrue('revno: 2\n' not in log)
 
198
        self.assertTrue('branch nick: branch2\n' in log)
 
199
        self.assertTrue('branch nick: branch1\n' not in log)
355
200
 
356
201
    def test_log_nonexistent_revno(self):
357
202
        self.make_minimal_branch()
358
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
359
 
                            "does not exist in branch:"],
360
 
                           ['log', '-r1234'])
 
203
        (out, err) = self.run_bzr_error(
 
204
            ["bzr: ERROR: Requested revision: '1234' "
 
205
             "does not exist in branch:"],
 
206
            ['log', '-r1234'])
361
207
 
362
208
    def test_log_nonexistent_dotted_revno(self):
363
209
        self.make_minimal_branch()
364
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
365
 
                            "does not exist in branch:"],
366
 
                           ['log',  '-r123.123'])
 
210
        (out, err) = self.run_bzr_error(
 
211
            ["bzr: ERROR: Requested revision: '123.123' "
 
212
             "does not exist in branch:"],
 
213
            ['log',  '-r123.123'])
 
214
 
 
215
    def test_log_change_revno(self):
 
216
        self.make_linear_branch()
 
217
        expected_log = self.run_bzr("log -r 1")[0]
 
218
        log = self.run_bzr("log -c 1")[0]
 
219
        self.assertEqualDiff(expected_log, log)
367
220
 
368
221
    def test_log_change_nonexistent_revno(self):
369
222
        self.make_minimal_branch()
370
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
371
 
                            "does not exist in branch:"],
372
 
                           ['log',  '-c1234'])
 
223
        (out, err) = self.run_bzr_error(
 
224
            ["bzr: ERROR: Requested revision: '1234' "
 
225
             "does not exist in branch:"],
 
226
            ['log',  '-c1234'])
373
227
 
374
228
    def test_log_change_nonexistent_dotted_revno(self):
375
229
        self.make_minimal_branch()
376
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
377
 
                            "does not exist in branch:"],
378
 
                           ['log', '-c123.123'])
 
230
        (out, err) = self.run_bzr_error(
 
231
            ["bzr: ERROR: Requested revision: '123.123' "
 
232
             "does not exist in branch:"],
 
233
            ['log', '-c123.123'])
379
234
 
380
235
    def test_log_change_single_revno_only(self):
381
236
        self.make_minimal_branch()
397
252
                              'Path unknown at end or start of revision range: '
398
253
                              'does-not-exist')
399
254
 
400
 
    def test_log_reversed_revspecs(self):
401
 
        self.make_linear_branch()
402
 
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
403
 
                            'the end revision.\n',),
404
 
                           ['log', '-r3..1'])
405
 
 
406
 
    def test_log_reversed_dotted_revspecs(self):
407
 
        self.make_merged_branch()
408
 
        self.run_bzr_error(('bzr: ERROR: Start revision not found in '
409
 
                            'history of end revision.\n',),
410
 
                           "log -r 1.1.1..1")
411
 
 
412
 
    def test_log_bad_message_re(self):
413
 
        """Bad --message argument gives a sensible message
414
 
 
415
 
        See https://bugs.launchpad.net/bzr/+bug/251352
416
 
        """
417
 
        self.make_minimal_branch()
418
 
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
419
 
        self.assertContainsRe(err, "ERROR.*Invalid pattern.*nothing to repeat")
420
 
        self.assertNotContainsRe(err, "Unprintable exception")
421
 
        self.assertEqual(out, '')
422
 
 
423
 
    def test_log_unsupported_timezone(self):
424
 
        self.make_linear_branch()
425
 
        self.run_bzr_error(['bzr: ERROR: Unsupported timezone format "foo", '
426
 
                            'options are "utc", "original", "local".'],
427
 
                           ['log', '--timezone', 'foo'])
428
 
 
429
 
    def test_log_exclude_ancestry_no_range(self):
430
 
        self.make_linear_branch()
431
 
        self.run_bzr_error(['bzr: ERROR: --exclude-common-ancestry'
432
 
                            ' requires -r with two revisions'],
433
 
                           ['log', '--exclude-common-ancestry'])
434
 
 
435
 
    def test_log_exclude_ancestry_single_revision(self):
436
 
        self.make_merged_branch()
437
 
        self.run_bzr_error(['bzr: ERROR: --exclude-common-ancestry'
438
 
                            ' requires two different revisions'],
439
 
                           ['log', '--exclude-common-ancestry',
440
 
                            '-r1.1.1..1.1.1'])
441
 
 
442
 
class TestLogTags(TestLog):
443
 
 
444
255
    def test_log_with_tags(self):
445
256
        tree = self.make_linear_branch(format='dirstate-tags')
446
257
        branch = tree.branch
471
282
        log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
472
283
        self.assertContainsRe(log, r'tags: tag1')
473
284
 
474
 
 
475
 
class TestLogSignatures(TestLog):
476
 
 
477
 
    def test_log_with_signatures(self):
478
 
        self.requireFeature(features.gpgme)
479
 
 
480
 
        tree = self.make_linear_branch(format='dirstate-tags')
481
 
 
482
 
        log = self.run_bzr("log --signatures")[0]
483
 
        self.assertTrue('signature: no signature' in log)
484
 
 
485
 
    def test_log_without_signatures(self):
486
 
        self.requireFeature(features.gpgme)
487
 
 
488
 
        tree = self.make_linear_branch(format='dirstate-tags')
489
 
 
490
 
        log = self.run_bzr("log")[0]
491
 
        self.assertFalse('signature: no signature' in log)
 
285
    def test_log_limit(self):
 
286
        tree = self.make_branch_and_tree('.')
 
287
        # We want more commits than our batch size starts at
 
288
        for pos in range(10):
 
289
            tree.commit("%s" % pos)
 
290
        log = self.run_bzr("log --limit 2")[0]
 
291
        self.assertNotContainsRe(log, r'revno: 1\n')
 
292
        self.assertNotContainsRe(log, r'revno: 2\n')
 
293
        self.assertNotContainsRe(log, r'revno: 3\n')
 
294
        self.assertNotContainsRe(log, r'revno: 4\n')
 
295
        self.assertNotContainsRe(log, r'revno: 5\n')
 
296
        self.assertNotContainsRe(log, r'revno: 6\n')
 
297
        self.assertNotContainsRe(log, r'revno: 7\n')
 
298
        self.assertNotContainsRe(log, r'revno: 8\n')
 
299
        self.assertContainsRe(log, r'revno: 9\n')
 
300
        self.assertContainsRe(log, r'revno: 10\n')
 
301
 
 
302
    def test_log_limit_short(self):
 
303
        self.make_linear_branch()
 
304
        log = self.run_bzr("log -l 2")[0]
 
305
        self.assertNotContainsRe(log, r'revno: 1\n')
 
306
        self.assertContainsRe(log, r'revno: 2\n')
 
307
        self.assertContainsRe(log, r'revno: 3\n')
 
308
 
 
309
    def test_log_bad_message_re(self):
 
310
        """Bad --message argument gives a sensible message
 
311
        
 
312
        See https://bugs.launchpad.net/bzr/+bug/251352
 
313
        """
 
314
        self.make_minimal_branch()
 
315
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
 
316
        self.assertEqual("bzr: ERROR: Invalid regular expression"
 
317
            " in log message filter"
 
318
            ": '*'"
 
319
            ": nothing to repeat\n", err)
 
320
        self.assertEqual('', out)
 
321
 
 
322
 
 
323
class TestLogTimeZone(TestLog):
 
324
 
 
325
    def test_log_unsupported_timezone(self):
 
326
        self.make_linear_branch()
 
327
        self.run_bzr_error(['bzr: ERROR: Unsupported timezone format "foo", '
 
328
                            'options are "utc", "original", "local".'],
 
329
                           ['log', '--timezone', 'foo'])
492
330
 
493
331
 
494
332
class TestLogVerbose(TestLog):
512
350
    def test_log_short_verbose(self):
513
351
        self.assertUseShortDeltaFormat(['log', '--short', '-v'])
514
352
 
515
 
    def test_log_s_verbose(self):
516
 
        self.assertUseShortDeltaFormat(['log', '-S', '-v'])
517
 
 
518
353
    def test_log_short_verbose_verbose(self):
519
354
        self.assertUseLongDeltaFormat(['log', '--short', '-vv'])
520
355
 
529
364
        self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
530
365
 
531
366
 
532
 
class TestLogMerges(TestLogWithLogCatcher):
 
367
class TestLogMerges(TestLog):
533
368
 
534
369
    def setUp(self):
535
370
        super(TestLogMerges, self).setUp()
537
372
 
538
373
    def make_branches_with_merges(self):
539
374
        level0 = self.make_branch_and_tree('level0')
540
 
        self.wt_commit(level0, 'in branch level0')
 
375
        level0.commit(message='in branch level0', **self.commit_options())
 
376
 
541
377
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
542
 
        self.wt_commit(level1, 'in branch level1')
 
378
        level1.commit(message='in branch level1', **self.commit_options())
 
379
 
543
380
        level2 = level1.bzrdir.sprout('level2').open_workingtree()
544
 
        self.wt_commit(level2, 'in branch level2')
 
381
        level2.commit(message='in branch level2', **self.commit_options())
 
382
 
545
383
        level1.merge_from_branch(level2.branch)
546
 
        self.wt_commit(level1, 'merge branch level2')
 
384
        level1.commit(message='merge branch level2', **self.commit_options())
 
385
 
547
386
        level0.merge_from_branch(level1.branch)
548
 
        self.wt_commit(level0, 'merge branch level1')
 
387
        level0.commit(message='merge branch level1', **self.commit_options())
549
388
 
550
389
    def test_merges_are_indented_by_level(self):
551
 
        self.run_bzr(['log', '-n0'], working_dir='level0')
552
 
        revnos_and_depth = [(r.revno, r.merge_depth)
553
 
                            for r in self.get_captured_revisions()]
554
 
        self.assertEqual([('2', 0), ('1.1.2', 1), ('1.2.1', 2), ('1.1.1', 1),
555
 
                          ('1', 0)],
556
 
                         [(r.revno, r.merge_depth)
557
 
                            for r in self.get_captured_revisions()])
 
390
        expected = """\
 
391
------------------------------------------------------------
 
392
revno: 2 [merge]
 
393
committer: Lorem Ipsum <test@example.com>
 
394
branch nick: level0
 
395
timestamp: Just now
 
396
message:
 
397
  merge branch level1
 
398
    ------------------------------------------------------------
 
399
    revno: 1.1.2 [merge]
 
400
    committer: Lorem Ipsum <test@example.com>
 
401
    branch nick: level1
 
402
    timestamp: Just now
 
403
    message:
 
404
      merge branch level2
 
405
        ------------------------------------------------------------
 
406
        revno: 1.2.1
 
407
        committer: Lorem Ipsum <test@example.com>
 
408
        branch nick: level2
 
409
        timestamp: Just now
 
410
        message:
 
411
          in branch level2
 
412
    ------------------------------------------------------------
 
413
    revno: 1.1.1
 
414
    committer: Lorem Ipsum <test@example.com>
 
415
    branch nick: level1
 
416
    timestamp: Just now
 
417
    message:
 
418
      in branch level1
 
419
------------------------------------------------------------
 
420
revno: 1
 
421
committer: Lorem Ipsum <test@example.com>
 
422
branch nick: level0
 
423
timestamp: Just now
 
424
message:
 
425
  in branch level0
 
426
"""
 
427
        self.check_log(expected, ['-n0'])
558
428
 
559
429
    def test_force_merge_revisions_off(self):
560
 
        self.assertLogRevnos(['-n1'], ['2', '1'], working_dir='level0')
 
430
        expected = """\
 
431
------------------------------------------------------------
 
432
revno: 2 [merge]
 
433
committer: Lorem Ipsum <test@example.com>
 
434
branch nick: level0
 
435
timestamp: Just now
 
436
message:
 
437
  merge branch level1
 
438
------------------------------------------------------------
 
439
revno: 1
 
440
committer: Lorem Ipsum <test@example.com>
 
441
branch nick: level0
 
442
timestamp: Just now
 
443
message:
 
444
  in branch level0
 
445
"""
 
446
        self.check_log(expected, ['--long', '-n1'])
561
447
 
562
448
    def test_force_merge_revisions_on(self):
563
 
        self.assertLogRevnos(['-n0'], ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
564
 
                             working_dir='level0')
 
449
        expected = """\
 
450
    2 Lorem Ipsum\t2005-11-22 [merge]
 
451
      merge branch level1
 
452
 
 
453
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
454
                merge branch level2
 
455
 
 
456
              1.2.1 Lorem Ipsum\t2005-11-22
 
457
                    in branch level2
 
458
 
 
459
          1.1.1 Lorem Ipsum\t2005-11-22
 
460
                in branch level1
 
461
 
 
462
    1 Lorem Ipsum\t2005-11-22
 
463
      in branch level0
 
464
 
 
465
"""
 
466
        self.check_log(expected, ['--short', '-n0'])
565
467
 
566
468
    def test_include_merges(self):
567
469
        # Confirm --include-merges gives the same output as -n0
568
 
        msg = ("The option '--include-merges' to 'bzr log' "
569
 
               "has been deprecated in bzr 2.5. "
570
 
               "Please use '--include-merged' instead.\n")
571
 
        self.assertLogRevnos(['--include-merges'],
572
 
                             ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
573
 
                             working_dir='level0', err=msg)
574
 
        self.assertLogRevnos(['--include-merges'],
575
 
                             ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
576
 
                             working_dir='level0', err=msg)
577
470
        out_im, err_im = self.run_bzr('log --include-merges',
578
471
                                      working_dir='level0')
579
472
        out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
580
 
        self.assertEqual(msg, err_im)
 
473
        self.assertEqual('', err_im)
581
474
        self.assertEqual('', err_n0)
582
475
        self.assertEqual(out_im, out_n0)
583
476
 
584
 
    def test_include_merged(self):
585
 
        # Confirm --include-merged gives the same output as -n0
586
 
        expected = ['2', '1.1.2', '1.2.1', '1.1.1', '1']
587
 
        self.assertLogRevnos(['--include-merged'],
588
 
                             expected, working_dir='level0')
589
 
        self.assertLogRevnos(['--include-merged'],
590
 
                             expected, working_dir='level0')
591
 
 
592
477
    def test_force_merge_revisions_N(self):
593
 
        self.assertLogRevnos(['-n2'],
594
 
                             ['2', '1.1.2', '1.1.1', '1'],
595
 
                             working_dir='level0')
 
478
        expected = """\
 
479
    2 Lorem Ipsum\t2005-11-22 [merge]
 
480
      merge branch level1
 
481
 
 
482
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
483
                merge branch level2
 
484
 
 
485
          1.1.1 Lorem Ipsum\t2005-11-22
 
486
                in branch level1
 
487
 
 
488
    1 Lorem Ipsum\t2005-11-22
 
489
      in branch level0
 
490
 
 
491
"""
 
492
        self.check_log(expected, ['--short', '-n2'])
596
493
 
597
494
    def test_merges_single_merge_rev(self):
598
 
        self.assertLogRevnosAndDepths(['-n0', '-r1.1.2'],
599
 
                                      [('1.1.2', 0), ('1.2.1', 1)],
600
 
                                      working_dir='level0')
 
495
        expected = """\
 
496
------------------------------------------------------------
 
497
revno: 1.1.2 [merge]
 
498
committer: Lorem Ipsum <test@example.com>
 
499
branch nick: level1
 
500
timestamp: Just now
 
501
message:
 
502
  merge branch level2
 
503
    ------------------------------------------------------------
 
504
    revno: 1.2.1
 
505
    committer: Lorem Ipsum <test@example.com>
 
506
    branch nick: level2
 
507
    timestamp: Just now
 
508
    message:
 
509
      in branch level2
 
510
"""
 
511
        self.check_log(expected, ['-n0', '-r1.1.2'])
601
512
 
602
513
    def test_merges_partial_range(self):
603
 
        self.assertLogRevnosAndDepths(
604
 
                ['-n0', '-r1.1.1..1.1.2'],
605
 
                [('1.1.2', 0), ('1.2.1', 1), ('1.1.1', 0)],
606
 
                working_dir='level0')
607
 
 
608
 
    def test_merges_partial_range_ignore_before_lower_bound(self):
609
 
        """Dont show revisions before the lower bound's merged revs"""
610
 
        self.assertLogRevnosAndDepths(
611
 
                ['-n0', '-r1.1.2..2'],
612
 
                [('2', 0), ('1.1.2', 1), ('1.2.1', 2)],
613
 
                working_dir='level0')
614
 
 
615
 
    def test_omit_merges_with_sidelines(self):
616
 
        self.assertLogRevnos(['--omit-merges', '-n0'], ['1.2.1', '1.1.1', '1'],
617
 
                             working_dir='level0')
618
 
 
619
 
    def test_omit_merges_without_sidelines(self):
620
 
        self.assertLogRevnos(['--omit-merges', '-n1'], ['1'],
621
 
                             working_dir='level0')
622
 
 
623
 
 
624
 
class TestLogDiff(TestLogWithLogCatcher):
625
 
 
626
 
    # FIXME: We need specific tests for each LogFormatter about how the diffs
627
 
    # are displayed: --long indent them by depth, --short use a fixed
628
 
    # indent and --line does't display them. -- vila 10019
 
514
        expected = """\
 
515
------------------------------------------------------------
 
516
revno: 1.1.2 [merge]
 
517
committer: Lorem Ipsum <test@example.com>
 
518
branch nick: level1
 
519
timestamp: Just now
 
520
message:
 
521
  merge branch level2
 
522
    ------------------------------------------------------------
 
523
    revno: 1.2.1
 
524
    committer: Lorem Ipsum <test@example.com>
 
525
    branch nick: level2
 
526
    timestamp: Just now
 
527
    message:
 
528
      in branch level2
 
529
------------------------------------------------------------
 
530
revno: 1.1.1
 
531
committer: Lorem Ipsum <test@example.com>
 
532
branch nick: level1
 
533
timestamp: Just now
 
534
message:
 
535
  in branch level1
 
536
"""
 
537
        self.check_log(expected, ['-n0', '-r1.1.1..1.1.2'])
 
538
 
 
539
 
 
540
class TestLogDiff(TestLog):
629
541
 
630
542
    def setUp(self):
631
543
        super(TestLogDiff, self).setUp()
636
548
        self.build_tree(['level0/file1', 'level0/file2'])
637
549
        level0.add('file1')
638
550
        level0.add('file2')
639
 
        self.wt_commit(level0, 'in branch level0')
 
551
        level0.commit(message='in branch level0', **self.commit_options())
640
552
 
641
553
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
642
554
        self.build_tree_contents([('level1/file2', 'hello\n')])
643
 
        self.wt_commit(level1, 'in branch level1')
 
555
        level1.commit(message='in branch level1', **self.commit_options())
644
556
        level0.merge_from_branch(level1.branch)
645
 
        self.wt_commit(level0, 'merge branch level1')
 
557
        level0.commit(message='merge branch level1', **self.commit_options())
646
558
 
647
 
    def _diff_file1_revno1(self):
648
 
        return """=== added file 'file1'
 
559
    def test_log_show_diff_long_with_merges(self):
 
560
        out,err = self.run_bzr('log -p -n0')
 
561
        self.assertEqual('', err)
 
562
        log = test_log.normalize_log(out)
 
563
        expected = """\
 
564
------------------------------------------------------------
 
565
revno: 2 [merge]
 
566
committer: Lorem Ipsum <test@example.com>
 
567
branch nick: level0
 
568
timestamp: Just now
 
569
message:
 
570
  merge branch level1
 
571
diff:
 
572
=== modified file 'file2'
 
573
--- file2\t2005-11-22 00:00:01 +0000
 
574
+++ file2\t2005-11-22 00:00:02 +0000
 
575
@@ -1,1 +1,1 @@
 
576
-contents of level0/file2
 
577
+hello
 
578
    ------------------------------------------------------------
 
579
    revno: 1.1.1
 
580
    committer: Lorem Ipsum <test@example.com>
 
581
    branch nick: level1
 
582
    timestamp: Just now
 
583
    message:
 
584
      in branch level1
 
585
    diff:
 
586
    === modified file 'file2'
 
587
    --- file2\t2005-11-22 00:00:01 +0000
 
588
    +++ file2\t2005-11-22 00:00:02 +0000
 
589
    @@ -1,1 +1,1 @@
 
590
    -contents of level0/file2
 
591
    +hello
 
592
------------------------------------------------------------
 
593
revno: 1
 
594
committer: Lorem Ipsum <test@example.com>
 
595
branch nick: level0
 
596
timestamp: Just now
 
597
message:
 
598
  in branch level0
 
599
diff:
 
600
=== added file 'file1'
649
601
--- file1\t1970-01-01 00:00:00 +0000
650
 
+++ file1\t2005-11-22 00:00:00 +0000
 
602
+++ file1\t2005-11-22 00:00:01 +0000
651
603
@@ -0,0 +1,1 @@
652
604
+contents of level0/file1
653
605
 
654
 
"""
655
 
 
656
 
    def _diff_file2_revno2(self):
657
 
        return """=== modified file 'file2'
658
 
--- file2\t2005-11-22 00:00:00 +0000
659
 
+++ file2\t2005-11-22 00:00:01 +0000
660
 
@@ -1,1 +1,1 @@
661
 
-contents of level0/file2
662
 
+hello
663
 
 
664
 
"""
665
 
 
666
 
    def _diff_file2_revno1_1_1(self):
667
 
        return """=== modified file 'file2'
668
 
--- file2\t2005-11-22 00:00:00 +0000
669
 
+++ file2\t2005-11-22 00:00:01 +0000
670
 
@@ -1,1 +1,1 @@
671
 
-contents of level0/file2
672
 
+hello
673
 
 
674
 
"""
675
 
 
676
 
    def _diff_file2_revno1(self):
677
 
        return """=== added file 'file2'
 
606
=== added file 'file2'
678
607
--- file2\t1970-01-01 00:00:00 +0000
679
 
+++ file2\t2005-11-22 00:00:00 +0000
 
608
+++ file2\t2005-11-22 00:00:01 +0000
680
609
@@ -0,0 +1,1 @@
681
610
+contents of level0/file2
682
 
 
683
 
"""
684
 
 
685
 
    def assertLogRevnosAndDiff(self, args, expected,
686
 
                            working_dir='.'):
687
 
        self.run_bzr(['log', '-p'] + args, working_dir=working_dir)
688
 
        expected_revnos_and_depths = [
689
 
            (revno, depth) for revno, depth, diff in expected]
690
 
        # Check the revnos and depths first to make debugging easier
691
 
        self.assertEqual(expected_revnos_and_depths,
692
 
                         [(r.revno, r.merge_depth)
693
 
                           for r in self.get_captured_revisions()])
694
 
        # Now check the diffs, adding the revno  in case of failure
695
 
        fmt = 'In revno %s\n%s'
696
 
        for expected_rev, actual_rev in izip(expected,
697
 
                                             self.get_captured_revisions()):
698
 
            revno, depth, expected_diff = expected_rev
699
 
            actual_diff = actual_rev.diff
700
 
            self.assertEqualDiff(fmt % (revno, expected_diff),
701
 
                                 fmt % (revno, actual_diff))
702
 
 
703
 
    def test_log_diff_with_merges(self):
704
 
        self.assertLogRevnosAndDiff(
705
 
            ['-n0'],
706
 
            [('2', 0, self._diff_file2_revno2()),
707
 
             ('1.1.1', 1, self._diff_file2_revno1_1_1()),
708
 
             ('1', 0, self._diff_file1_revno1()
709
 
              + self._diff_file2_revno1())],
710
 
            working_dir='level0')
711
 
 
712
 
 
713
 
    def test_log_diff_file1(self):
714
 
        self.assertLogRevnosAndDiff(['-n0', 'file1'],
715
 
                                    [('1', 0, self._diff_file1_revno1())],
716
 
                                    working_dir='level0')
717
 
 
718
 
    def test_log_diff_file2(self):
719
 
        self.assertLogRevnosAndDiff(['-n1', 'file2'],
720
 
                                    [('2', 0, self._diff_file2_revno2()),
721
 
                                     ('1', 0, self._diff_file2_revno1())],
722
 
                                    working_dir='level0')
 
611
"""
 
612
        self.check_log(expected, ['-p', '-n0'])
 
613
 
 
614
    def test_log_show_diff_short(self):
 
615
        expected = """\
 
616
    2 Lorem Ipsum\t2005-11-22 [merge]
 
617
      merge branch level1
 
618
      === modified file 'file2'
 
619
      --- file2\t2005-11-22 00:00:01 +0000
 
620
      +++ file2\t2005-11-22 00:00:02 +0000
 
621
      @@ -1,1 +1,1 @@
 
622
      -contents of level0/file2
 
623
      +hello
 
624
 
 
625
    1 Lorem Ipsum\t2005-11-22
 
626
      in branch level0
 
627
      === added file 'file1'
 
628
      --- file1\t1970-01-01 00:00:00 +0000
 
629
      +++ file1\t2005-11-22 00:00:01 +0000
 
630
      @@ -0,0 +1,1 @@
 
631
      +contents of level0/file1
 
632
\x20\x20\x20\x20\x20\x20
 
633
      === added file 'file2'
 
634
      --- file2\t1970-01-01 00:00:00 +0000
 
635
      +++ file2\t2005-11-22 00:00:01 +0000
 
636
      @@ -0,0 +1,1 @@
 
637
      +contents of level0/file2
 
638
 
 
639
Use --include-merges or -n0 to see merged revisions.
 
640
"""
 
641
        self.check_log(expected, ['-p', '--short'])
 
642
 
 
643
    def test_log_show_diff_line(self):
 
644
        # Not supported by this formatter so expect plain output
 
645
        expected = """\
 
646
2: Lorem Ipsum 2005-11-22 [merge] merge branch level1
 
647
1: Lorem Ipsum 2005-11-22 in branch level0
 
648
"""
 
649
        self.check_log(expected, ['-p', '--line'])
 
650
 
 
651
    def test_log_show_diff_file1(self):
 
652
        """Only the diffs for the given file are to be shown"""
 
653
        expected = """\
 
654
    1 Lorem Ipsum\t2005-11-22
 
655
      in branch level0
 
656
      === added file 'file1'
 
657
      --- file1\t1970-01-01 00:00:00 +0000
 
658
      +++ file1\t2005-11-22 00:00:01 +0000
 
659
      @@ -0,0 +1,1 @@
 
660
      +contents of level0/file1
 
661
 
 
662
"""
 
663
        self.check_log(expected, ['-p', '--short', 'file1'])
 
664
 
 
665
    def test_log_show_diff_file2(self):
 
666
        """Only the diffs for the given file are to be shown"""
 
667
        expected = """\
 
668
    2 Lorem Ipsum\t2005-11-22 [merge]
 
669
      merge branch level1
 
670
      === modified file 'file2'
 
671
      --- file2\t2005-11-22 00:00:01 +0000
 
672
      +++ file2\t2005-11-22 00:00:02 +0000
 
673
      @@ -1,1 +1,1 @@
 
674
      -contents of level0/file2
 
675
      +hello
 
676
 
 
677
    1 Lorem Ipsum\t2005-11-22
 
678
      in branch level0
 
679
      === added file 'file2'
 
680
      --- file2\t1970-01-01 00:00:00 +0000
 
681
      +++ file2\t2005-11-22 00:00:01 +0000
 
682
      @@ -0,0 +1,1 @@
 
683
      +contents of level0/file2
 
684
 
 
685
Use --include-merges or -n0 to see merged revisions.
 
686
"""
 
687
        self.check_log(expected, ['-p', '--short', 'file2'])
723
688
 
724
689
 
725
690
class TestLogUnicodeDiff(TestLog):
768
733
 
769
734
    def setUp(self):
770
735
        super(TestLogEncodings, self).setUp()
771
 
        self.overrideAttr(osutils, '_cached_user_encoding')
 
736
        self.user_encoding = osutils._cached_user_encoding
 
737
        def restore():
 
738
            osutils._cached_user_encoding = self.user_encoding
 
739
        self.addCleanup(restore)
772
740
 
773
741
    def create_branch(self):
774
742
        bzr = self.run_bzr
775
743
        bzr('init')
776
 
        self.build_tree_contents([('a', 'some stuff\n')])
 
744
        open('a', 'wb').write('some stuff\n')
777
745
        bzr('add a')
778
746
        bzr(['commit', '-m', self._message])
779
747
 
837
805
        test_in_cp866 = '\x92\xa5\xe1\xe2'
838
806
        test_in_cp1251 = '\xd2\xe5\xf1\xf2'
839
807
        # Make sure the log string is encoded in cp866
840
 
        self.assertEqual(test_in_cp866, message[2:])
 
808
        self.assertEquals(test_in_cp866, message[2:])
841
809
        # Make sure the cp1251 string is not found anywhere
842
 
        self.assertEqual(-1, stdout.find(test_in_cp1251))
843
 
 
844
 
 
845
 
class TestLogFile(TestLogWithLogCatcher):
 
810
        self.assertEquals(-1, stdout.find(test_in_cp1251))
 
811
 
 
812
 
 
813
class TestLogFile(tests.TestCaseWithTransport):
846
814
 
847
815
    def test_log_local_branch_file(self):
848
816
        """We should be able to log files in local treeless branches"""
877
845
            tree.commit('remove file1')
878
846
        os.chdir('parent')
879
847
 
880
 
    # FIXME: It would be good to parametrize the following tests against all
881
 
    # formatters. But the revisions selection is not *currently* part of the
882
 
    # LogFormatter contract, so using LogCatcher is sufficient -- vila 100118
883
 
    def test_log_file1(self):
884
 
        self.prepare_tree()
885
 
        self.assertLogRevnos(['-n0', 'file1'], ['1'])
886
 
 
887
 
    def test_log_file2(self):
888
 
        self.prepare_tree()
889
 
        # file2 full history
890
 
        self.assertLogRevnos(['-n0', 'file2'], ['4', '3.1.1', '2'])
891
 
        # file2 in a merge revision
892
 
        self.assertLogRevnos(['-n0', '-r3.1.1', 'file2'], ['3.1.1'])
893
 
        # file2 in a mainline revision
894
 
        self.assertLogRevnos(['-n0', '-r4', 'file2'], ['4', '3.1.1'])
895
 
        # file2 since a revision
896
 
        self.assertLogRevnos(['-n0', '-r3..', 'file2'], ['4', '3.1.1'])
897
 
        # file2 up to a revision
898
 
        self.assertLogRevnos(['-n0', '-r..3', 'file2'], ['2'])
899
 
 
900
 
    def test_log_file3(self):
901
 
        self.prepare_tree()
902
 
        self.assertLogRevnos(['-n0', 'file3'], ['3'])
 
848
    def test_log_file(self):
 
849
        """The log for a particular file should only list revs for that file"""
 
850
        self.prepare_tree()
 
851
        log = self.run_bzr('log -n0 file1')[0]
 
852
        self.assertContainsRe(log, 'revno: 1\n')
 
853
        self.assertNotContainsRe(log, 'revno: 2\n')
 
854
        self.assertNotContainsRe(log, 'revno: 3\n')
 
855
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
856
        self.assertNotContainsRe(log, 'revno: 4 ')
 
857
        log = self.run_bzr('log -n0 file2')[0]
 
858
        self.assertNotContainsRe(log, 'revno: 1\n')
 
859
        self.assertContainsRe(log, 'revno: 2\n')
 
860
        self.assertNotContainsRe(log, 'revno: 3\n')
 
861
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
862
        self.assertContainsRe(log, 'revno: 4 ')
 
863
        log = self.run_bzr('log -n0 file3')[0]
 
864
        self.assertNotContainsRe(log, 'revno: 1\n')
 
865
        self.assertNotContainsRe(log, 'revno: 2\n')
 
866
        self.assertContainsRe(log, 'revno: 3\n')
 
867
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
868
        self.assertNotContainsRe(log, 'revno: 4 ')
 
869
        log = self.run_bzr('log -n0 -r3.1.1 file2')[0]
 
870
        self.assertNotContainsRe(log, 'revno: 1\n')
 
871
        self.assertNotContainsRe(log, 'revno: 2\n')
 
872
        self.assertNotContainsRe(log, 'revno: 3\n')
 
873
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
874
        self.assertNotContainsRe(log, 'revno: 4 ')
 
875
        log = self.run_bzr('log -n0 -r4 file2')[0]
 
876
        self.assertNotContainsRe(log, 'revno: 1\n')
 
877
        self.assertNotContainsRe(log, 'revno: 2\n')
 
878
        self.assertNotContainsRe(log, 'revno: 3\n')
 
879
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
880
        self.assertContainsRe(log, 'revno: 4 ')
 
881
        log = self.run_bzr('log -n0 -r3.. file2')[0]
 
882
        self.assertNotContainsRe(log, 'revno: 1\n')
 
883
        self.assertNotContainsRe(log, 'revno: 2\n')
 
884
        self.assertNotContainsRe(log, 'revno: 3\n')
 
885
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
886
        self.assertContainsRe(log, 'revno: 4 ')
 
887
        log = self.run_bzr('log -n0 -r..3 file2')[0]
 
888
        self.assertNotContainsRe(log, 'revno: 1\n')
 
889
        self.assertContainsRe(log, 'revno: 2\n')
 
890
        self.assertNotContainsRe(log, 'revno: 3\n')
 
891
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
892
        self.assertNotContainsRe(log, 'revno: 4 ')
903
893
 
904
894
    def test_log_file_historical_missing(self):
905
895
        # Check logging a deleted file gives an error if the
913
903
        # Check logging a deleted file is ok if the file existed
914
904
        # at the end the revision range
915
905
        self.prepare_tree(complex=True)
916
 
        self.assertLogRevnos(['-n0', '-r..4', 'file2'], ['4', '3.1.1', '2'])
 
906
        log, err = self.run_bzr('log -n0 -r..4 file2')
 
907
        self.assertEquals('', err)
 
908
        self.assertNotContainsRe(log, 'revno: 1\n')
 
909
        self.assertContainsRe(log, 'revno: 2\n')
 
910
        self.assertNotContainsRe(log, 'revno: 3\n')
 
911
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
912
        self.assertContainsRe(log, 'revno: 4 ')
917
913
 
918
914
    def test_log_file_historical_start(self):
919
915
        # Check logging a deleted file is ok if the file existed
920
916
        # at the start of the revision range
921
917
        self.prepare_tree(complex=True)
922
 
        self.assertLogRevnos(['file1'], ['1'])
 
918
        log, err = self.run_bzr('log file1')
 
919
        self.assertEquals('', err)
 
920
        self.assertContainsRe(log, 'revno: 1\n')
 
921
        self.assertNotContainsRe(log, 'revno: 2\n')
 
922
        self.assertNotContainsRe(log, 'revno: 3\n')
 
923
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
924
        self.assertNotContainsRe(log, 'revno: 4 ')
923
925
 
924
926
    def test_log_file_renamed(self):
925
927
        """File matched against revision range, not current tree."""
931
933
        self.assertContainsRe(err, err_msg)
932
934
 
933
935
        # Check we can see a renamed file if we give the right end revision
934
 
        self.assertLogRevnos(['-r..4', 'file3'], ['3'])
935
 
 
936
 
 
937
 
class TestLogMultiple(TestLogWithLogCatcher):
 
936
        log, err = self.run_bzr('log -r..4 file3')
 
937
        self.assertEquals('', err)
 
938
        self.assertNotContainsRe(log, 'revno: 1\n')
 
939
        self.assertNotContainsRe(log, 'revno: 2\n')
 
940
        self.assertContainsRe(log, 'revno: 3\n')
 
941
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
942
        self.assertNotContainsRe(log, 'revno: 4 ')
 
943
 
 
944
    def test_line_log_file(self):
 
945
        """The line log for a file should only list relevant mainline revs"""
 
946
        # Note: this also implicitly  covers the short logging case.
 
947
        # We test using --line in preference to --short because matching
 
948
        # revnos in the output of --line is more reliable.
 
949
        self.prepare_tree()
 
950
 
 
951
        # full history of file1
 
952
        log = self.run_bzr('log --line file1')[0]
 
953
        self.assertContainsRe(log, '^1:', re.MULTILINE)
 
954
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
955
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
956
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
957
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
958
 
 
959
        # full history of file2
 
960
        log = self.run_bzr('log --line file2')[0]
 
961
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
962
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
963
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
964
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
965
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
966
 
 
967
        # full history of file3
 
968
        log = self.run_bzr('log --line file3')[0]
 
969
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
970
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
971
        self.assertContainsRe(log, '^3:', re.MULTILINE)
 
972
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
973
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
974
 
 
975
        # file in a merge revision
 
976
        log = self.run_bzr('log --line -r3.1.1 file2')[0]
 
977
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
978
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
979
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
980
        self.assertContainsRe(log, '^3.1.1:', re.MULTILINE)
 
981
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
982
 
 
983
        # file in a mainline revision
 
984
        log = self.run_bzr('log --line -r4 file2')[0]
 
985
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
986
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
987
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
988
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
989
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
990
 
 
991
        # file since a revision
 
992
        log = self.run_bzr('log --line -r3.. file2')[0]
 
993
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
994
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
995
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
996
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
997
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
998
 
 
999
        # file up to a revision
 
1000
        log = self.run_bzr('log --line -r..3 file2')[0]
 
1001
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
1002
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
1003
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
1004
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
1005
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
1006
 
 
1007
 
 
1008
class TestLogMultiple(tests.TestCaseWithTransport):
938
1009
 
939
1010
    def prepare_tree(self):
940
1011
        tree = self.make_branch_and_tree('parent')
963
1034
        tree.commit(message='merge child branch')
964
1035
        os.chdir('parent')
965
1036
 
 
1037
    def assertRevnos(self, paths_str, expected_revnos):
 
1038
        # confirm the revision numbers in log --line output are those expected
 
1039
        out, err = self.run_bzr('log --line -n0 %s' % (paths_str,))
 
1040
        self.assertEqual('', err)
 
1041
        revnos = [s.split(':', 1)[0].lstrip() for s in out.splitlines()]
 
1042
        self.assertEqual(expected_revnos, revnos)
 
1043
 
966
1044
    def test_log_files(self):
967
1045
        """The log for multiple file should only list revs for those files"""
968
1046
        self.prepare_tree()
969
 
        self.assertLogRevnos(['file1', 'file2', 'dir1/dir2/file3'],
970
 
                             ['6', '5.1.1', '3', '2', '1'])
 
1047
        self.assertRevnos('file1 file2 dir1/dir2/file3',
 
1048
            ['6', '5.1.1', '3', '2', '1'])
971
1049
 
972
1050
    def test_log_directory(self):
973
1051
        """The log for a directory should show all nested files."""
974
1052
        self.prepare_tree()
975
 
        self.assertLogRevnos(['dir1'], ['5', '3'])
 
1053
        self.assertRevnos('dir1', ['5', '3'])
976
1054
 
977
1055
    def test_log_nested_directory(self):
978
1056
        """The log for a directory should show all nested files."""
979
1057
        self.prepare_tree()
980
 
        self.assertLogRevnos(['dir1/dir2'], ['3'])
 
1058
        self.assertRevnos('dir1/dir2', ['3'])
981
1059
 
982
1060
    def test_log_in_nested_directory(self):
983
1061
        """The log for a directory should show all nested files."""
984
1062
        self.prepare_tree()
985
1063
        os.chdir("dir1")
986
 
        self.assertLogRevnos(['.'], ['5', '3'])
 
1064
        self.assertRevnos('.', ['5', '3'])
987
1065
 
988
1066
    def test_log_files_and_directories(self):
989
1067
        """Logging files and directories together should be fine."""
990
1068
        self.prepare_tree()
991
 
        self.assertLogRevnos(['file4', 'dir1/dir2'], ['4', '3'])
 
1069
        self.assertRevnos('file4 dir1/dir2', ['4', '3'])
992
1070
 
993
1071
    def test_log_files_and_dirs_in_nested_directory(self):
994
1072
        """The log for a directory should show all nested files."""
995
1073
        self.prepare_tree()
996
1074
        os.chdir("dir1")
997
 
        self.assertLogRevnos(['dir2', 'file5'], ['5', '3'])
998
 
 
999
 
 
1000
 
class MainlineGhostTests(TestLogWithLogCatcher):
1001
 
 
1002
 
    def setUp(self):
1003
 
        super(MainlineGhostTests, self).setUp()
1004
 
        tree = self.make_branch_and_tree('')
1005
 
        tree.set_parent_ids(["spooky"], allow_leftmost_as_ghost=True)
1006
 
        tree.add('')
1007
 
        tree.commit('msg1', rev_id='rev1')
1008
 
        tree.commit('msg2', rev_id='rev2')
1009
 
 
1010
 
    def test_log_range(self):
1011
 
        self.assertLogRevnos(["-r1..2"], ["2", "1"])
1012
 
 
1013
 
    def test_log_norange(self):
1014
 
        self.assertLogRevnos([], ["2", "1"])
1015
 
 
1016
 
    def test_log_range_open_begin(self):
1017
 
        self.knownFailure("log with ghosts fails. bug #726466")
1018
 
        (stdout, stderr) = self.run_bzr(['log', '-r..2'], retcode=3)
1019
 
        self.assertEqual(["2", "1"],
1020
 
                         [r.revno for r in self.get_captured_revisions()])
1021
 
        self.assertEqual("bzr: ERROR: Further revision history missing.", stderr)
1022
 
 
1023
 
    def test_log_range_open_end(self):
1024
 
        self.assertLogRevnos(["-r1.."], ["2", "1"])
1025
 
 
1026
 
class TestLogMatch(TestLogWithLogCatcher):
1027
 
    def prepare_tree(self):
1028
 
        tree = self.make_branch_and_tree('')
1029
 
        self.build_tree(
1030
 
            ['/hello.txt', '/goodbye.txt'])
1031
 
        tree.add('hello.txt')
1032
 
        tree.commit(message='message1', committer='committer1', authors=['author1'])
1033
 
        tree.add('goodbye.txt')
1034
 
        tree.commit(message='message2', committer='committer2', authors=['author2'])
1035
 
    
1036
 
    def test_message(self):
1037
 
        self.prepare_tree()
1038
 
        self.assertLogRevnos(["-m", "message1"], ["1"])
1039
 
        self.assertLogRevnos(["-m", "message2"], ["2"])
1040
 
        self.assertLogRevnos(["-m", "message"], ["2", "1"])
1041
 
        self.assertLogRevnos(["-m", "message1", "-m", "message2"], ["2", "1"])
1042
 
        self.assertLogRevnos(["--match-message", "message1"], ["1"])
1043
 
        self.assertLogRevnos(["--match-message", "message2"], ["2"])
1044
 
        self.assertLogRevnos(["--match-message", "message"], ["2", "1"])
1045
 
        self.assertLogRevnos(["--match-message", "message1", 
1046
 
                              "--match-message", "message2"], ["2", "1"])
1047
 
        self.assertLogRevnos(["--message", "message1"], ["1"])
1048
 
        self.assertLogRevnos(["--message", "message2"], ["2"])
1049
 
        self.assertLogRevnos(["--message", "message"], ["2", "1"])
1050
 
        self.assertLogRevnos(["--match-message", "message1", 
1051
 
                              "--message", "message2"], ["2", "1"])
1052
 
        self.assertLogRevnos(["--message", "message1", 
1053
 
                              "--match-message", "message2"], ["2", "1"])
1054
 
 
1055
 
    def test_committer(self):
1056
 
        self.prepare_tree()
1057
 
        self.assertLogRevnos(["-m", "committer1"], ["1"])
1058
 
        self.assertLogRevnos(["-m", "committer2"], ["2"])
1059
 
        self.assertLogRevnos(["-m", "committer"], ["2", "1"])
1060
 
        self.assertLogRevnos(["-m", "committer1", "-m", "committer2"], 
1061
 
                             ["2", "1"])
1062
 
        self.assertLogRevnos(["--match-committer", "committer1"], ["1"])
1063
 
        self.assertLogRevnos(["--match-committer", "committer2"], ["2"])
1064
 
        self.assertLogRevnos(["--match-committer", "committer"], ["2", "1"])
1065
 
        self.assertLogRevnos(["--match-committer", "committer1", 
1066
 
                              "--match-committer", "committer2"], ["2", "1"])
1067
 
 
1068
 
    def test_author(self):
1069
 
        self.prepare_tree()
1070
 
        self.assertLogRevnos(["-m", "author1"], ["1"])
1071
 
        self.assertLogRevnos(["-m", "author2"], ["2"])
1072
 
        self.assertLogRevnos(["-m", "author"], ["2", "1"])
1073
 
        self.assertLogRevnos(["-m", "author1", "-m", "author2"], 
1074
 
                             ["2", "1"])
1075
 
        self.assertLogRevnos(["--match-author", "author1"], ["1"])
1076
 
        self.assertLogRevnos(["--match-author", "author2"], ["2"])
1077
 
        self.assertLogRevnos(["--match-author", "author"], ["2", "1"])
1078
 
        self.assertLogRevnos(["--match-author", "author1", 
1079
 
                              "--match-author", "author2"], ["2", "1"])
1080
 
 
1081
 
 
1082
 
class TestSmartServerLog(tests.TestCaseWithTransport):
1083
 
 
1084
 
    def test_standard_log(self):
1085
 
        self.setup_smart_server_with_call_log()
1086
 
        t = self.make_branch_and_tree('branch')
1087
 
        self.build_tree_contents([('branch/foo', 'thecontents')])
1088
 
        t.add("foo")
1089
 
        t.commit("message")
1090
 
        self.reset_smart_call_log()
1091
 
        out, err = self.run_bzr(['log', self.get_url('branch')])
1092
 
        # This figure represent the amount of work to perform this use case. It
1093
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
1094
 
        # being too low. If rpc_count increases, more network roundtrips have
1095
 
        # become necessary for this use case. Please do not adjust this number
1096
 
        # upwards without agreement from bzr's network support maintainers.
1097
 
        self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
1098
 
        self.assertLength(1, self.hpss_connections)
1099
 
        self.assertLength(9, self.hpss_calls)
1100
 
 
1101
 
    def test_verbose_log(self):
1102
 
        self.setup_smart_server_with_call_log()
1103
 
        t = self.make_branch_and_tree('branch')
1104
 
        self.build_tree_contents([('branch/foo', 'thecontents')])
1105
 
        t.add("foo")
1106
 
        t.commit("message")
1107
 
        self.reset_smart_call_log()
1108
 
        out, err = self.run_bzr(['log', '-v', self.get_url('branch')])
1109
 
        # This figure represent the amount of work to perform this use case. It
1110
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
1111
 
        # being too low. If rpc_count increases, more network roundtrips have
1112
 
        # become necessary for this use case. Please do not adjust this number
1113
 
        # upwards without agreement from bzr's network support maintainers.
1114
 
        self.assertLength(10, self.hpss_calls)
1115
 
        self.assertLength(1, self.hpss_connections)
1116
 
        self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
1117
 
 
1118
 
    def test_per_file(self):
1119
 
        self.setup_smart_server_with_call_log()
1120
 
        t = self.make_branch_and_tree('branch')
1121
 
        self.build_tree_contents([('branch/foo', 'thecontents')])
1122
 
        t.add("foo")
1123
 
        t.commit("message")
1124
 
        self.reset_smart_call_log()
1125
 
        out, err = self.run_bzr(['log', '-v', self.get_url('branch') + "/foo"])
1126
 
        # This figure represent the amount of work to perform this use case. It
1127
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
1128
 
        # being too low. If rpc_count increases, more network roundtrips have
1129
 
        # become necessary for this use case. Please do not adjust this number
1130
 
        # upwards without agreement from bzr's network support maintainers.
1131
 
        self.assertLength(14, self.hpss_calls)
1132
 
        self.assertLength(1, self.hpss_connections)
1133
 
        self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
 
1075
        self.assertRevnos('dir2 file5', ['5', '3'])