~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Vincent Ladeuil
  • Date: 2012-03-13 16:42:20 UTC
  • mto: This revision was merged to the branch mainline in revision 6512.
  • Revision ID: v.ladeuil+lp@free.fr-20120313164220-atkou2zprhlspmwg
Mention that a given config option cannot be safely handled via both APIs at the same time.

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'])
 
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')
538
607
 
539
608
    def test_merges_partial_range_ignore_before_lower_bound(self):
540
609
        """Dont show revisions before the lower bound's merged revs"""
541
 
        expected = """\
542
 
    2 Lorem Ipsum\t2005-11-22 [merge]
543
 
      merge branch level1
544
 
 
545
 
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
546
 
                merge branch level2
547
 
 
548
 
              1.2.1 Lorem Ipsum\t2005-11-22
549
 
                    in branch level2
550
 
 
551
 
"""
552
 
        self.check_log(expected, ['--short', '-n0', '-r1.1.2..2'])
553
 
 
554
 
 
555
 
class TestLogDiff(TestLog):
 
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
556
629
 
557
630
    def setUp(self):
558
631
        super(TestLogDiff, self).setUp()
563
636
        self.build_tree(['level0/file1', 'level0/file2'])
564
637
        level0.add('file1')
565
638
        level0.add('file2')
566
 
        level0.commit(message='in branch level0', **self.commit_options())
 
639
        self.wt_commit(level0, 'in branch level0')
567
640
 
568
641
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
569
642
        self.build_tree_contents([('level1/file2', 'hello\n')])
570
 
        level1.commit(message='in branch level1', **self.commit_options())
 
643
        self.wt_commit(level1, 'in branch level1')
571
644
        level0.merge_from_branch(level1.branch)
572
 
        level0.commit(message='merge branch level1', **self.commit_options())
 
645
        self.wt_commit(level0, 'merge branch level1')
573
646
 
574
 
    def test_log_show_diff_long_with_merges(self):
575
 
        out,err = self.run_bzr('log -p -n0')
576
 
        self.assertEqual('', err)
577
 
        log = test_log.normalize_log(out)
578
 
        expected = """\
579
 
------------------------------------------------------------
580
 
revno: 2 [merge]
581
 
committer: Lorem Ipsum <test@example.com>
582
 
branch nick: level0
583
 
timestamp: Just now
584
 
message:
585
 
  merge branch level1
586
 
diff:
587
 
=== modified file 'file2'
588
 
--- file2\t2005-11-22 00:00:01 +0000
589
 
+++ file2\t2005-11-22 00:00:02 +0000
590
 
@@ -1,1 +1,1 @@
591
 
-contents of level0/file2
592
 
+hello
593
 
    ------------------------------------------------------------
594
 
    revno: 1.1.1
595
 
    committer: Lorem Ipsum <test@example.com>
596
 
    branch nick: level1
597
 
    timestamp: Just now
598
 
    message:
599
 
      in branch level1
600
 
    diff:
601
 
    === modified file 'file2'
602
 
    --- file2\t2005-11-22 00:00:01 +0000
603
 
    +++ file2\t2005-11-22 00:00:02 +0000
604
 
    @@ -1,1 +1,1 @@
605
 
    -contents of level0/file2
606
 
    +hello
607
 
------------------------------------------------------------
608
 
revno: 1
609
 
committer: Lorem Ipsum <test@example.com>
610
 
branch nick: level0
611
 
timestamp: Just now
612
 
message:
613
 
  in branch level0
614
 
diff:
615
 
=== added file 'file1'
 
647
    def _diff_file1_revno1(self):
 
648
        return """=== added file 'file1'
616
649
--- file1\t1970-01-01 00:00:00 +0000
617
 
+++ file1\t2005-11-22 00:00:01 +0000
 
650
+++ file1\t2005-11-22 00:00:00 +0000
618
651
@@ -0,0 +1,1 @@
619
652
+contents of level0/file1
620
653
 
621
 
=== 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'
622
678
--- file2\t1970-01-01 00:00:00 +0000
623
 
+++ file2\t2005-11-22 00:00:01 +0000
 
679
+++ file2\t2005-11-22 00:00:00 +0000
624
680
@@ -0,0 +1,1 @@
625
681
+contents of level0/file2
626
 
"""
627
 
        self.check_log(expected, ['-p', '-n0'])
628
 
 
629
 
    def test_log_show_diff_short(self):
630
 
        expected = """\
631
 
    2 Lorem Ipsum\t2005-11-22 [merge]
632
 
      merge branch level1
633
 
      === modified file 'file2'
634
 
      --- file2\t2005-11-22 00:00:01 +0000
635
 
      +++ file2\t2005-11-22 00:00:02 +0000
636
 
      @@ -1,1 +1,1 @@
637
 
      -contents of level0/file2
