~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-01-07 17:02:44 UTC
  • mfrom: (4934.1.14 2.1.0rc1-set-mtime)
  • Revision ID: pqm@pqm.ubuntu.com-20100107170244-3cgdapvuokgf8l42
(jam,
        gz) (bug #488724) Set the mtime of files touched in a TreeTransform.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
 
18
18
"""Black-box tests for bzr log."""
19
19
 
20
 
from itertools import izip
21
20
import os
22
21
import re
23
22
 
24
23
from bzrlib import (
25
 
    branchbuilder,
26
 
    errors,
27
 
    log,
28
24
    osutils,
29
25
    tests,
30
26
    )
31
 
from bzrlib.tests import (
32
 
    script,
33
 
    test_log,
34
 
    )
35
 
 
36
 
 
37
 
class TestLog(tests.TestCaseWithTransport, test_log.TestLogMixin):
 
27
from bzrlib.tests import test_log
 
28
 
 
29
 
 
30
class TestLog(tests.TestCaseWithTransport):
 
31
 
 
32
    def setUp(self):
 
33
        super(TestLog, self).setUp()
 
34
        self.timezone = 0 # UTC
 
35
        self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
38
36
 
39
37
    def make_minimal_branch(self, path='.', format=None):
40
38
        tree = self.make_branch_and_tree(path, format=format)
65
63
        tree.commit(message='merge')
66
64
        return tree
67
65
 
68
 
 
69
 
class TestLogWithLogCatcher(TestLog):
70
 
 
71
 
    def setUp(self):
72
 
        super(TestLogWithLogCatcher, self).setUp()
73
 
        # Capture log formatter creations
74
 
        class MyLogFormatter(test_log.LogCatcher):
75
 
 
76
 
            def __new__(klass, *args, **kwargs):
77
 
                self.log_catcher = test_log.LogCatcher(*args, **kwargs)
78
 
                # Always return our own log formatter
79
 
                return self.log_catcher
80
 
 
81
 
        def getme(branch):
82
 
                # Always return our own log formatter class hijacking the
83
 
                # default behavior (which requires setting up a config
84
 
                # variable)
85
 
            return MyLogFormatter
86
 
        self.overrideAttr(log.log_formatter_registry, 'get_default', getme)
87
 
 
88
 
    def get_captured_revisions(self):
89
 
        return self.log_catcher.revisions
90
 
 
91
 
    def assertLogRevnos(self, args, expected_revnos, working_dir='.'):
92
 
        self.run_bzr(['log'] + args, working_dir=working_dir)
93
 
        self.assertEqual(expected_revnos,
94
 
                         [r.revno for r in self.get_captured_revisions()])
95
 
 
96
 
    def assertLogRevnosAndDepths(self, args, expected_revnos_and_depths,
97
 
                                working_dir='.'):
98
 
        self.run_bzr(['log'] + args, working_dir=working_dir)
99
 
        self.assertEqual(expected_revnos_and_depths,
100
 
                         [(r.revno, r.merge_depth)
101
 
                           for r in self.get_captured_revisions()])
102
 
 
103
 
 
104
 
class TestLogRevSpecs(TestLogWithLogCatcher):
105
 
 
106
 
    def test_log_no_revspec(self):
107
 
        self.make_linear_branch()
108
 
        self.assertLogRevnos([], ['3', '2', '1'])
 
66
    def assertRevnos(self, log, must_have=(), must_not_have=()):
 
67
        """Check if revnos are in or not in the log output"""
 
68
        for revno in must_have:
 
69
            self.assertTrue(('revno: %s\n' % revno) in log,
 
70
                'Does not contain expected revno %s' % revno)
 
71
        for revno in must_not_have:
 
72
            self.assertFalse(('revno: %s\n' % revno) in log,
 
73
                'Contains unexpected revno %s' % revno)
 
74
 
 
75
    def commit_options(self):
 
76
        """Use some mostly fixed values for commits to simplify tests.
 
77
 
 
78
        Tests can use this function to get some commit attributes. The time
 
79
        stamp is incremented at each commit.
 
80
        """
 
81
        self.timestamp += 1 # 1 second between each commit
 
82
        return dict(committer='Lorem Ipsum <joe@foo.com>',
 
83
                 timezone=self.timezone,
 
84
                 timestamp=self.timestamp,
 
85
                 )
 
86
 
 
87
    def check_log(self, expected, args, working_dir='level0'):
 
88
        out, err = self.run_bzr(['log', '--timezone', 'utc'] + args,
 
89
                                working_dir=working_dir)
 
90
        self.assertEqual('', err)
 
91
        self.assertEqualDiff(expected, test_log.normalize_log(out))
 
92
 
 
93
 
 
94
class TestLogRevSpecs(TestLog):
109
95
 
110
96
    def test_log_null_end_revspec(self):
111
97
        self.make_linear_branch()
112
 
        self.assertLogRevnos(['-r1..'], ['3', '2', '1'])
 
98
        log = self.run_bzr(['log'])[0]
 
99
        self.assertTrue('revno: 1\n' in log)
 
100
        self.assertTrue('revno: 2\n' in log)
 
101
        self.assertTrue('revno: 3\n' in log)
 
102
        self.assertTrue('message:\n  message1\n' in log)
 
103
        self.assertTrue('message:\n  message2\n' in log)
 
104
        self.assertTrue('message:\n  message3\n' in log)
 
105
 
 
106
        full_log = self.run_bzr(['log'])[0]
 
107
        log = self.run_bzr("log -r 1..")[0]
 
108
        self.assertEqualDiff(log, full_log)
113
109
 
114
110
    def test_log_null_begin_revspec(self):
115
111
        self.make_linear_branch()
116
 
        self.assertLogRevnos(['-r..3'], ['3', '2', '1'])
 
112
        full_log = self.run_bzr(['log'])[0]
 
113
        log = self.run_bzr("log -r ..3")[0]
 
114
        self.assertEqualDiff(full_log, log)
117
115
 
118
116
    def test_log_null_both_revspecs(self):
119
117
        self.make_linear_branch()
120
 
        self.assertLogRevnos(['-r..'], ['3', '2', '1'])
 
118
        full_log = self.run_bzr(['log'])[0]
 
119
        log = self.run_bzr("log -r ..")[0]
 
120
        self.assertEqualDiff(full_log, log)
 
121
 
 
122
    def test_log_zero_revspec(self):
 
123
        self.make_minimal_branch()
 
124
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
125
                           ['log', '-r0'])
 
