~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

(vila) Revise legal option names to be less drastic. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
 
1
# Copyright (C) 2006-2010 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
20
21
import os
21
 
import re
22
22
 
23
23
from bzrlib import (
 
24
    branchbuilder,
 
25
    errors,
 
26
    log,
24
27
    osutils,
25
28
    tests,
26
29
    )
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
 
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):
36
38
 
37
39
    def make_minimal_branch(self, path='.', format=None):
38
40
        tree = self.make_branch_and_tree(path, format=format)
63
65
        tree.commit(message='merge')
64
66
        return tree
65
67
 
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):
 
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'])
95
115
 
96
116
    def test_log_null_end_revspec(self):
97
117
        self.make_linear_branch()
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)
 
118
        self.assertLogRevnos(['-r1..'], ['3', '2', '1'])
109
119
 
110
120
    def test_log_null_begin_revspec(self):
111
121
        self.make_linear_branch()
112
 
        full_log = self.run_bzr(['log'])[0]
113
 
        log = self.run_bzr("log -r ..3")[0]
114
 
        self.assertEqualDiff(full_log, log)
 
122
        self.assertLogRevnos(['-r..3'], ['3', '2', '1'])
115
123
 
116
124
    def test_log_null_both_revspecs(self):
117
125
        self.make_linear_branch()
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'])
 
126
        self.assertLogRevnos(['-r..'], ['3', '2', '1'])
136
127
 
137
128
    def test_log_negative_begin_revspec_full_log(self):
138
129
        self.make_linear_branch()
139
 
        full_log = self.run_bzr(['log'])[0]
140
 
        log = self.run_bzr("log -r -3..")[0]
141
 
        self.assertEqualDiff(full_log, log)
 
130
        self.assertLogRevnos(['-r-3..'], ['3', '2', '1'])
142
131
 
143
132
    def test_log_negative_both_revspec_full_log(self):
144
133
        self.make_linear_branch()
145
 
        full_log = self.run_bzr(['log'])[0]
146
 
        log = self.run_bzr("log -r -3..-1")[0]
147
 
        self.assertEqualDiff(full_log, log)
 
134
        self.assertLogRevnos(['-r-3..-1'], ['3', '2', '1'])
148
135
 
149
136
    def test_log_negative_both_revspec_partial(self):
150
137
        self.make_linear_branch()
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)
 
138
        self.assertLogRevnos(['-r-3..-2'], ['2', '1'])
155
139
 
156
140
    def test_log_negative_begin_revspec(self):
157
141
        self.make_linear_branch()
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)
 
142
        self.assertLogRevnos(['-r-2..'], ['3', '2'])
162
143
 
163
144
    def test_log_positive_revspecs(self):
164
145
        self.make_linear_branch()
165
 
        full_log = self.run_bzr(['log'])[0]
166
 
        log = self.run_bzr("log -r 1..3")[0]
167
 
        self.assertEqualDiff(full_log, log)
 
146
        self.assertLogRevnos(['-r1..3'], ['3', '2', '1'])
168
147
 
169
148
    def test_log_dotted_revspecs(self):
170
149
        self.make_merged_branch()
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")
 
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):
 
320
        self.make_linear_branch('branch1')
 
321
        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'])
185
330
 
186
331
    def test_log_revno_n_path(self):
187
 
        self.make_linear_branch('branch1')
188
332
        self.make_linear_branch('branch2')
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)
 
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'])
200
355
 
201
356
    def test_log_nonexistent_revno(self):
202
357
        self.make_minimal_branch()
203
 
        (out, err) = self.run_bzr_error(
204
 
            ["bzr: ERROR: Requested revision: '1234' "
205
 
             "does not exist in branch:"],
206
 
            ['log', '-r1234'])
 
358
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
 
359
                            "does not exist in branch:"],
 
360
                           ['log', '-r1234'])
207
361
 
208
362
    def test_log_nonexistent_dotted_revno(self):
209
363
        self.make_minimal_branch()
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)
 
364
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
 
365
                            "does not exist in branch:"],
 
366
                           ['log',  '-r123.123'])
220
367
 
221
368
    def test_log_change_nonexistent_revno(self):
222
369
        self.make_minimal_branch()
223
 
        (out, err) = self.run_bzr_error(
224
 
            ["bzr: ERROR: Requested revision: '1234' "
225
 
             "does not exist in branch:"],
226
 
            ['log',  '-c1234'])
 
370
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
 
371
                            "does not exist in branch:"],
 
372
                           ['log',  '-c1234'])
227
373
 
228
374
    def test_log_change_nonexistent_dotted_revno(self):
229
375
        self.make_minimal_branch()
230
 
        (out, err) = self.run_bzr_error(
231
 
            ["bzr: ERROR: Requested revision: '123.123' "
232
 
             "does not exist in branch:"],
233
 
            ['log', '-c123.123'])
 
376
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
 
377
                            "does not exist in branch:"],
 
378
                           ['log', '-c123.123'])
234
379
 
235
380
    def test_log_change_single_revno_only(self):
236
381
        self.make_minimal_branch()
252
397
                              'Path unknown at end or start of revision range: '
