~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Aaron Bentley
  • Date: 2009-05-11 18:35:20 UTC
  • mto: This revision was merged to the branch mainline in revision 4351.
  • Revision ID: aaron@aaronbentley.com-20090511183520-xtvepoauy7zjtb7x
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.

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)
55
53
        tree.commit(message='message3')
56
54
        return tree
57
55
 
58
 
    def make_merged_branch(self, path='.', format=None):
59
 
        tree = self.make_linear_branch(path, format)
60
 
        tree2 = tree.bzrdir.sprout('tree2',
61
 
            revision_id=tree.branch.get_rev_id(1)).open_workingtree()
62
 
        tree2.commit(message='tree2 message2')
63
 
        tree2.commit(message='tree2 message3')
64
 
        tree.merge_from_branch(tree2.branch)
65
 
        tree.commit(message='merge')
66
 
        return tree
67
 
 
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'])
 
56
    def commit_options(self):
 
57
        """Use some mostly fixed values for commits to simplify tests.
 
58
 
 
59
        Tests can use this function to get some commit attributes. The time
 
60
        stamp is incremented at each commit.
 
61
        """
 
62
        self.timestamp += 1 # 1 second between each commit
 
63
        return dict(committer='Lorem Ipsum <joe@foo.com>',
 
64
                 timezone=self.timezone,
 
65
                 timestamp=self.timestamp,
 
66
                 )
 
67
 
 
68
    def check_log(self, expected, args, working_dir='level0'):
 
69
        out, err = self.run_bzr(['log', '--timezone', 'utc'] + args,
 
70
                                working_dir=working_dir)
 
71
        self.assertEqual('', err)
 
72
        self.assertEqualDiff(expected, test_log.normalize_log(out))
 
73
 
 
74
 
 
75
class TestLogRevSpecs(TestLog):
109
76
 
110
77
    def test_log_null_end_revspec(self):
111
78
        self.make_linear_branch()
112
 
        self.assertLogRevnos(['-r1..'], ['3', '2', '1'])
 
79
        log = self.run_bzr(['log'])[0]
 
80
        self.assertTrue('revno: 1\n' in log)
 
81
        self.assertTrue('revno: 2\n' in log)
 
82
        self.assertTrue('revno: 3\n' in log)
 
83
        self.assertTrue('message:\n  message1\n' in log)
 
84
        self.assertTrue('message:\n  message2\n' in log)
 
85
        self.assertTrue('message:\n  message3\n' in log)
 
86
 
 
87
        full_log = self.run_bzr(['log'])[0]
 
88
        log = self.run_bzr("log -r 1..")[0]
 
89
        self.assertEqualDiff(log, full_log)
113
90
 
114
91
    def test_log_null_begin_revspec(self):
115
92
        self.make_linear_branch()
116
 
        self.assertLogRevnos(['-r..3'], ['3', '2', '1'])
 
93
        full_log = self.run_bzr(['log'])[0]
 
94
        log = self.run_bzr("log -r ..3")[0]
 
95
        self.assertEqualDiff(full_log, log)
117
96
 
118
97
    def test_log_null_both_revspecs(self):
119
98
        self.make_linear_branch()
120
 
        self.assertLogRevnos(['-r..'], ['3', '2', '1'])
 
99
        full_log = self.run_bzr(['log'])[0]
 
100
        log = self.run_bzr("log -r ..")[0]
 
101
        self.assertEqualDiff(full_log, log)
 
102
 
 
103
    def test_log_zero_revspec(self):
 
104
        self.make_minimal_branch()
 
105
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
106
                           ['log', '-r0'])
 
107
 
 
108
    def test_log_zero_begin_revspec(self):
 
109
        self.make_linear_branch()
 
110
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
111
                           ['log', '-r0..2'])
 
112
 
 
113
    def test_log_zero_end_revspec(self):
 
114
        self.make_linear_branch()
 
115
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
116
                           ['log', '-r-2..0'])
121
117
 
122
118
    def test_log_negative_begin_revspec_full_log(self):
123
119
        self.make_linear_branch()
124
 
        self.assertLogRevnos(['-r-3..'], ['3', '2', '1'])
 
120
        full_log = self.run_bzr(['log'])[0]
 
121
        log = self.run_bzr("log -r -3..")[0]
 
122
        self.assertEqualDiff(full_log, log)
125
123
 
126
124
    def test_log_negative_both_revspec_full_log(self):
127
125
        self.make_linear_branch()
128
 
        self.assertLogRevnos(['-r-3..-1'], ['3', '2', '1'])
 