126
 
 
127
    def test_log_zero_begin_revspec(self):
 
128
        self.make_linear_branch()
 
129
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
130
                           ['log', '-r0..2'])
 
131
 
 
132
    def test_log_zero_end_revspec(self):
 
133
        self.make_linear_branch()
 
134
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
135
                           ['log', '-r-2..0'])
121
136
 
122
137
    def test_log_negative_begin_revspec_full_log(self):
123
138
        self.make_linear_branch()
124
 
        self.assertLogRevnos(['-r-3..'], ['3', '2', '1'])
 
139
        full_log = self.run_bzr(['log'])[0]
 
140
        log = self.run_bzr("log -r -3..")[0]
 
141
        self.assertEqualDiff(full_log, log)
125
142
 
126
143
    def test_log_negative_both_revspec_full_log(self):
127
144
        self.make_linear_branch()
128
 
        self.assertLogRevnos(['-r-3..-1'], ['3', '2', '1'])
 
145
        full_log = self.run_bzr(['log'])[0]
 
146
        log = self.run_bzr("log -r -3..-1")[0]
 
147
        self.assertEqualDiff(full_log, log)
129
148
 
130
149
    def test_log_negative_both_revspec_partial(self):
131
150
        self.make_linear_branch()
132
 
        self.assertLogRevnos(['-r-3..-2'], ['2', '1'])
 
151
        log = self.run_bzr("log -r -3..-2")[0]
 
152
        self.assertTrue('revno: 1\n' in log)
 
153
        self.assertTrue('revno: 2\n' in log)
 
154
        self.assertTrue('revno: 3\n' not in log)
133
155
 
134
156
    def test_log_negative_begin_revspec(self):
135
157
        self.make_linear_branch()
136
 
        self.assertLogRevnos(['-r-2..'], ['3', '2'])
 
158
        log = self.run_bzr("log -r -2..")[0]
 
159
        self.assertTrue('revno: 1\n' not in log)
 
160
        self.assertTrue('revno: 2\n' in log)
 
161
        self.assertTrue('revno: 3\n' in log)
137
162
 
138
163
    def test_log_positive_revspecs(self):
139
164
        self.make_linear_branch()
140
 
        self.assertLogRevnos(['-r1..3'], ['3', '2', '1'])
 
165
        full_log = self.run_bzr(['log'])[0]
 
166
        log = self.run_bzr("log -r 1..3")[0]
 
167
        self.assertEqualDiff(full_log, log)
141
168
 
142
169
    def test_log_dotted_revspecs(self):
143
170
        self.make_merged_branch()
144
 
        self.assertLogRevnos(['-n0', '-r1..1.1.1'], ['1.1.1', '1'])
145
 
 
146
 
    def test_log_limit(self):
147
 
        tree = self.make_branch_and_tree('.')
148
 
        # We want more commits than our batch size starts at
149
 
        for pos in range(10):
150
 
            tree.commit("%s" % pos)
151
 
        self.assertLogRevnos(['--limit', '2'], ['10', '9'])
152
 
 
153
 
    def test_log_limit_short(self):
154
 
        self.make_linear_branch()
155
 
        self.assertLogRevnos(['-l', '2'], ['3', '2'])
156
 
 
157
 
    def test_log_change_revno(self):
158
 
        self.make_linear_branch()
159
 
        self.assertLogRevnos(['-c1'], ['1'])
160
 
 
161
 
 
162
 
class TestLogMergedLinearAncestry(TestLogWithLogCatcher):
163
 
 
164
 
    def setUp(self):
165
 
        super(TestLogMergedLinearAncestry, self).setUp()
166
 
        # FIXME: Using a MemoryTree would be even better here (but until we
167
 
        # stop calling run_bzr, there is no point) --vila 100118.
168
 
        builder = branchbuilder.BranchBuilder(self.get_transport())
169
 
        builder.start_series()
170
 
        # mainline