253
398
                              'does-not-exist')
254
399
 
 
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
 
255
444
    def test_log_with_tags(self):
256
445
        tree = self.make_linear_branch(format='dirstate-tags')
257
446
        branch = tree.branch
282
471
        log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
283
472
        self.assertContainsRe(log, r'tags: tag1')
284
473
 
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'])
 
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)
330
492
 
331
493
 
332
494
class TestLogVerbose(TestLog):
350
512
    def test_log_short_verbose(self):
351
513
        self.assertUseShortDeltaFormat(['log', '--short', '-v'])
352
514
 
 
515
    def test_log_s_verbose(self):
 
516
        self.assertUseShortDeltaFormat(['log', '-S', '-v'])
 
517
 
353
518
    def test_log_short_verbose_verbose(self):
354
519
        self.assertUseLongDeltaFormat(['log', '--short', '-vv'])
355
520
 
364
529
        self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
365
530
 
366
531
 
367
 
class TestLogMerges(TestLog):
 
532
class TestLogMerges(TestLogWithLogCatcher):
368
533
 
369
534
    def setUp(self):
370
535
        super(TestLogMerges, self).setUp()
372
537
 
373
538
    def make_branches_with_merges(self):
374
539
        level0 = self.make_branch_and_tree('level0')
375
 
        level0.commit(message='in branch level0', **self.commit_options())
376
 
 
 
540
        self.wt_commit(level0, 'in branch level0')
377
541
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
378
 
        level1.commit(message='in branch level1', **self.commit_options())
379
 
 
 
542
        self.wt_commit(level1, 'in branch level1')
380
543
        level2 = level1.bzrdir.sprout('level2').open_workingtree()
381
 
        level2.commit(message='in branch level2', **self.commit_options())
382
 
 
 
544
        self.wt_commit(level2, 'in branch level2')
383
545
        level1.merge_from_branch(level2.branch)
384
 
        level1.commit(message='merge branch level2', **self.commit_options())
385
 
 
 
546
        self.wt_commit(level1, 'merge branch level2')
386
547
        level0.merge_from_branch(level1.branch)
387
 
        level0.commit(message='merge branch level1', **self.commit_options())
 
548
        self.wt_commit(level0, 'merge branch level1')
388
549
 
389
550
    def test_merges_are_indented_by_level(self):
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'])
 
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()])
428
558
 
429
559
    def test_force_merge_revisions_off(self):
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'])
 
560
        self.assertLogRevnos(['-n1'], ['2', '1'], working_dir='level0')
447
561
 
448
562
    def test_force_merge_revisions_on(self):
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'])
 
563
        self.assertLogRevnos(['-n0'], ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
 
564
                             working_dir='level0')
467
565
 
468
566
    def test_include_merges(self):
469
567
        # 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)
470
577
        out_im, err_im = self.run_bzr('log --include-merges',
471
578
                                      working_dir='level0')
472
579
        out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
473
 
        self.assertEqual('', err_im)
 
580
        self.assertEqual(msg, err_im)
474
581
        self.assertEqual('', err_n0)
475
582
        self.assertEqual(out_im, out_n0)
476
583
 
 
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
 
477
592
    def test_force_merge_revisions_N(self):
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'])
 
593
        self.assertLogRevnos(['-n2'],
 
594
                             ['2', '1.1.2', '1.1.1', '1'],
 
595
                             working_dir='level0')
493
596
 
494
597
    def test_merges_single_merge_rev(self):
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'])
 
598
        self.assertLogRevnosAndDepths(['-n0', '-r1.1.2'],
 
599
                                      [('1.1.2', 0), ('1.2.1', 1)],
 
600
                                      working_dir='level0')
512
601
 
513
602
    def test_merges_partial_range(self):
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):
 
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
541
629
 
542
630
    def setUp(self):
543
631
        super(TestLogDiff, self).setUp()
548
636
        self.build_tree(['level0/file1', 'level0/file2'])
549
637
        level0.add('file1')
550
638
        level0.add('file2')
551
 
        level0.commit(message='in branch level0', **self.commit_options())
 
639
        self.wt_commit(level0, 'in branch level0')
552
640
 
553
641
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
554
642
        self.build_tree_contents([('level1/file2', 'hello\n')])
555
 
        level1.commit(message='in branch level1', **self.commit_options())
 
643
        self.wt_commit(level1, 'in branch level1')
556
644
        level0.merge_from_branch(level1.branch)
557
 
        level0.commit(message='merge branch level1', **self.commit_options())
 
645
        self.wt_commit(level0, 'merge branch level1')
558
646
 
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'
 
647
    def _diff_file1_revno1(self):
 