126
        full_log = self.run_bzr(['log'])[0]
 
127
        log = self.run_bzr("log -r -3..-1")[0]
 
128
        self.assertEqualDiff(full_log, log)
129
129
 
130
130
    def test_log_negative_both_revspec_partial(self):
131
131
        self.make_linear_branch()
132
 
        self.assertLogRevnos(['-r-3..-2'], ['2', '1'])
 
132
        log = self.run_bzr("log -r -3..-2")[0]
 
133
        self.assertTrue('revno: 1\n' in log)
 
134
        self.assertTrue('revno: 2\n' in log)
 
135
        self.assertTrue('revno: 3\n' not in log)
133
136
 
134
137
    def test_log_negative_begin_revspec(self):
135
138
        self.make_linear_branch()
136
 
        self.assertLogRevnos(['-r-2..'], ['3', '2'])
 
139
        log = self.run_bzr("log -r -2..")[0]
 
140
        self.assertTrue('revno: 1\n' not in log)
 
141
        self.assertTrue('revno: 2\n' in log)
 
142
        self.assertTrue('revno: 3\n' in log)
137
143
 
138
144
    def test_log_positive_revspecs(self):
139
145
        self.make_linear_branch()
140
 
        self.assertLogRevnos(['-r1..3'], ['3', '2', '1'])
141
 
 
142
 
    def test_log_dotted_revspecs(self):
143
 
        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):
 
146
        full_log = self.run_bzr(['log'])[0]
 
147
        log = self.run_bzr("log -r 1..3")[0]
 
148
        self.assertEqualDiff(full_log, log)
 
149
 
 
150
    def test_log_reversed_revspecs(self):
 
151
        self.make_linear_branch()
 
152
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
 
153
                            'the end revision.\n',),
 
154
                           ['log', '-r3..1'])
 
155
 
 
156
    def test_log_revno_n_path(self):
257
157
        self.make_linear_branch('branch1')
258
158
        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'])
 
159
        # Swapped revisions
 
160
        self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)[0]
 
161
        # Correct order
 
162
        log = self.run_bzr("log -r revno:1:branch2..revno:3:branch2")[0]
 
163
        full_log = self.run_bzr(['log'], working_dir='branch2')[0]
 
164
        self.assertEqualDiff(full_log, log)
 
165
        log = self.run_bzr("log -r revno:1:branch2")[0]
 
166
        self.assertTrue('revno: 1\n' in log)
 
167
        self.assertTrue('revno: 2\n' not in log)
 
168
        self.assertTrue('branch nick: branch2\n' in log)
 
169
        self.assertTrue('branch nick: branch1\n' not in log)
292
170
 
293
171
    def test_log_nonexistent_revno(self):
294
172
        self.make_minimal_branch()
295
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
296
 
                            "does not exist in branch:"],
297
 
                           ['log', '-r1234'])
 
173
        (out, err) = self.run_bzr_error(
 
174
            ["bzr: ERROR: Requested revision: '1234' "
 
175
             "does not exist in branch:"],
 
176
            ['log', '-r1234'])
298
177
 
299
178
    def test_log_nonexistent_dotted_revno(self):
300
179
        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'])
 
180
        (out, err) = self.run_bzr_error(
 
181
            ["bzr: ERROR: Requested revision: '123.123' "
 
182
             "does not exist in branch:"],
 
183
            ['log',  '-r123.123'])
 
184
 
 
185
    def test_log_change_revno(self):
 
186
        self.make_linear_branch()
 
187
        expected_log = self.run_bzr("log -r 1")[0]
 
188
        log = self.run_bzr("log -c 1")[0]
 
189
        self.assertEqualDiff(expected_log, log)
304
190
 
305
191
    def test_log_change_nonexistent_revno(self):
306
192
        self.make_minimal_branch()
307
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
308
 
                            "does not exist in branch:"],
309
 
                           ['log',  '-c1234'])
 
193
        (out, err) = self.run_bzr_error(
 
194
            ["bzr: ERROR: Requested revision: '1234' "
 
195
             "does not exist in branch:"],
 
196
            ['log',  '-c1234'])
310
197
 
311
198
    def test_log_change_nonexistent_dotted_revno(self):
312
199
        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'])
 
200
        (out, err) = self.run_bzr_error(
 
201
            ["bzr: ERROR: Requested revision: '123.123' "
 
202
             "does not exist in branch:"],
 
203
            ['log', '-c123.123'])
316
204
 
317
205
    def test_log_change_single_revno_only(self):
318
206
        self.make_minimal_branch()
334
222
                              'Path unknown at end or start of revision range: '