171
 
        builder.build_snapshot('1', None, [
172
 
            ('add', ('', 'root-id', 'directory', ''))])
173
 
        builder.build_snapshot('2', ['1'], [])
174
 
        # branch
175
 
        builder.build_snapshot('1.1.1', ['1'], [])
176
 
        # merge branch into mainline
177
 
        builder.build_snapshot('3', ['2', '1.1.1'], [])
178
 
        # new commits in branch
179
 
        builder.build_snapshot('1.1.2', ['1.1.1'], [])
180
 
        builder.build_snapshot('1.1.3', ['1.1.2'], [])
181
 
        # merge branch into mainline
182
 
        builder.build_snapshot('4', ['3', '1.1.3'], [])
183
 
        # merge mainline into branch
184
 
        builder.build_snapshot('1.1.4', ['1.1.3', '4'], [])
185
 
        # merge branch into mainline
186
 
        builder.build_snapshot('5', ['4', '1.1.4'], [])
187
 
        builder.finish_series()
188
 
 
189
 
    def test_n0(self):
190
 
        self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4'],
191
 
                             ['1.1.4', '4', '1.1.3', '1.1.2', '3', '1.1.1'])
192
 
    def test_n0_forward(self):
193
 
        self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4', '--forward'],
194
 
                             ['3', '1.1.1', '4', '1.1.2', '1.1.3', '1.1.4'])
195
 
 
196
 
    def test_n1(self):
197
 
        # starting from 1.1.4 we follow the left-hand ancestry
198
 
        self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4'],
199
 
                             ['1.1.4', '1.1.3', '1.1.2', '1.1.1'])
200
 
 
201
 
    def test_n1_forward(self):
202
 
        self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4', '--forward'],
203
 
                             ['1.1.1', '1.1.2', '1.1.3', '1.1.4'])
204
 
 
205
 
 
206
 
class Test_GenerateAllRevisions(TestLogWithLogCatcher):
207
 
 
208
 
    def setUp(self):
209
 
        super(Test_GenerateAllRevisions, self).setUp()
210
 
        builder = self.make_branch_with_many_merges()
211
 
        b = builder.get_branch()
212
 
        b.lock_read()
213
 
        self.addCleanup(b.unlock)
214
 
        self.branch = b
215
 
 
216
 
    def make_branch_with_many_merges(self, path='.', format=None):
217
 
        builder = branchbuilder.BranchBuilder(self.get_transport())
218
 
        builder.start_series()
219
 
        # The graph below may look a bit complicated (and it may be but I've
220
 
        # banged my head enough on it) but the bug requires at least dotted
221
 
        # revnos *and* merged revisions below that.
222
 
        builder.build_snapshot('1', None, [
223
 
            ('add', ('', 'root-id', 'directory', ''))])
224
 
        builder.build_snapshot('2', ['1'], [])
225
 
        builder.build_snapshot('1.1.1', ['1'], [])
226
 
        builder.build_snapshot('2.1.1', ['2'], [])
227
 
        builder.build_snapshot('3', ['2', '1.1.1'], [])
228
 
        builder.build_snapshot('2.1.2', ['2.1.1'], [])
229
 
        builder.build_snapshot('2.2.1', ['2.1.1'], [])
230
 
        builder.build_snapshot('2.1.3', ['2.1.2', '2.2.1'], [])
231
 
        builder.build_snapshot('4', ['3', '2.1.3'], [])
232
 
        builder.build_snapshot('5', ['4', '2.1.2'], [])
233
 
        builder.finish_series()
234
 
        return builder
235
 
 
236
 
    def test_not_an_ancestor(self):
237
 
        self.assertRaises(errors.BzrCommandError,
238
 
                          log._generate_all_revisions,
239
 
                          self.branch, '1.1.1', '2.1.3', 'reverse',
240
 
                          delayed_graph_generation=True)
241
 
 
242
 
    def test_wrong_order(self):
243
 
        self.assertRaises(errors.BzrCommandError,
244
 
                          log._generate_all_revisions,
245
 
                          self.branch, '5', '2.1.3', 'reverse',
246
 
                          delayed_graph_generation=True)
247
 
 
248
 
    def test_no_start_rev_id_with_end_rev_id_being_a_merge(self):
249
 
        revs = log._generate_all_revisions(
250
 
            self.branch, None, '2.1.3',
251
 
            'reverse', delayed_graph_generation=True)
252
 
 
253
 
 
254
 
class TestLogRevSpecsWithPaths(TestLogWithLogCatcher):
255
 
 
256
 
    def test_log_revno_n_path_wrong_namespace(self):
 
171
        log = self.run_bzr("log -n0 -r 1..1.1.1")[0]
 
172
        self.assertRevnos(log, (1, '1.1.1'), (2, 3, '1.1.2', 4))
 
173
 
 
174
    def test_log_reversed_revspecs(self):
 
175
        self.make_linear_branch()
 
176
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
 
177
                            'the end revision.\n',),
 
178
                           ['log', '-r3..1'])
 
179
 
 
180
    def test_log_reversed_dotted_revspecs(self):
 
181
        self.make_merged_branch()
 
182
        self.run_bzr_error(('bzr: ERROR: Start revision not found in '
 
183
                            'left-hand history of end revision.\n',),
 
184
                           "log -r 1.1.1..1")
 