638
 
      +hello
639
 
 
640
 
    1 Lorem Ipsum\t2005-11-22
641
 
      in branch level0
642
 
      === added file 'file1'
643
 
      --- file1\t1970-01-01 00:00:00 +0000
644
 
      +++ file1\t2005-11-22 00:00:01 +0000
645
 
      @@ -0,0 +1,1 @@
646
 
      +contents of level0/file1
647
 
\x20\x20\x20\x20\x20\x20
648
 
      === added file 'file2'
649
 
      --- file2\t1970-01-01 00:00:00 +0000
650
 
      +++ file2\t2005-11-22 00:00:01 +0000
651
 
      @@ -0,0 +1,1 @@
652
 
      +contents of level0/file2
653
 
 
654
 
Use --include-merges or -n0 to see merged revisions.
655
 
"""
656
 
        self.check_log(expected, ['-p', '--short'])
657
 
 
658
 
    def test_log_show_diff_line(self):
659
 
        # Not supported by this formatter so expect plain output
660
 
        expected = """\
661
 
2: Lorem Ipsum 2005-11-22 [merge] merge branch level1
662
 
1: Lorem Ipsum 2005-11-22 in branch level0
663
 
"""
664
 
        self.check_log(expected, ['-p', '--line'])
665
 
 
666
 
    def test_log_show_diff_file1(self):
667
 
        """Only the diffs for the given file are to be shown"""
668
 
        expected = """\
669
 
    1 Lorem Ipsum\t2005-11-22
670
 
      in branch level0
671
 
      === added file 'file1'
672
 
      --- file1\t1970-01-01 00:00:00 +0000
673
 
      +++ file1\t2005-11-22 00:00:01 +0000
674
 
      @@ -0,0 +1,1 @@
675
 
      +contents of level0/file1
676
 
 
677
 
"""
678
 
        self.check_log(expected, ['-p', '--short', 'file1'])
679
 
 
680
 
    def test_log_show_diff_file2(self):
681
 
        """Only the diffs for the given file are to be shown"""
682
 
        expected = """\
683
 
    2 Lorem Ipsum\t2005-11-22 [merge]
684
 
      merge branch level1
685
 
      === modified file 'file2'
686
 
      --- file2\t2005-11-22 00:00:01 +0000
687
 
      +++ file2\t2005-11-22 00:00:02 +0000
688
 
      @@ -1,1 +1,1 @@
689
 
      -contents of level0/file2
690
 
      +hello
691
 
 
692
 
    1 Lorem Ipsum\t2005-11-22
693
 
      in branch level0
694
 
      === added file 'file2'
695
 
      --- file2\t1970-01-01 00:00:00 +0000
696
 
      +++ file2\t2005-11-22 00:00:01 +0000
697
 
      @@ -0,0 +1,1 @@
698
 
      +contents of level0/file2
699
 
 
700
 
Use --include-merges or -n0 to see merged revisions.
701
 