335
223
                              'does-not-exist')
336
224
 
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
225
    def test_log_with_tags(self):
384
226
        tree = self.make_linear_branch(format='dirstate-tags')
385
227
        branch = tree.branch
410
252
        log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
411
253
        self.assertContainsRe(log, r'tags: tag1')
412
254
 
 
255
    def test_log_limit(self):
 
256
        tree = self.make_branch_and_tree('.')
 
257
        # We want more commits than our batch size starts at
 
258
        for pos in range(10):
 
259
            tree.commit("%s" % pos)
 
260
        log = self.run_bzr("log --limit 2")[0]
 
261
        self.assertNotContainsRe(log, r'revno: 1\n')
 
262
        self.assertNotContainsRe(log, r'revno: 2\n')
 
263
        self.assertNotContainsRe(log, r'revno: 3\n')
 
264
        self.assertNotContainsRe(log, r'revno: 4\n')
 
265
        self.assertNotContainsRe(log, r'revno: 5\n')
 
266
        self.assertNotContainsRe(log, r'revno: 6\n')
 
267
        self.assertNotContainsRe(log, r'revno: 7\n')
 
268
        self.assertNotContainsRe(log, r'revno: 8\n')
 
269
        self.assertContainsRe(log, r'revno: 9\n')
 
270
        self.assertContainsRe(log, r'revno: 10\n')
 
271
 
 
272
    def test_log_limit_short(self):
 
273
        self.make_linear_branch()
 
274
        log = self.run_bzr("log -l 2")[0]
 
275
        self.assertNotContainsRe(log, r'revno: 1\n')
 
276
        self.assertContainsRe(log, r'revno: 2\n')
 
277
        self.assertContainsRe(log, r'revno: 3\n')
 
278
 
 
279
    def test_log_bad_message_re(self):
 
280
        """Bad --message argument gives a sensible message
 
281
        
 
282
        See https://bugs.launchpad.net/bzr/+bug/251352
 
283
        """
 
284
        self.make_minimal_branch()
 
285
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
 
286
        self.assertEqual("bzr: ERROR: Invalid regular expression"
 
287
            " in log message filter"
 
288
            ": '*'"
 
289
            ": nothing to repeat\n", err)
 
290
        self.assertEqual('', out)
 
291
 
 
292
 
 
293
class TestLogTimeZone(TestLog):
 
294
 
 
295
    def test_log_unsupported_timezone(self):
 
296
        self.make_linear_branch()
 
297
        self.run_bzr_error(['bzr: ERROR: Unsupported timezone format "foo", '
 
298
                            'options are "utc", "original", "local".'],
 
299
                           ['log', '--timezone', 'foo'])
 
300
 
413
301
 
414
302
class TestLogVerbose(TestLog):
415
303
 
446
334
        self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
447
335
 
448
336
 
449
 
class TestLogMerges(TestLogWithLogCatcher):
 
337
class TestLogMerges(TestLog):
450
338
 
451
339
    def setUp(self):
452
340
        super(TestLogMerges, self).setUp()
454
342
 
455
343
    def make_branches_with_merges(self):
456
344
        level0 = self.make_branch_and_tree('level0')
457
 
        self.wt_commit(level0, 'in branch level0')
 
345
        level0.commit(message='in branch level0', **self.commit_options())
 
346
 
458
347
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
459
 
        self.wt_commit(level1, 'in branch level1')
 
348
        level1.commit(message='in branch level1', **self.commit_options())
 
349
 
460
350
        level2 = level1.bzrdir.sprout('level2').open_workingtree()
461
 
        self.wt_commit(level2, 'in branch level2')
 
351
        level2.commit(message='in branch level2', **self.commit_options())
 
352
 
462
353
        level1.merge_from_branch(level2.branch)
463
 
        self.wt_commit(level1, 'merge branch level2')
 
354
        level1.commit(message='merge branch level2', **self.commit_options())
 
355
 
464
356
        level0.merge_from_branch(level1.branch)
465
 
        self.wt_commit(level0, 'merge branch level1')
 
357
        level0.commit(message='merge branch level1', **self.commit_options())
466
358
 
467
359
    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()])
 