185
 
 
186
    def test_log_revno_n_path(self):
257
187
        self.make_linear_branch('branch1')
258
188
        self.make_linear_branch('branch2')
259
 
        # There is no guarantee that a path exist between two arbitrary
260
 
        # revisions.
261
 
        self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)
262
 
 
263
 
    def test_log_revno_n_path_correct_order(self):
264
 
        self.make_linear_branch('branch2')
265
 
        self.assertLogRevnos(['-rrevno:1:branch2..revno:3:branch2'],
266
 
                             ['3', '2','1'])
267
 
 
268
 
    def test_log_revno_n_path(self):
269
 
        self.make_linear_branch('branch2')
270
 
        self.assertLogRevnos(['-rrevno:1:branch2'],
271
 
                             ['1'])
272
 
        rev_props = self.log_catcher.revisions[0].rev.properties
273
 
        self.assertEqual('branch2', rev_props['branch-nick'])
274
 
 
275
 
 
276
 
class TestLogErrors(TestLog):
277
 
 
278
 
    def test_log_zero_revspec(self):
279
 
        self.make_minimal_branch()
280
 
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
281
 
                           ['log', '-r0'])
282
 
 
283
 
    def test_log_zero_begin_revspec(self):
284
 
        self.make_linear_branch()
285
 
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
286
 
                           ['log', '-r0..2'])
287
 
 
288
 
    def test_log_zero_end_revspec(self):
289
 
        self.make_linear_branch()
290
 
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
291
 
                           ['log', '-r-2..0'])
 
189
        # Swapped revisions
 
190
        self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)[0]
 
191
        # Correct order
 
192
        log = self.run_bzr("log -r revno:1:branch2..revno:3:branch2")[0]
 
193
        full_log = self.run_bzr(['log'], working_dir='branch2')[0]
 
194
        self.assertEqualDiff(full_log, log)
 
195
        log = self.run_bzr("log -r revno:1:branch2")[0]
 
196
        self.assertTrue('revno: 1\n' in log)
 
197
        self.assertTrue('revno: 2\n' not in log)
 
198
        self.assertTrue('branch nick: branch2\n' in log)
 
199
        self.assertTrue('branch nick: branch1\n' not in log)
292
200
 
293
201
    def test_log_nonexistent_revno(self):
294
202
        self.make_minimal_branch()
295
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
296
 
                            "does not exist in branch:"],
297
 
                           ['log', '-r1234'])
 
203
        (out, err) = self.run_bzr_error(
 
204
            ["bzr: ERROR: Requested revision: '1234' "
 
205
             "does not exist in branch:"],
 
206
            ['log', '-r1234'])
298
207
 
299
208
    def test_log_nonexistent_dotted_revno(self):
300
209
        self.make_minimal_branch()
301
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
302
 
                            "does not exist in branch:"],
303
 
                           ['log',  '-r123.123'])
 
210
        (out, err) = self.run_bzr_error(
 
211
            ["bzr: ERROR: Requested revision: '123.123' "
 
212
             "does not exist in branch:"],
 
213
            ['log',  '-r123.123'])
 
214
 
 
215
    def test_log_change_revno(self):
 
216
        self.make_linear_branch()
 
217
        expected_log = self.run_bzr("log -r 1")[0]
 
218
        log = self.run_bzr("log -c 1")[0]
 
219
        self.assertEqualDiff(expected_log, log)
304
220
 
305
221
    def test_log_change_nonexistent_revno(self):
306
222
        self.make_minimal_branch()
307
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
308
 
                            "does not exist in branch:"],
309
 
                           ['log',  '-c1234'])
 
223
        (out, err) = self.run_bzr_error(
 
224
            ["bzr: ERROR: Requested revision: '1234' "
 
225
             "does not exist in branch:"],
 
226
            ['log',  '-c1234'])
310
227
 
311
228
    def test_log_change_nonexistent_dotted_revno(self):
312
229
        self.make_minimal_branch()
313
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
314
 
                            "does not exist in branch:"],
315
 
                           ['log', '-c123.123'])
 
230
        (out, err) = self.run_bzr_error(
 
231
            ["bzr: ERROR: Requested revision: '123.123' "
 
232
             "does not exist in branch:"],
 
233
            ['log', '-c123.123'])
316
234
 
317
235
    def test_log_change_single_revno_only(self):
318
236
        self.make_minimal_branch()
334
252
                              'Path unknown at end or start of revision range: '
335
253
                              'does-not-exist')
336
254
 
337
 
    def test_log_reversed_revspecs(self):
338
 
        self.make_linear_branch()
339
 
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
340
 
                            'the end revision.\n',),
341
 
                           ['log', '-r3..1'])
342
 
 
343
 
    def test_log_reversed_dotted_revspecs(self):
344
 
        self.make_merged_branch()
345
 
        self.run_bzr_error(('bzr: ERROR: Start revision not found in '
346
 
                            'left-hand history of end revision.\n',),
347
 
                           "log -r 1.1.1..1")
348
 
 
349
 
    def test_log_bad_message_re(self):
350
 
        """Bad --message argument gives a sensible message
351
 
        
352
 
        See https://bugs.launchpad.net/bzr/+bug/251352
353
 
        """