648
        return """=== added file 'file1'
601
649
--- file1\t1970-01-01 00:00:00 +0000
602
 
+++ file1\t2005-11-22 00:00:01 +0000
 
650
+++ file1\t2005-11-22 00:00:00 +0000
603
651
@@ -0,0 +1,1 @@
604
652
+contents of level0/file1
605
653
 
606
 
=== added file 'file2'
 
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'
607
678
--- file2\t1970-01-01 00:00:00 +0000
608
 
+++ file2\t2005-11-22 00:00:01 +0000
 
679
+++ file2\t2005-11-22 00:00:00 +0000
609
680
@@ -0,0 +1,1 @@
610
681
+contents of level0/file2
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'])
 
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')
688
723
 
689
724
 
690
725
class TestLogUnicodeDiff(TestLog):
733
768
 
734
769
    def setUp(self):
735
770
        super(TestLogEncodings, self).setUp()
736
 
        self.user_encoding = osutils._cached_user_encoding
737
 
        def restore():
738
 
            osutils._cached_user_encoding = self.user_encoding
739
 
        self.addCleanup(restore)
 
771
        self.overrideAttr(osutils, '_cached_user_encoding')
740
772
 
741
773
    def create_branch(self):
742
774
        bzr = self.run_bzr
743
775
        bzr('init')
744
 
        open('a', 'wb').write('some stuff\n')
 
776
        self.build_tree_contents([('a', 'some stuff\n')])
745
777
        bzr('add a')
746
778
        bzr(['commit', '-m', self._message])
747
779
 
810
842
        self.assertEquals(-1, stdout.find(test_in_cp1251))
811
843
 
812
844
 
813
 
class TestLogFile(tests.TestCaseWithTransport):
 
845
class TestLogFile(TestLogWithLogCatcher):
814
846
 
815
847
    def test_log_local_branch_file(self):
816
848
        """We should be able to log files in local treeless branches"""
845
877
            tree.commit('remove file1')
846
878
        os.chdir('parent')
847
879
 
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 ')
 
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'])
893
903
 
894
904
    def test_log_file_historical_missing(self):
895
905
        # Check logging a deleted file gives an error if the
903
913
        # Check logging a deleted file is ok if the file existed
904
914
        # at the end the revision range
905
915
        self.prepare_tree(complex=True)
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 ')
 
916
        self.assertLogRevnos(['-n0', '-r..4', 'file2'], ['4', '3.1.1', '2'])
913
917
 
914
918
    def test_log_file_historical_start(self):
915
919
        # Check logging a deleted file is ok if the file existed
916
920
        # at the start of the revision range
917
921
        self.prepare_tree(complex=True)
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 ')
 
922
        self.assertLogRevnos(['file1'], ['1'])
925
923
 
926
924
    def test_log_file_renamed(self):
927
925
        """File matched against revision range, not current tree."""
933
931
        self.assertContainsRe(err, err_msg)
934
932
 
935
933
        # Check we can see a renamed file if we give the right end revision
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):
 
934
        self.assertLogRevnos(['-r..4', 'file3'], ['3'])
 
935
 
 
936
 
 
937
class TestLogMultiple(TestLogWithLogCatcher):
1009
938
 
1010
939
    def prepare_tree(self):
1011
940
        tree = self.make_branch_and_tree('parent')
1034
963
        tree.commit(message='merge child branch')
1035
964
        os.chdir('parent')
1036
965
 
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
 
 
1044
966
    def test_log_files(self):
1045
967
        """The log for multiple file should only list revs for those files"""
1046
968
        self.prepare_tree()
1047
 
        self.assertRevnos('file1 file2 dir1/dir2/file3',
1048
 
            ['6', '5.1.1', '3', '2', '1'])
 
969
        self.assertLogRevnos(['file1', 'file2', 'dir1/dir2/file3'],
 
970
                             ['6', '5.1.1', '3', '2', '1'])
1049
971
 
1050
972
    def test_log_directory(self):
1051
973
        """The log for a directory should show all nested files."""
1052
974
        self.prepare_tree()
1053
 
        self.assertRevnos('dir1', ['5', '3'])
 
975
        self.assertLogRevnos(['dir1'], ['5', '3'])
1054
976
 
1055
977
    def test_log_nested_directory(self):
1056
978
        """The log for a directory should show all nested files."""
1057
979
        self.prepare_tree()
1058
 
        self.assertRevnos('dir1/dir2', ['3'])
 
980
        self.assertLogRevnos(['dir1/dir2'], ['3'])
1059
981
 
1060
982
    def test_log_in_nested_directory(self):
1061
983
        """The log for a directory should show all nested files."""
1062
984
        self.prepare_tree()
1063
985
        os.chdir("dir1")
1064
 
        self.assertRevnos('.', ['5', '3'])
 
986
        self.assertLogRevnos(['.'], ['5', '3'])
1065
987
 
1066
988
    def test_log_files_and_directories(self):
1067
989
        """Logging files and directories together should be fine."""
1068
990
        self.prepare_tree()
1069
 
        self.assertRevnos('file4 dir1/dir2', ['4', '3'])
 
991
        self.assertLogRevnos(['file4', 'dir1/dir2'], ['4', '3'])
1070
992
 
1071
993
    def test_log_files_and_dirs_in_nested_directory(self):
1072
994
        """The log for a directory should show all nested files."""
1073
995
        self.prepare_tree()
1074
996
        os.chdir("dir1")
1075
 
        self.assertRevnos('dir2 file5', ['5', '3'])
 
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.assertEquals("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)