360
        expected = """\
 
361
------------------------------------------------------------
 
362
revno: 2 [merge]
 
363
committer: Lorem Ipsum <test@example.com>
 
364
branch nick: level0
 
365
timestamp: Just now
 
366
message:
 
367
  merge branch level1
 
368
    ------------------------------------------------------------
 
369
    revno: 1.1.2 [merge]
 
370
    committer: Lorem Ipsum <test@example.com>
 
371
    branch nick: level1
 
372
    timestamp: Just now
 
373
    message:
 
374
      merge branch level2
 
375
        ------------------------------------------------------------
 
376
        revno: 1.2.1
 
377
        committer: Lorem Ipsum <test@example.com>
 
378
        branch nick: level2
 
379
        timestamp: Just now
 
380
        message:
 
381
          in branch level2
 
382
    ------------------------------------------------------------
 
383
    revno: 1.1.1
 
384
    committer: Lorem Ipsum <test@example.com>
 
385
    branch nick: level1
 
386
    timestamp: Just now
 
387
    message:
 
388
      in branch level1
 
389
------------------------------------------------------------
 
390
revno: 1
 
391
committer: Lorem Ipsum <test@example.com>
 
392
branch nick: level0
 
393
timestamp: Just now
 
394
message:
 
395
  in branch level0
 
396
"""
 
397
        self.check_log(expected, ['-n0'])
475
398
 
476
399
    def test_force_merge_revisions_off(self):
477
 
        self.assertLogRevnos(['-n1'], ['2', '1'], working_dir='level0')
 
400
        expected = """\
 
401
------------------------------------------------------------
 
402
revno: 2 [merge]
 
403
committer: Lorem Ipsum <test@example.com>
 
404
branch nick: level0
 
405
timestamp: Just now
 
406
message:
 
407
  merge branch level1
 
408
------------------------------------------------------------
 
409
revno: 1
 
410
committer: Lorem Ipsum <test@example.com>
 
411
branch nick: level0
 
412
timestamp: Just now
 
413
message:
 
414
  in branch level0
 
415
"""
 
416
        self.check_log(expected, ['--long', '-n1'])
478
417
 
479
418
    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')
 
419
        expected = """\
 
420
    2 Lorem Ipsum\t2005-11-22 [merge]
 
421
      merge branch level1
 
422
 
 
423
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
424
                merge branch level2
 
425
 
 
426
              1.2.1 Lorem Ipsum\t2005-11-22
 
427
                    in branch level2
 
428
 
 
429
          1.1.1 Lorem Ipsum\t2005-11-22
 
430
                in branch level1
 
431
 
 
432
    1 Lorem Ipsum\t2005-11-22
 
433
      in branch level0
 
434
 
 
435
"""
 
436
        self.check_log(expected, ['--short', '-n0'])
482
437
 
483
438
    def test_include_merges(self):
484
439
        # 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
440
        out_im, err_im = self.run_bzr('log --include-merges',
492
441
                                      working_dir='level0')
493
442
        out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
496
445
        self.assertEqual(out_im, out_n0)
497
446
 
498
447
    def test_force_merge_revisions_N(self):
499
 
        self.assertLogRevnos(['-n2'],
500
 
                             ['2', '1.1.2', '1.1.1', '1'],
501
 
                             working_dir='level0')
 
448
        expected = """\
 
449
    2 Lorem Ipsum\t2005-11-22 [merge]
 
450
      merge branch level1
 
451
 
 
452
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
453
                merge branch level2
 
454
 
 
455
          1.1.1 Lorem Ipsum\t2005-11-22
 
456
                in branch level1
 
457
 
 
458
    1 Lorem Ipsum\t2005-11-22
 
459
      in branch level0
 
460
 
 
461
"""
 
462
        self.check_log(expected, ['--short', '-n2'])
502
463
 
503
464
    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')
 
465
        expected = """\
 
466
------------------------------------------------------------
 
467
revno: 1.1.2 [merge]
 
468
committer: Lorem Ipsum <test@example.com>
 
469
branch nick: level1
 
470
timestamp: Just now
 
471
message:
 
472
  merge branch level2
 
473
    ------------------------------------------------------------
 
474
    revno: 1.2.1
 
475
    committer: Lorem Ipsum <test@example.com>
 
476
    branch nick: level2
 
477
    timestamp: Just now
 
478
    message:
 
479
      in branch level2
 
480
"""
 
481
        self.check_log(expected, ['-n0', '-r1.1.2'])
507
482
 
508
483
    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')
513
 
 
514
 
    def test_merges_partial_range_ignore_before_lower_bound(self):
515
 
        """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
 
484
        expected = """\
 
485
------------------------------------------------------------
 
486
revno: 1.1.2 [merge]
 
487
committer: Lorem Ipsum <test@example.com>
 
488
branch nick: level1
 
489
timestamp: Just now
 
490
message:
 
491
  merge branch level2
 
492
    ------------------------------------------------------------
 
493
    revno: 1.2.1
 