354
 
        self.make_minimal_branch()
355
 
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
356
 
        self.assertEqual("bzr: ERROR: Invalid regular expression"
357
 
            " in log message filter"
358
 
            ": '*'"
359
 
            ": nothing to repeat\n", err)
360
 
        self.assertEqual('', out)
361
 
 
362
 
    def test_log_unsupported_timezone(self):
363
 
        self.make_linear_branch()
364
 
        self.run_bzr_error(['bzr: ERROR: Unsupported timezone format "foo", '
365
 
                            'options are "utc", "original", "local".'],
366
 
                           ['log', '--timezone', 'foo'])
367
 
 
368
 
    def test_log_exclude_ancestry_no_range(self):
369
 
        self.make_linear_branch()
370
 
        self.run_bzr_error(['bzr: ERROR: --exclude-common-ancestry'
371
 
                            ' requires -r with two revisions'],
372
 
                           ['log', '--exclude-common-ancestry'])
373
 
 
374
 
    def test_log_exclude_ancestry_single_revision(self):
375
 
        self.make_merged_branch()
376
 
        self.run_bzr_error(['bzr: ERROR: --exclude-common-ancestry'
377
 
                            ' requires two different revisions'],
378
 
                           ['log', '--exclude-common-ancestry',
379
 
                            '-r1.1.1..1.1.1'])
380
 
 
381
 
class TestLogTags(TestLog):
382
 
 
383
255
    def test_log_with_tags(self):
384
256
        tree = self.make_linear_branch(format='dirstate-tags')
385
257
        branch = tree.branch
410
282
        log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
411
283
        self.assertContainsRe(log, r'tags: tag1')
412
284
 
 
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'])
 
330
 
413
331
 
414
332
class TestLogVerbose(TestLog):
415
333
 
446
364
        self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
447
365
 
448
366
 
449
 
class TestLogMerges(TestLogWithLogCatcher):
 
367
class TestLogMerges(TestLog):
450
368
 
451
369
    def setUp(self):
452
370
        super(TestLogMerges, self).setUp()
454
372
 
455
373
    def make_branches_with_merges(self):
456
374
        level0 = self.make_branch_and_tree('level0')
457
 
        self.wt_commit(level0, 'in branch level0')
 
375
        level0.commit(message='in branch level0', **self.commit_options())
 
376
 
458
377
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
459
 
        self.wt_commit(level1, 'in branch level1')
 
378
        level1.commit(message='in branch level1', **self.commit_options())
 
379
 
460
380
        level2 = level1.bzrdir.sprout('level2').open_workingtree()
461
 
        self.wt_commit(level2, 'in branch level2')
 
381
        level2.commit(message='in branch level2', **self.commit_options())
 
382
 
462
383
        level1.merge_from_branch(level2.branch)
463
 
        self.wt_commit(level1, 'merge branch level2')
 
384
        level1.commit(message='merge branch level2', **self.commit_options())
 
385
 
464
386
        level0.merge_from_branch(level1.branch)
465
 
        self.wt_commit(level0, 'merge branch level1')
 
387
        level0.commit(message='merge branch level1', **self.commit_options())
466
388
 
467
389
    def test_merges_are_indented_by_level(self):
468
 
        self.run_bzr(['log', '-n0'], working_dir='level0')
469
 
        revnos_and_depth = [(r.revno, r.merge_depth)
470
 
                            for r in self.get_captured_revisions()]
471
 
        self.assertEqual([('2', 0), ('1.1.2', 1), ('1.2.1', 2), ('1.1.1', 1),
472
 
                          ('1', 0)],
473
 
                         [(r.revno, r.merge_depth)
474
 
                            for r in self.get_captured_revisions()])
 
390
        expected = """\
 
391
------------------------------------------------------------
 
392
revno: 2 [merge]
 
393
committer: Lorem Ipsum <test@example.com>
 
394
branch nick: level0
 
395
timestamp: Just now
 
396
message:
 
397
  merge branch level1
 
398
    ------------------------------------------------------------
 
399
    revno: 1.1.2 [merge]
 
400
    committer: Lorem Ipsum <test@example.com>
 
401
    branch nick: level1
 
402
    timestamp: Just now
 
403
    message:
 
404
      merge branch level2
 
405
        ------------------------------------------------------------
 
406
        revno: 1.2.1
 
407
        committer: Lorem Ipsum <test@example.com>
 
408
        branch nick: level2
 
409
        timestamp: Just now
 
410
        message:
 
411
          in branch level2
 
412
    ------------------------------------------------------------
 
413
    revno: 1.1.1
 
414
    committer: Lorem Ipsum <test@example.com>
 
415
    branch nick: level1
 
416
    timestamp: Just now
 
417
    message:
 
418
      in branch level1
 
419
------------------------------------------------------------
 
420
revno: 1
 
421
committer: Lorem Ipsum <test@example.com>
 
422
branch nick: level0
 
423
timestamp: Just now
 
424
message:
 
425
  in branch level0
 
426
"""
 
427
        self.check_log(expected, ['-n0'])
475
428
 
476
429
    def test_force_merge_revisions_off(self):