"""
702
 
        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')
703
723
 
704
724
 
705
725
class TestLogUnicodeDiff(TestLog):
748
768
 
749
769
    def setUp(self):
750
770
        super(TestLogEncodings, self).setUp()
751
 
        self.user_encoding = osutils._cached_user_encoding
752
 
        def restore():
753
 
            osutils._cached_user_encoding = self.user_encoding
754
 
        self.addCleanup(restore)
 
771
        self.overrideAttr(osutils, '_cached_user_encoding')
755
772
 
756
773
    def create_branch(self):
757
774
        bzr = self.run_bzr
758
775
        bzr('init')
759
 
        open('a', 'wb').write('some stuff\n')
 
776
        self.build_tree_contents([('a', 'some stuff\n')])
760
777
        bzr('add a')
761
778
        bzr(['commit', '-m', self._message])
762
779
 
825
842
        self.assertEquals(-1, stdout.find(test_in_cp1251))
826
843
 
827
844
 
828
 
class TestLogFile(tests.TestCaseWithTransport):
 
845
class TestLogFile(TestLogWithLogCatcher):
829
846
 
830
847
    def test_log_local_branch_file(self):
831
848
        """We should be able to log files in local treeless branches"""
860
877
            tree.commit('remove file1')
861
878
        os.chdir('parent')
862
879
 
863
 
    def test_log_file(self):
864
 
        """The log for a particular file should only list revs for that file"""
865
 
        self.prepare_tree()
866
 
        log = self.run_bzr('log -n0 file1')[0]
867
 
        self.assertContainsRe(log, 'revno: 1\n')
868
 
        self.assertNotContainsRe(log, 'revno: 2\n')
869
 
        self.assertNotContainsRe(log, 'revno: 3\n')
870
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
871
 
        self.assertNotContainsRe(log, 'revno: 4 ')
872
 
        log = self.run_bzr('log -n0 file2')[0]
873
 
        self.assertNotContainsRe(log, 'revno: 1\n')
874
 
        self.assertContainsRe(log, 'revno: 2\n')
875
 
        self.assertNotContainsRe(log, 'revno: 3\n')
876
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
877
 
        self.assertContainsRe(log, 'revno: 4 ')
878
 
        log = self.run_bzr('log -n0 file3')[0]
879
 
        self.assertNotContainsRe(log, 'revno: 1\n')
880
 
        self.assertNotContainsRe(log, 'revno: 2\n')
881
 
        self.assertContainsRe(log, 'revno: 3\n')
882
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
883
 
        self.assertNotContainsRe(log, 'revno: 4 ')
884
 
        log = self.run_bzr('log -n0 -r3.1.1 file2')[0]
885
 
        self.assertNotContainsRe(log, 'revno: 1\n')
886
 
        self.assertNotContainsRe(log, 'revno: 2\n')
887
 
        self.assertNotContainsRe(log, 'revno: 3\n')
888
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
889
 
        self.assertNotContainsRe(log, 'revno: 4 ')
890
 
        log = self.run_bzr('log -n0 -r4 file2')[0]
891
 
        self.assertNotContainsRe(log, 'revno: 1\n')
892
 
        self.assertNotContainsRe(log, 'revno: 2\n')
893
 
        self.assertNotContainsRe(log, 'revno: 3\n')
894
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
895
 
        self.assertContainsRe(log, 'revno: 4 ')
896
 
        log = self.run_bzr('log -n0 -r3.. file2')[0]
897
 
        self.assertNotContainsRe(log, 'revno: 1\n')
898
 
        self.assertNotContainsRe(log, 'revno: 2\n')
899
 
        self.assertNotContainsRe(log, 'revno: 3\n')
900
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
901
 
        self.assertContainsRe(log, 'revno: 4 ')
902
 
        log = self.run_bzr('log -n0 -r..3 file2')[0]
903
 
        self.assertNotContainsRe(log, 'revno: 1\n')
904
 
        self.assertContainsRe(log, 'revno: 2\n')
905
 
        self.assertNotContainsRe(log, 'revno: 3\n')
906
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
907
 
        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'])
908
903
 
909
904
    def test_log_file_historical_missing(self):
910
905
        # Check logging a deleted file gives an error if the
918
913
        # Check logging a deleted file is ok if the file existed
919
914
        # at the end the revision range
920
915
        self.prepare_tree(complex=True)
921
 
        log, err = self.run_bzr('log -n0 -r..4 file2')
922
 
        self.assertEquals('', err)
923
 
        self.assertNotContainsRe(log, 'revno: 1\n')
924
 
        self.assertContainsRe(log, 'revno: 2\n')
925
 
        self.assertNotContainsRe(log, 'revno: 3\n')
926
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
927
 
        self.assertContainsRe(log, 'revno: 4 ')
 
916
        self.assertLogRevnos(['-n0', '-r..4', 'file2'], ['4', '3.1.1', '2'])
928
917
 
929
918
    def test_log_file_historical_start(self):
930
919
        # Check logging a deleted file is ok if the file existed
931
920
        # at the start of the revision range
932
921
        self.prepare_tree(complex=True)
933
 
        log, err = self.run_bzr('log file1')
934
 
        self.assertEquals('', err)
935
 
        self.assertContainsRe(log, 'revno: 1\n')
936
 
        self.assertNotContainsRe(log, 'revno: 2\n')
937
 
        self.assertNotContainsRe(log, 'revno: 3\n')
938
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
939
 
        self.assertNotContainsRe(log, 'revno: 4 ')
 
922
        self.assertLogRevnos(['file1'], ['1'])
940
923
 
941
924
    def test_log_file_renamed(self):
942
925
        """File matched against revision range, not current tree."""
948
931
        self.assertContainsRe(err, err_msg)
949
932
 
950
933
        # Check we can see a renamed file if we give the right end revision
951
 
        log, err = self.run_bzr('log -r..4 file3')
952
 
        self.assertEquals('', err)
953
 
        self.assertNotContainsRe(log, 'revno: 1\n')
954
 
        self.assertNotContainsRe(log, 'revno: 2\n')
955
 
        self.assertContainsRe(log, 'revno: 3\n')
956
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
957
 
        self.assertNotContainsRe(log, 'revno: 4 ')
958
 
 
959
 
    def test_line_log_file(self):
960
 
        """The line log for a file should only list relevant mainline revs"""
961
 
        # Note: this also implicitly  covers the short logging case.
962
 
        # We test using --line in preference to --short because matching
963
 
        # revnos in the output of --line is more reliable.
964
 
        self.prepare_tree()
965
 
 
966
 
        # full history of file1
967
 
        log = self.run_bzr('log --line file1')[0]
968
 
        self.assertContainsRe(log, '^1:', re.MULTILINE)
969
 
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
970
 
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
971
 
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
972
 
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
973
 
 
974
 
        # full history of file2
975
 
        log = self.run_bzr('log --line file2')[0]
976
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
977
 
        self.assertContainsRe(log, '^2:', re.MULTILINE)
978
 
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
979
 
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
980
 
        self.assertContainsRe(log, '^4:', re.MULTILINE)
981
 
 
982
 
        # full history of file3
983
 
        log = self.run_bzr('log --line file3')[0]
984
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
985
 
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
986
 
        self.assertContainsRe(log, '^3:', re.MULTILINE)
987
 
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
988
 
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
989
 
 
990
 
        # file in a merge revision
991
 
        log = self.run_bzr('log --line -r3.1.1 file2')[0]
992
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
993
 
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
994
 
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
995
 
        self.assertContainsRe(log, '^3.1.1:', re.MULTILINE)
996
 
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
997
 
 
998
 
        # file in a mainline revision
999
 
        log = self.run_bzr('log --line -r4 file2')[0]
1000
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
1001
 
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
1002
 
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
1003
 
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
1004
 
        self.assertContainsRe(log, '^4:', re.MULTILINE)
1005
 
 
1006
 
        # file since a revision
1007
 
        log = self.run_bzr('log --line -r3.. file2')[0]
1008
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
1009
 
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
1010
 
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
1011
 
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
1012
 
        self.assertContainsRe(log, '^4:', re.MULTILINE)
1013
 
 
1014
 
        # file up to a revision
1015
 
        log = self.run_bzr('log --line -r..3 file2')[0]
1016
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
1017
 
        self.assertContainsRe(log, '^2:', re.MULTILINE)
1018
 
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
1019
 
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
1020
 
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
1021
 
 
1022
 
 
1023
 
class TestLogMultiple(tests.TestCaseWithTransport):
 
934
        self.assertLogRevnos(['-r..4', 'file3'], ['3'])
 
935
 
 
936
 
 
937
class TestLogMultiple(TestLogWithLogCatcher):
1024
938
 
1025
939
    def prepare_tree(self):
1026
940
        tree = self.make_branch_and_tree('parent')
1049
963
        tree.commit(message='merge child branch')
1050
964
        os.chdir('parent')
1051
965
 
1052
 
    def assertRevnos(self, paths_str, expected_revnos):
1053
 
        # confirm the revision numbers in log --line output are those expected
1054
 
        out, err = self.run_bzr('log --line -n0 %s' % (paths_str,))
1055
 
        self.assertEqual('', err)
1056
 
        revnos = [s.split(':', 1)[0].lstrip() for s in out.splitlines()]
1057
 
        self.assertEqual(expected_revnos, revnos)
1058
 
 
1059
966
    def test_log_files(self):
1060
967
        """The log for multiple file should only list revs for those files"""
1061
968
        self.prepare_tree()
1062
 
        self.assertRevnos('file1 file2 dir1/dir2/file3',
1063
 
            ['6', '5.1.1', '3', '2', '1'])
 
969
        self.assertLogRevnos(['file1', 'file2', 'dir1/dir2/file3'],
 
970
                             ['6', '5.1.1', '3', '2', '1'])
1064
971
 
1065
972
    def test_log_directory(self):
1066
973
        """The log for a directory should show all nested files."""
1067
974
        self.prepare_tree()
1068
 
        self.assertRevnos('dir1', ['5', '3'])
 
975
        self.assertLogRevnos(['dir1'], ['5', '3'])
1069
976
 
1070
977
    def test_log_nested_directory(self):
1071
978
        """The log for a directory should show all nested files."""
1072
979
        self.prepare_tree()
1073
 
        self.assertRevnos('dir1/dir2', ['3'])
 
980
        self.assertLogRevnos(['dir1/dir2'], ['3'])
1074
981
 
1075
982
    def test_log_in_nested_directory(self):
1076
983
        """The log for a directory should show all nested files."""
1077
984
        self.prepare_tree()
1078
985
        os.chdir("dir1")
1079
 
        self.assertRevnos('.', ['5', '3'])
 
986
        self.assertLogRevnos(['.'], ['5', '3'])
1080
987
 
1081
988
    def test_log_files_and_directories(self):
1082
989
        """Logging files and directories together should be fine."""
1083
990
        self.prepare_tree()
1084
 
        self.assertRevnos('file4 dir1/dir2', ['4', '3'])
 
991
        self.assertLogRevnos(['file4', 'dir1/dir2'], ['4', '3'])
1085
992
 
1086
993
    def test_log_files_and_dirs_in_nested_directory(self):
1087
994
        """The log for a directory should show all nested files."""
1088
995
        self.prepare_tree()
1089
996
        os.chdir("dir1")
1090
 
        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)