494
    committer: Lorem Ipsum <test@example.com>
 
495
    branch nick: level2
 
496
    timestamp: Just now
 
497
    message:
 
498
      in branch level2
 
499
------------------------------------------------------------
 
500
revno: 1.1.1
 
501
committer: Lorem Ipsum <test@example.com>
 
502
branch nick: level1
 
503
timestamp: Just now
 
504
message:
 
505
  in branch level1
 
506
"""
 
507
        self.check_log(expected, ['-n0', '-r1.1.1..1.1.2'])
 
508
 
 
509
 
 
510
class TestLogDiff(TestLog):
527
511
 
528
512
    def setUp(self):
529
513
        super(TestLogDiff, self).setUp()
534
518
        self.build_tree(['level0/file1', 'level0/file2'])
535
519
        level0.add('file1')
536
520
        level0.add('file2')
537
 
        self.wt_commit(level0, 'in branch level0')
 
521
        level0.commit(message='in branch level0', **self.commit_options())
538
522
 
539
523
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
540
524
        self.build_tree_contents([('level1/file2', 'hello\n')])
541
 
        self.wt_commit(level1, 'in branch level1')
 
525
        level1.commit(message='in branch level1', **self.commit_options())
542
526
        level0.merge_from_branch(level1.branch)
543
 
        self.wt_commit(level0, 'merge branch level1')
 
527
        level0.commit(message='merge branch level1', **self.commit_options())
544
528
 
545
 
    def _diff_file1_revno1(self):
546
 
        return """=== added file 'file1'
 
529
    def test_log_show_diff_long_with_merges(self):
 
530
        out,err = self.run_bzr('log -p -n0')
 
531
        self.assertEqual('', err)
 
532
        log = test_log.normalize_log(out)
 
533
        expected = """\
 
534
------------------------------------------------------------
 
535
revno: 2 [merge]
 
536
committer: Lorem Ipsum <test@example.com>
 
537
branch nick: level0
 
538
timestamp: Just now
 
539
message:
 
540
  merge branch level1
 
541
diff:
 
542
=== modified file 'file2'
 
543
--- file2\t2005-11-22 00:00:01 +0000
 
544
+++ file2\t2005-11-22 00:00:02 +0000
 
545
@@ -1,1 +1,1 @@
 
546
-contents of level0/file2
 
547
+hello
 
548
    ------------------------------------------------------------
 
549
    revno: 1.1.1
 
550
    committer: Lorem Ipsum <test@example.com>
 
551
    branch nick: level1
 
552
    timestamp: Just now
 
553
    message:
 
554
      in branch level1
 
555
    diff:
 
556
    === modified file 'file2'
 
557
    --- file2\t2005-11-22 00:00:01 +0000
 
558
    +++ file2\t2005-11-22 00:00:02 +0000
 
559
    @@ -1,1 +1,1 @@
 
560
    -contents of level0/file2
 
561
    +hello
 
562
------------------------------------------------------------
 
563
revno: 1
 
564
committer: Lorem Ipsum <test@example.com>
 
565
branch nick: level0
 
566
timestamp: Just now
 
567
message:
 
568
  in branch level0
 
569
diff:
 
570
=== added file 'file1'
547
571
--- file1\t1970-01-01 00:00:00 +0000
548
 
+++ file1\t2005-11-22 00:00:00 +0000
 
572
+++ file1\t2005-11-22 00:00:01 +0000
549
573
@@ -0,0 +1,1 @@
550
574
+contents of level0/file1
551
575
 
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'
 
576
=== added file 'file2'
576
577
--- file2\t1970-01-01 00:00:00 +0000
577
 
+++ file2\t2005-11-22 00:00:00 +0000
 
578
+++ file2\t2005-11-22 00:00:01 +0000
578
579
@@ -0,0 +1,1 @@
579
580
+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')
 
581
"""
 
582
        self.check_log(expected, ['-p', '-n0'])
 
583
 
 
584
    def test_log_show_diff_short(self):
 
585
        expected = """\
 
586
    2 Lorem Ipsum\t2005-11-22 [merge]
 
587
      merge branch level1
 
588
      === modified file 'file2'
 
589
      --- file2\t2005-11-22 00:00:01 +0000
 
590
      +++ file2\t2005-11-22 00:00:02 +0000
 
591
      @@ -1,1 +1,1 @@
 
592
      -contents of level0/file2
 
593
      +hello
 
594
 
 
595
    1 Lorem Ipsum\t2005-11-22
 
596
      in branch level0
 
597
      === added file 'file1'
 
598
      --- file1\t1970-01-01 00:00:00 +0000
 
599
      +++ file1\t2005-11-22 00:00:01 +0000
 
600
      @@ -0,0 +1,1 @@
 
601
      +contents of level0/file1
 
602
\x20\x20\x20\x20\x20\x20
 
603
      === added file 'file2'
 
604
      --- file2\t1970-01-01 00:00:00 +0000
 
605
      +++ file2\t2005-11-22 00:00:01 +0000
 
606
      @@ -0,0 +1,1 @@
 
607
      +contents of level0/file2
 
608
 
 
609
Use --include-merges or -n0 to see merged revisions.
 
610
"""
 