477
 
        self.assertLogRevnos(['-n1'], ['2', '1'], working_dir='level0')
 
430
        expected = """\
 
431
------------------------------------------------------------
 
432
revno: 2 [merge]
 
433
committer: Lorem Ipsum <test@example.com>
 
434
branch nick: level0
 
435
timestamp: Just now
 
436
message:
 
437
  merge branch level1
 
438
------------------------------------------------------------
 
439
revno: 1
 
440
committer: Lorem Ipsum <test@example.com>
 
441
branch nick: level0
 
442
timestamp: Just now
 
443
message:
 
444
  in branch level0
 
445
"""
 
446
        self.check_log(expected, ['--long', '-n1'])
478
447
 
479
448
    def test_force_merge_revisions_on(self):
480
 
        self.assertLogRevnos(['-n0'], ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
481
 
                             working_dir='level0')
 
449
        expected = """\
 
450
    2 Lorem Ipsum\t2005-11-22 [merge]
 
451
      merge branch level1
 
452
 
 
453
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
454
                merge branch level2
 
455
 
 
456
              1.2.1 Lorem Ipsum\t2005-11-22
 
457
                    in branch level2
 
458
 
 
459
          1.1.1 Lorem Ipsum\t2005-11-22
 
460
                in branch level1
 
461
 
 
462
    1 Lorem Ipsum\t2005-11-22
 
463
      in branch level0
 
464
 
 
465
"""
 
466
        self.check_log(expected, ['--short', '-n0'])
482
467
 
483
468
    def test_include_merges(self):
484
469
        # Confirm --include-merges gives the same output as -n0
485
 
        self.assertLogRevnos(['--include-merges'],
486
 
                             ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
487
 
                             working_dir='level0')
488
 
        self.assertLogRevnos(['--include-merges'],
489
 
                             ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
490
 
                             working_dir='level0')
491
470
        out_im, err_im = self.run_bzr('log --include-merges',
492
471
                                      working_dir='level0')
493
472
        out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
496
475
        self.assertEqual(out_im, out_n0)
497
476
 
498
477
    def test_force_merge_revisions_N(self):
499
 
        self.assertLogRevnos(['-n2'],
500
 
                             ['2', '1.1.2', '1.1.1', '1'],
501
 
                             working_dir='level0')
 
478
        expected = """\
 
479
    2 Lorem Ipsum\t2005-11-22 [merge]
 
480
      merge branch level1
 
481
 
 
482
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
483
                merge branch level2
 
484
 
 
485
          1.1.1 Lorem Ipsum\t2005-11-22
 
486
                in branch level1
 
487
 
 
488
    1 Lorem Ipsum\t2005-11-22
 
489
      in branch level0
 
490
 
 
491
"""
 
492
        self.check_log(expected, ['--short', '-n2'])
502
493
 
503
494
    def test_merges_single_merge_rev(self):
504
 
        self.assertLogRevnosAndDepths(['-n0', '-r1.1.2'],
505
 
                                      [('1.1.2', 0), ('1.2.1', 1)],
506
 
                                      working_dir='level0')
 
495
        expected = """\
 
496
------------------------------------------------------------
 
497
revno: 1.1.2 [merge]
 
498
committer: Lorem Ipsum <test@example.com>
 
499
branch nick: level1
 
500
timestamp: Just now
 
501
message:
 
502
  merge branch level2
 
503
    ------------------------------------------------------------
 
504
    revno: 1.2.1
 
505
    committer: Lorem Ipsum <test@example.com>
 
506
    branch nick: level2
 
507
    timestamp: Just now
 
508
    message:
 
509
      in branch level2
 
510
"""
 
511
        self.check_log(expected, ['-n0', '-r1.1.2'])
507
512
 
508
513
    def test_merges_partial_range(self):
509
 
        self.assertLogRevnosAndDepths(
510
 
                ['-n0', '-r1.1.1..1.1.2'],
511
 
                [('1.1.2', 0), ('1.2.1', 1), ('1.1.1', 0)],
512
 
                working_dir='level0')
 
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'])
513
538
 
514
539
    def test_merges_partial_range_ignore_before_lower_bound(self):
515
540
        """Dont show revisions before the lower bound's merged revs"""
516
 
        self.assertLogRevnosAndDepths(
517
 
                ['-n0', '-r1.1.2..2'],
518
 
                [('2', 0), ('1.1.2', 1), ('1.2.1', 2)],
519
 
                working_dir='level0')
520
 
 
521
 
 
522
 
class TestLogDiff(TestLogWithLogCatcher):
523
 
 
524
 
    # FIXME: We need specific tests for each LogFormatter about how the diffs
525
 
    # are displayed: --long indent them by depth, --short use a fixed
526
 
    # indent and --line does't display them. -- vila 10019
 
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):
527
556
 
528
557
    def setUp(self):
529
558
        super(TestLogDiff, self).setUp()
534
563
        self.build_tree(['level0/file1', 'level0/file2'])
535
564
        level0.add('file1')
536
565
        level0.add('file2')
537
 
        self.wt_commit(level0, 'in branch level0')
 
566
        level0.commit(message='in branch level0', **self.commit_options())
538
567
 
539
568
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
540
569
        self.build_tree_contents([('level1/file2', 'hello\n')])