611
        self.check_log(expected, ['-p', '--short'])
 
612
 
 
613
    def test_log_show_diff_line(self):
 
614
        # Not supported by this formatter so expect plain output
 
615
        expected = """\
 
616
2: Lorem Ipsum 2005-11-22 [merge] merge branch level1
 
617
1: Lorem Ipsum 2005-11-22 in branch level0
 
618
"""
 
619
        self.check_log(expected, ['-p', '--line'])
 
620
 
 
621
    def test_log_show_diff_file1(self):
 
622
        """Only the diffs for the given file are to be shown"""
 
623
        expected = """\
 
624
    1 Lorem Ipsum\t2005-11-22
 
625
      in branch level0
 
626
      === added file 'file1'
 
627
      --- file1\t1970-01-01 00:00:00 +0000
 
628
      +++ file1\t2005-11-22 00:00:01 +0000
 
629
      @@ -0,0 +1,1 @@
 
630
      +contents of level0/file1
 
631
 
 
632
"""
 
633
        self.check_log(expected, ['-p', '--short', 'file1'])
 
634
 
 
635
    def test_log_show_diff_file2(self):
 
636
        """Only the diffs for the given file are to be shown"""
 
637
        expected = """\
 
638
    2 Lorem Ipsum\t2005-11-22 [merge]
 
639
      merge branch level1
 
640
      === modified file 'file2'
 
641
      --- file2\t2005-11-22 00:00:01 +0000
 
642
      +++ file2\t2005-11-22 00:00:02 +0000
 
643
      @@ -1,1 +1,1 @@
 
644
      -contents of level0/file2
 
645
      +hello
 
646
 
 
647
    1 Lorem Ipsum\t2005-11-22
 
648
      in branch level0
 
649
      === added file 'file2'
 
650
      --- file2\t1970-01-01 00:00:00 +0000
 
651
      +++ file2\t2005-11-22 00:00:01 +0000
 
652
      @@ -0,0 +1,1 @@
 
653
      +contents of level0/file2
 
654
 
 
655
Use --include-merges or -n0 to see merged revisions.
 
656
"""
 
657
        self.check_log(expected, ['-p', '--short', 'file2'])
621
658
 
622
659
 
623
660
class TestLogUnicodeDiff(TestLog):
666
703
 
667
704
    def setUp(self):
668
705
        super(TestLogEncodings, self).setUp()
669
 
        self.overrideAttr(osutils, '_cached_user_encoding')
 
706
        self.user_encoding = osutils._cached_user_encoding
 
707
        def restore():
 
708
            osutils._cached_user_encoding = self.user_encoding
 
709
        self.addCleanup(restore)
670
710
 
671
711
    def create_branch(self):
672
712
        bzr = self.run_bzr
673
713
        bzr('init')
674
 
        self.build_tree_contents([('a', 'some stuff\n')])
 
714
        open('a', 'wb').write('some stuff\n')
675
715
        bzr('add a')
676
716
        bzr(['commit', '-m', self._message])
677
717
 
740
780
        self.assertEquals(-1, stdout.find(test_in_cp1251))
741
781
 
742
782
 
743
 
class TestLogFile(TestLogWithLogCatcher):
 
783
class TestLogFile(tests.TestCaseWithTransport):
744
784
 
745
785
    def test_log_local_branch_file(self):
746
786
        """We should be able to log files in local treeless branches"""
775
815
            tree.commit('remove file1')
776
816
        os.chdir('parent')
777
817
 
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'])
 
818
    def test_log_file(self):
 
819
        """The log for a particular file should only list revs for that file"""
 
820
        self.prepare_tree()
 
821
        log = self.run_bzr('log -n0 file1')[0]
 
822
        self.assertContainsRe(log, 'revno: 1\n')
 
823
        self.assertNotContainsRe(log, 'revno: 2\n')
 
824
        self.assertNotContainsRe(log, 'revno: 3\n')
 
825
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
826
        self.assertNotContainsRe(log, 'revno: 4 ')
 
827
        log = self.run_bzr('log -n0 file2')[0]
 
828
        self.assertNotContainsRe(log, 'revno: 1\n')
 
829
        self.assertContainsRe(log, 'revno: 2\n')
 
830
        self.assertNotContainsRe(log, 'revno: 3\n')
 
831
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
832
        self.assertContainsRe(log, 'revno: 4 ')
 
833
        log = self.run_bzr('log -n0 file3')[0]
 
834
        self.assertNotContainsRe(log, 'revno: 1\n')
 
835
        self.assertNotContainsRe(log, 'revno: 2\n')
 
836
        self.assertContainsRe(log, 'revno: 3\n')
 
837
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
838
        self.assertNotContainsRe(log, 'revno: 4 ')
 
839
        log = self.run_bzr('log -n0 -r3.1.1 file2')[0]
 
840
        self.assertNotContainsRe(log, 'revno: 1\n')
 
841
        self.assertNotContainsRe(log, 'revno: 2\n')
 
842
        self.assertNotContainsRe(log, 'revno: 3\n')
 
843
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
844
        self.assertNotContainsRe(log, 'revno: 4 ')
 
845
        log = self.run_bzr('log -n0 -r4 file2')[0]
 
846
        self.assertNotContainsRe(log, 'revno: 1\n')
 
847
        self.assertNotContainsRe(log, 'revno: 2\n')
 
848
        self.assertNotContainsRe(log, 'revno: 3\n')
 
849
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
850
        self.assertContainsRe(log, 'revno: 4 ')
 
851
        log = self.run_bzr('log -n0 -r3.. file2')[0]
 
852
        self.assertNotContainsRe(log, 'revno: 1\n')
 
853
        self.assertNotContainsRe(log, 'revno: 2\n')
 
854
        self.assertNotContainsRe(log, 'revno: 3\n')
 
855
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
856
        self.assertContainsRe(log, 'revno: 4 ')
 
857
        log = self.run_bzr('log -n0 -r..3 file2')[0]
 
858
        self.assertNotContainsRe(log, 'revno: 1\n')
 
859
        self.assertContainsRe(log, 'revno: 2\n')
 
860
        self.assertNotContainsRe(log, 'revno: 3\n')
 
861
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
862
        self.assertNotContainsRe(log, 'revno: 4 ')
801
863
 
802
864
    def test_log_file_historical_missing(self):
803
865
        # Check logging a deleted file gives an error if the
811
873
        # Check logging a deleted file is ok if the file existed
812
874
        # at the end the revision range
813
875
        self.prepare_tree(complex=True)
814
 
        self.assertLogRevnos(['-n0', '-r..4', 'file2'], ['4', '3.1.1', '2'])
 
876
        log, err = self.run_bzr('log -n0 -r..4 file2')
 
877
        self.assertEquals('', err)
 
878
        self.assertNotContainsRe(log, 'revno: 1\n')
 
879
        self.assertContainsRe(log, 'revno: 2\n')
 
880
        self.assertNotContainsRe(log, 'revno: 3\n')
 
881
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
882
        self.assertContainsRe(log, 'revno: 4 ')
815
883
 
816
884
    def test_log_file_historical_start(self):
817
885
        # Check logging a deleted file is ok if the file existed
818
886
        # at the start of the revision range
819
887
        self.prepare_tree(complex=True)
820
 
        self.assertLogRevnos(['file1'], ['1'])
 
888
        log, err = self.run_bzr('log file1')
 
889
        self.assertEquals('', err)
 
890
        self.assertContainsRe(log, 'revno: 1\n')
 
891
        self.assertNotContainsRe(log, 'revno: 2\n')
 
892
        self.assertNotContainsRe(log, 'revno: 3\n')
 
893
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
894
        self.assertNotContainsRe(log, 'revno: 4 ')
821
895
 
822
896
    def test_log_file_renamed(self):
823
897
        """File matched against revision range, not current tree."""
829
903
        self.assertContainsRe(err, err_msg)
830
904
 
831
905
        # 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):
 
906
        log, err = self.run_bzr('log -r..4 file3')
 
907
        self.assertEquals('', err)
 
908
        self.assertNotContainsRe(log, 'revno: 1\n')
 
909
        self.assertNotContainsRe(log, 'revno: 2\n')
 
910
        self.assertContainsRe(log, 'revno: 3\n')
 
911
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
912
        self.assertNotContainsRe(log, 'revno: 4 ')
 
913
 
 
914
    def test_line_log_file(self):
 
915
        """The line log for a file should only list relevant mainline revs"""
 