541
 
        self.wt_commit(level1, 'in branch level1')
 
570
        level1.commit(message='in branch level1', **self.commit_options())
542
571
        level0.merge_from_branch(level1.branch)
543
 
        self.wt_commit(level0, 'merge branch level1')
 
572
        level0.commit(message='merge branch level1', **self.commit_options())
544
573
 
545
 
    def _diff_file1_revno1(self):
546
 
        return """=== added file 'file1'
 
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'
547
616
--- file1\t1970-01-01 00:00:00 +0000
548
 
+++ file1\t2005-11-22 00:00:00 +0000
 
617
+++ file1\t2005-11-22 00:00:01 +0000
549
618
@@ -0,0 +1,1 @@
550
619
+contents of level0/file1
551
620
 
552
 
"""
553
 
 
554
 
    def _diff_file2_revno2(self):
555
 
        return """=== modified file 'file2'
556
 
--- file2\t2005-11-22 00:00:00 +0000
557
 
+++ file2\t2005-11-22 00:00:01 +0000
558
 
@@ -1,1 +1,1 @@
559
 
-contents of level0/file2
560
 
+hello
561
 
 
562
 
"""
563
 
 
564
 
    def _diff_file2_revno1_1_1(self):
565
 
        return """=== modified file 'file2'
566
 
--- file2\t2005-11-22 00:00:00 +0000
567
 
+++ file2\t2005-11-22 00:00:01 +0000
568
 
@@ -1,1 +1,1 @@
569
 
-contents of level0/file2
570
 
+hello
571
 
 
572
 
"""
573
 
 
574
 
    def _diff_file2_revno1(self):
575
 
        return """=== added file 'file2'
 
621
=== added file 'file2'
576
622
--- file2\t1970-01-01 00:00:00 +0000
577
 
+++ file2\t2005-11-22 00:00:00 +0000
 
623
+++ file2\t2005-11-22 00:00:01 +0000
578
624
@@ -0,0 +1,1 @@
579
625
+contents of level0/file2
580
 
 
581
 
"""
582
 
 
583
 
    def assertLogRevnosAndDiff(self, args, expected,
584
 
                            working_dir='.'):
585
 
        self.run_bzr(['log', '-p'] + args, working_dir=working_dir)
586
 
        expected_revnos_and_depths = [
587
 
            (revno, depth) for revno, depth, diff in expected]
588
 
        # Check the revnos and depths first to make debugging easier
589
 
        self.assertEqual(expected_revnos_and_depths,
590
 
                         [(r.revno, r.merge_depth)
591
 
                           for r in self.get_captured_revisions()])
592
 
        # Now check the diffs, adding the revno  in case of failure
593
 
        fmt = 'In revno %s\n%s'
594
 
        for expected_rev, actual_rev in izip(expected,
595
 
                                             self.get_captured_revisions()):
596
 
            revno, depth, expected_diff = expected_rev
597
 
            actual_diff = actual_rev.diff
598
 
            self.assertEqualDiff(fmt % (revno, expected_diff),
599
 
                                 fmt % (revno, actual_diff))
600
 
 
601
 
    def test_log_diff_with_merges(self):
602
 
        self.assertLogRevnosAndDiff(
603
 
            ['-n0'],
604
 
            [('2', 0, self._diff_file2_revno2()),
605
 
             ('1.1.1', 1, self._diff_file2_revno1_1_1()),
606
 
             ('1', 0, self._diff_file1_revno1()
607
 
              + self._diff_file2_revno1())],
608
 
            working_dir='level0')
609
 
 
610
 
 
611
 
    def test_log_diff_file1(self):
612
 
        self.assertLogRevnosAndDiff(['-n0', 'file1'],
613
 
                                    [('1', 0, self._diff_file1_revno1())],
614
 
                                    working_dir='level0')
615
 
 
616
 
    def test_log_diff_file2(self):
617
 
        self.assertLogRevnosAndDiff(['-n1', 'file2'],
618
 
                                    [('2', 0, self._diff_file2_revno2()),
619
 
                                     ('1', 0, self._diff_file2_revno1())],
620
 
                                    working_dir='level0')
 
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'])
621
703
 
622
704
 
623
705
class TestLogUnicodeDiff(TestLog):
666
748
 
667
749
    def setUp(self):
668
750
        super(TestLogEncodings, self).setUp()
669
 
        self.overrideAttr(osutils, '_cached_user_encoding')
 
751
        self.user_encoding = osutils._cached_user_encoding
 
752
        def restore():
 
753
            osutils._cached_user_encoding = self.user_encoding
 
754
        self.addCleanup(restore)
670
755
 
671
756
    def create_branch(self):
672
757
        bzr = self.run_bzr
673
758
        bzr('init')
674
 
        self.build_tree_contents([('a', 'some stuff\n')])
 
759
        open('a', 'wb').write('some stuff\n')
675
760
        bzr('add a')
676
761
        bzr(['commit', '-m', self._message])
677
762
 
740
825
        self.assertEquals(-1, stdout.find(test_in_cp1251))
741
826
 
742
827
 
743
 
class TestLogFile(TestLogWithLogCatcher):
 
828
class TestLogFile(tests.TestCaseWithTransport):
744
829
 
745
830
    def test_log_local_branch_file(self):
746
831
        """We should be able to log files in local treeless branches"""
775
860
            tree.commit('remove file1')
776
861
        os.chdir('parent')
777
862
 
778
 
    # FIXME: It would be good to parametrize the following tests against all
779
 
    # formatters. But the revisions selection is not *currently* part of the
780
 
    # LogFormatter contract, so using LogCatcher is sufficient -- vila 100118
781
 
    def test_log_file1(self):
782
 
        self.prepare_tree()
783
 
        self.assertLogRevnos(['-n0', 'file1'], ['1'])
784
 
 
785
 
    def test_log_file2(self):
786
 
        self.prepare_tree()
787
 
        # file2 full history
788
 
        self.assertLogRevnos(['-n0', 'file2'], ['4', '3.1.1', '2'])
789
 
        # file2 in a merge revision
790
 
        self.assertLogRevnos(['-n0', '-r3.1.1', 'file2'], ['3.1.1'])
791
 
        # file2 in a mainline revision
792
 
        self.assertLogRevnos(['-n0', '-r4', 'file2'], ['4', '3.1.1'])
793
 
        # file2 since a revision
794
 
        self.assertLogRevnos(['-n0', '-r3..', 'file2'], ['4', '3.1.1'])
795
 
        # file2 up to a revision
796
 
        self.assertLogRevnos(['-n0', '-r..3', 'file2'], ['2'])
797
 
 
798
 
    def test_log_file3(self):
799
 
        self.prepare_tree()
800
 
        self.assertLogRevnos(['-n0', 'file3'], ['3'])
 
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 ')
801
908
 
802
909
    def test_log_file_historical_missing(self):
803
910
        # Check logging a deleted file gives an error if the
811
918
        # Check logging a deleted file is ok if the file existed
812
919
        # at the end the revision range
813
920
        self.prepare_tree(complex=True)
814
 
        self.assertLogRevnos(['-n0', '-r..4', 'file2'], ['4', '3.1.1', '2'])
 
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 ')
815
928
 
816
929
    def test_log_file_historical_start(self):
817
930
        # Check logging a deleted file is ok if the file existed
818
931
        # at the start of the revision range
819
932
        self.prepare_tree(complex=True)
820
 
        self.assertLogRevnos(['file1'], ['1'])
 
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 ')
821
940
 
822
941
    def test_log_file_renamed(self):
823
942
        """File matched against revision range, not current tree."""
829
948
        self.assertContainsRe(err, err_msg)
830
949
 
831
950
        # Check we can see a renamed file if we give the right end revision
832
 
        self.assertLogRevnos(['-r..4', 'file3'], ['3'])
833
 
 
834
 
 
835
 
class TestLogMultiple(TestLogWithLogCatcher):
 
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):
836
1024
 
837
1025
    def prepare_tree(self):
838
1026
        tree = self.make_branch_and_tree('parent')
861
1049
        tree.commit(message='merge child branch')
862
1050
        os.chdir('parent')
863
1051
 
 
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
 
864
1059
    def test_log_files(self):
865
1060
        """The log for multiple file should only list revs for those files"""
866
1061
        self.prepare_tree()
867
 
        self.assertLogRevnos(['file1', 'file2', 'dir1/dir2/file3'],
868
 
                             ['6', '5.1.1', '3', '2', '1'])
 
1062
        self.assertRevnos('file1 file2 dir1/dir2/file3',
 
1063
            ['6', '5.1.1', '3', '2', '1'])
869
1064
 
870
1065
    def test_log_directory(self):
871
1066
        """The log for a directory should show all nested files."""
872
1067
        self.prepare_tree()
873
 
        self.assertLogRevnos(['dir1'], ['5', '3'])
 
1068
        self.assertRevnos('dir1', ['5', '3'])
874
1069
 
875
1070
    def test_log_nested_directory(self):
876
1071
        """The log for a directory should show all nested files."""
877
1072
        self.prepare_tree()
878
 
        self.assertLogRevnos(['dir1/dir2'], ['3'])
 
1073
        self.assertRevnos('dir1/dir2', ['3'])
879
1074
 
880
1075
    def test_log_in_nested_directory(self):
881
1076
        """The log for a directory should show all nested files."""
882
1077
        self.prepare_tree()
883
1078
        os.chdir("dir1")
884
 
        self.assertLogRevnos(['.'], ['5', '3'])
 
1079
        self.assertRevnos('.', ['5', '3'])
885
1080
 
886
1081
    def test_log_files_and_directories(self):
887
1082
        """Logging files and directories together should be fine."""
888
1083
        self.prepare_tree()
889
 
        self.assertLogRevnos(['file4', 'dir1/dir2'], ['4', '3'])
 
1084
        self.assertRevnos('file4 dir1/dir2', ['4', '3'])
890
1085
 
891
1086
    def test_log_files_and_dirs_in_nested_directory(self):
892
1087
        """The log for a directory should show all nested files."""
893
1088
        self.prepare_tree()
894
1089
        os.chdir("dir1")
895
 
        self.assertLogRevnos(['dir2', 'file5'], ['5', '3'])
 
1090
        self.assertRevnos('dir2 file5', ['5', '3'])