916
        # Note: this also implicitly  covers the short logging case.
 
917
        # We test using --line in preference to --short because matching
 
918
        # revnos in the output of --line is more reliable.
 
919
        self.prepare_tree()
 
920
 
 
921
        # full history of file1
 
922
        log = self.run_bzr('log --line file1')[0]
 
923
        self.assertContainsRe(log, '^1:', re.MULTILINE)
 
924
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
925
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
926
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
927
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
928
 
 
929
        # full history of file2
 
930
        log = self.run_bzr('log --line file2')[0]
 
931
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
932
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
933
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
934
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
935
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
936
 
 
937
        # full history of file3
 
938
        log = self.run_bzr('log --line file3')[0]
 
939
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
940
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
941
        self.assertContainsRe(log, '^3:', re.MULTILINE)
 
942
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
943
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
944
 
 
945
        # file in a merge revision
 
946
        log = self.run_bzr('log --line -r3.1.1 file2')[0]
 
947
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
948
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
949
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
950
        self.assertContainsRe(log, '^3.1.1:', re.MULTILINE)
 
951
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
952
 
 
953
        # file in a mainline revision
 
954
        log = self.run_bzr('log --line -r4 file2')[0]
 
955
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
956
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
957
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
958
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
959
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
960
 
 
961
        # file since a revision
 
962
        log = self.run_bzr('log --line -r3.. file2')[0]
 
963
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
964
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
965
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
966
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
967
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
968
 
 
969
        # file up to a revision
 
970
        log = self.run_bzr('log --line -r..3 file2')[0]
 
971
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
972
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
973
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
974
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
975
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
976
 
 
977
 
 
978
class TestLogMultiple(tests.TestCaseWithTransport):
836
979
 
837
980
    def prepare_tree(self):
838
981
        tree = self.make_branch_and_tree('parent')
861
1004
        tree.commit(message='merge child branch')
862
1005
        os.chdir('parent')
863
1006
 
 
1007
    def assertRevnos(self, paths_str, expected_revnos):
 
1008
        # confirm the revision numbers in log --line output are those expected
 
1009
        out, err = self.run_bzr('log --line -n0 %s' % (paths_str,))
 
1010
        self.assertEqual('', err)
 
1011
        revnos = [s.split(':', 1)[0].lstrip() for s in out.splitlines()]
 
1012
        self.assertEqual(expected_revnos, revnos)
 
1013
 
864
1014
    def test_log_files(self):
865
1015
        """The log for multiple file should only list revs for those files"""
866
1016
        self.prepare_tree()
867
 
        self.assertLogRevnos(['file1', 'file2', 'dir1/dir2/file3'],
868
 
                             ['6', '5.1.1', '3', '2', '1'])
 
1017
        self.assertRevnos('file1 file2 dir1/dir2/file3',
 
1018
            ['6', '5.1.1', '3', '2', '1'])
869
1019
 
870
1020
    def test_log_directory(self):
871
1021
        """The log for a directory should show all nested files."""
872
1022
        self.prepare_tree()
873
 
        self.assertLogRevnos(['dir1'], ['5', '3'])
 
1023
        self.assertRevnos('dir1', ['5', '3'])
874
1024
 
875
1025
    def test_log_nested_directory(self):
876
1026
        """The log for a directory should show all nested files."""
877
1027
        self.prepare_tree()
878
 
        self.assertLogRevnos(['dir1/dir2'], ['3'])
 
1028
        self.assertRevnos('dir1/dir2', ['3'])
879
1029
 
880
1030
    def test_log_in_nested_directory(self):
881
1031
        """The log for a directory should show all nested files."""
882
1032
        self.prepare_tree()
883
1033
        os.chdir("dir1")
884
 
        self.assertLogRevnos(['.'], ['5', '3'])
 
1034
        self.assertRevnos('.', ['5', '3'])
885
1035
 
886
1036
    def test_log_files_and_directories(self):
887
1037
        """Logging files and directories together should be fine."""
888
1038
        self.prepare_tree()
889
 
        self.assertLogRevnos(['file4', 'dir1/dir2'], ['4', '3'])
 
1039
        self.assertRevnos('file4 dir1/dir2', ['4', '3'])
890
1040
 
891
1041
    def test_log_files_and_dirs_in_nested_directory(self):
892
1042
        """The log for a directory should show all nested files."""
893
1043
        self.prepare_tree()
894
1044
        os.chdir("dir1")
895
 
        self.assertLogRevnos(['dir2', 'file5'], ['5', '3'])
 
1045
        self.assertRevnos('dir2 file5', ['5', '3'])