~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-09-01 08:02:42 UTC
  • mfrom: (5390.3.3 faster-revert-593560)
  • Revision ID: pqm@pqm.ubuntu.com-20100901080242-esg62ody4frwmy66
(spiv) Avoid repeatedly calling self.target.all_file_ids() in
 InterTree.iter_changes. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
 
18
18
"""Black-box tests for bzr log."""
19
19
 
20
 
import os, re
21
 
 
22
 
from bzrlib import osutils
23
 
from bzrlib.tests.blackbox import ExternalBase
24
 
from bzrlib.tests import KnownFailure, TestCaseInTempDir, TestCaseWithTransport
25
 
from bzrlib.tests.test_log import (
26
 
    normalize_log,
27
 
    )
28
 
from bzrlib.tests import test_log
29
 
 
30
 
 
31
 
class TestCaseWithoutPropsHandler(ExternalBase,
32
 
                                  test_log.TestCaseWithoutPropsHandler):
33
 
    pass
34
 
 
35
 
 
36
 
class TestLog(ExternalBase):
37
 
 
38
 
    def _prepare(self, path='.', format=None):
 
20
from itertools import izip
 
21
import os
 
22
import re
 
23
 
 
24
from bzrlib import (
 
25
    branchbuilder,
 
26
    errors,
 
27
    log,
 
28
    osutils,
 
29
    tests,
 
30
    )
 
31
from bzrlib.tests import (
 
32
    script,
 
33
    test_log,
 
34
    )
 
35
 
 
36
 
 
37
class TestLog(tests.TestCaseWithTransport, test_log.TestLogMixin):
 
38
 
 
39
    def make_minimal_branch(self, path='.', format=None):
 
40
        tree = self.make_branch_and_tree(path, format=format)
 
41
        self.build_tree([path + '/hello.txt'])
 
42
        tree.add('hello.txt')
 
43
        tree.commit(message='message1')
 
44
        return tree
 
45
 
 
46
    def make_linear_branch(self, path='.', format=None):
39
47
        tree = self.make_branch_and_tree(path, format=format)
40
48
        self.build_tree(
41
49
            [path + '/hello.txt', path + '/goodbye.txt', path + '/meep.txt'])
45
53
        tree.commit(message='message2')
46
54
        tree.add('meep.txt')
47
55
        tree.commit(message='message3')
48
 
        self.full_log = self.run_bzr(["log", path])[0]
49
 
        return tree
 
56
        return tree
 
57
 
 
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'])
50
109
 
51
110
    def test_log_null_end_revspec(self):
52
 
        self._prepare()
53
 
        self.assertTrue('revno: 1\n' in self.full_log)
54
 
        self.assertTrue('revno: 2\n' in self.full_log)
55
 
        self.assertTrue('revno: 3\n' in self.full_log)
56
 
        self.assertTrue('message:\n  message1\n' in self.full_log)
57
 
        self.assertTrue('message:\n  message2\n' in self.full_log)
58
 
        self.assertTrue('message:\n  message3\n' in self.full_log)
59
 
 
60
 
        log = self.run_bzr("log -r 1..")[0]
61
 
        self.assertEqualDiff(log, self.full_log)
 
111
        self.make_linear_branch()
 
112
        self.assertLogRevnos(['-r1..'], ['3', '2', '1'])
62
113
 
63
114
    def test_log_null_begin_revspec(self):
64
 
        self._prepare()
65
 
        log = self.run_bzr("log -r ..3")[0]
66
 
        self.assertEqualDiff(self.full_log, log)
 
115
        self.make_linear_branch()
 
116
        self.assertLogRevnos(['-r..3'], ['3', '2', '1'])
67
117
 
68
118
    def test_log_null_both_revspecs(self):
69
 
        self._prepare()
70
 
        log = self.run_bzr("log -r ..")[0]
71
 
        self.assertEqualDiff(self.full_log, log)
 
119
        self.make_linear_branch()
 
120
        self.assertLogRevnos(['-r..'], ['3', '2', '1'])
 
121
 
 
122
    def test_log_negative_begin_revspec_full_log(self):
 
123
        self.make_linear_branch()
 
124
        self.assertLogRevnos(['-r-3..'], ['3', '2', '1'])
 
125
 
 
126
    def test_log_negative_both_revspec_full_log(self):
 
127
        self.make_linear_branch()
 
128
        self.assertLogRevnos(['-r-3..-1'], ['3', '2', '1'])
 
129
 
 
130
    def test_log_negative_both_revspec_partial(self):
 
131
        self.make_linear_branch()
 
132
        self.assertLogRevnos(['-r-3..-2'], ['2', '1'])
 
133
 
 
134
    def test_log_negative_begin_revspec(self):
 
135
        self.make_linear_branch()
 
136
        self.assertLogRevnos(['-r-2..'], ['3', '2'])
 
137
 
 
138
    def test_log_positive_revspecs(self):
 
139
        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
    def test_branch_revspec(self):
 
162
        foo = self.make_branch_and_tree('foo')
 
163
        bar = self.make_branch_and_tree('bar')
 
164
        self.build_tree(['foo/foo.txt', 'bar/bar.txt'])
 
165
        foo.add('foo.txt')
 
166
        bar.add('bar.txt')
 
167
        foo.commit(message='foo')
 
168
        bar.commit(message='bar')
 
169
        self.run_bzr('log -r branch:../bar', working_dir='foo')
 
170
        self.assertEqual([bar.branch.get_rev_id(1)],
 
171
                         [r.rev.revision_id
 
172
                          for r in self.get_captured_revisions()])
 
173
 
 
174
 
 
175
class TestLogExcludeCommonAncestry(TestLogWithLogCatcher):
 
176
 
 
177
    def test_exclude_common_ancestry_simple_revnos(self):
 
178
        self.make_linear_branch()
 
179
        self.assertLogRevnos(['-r1..3', '--exclude-common-ancestry'],
 
180
                             ['3', '2'])
 
181
 
 
182
 
 
183
class TestLogMergedLinearAncestry(TestLogWithLogCatcher):
 
184
 
 
185
    def setUp(self):
 
186
        super(TestLogMergedLinearAncestry, self).setUp()
 
187
        # FIXME: Using a MemoryTree would be even better here (but until we
 
188
        # stop calling run_bzr, there is no point) --vila 100118.
 
189
        builder = branchbuilder.BranchBuilder(self.get_transport())
 
190
        builder.start_series()
 
191
        # 1
 
192
        # | \
 
193
        # 2  1.1.1
 
194
        # | / |
 
195
        # 3  1.1.2
 
196
        # |   |
 
197
        # |  1.1.3
 
198
        # | / |
 
199
        # 4  1.1.4
 
200
        # | /
 
201
        # 5
 
202
 
 
203
        # mainline
 
204
        builder.build_snapshot('1', None, [
 
205
            ('add', ('', 'root-id', 'directory', ''))])
 
206
        builder.build_snapshot('2', ['1'], [])
 
207
        # branch
 
208
        builder.build_snapshot('1.1.1', ['1'], [])
 
209
        # merge branch into mainline
 
210
        builder.build_snapshot('3', ['2', '1.1.1'], [])
 
211
        # new commits in branch
 
212
        builder.build_snapshot('1.1.2', ['1.1.1'], [])
 
213
        builder.build_snapshot('1.1.3', ['1.1.2'], [])
 
214
        # merge branch into mainline
 
215
        builder.build_snapshot('4', ['3', '1.1.3'], [])
 
216
        # merge mainline into branch
 
217
        builder.build_snapshot('1.1.4', ['1.1.3', '4'], [])
 
218
        # merge branch into mainline
 
219
        builder.build_snapshot('5', ['4', '1.1.4'], [])
 
220
        builder.finish_series()
 
221
 
 
222
    def test_n0(self):
 
223
        self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4'],
 
224
                             ['1.1.4', '4', '1.1.3', '1.1.2', '3', '1.1.1'])
 
225
    def test_n0_forward(self):
 
226
        self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4', '--forward'],
 
227
                             ['3', '1.1.1', '4', '1.1.2', '1.1.3', '1.1.4'])
 
228
 
 
229
    def test_n1(self):
 
230
        # starting from 1.1.4 we follow the left-hand ancestry
 
231
        self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4'],
 
232
                             ['1.1.4', '1.1.3', '1.1.2', '1.1.1'])
 
233
 
 
234
    def test_n1_forward(self):
 
235
        self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4', '--forward'],
 
236
                             ['1.1.1', '1.1.2', '1.1.3', '1.1.4'])
 
237
 
 
238
 
 
239
class Test_GenerateAllRevisions(TestLogWithLogCatcher):
 
240
 
 
241
    def setUp(self):
 
242
        super(Test_GenerateAllRevisions, self).setUp()
 
243
        builder = self.make_branch_with_many_merges()
 
244
        b = builder.get_branch()
 
245
        b.lock_read()
 
246
        self.addCleanup(b.unlock)
 
247
        self.branch = b
 
248
 
 
249
    def make_branch_with_many_merges(self, path='.', format=None):
 
250
        builder = branchbuilder.BranchBuilder(self.get_transport())
 
251
        builder.start_series()
 
252
        # The graph below may look a bit complicated (and it may be but I've
 
253
        # banged my head enough on it) but the bug requires at least dotted
 
254
        # revnos *and* merged revisions below that.
 
255
        builder.build_snapshot('1', None, [
 
256
            ('add', ('', 'root-id', 'directory', ''))])
 
257
        builder.build_snapshot('2', ['1'], [])
 
258
        builder.build_snapshot('1.1.1', ['1'], [])
 
259
        builder.build_snapshot('2.1.1', ['2'], [])
 
260
        builder.build_snapshot('3', ['2', '1.1.1'], [])
 
261
        builder.build_snapshot('2.1.2', ['2.1.1'], [])
 
262
        builder.build_snapshot('2.2.1', ['2.1.1'], [])
 
263
        builder.build_snapshot('2.1.3', ['2.1.2', '2.2.1'], [])
 
264
        builder.build_snapshot('4', ['3', '2.1.3'], [])
 
265
        builder.build_snapshot('5', ['4', '2.1.2'], [])
 
266
        builder.finish_series()
 
267
        return builder
 
268
 
 
269
    def test_not_an_ancestor(self):
 
270
        self.assertRaises(errors.BzrCommandError,
 
271
                          log._generate_all_revisions,
 
272
                          self.branch, '1.1.1', '2.1.3', 'reverse',
 
273
                          delayed_graph_generation=True)
 
274
 
 
275
    def test_wrong_order(self):
 
276
        self.assertRaises(errors.BzrCommandError,
 
277
                          log._generate_all_revisions,
 
278
                          self.branch, '5', '2.1.3', 'reverse',
 
279
                          delayed_graph_generation=True)
 
280
 
 
281
    def test_no_start_rev_id_with_end_rev_id_being_a_merge(self):
 
282
        revs = log._generate_all_revisions(
 
283
            self.branch, None, '2.1.3',
 
284
            'reverse', delayed_graph_generation=True)
 
285
 
 
286
 
 
287
class TestLogRevSpecsWithPaths(TestLogWithLogCatcher):
 
288
 
 
289
    def test_log_revno_n_path_wrong_namespace(self):
 
290
        self.make_linear_branch('branch1')
 
291
        self.make_linear_branch('branch2')
 
292
        # There is no guarantee that a path exist between two arbitrary
 
293
        # revisions.
 
294
        self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)
 
295
 
 
296
    def test_log_revno_n_path_correct_order(self):
 
297
        self.make_linear_branch('branch2')
 
298
        self.assertLogRevnos(['-rrevno:1:branch2..revno:3:branch2'],
 
299
                             ['3', '2','1'])
 
300
 
 
301
    def test_log_revno_n_path(self):
 
302
        self.make_linear_branch('branch2')
 
303
        self.assertLogRevnos(['-rrevno:1:branch2'],
 
304
                             ['1'])
 
305
        rev_props = self.log_catcher.revisions[0].rev.properties
 
306
        self.assertEqual('branch2', rev_props['branch-nick'])
 
307
 
 
308
 
 
309
class TestLogErrors(TestLog):
72
310
 
73
311
    def test_log_zero_revspec(self):
74
 
        self._prepare()
75
 
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
312
        self.make_minimal_branch()
 
313
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
76
314
                           ['log', '-r0'])
77
315
 
78
316
    def test_log_zero_begin_revspec(self):
79
 
        self._prepare()
80
 
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
317
        self.make_linear_branch()
 
318
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
81
319
                           ['log', '-r0..2'])
82
320
 
83
321
    def test_log_zero_end_revspec(self):
84
 
        self._prepare()
85
 
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
322
        self.make_linear_branch()
 
323
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
86
324
                           ['log', '-r-2..0'])
87
325
 
88
 
    def test_log_unsupported_timezone(self):
89
 
        self._prepare()
90
 
        self.run_bzr_error('bzr: ERROR: Unsupported timezone format "foo", '
91
 
                           'options are "utc", "original", "local".',
92
 
                           ['log', '--timezone', 'foo'])
93
 
 
94
 
    def test_log_negative_begin_revspec_full_log(self):
95
 
        self._prepare()
96
 
        log = self.run_bzr("log -r -3..")[0]
97
 
        self.assertEqualDiff(self.full_log, log)
98
 
 
99
 
    def test_log_negative_both_revspec_full_log(self):
100
 
        self._prepare()
101
 
        log = self.run_bzr("log -r -3..-1")[0]
102
 
        self.assertEqualDiff(self.full_log, log)
103
 
 
104
 
    def test_log_negative_both_revspec_partial(self):
105
 
        self._prepare()
106
 
        log = self.run_bzr("log -r -3..-2")[0]
107
 
        self.assertTrue('revno: 1\n' in log)
108
 
        self.assertTrue('revno: 2\n' in log)
109
 
        self.assertTrue('revno: 3\n' not in log)
110
 
 
111
 
    def test_log_negative_begin_revspec(self):
112
 
        self._prepare()
113
 
        log = self.run_bzr("log -r -2..")[0]
114
 
        self.assertTrue('revno: 1\n' not in log)
115
 
        self.assertTrue('revno: 2\n' in log)
116
 
        self.assertTrue('revno: 3\n' in log)
117
 
 
118
 
    def test_log_positive_revspecs(self):
119
 
        self._prepare()
120
 
        log = self.run_bzr("log -r 1..3")[0]
121
 
        self.assertEqualDiff(self.full_log, log)
122
 
 
123
 
    def test_log_reversed_revspecs(self):
124
 
        self._prepare()
125
 
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
126
 
                            'the end revision.\n',),
127
 
                           ['log', '-r3..1'])
128
 
 
129
 
    def test_log_revno_n_path(self):
130
 
        self._prepare(path='branch1')
131
 
        self._prepare(path='branch2')
132
 
        log = self.run_bzr("log -r revno:2:branch1..revno:3:branch2",
133
 
                          retcode=3)[0]
134
 
        log = self.run_bzr("log -r revno:1:branch2..revno:3:branch2")[0]
135
 
        self.assertEqualDiff(self.full_log, log)
136
 
        log = self.run_bzr("log -r revno:1:branch2")[0]
137
 
        self.assertTrue('revno: 1\n' in log)
138
 
        self.assertTrue('revno: 2\n' not in log)
139
 
        self.assertTrue('branch nick: branch2\n' in log)
140
 
        self.assertTrue('branch nick: branch1\n' not in log)
141
 
 
142
326
    def test_log_nonexistent_revno(self):
143
 
        self._prepare()
144
 
        (out, err) = self.run_bzr_error(args="log -r 1234",
145
 
            error_regexes=["bzr: ERROR: Requested revision: '1234' "
146
 
                "does not exist in branch:"])
 
327
        self.make_minimal_branch()
 
328
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
 
329
                            "does not exist in branch:"],
 
330
                           ['log', '-r1234'])
147
331
 
148
332
    def test_log_nonexistent_dotted_revno(self):
149
 
        self._prepare()
150
 
        (out, err) = self.run_bzr_error(args="log -r 123.123",
151
 
            error_regexes=["bzr: ERROR: Requested revision: '123.123' "
152
 
                "does not exist in branch:"])
153
 
 
154
 
    def test_log_change_revno(self):
155
 
        self._prepare()
156
 
        expected_log = self.run_bzr("log -r 1")[0]
157
 
        log = self.run_bzr("log -c 1")[0]
158
 
        self.assertEqualDiff(expected_log, log)
 
333
        self.make_minimal_branch()
 
334
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
 
335
                            "does not exist in branch:"],
 
336
                           ['log',  '-r123.123'])
159
337
 
160
338
    def test_log_change_nonexistent_revno(self):
161
 
        self._prepare()
162
 
        (out, err) = self.run_bzr_error(args="log -c 1234",
163
 
            error_regexes=["bzr: ERROR: Requested revision: '1234' "
164
 
                "does not exist in branch:"])
 
339
        self.make_minimal_branch()
 
340
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
 
341
                            "does not exist in branch:"],
 
342
                           ['log',  '-c1234'])
165
343
 
166
344
    def test_log_change_nonexistent_dotted_revno(self):
167
 
        self._prepare()
168
 
        (out, err) = self.run_bzr_error(args="log -c 123.123",
169
 
            error_regexes=["bzr: ERROR: Requested revision: '123.123' "
170
 
                "does not exist in branch:"])
 
345
        self.make_minimal_branch()
 
346
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
 
347
                            "does not exist in branch:"],
 
348
                           ['log', '-c123.123'])
171
349
 
172
 
    def test_log_change_single_revno(self):
173
 
        self._prepare()
174
 
        self.run_bzr_error('bzr: ERROR: Option --change does not'
175
 
                           ' accept revision ranges',
 
350
    def test_log_change_single_revno_only(self):
 
351
        self.make_minimal_branch()
 
352
        self.run_bzr_error(['bzr: ERROR: Option --change does not'
 
353
                           ' accept revision ranges'],
176
354
                           ['log', '--change', '2..3'])
177
355
 
178
356
    def test_log_change_incompatible_with_revision(self):
179
 
        self._prepare()
180
 
        self.run_bzr_error('bzr: ERROR: --revision and --change'
181
 
                           ' are mutually exclusive',
 
357
        self.run_bzr_error(['bzr: ERROR: --revision and --change'
 
358
                           ' are mutually exclusive'],
182
359
                           ['log', '--change', '2', '--revision', '3'])
183
360
 
184
361
    def test_log_nonexistent_file(self):
 
362
        self.make_minimal_branch()
185
363
        # files that don't exist in either the basis tree or working tree
186
364
        # should give an error
187
 
        wt = self.make_branch_and_tree('.')
188
365
        out, err = self.run_bzr('log does-not-exist', retcode=3)
189
 
        self.assertContainsRe(
190
 
            err, 'Path unknown at end or start of revision range: does-not-exist')
 
366
        self.assertContainsRe(err,
 
367
                              'Path unknown at end or start of revision range: '
 
368
                              'does-not-exist')
 
369
 
 
370
    def test_log_reversed_revspecs(self):
 
371
        self.make_linear_branch()
 
372
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
 
373
                            'the end revision.\n',),
 
374
                           ['log', '-r3..1'])
 
375
 
 
376
    def test_log_reversed_dotted_revspecs(self):
 
377
        self.make_merged_branch()
 
378
        self.run_bzr_error(('bzr: ERROR: Start revision not found in '
 
379
                            'left-hand history of end revision.\n',),
 
380
                           "log -r 1.1.1..1")
 
381
 
 
382
    def test_log_bad_message_re(self):
 
383
        """Bad --message argument gives a sensible message
 
384
 
 
385
        See https://bugs.launchpad.net/bzr/+bug/251352
 
386
        """
 
387
        self.make_minimal_branch()
 
388
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
 
389
        self.assertContainsRe(err, "ERROR.*Invalid pattern.*nothing to repeat")
 
390
        self.assertNotContainsRe(err, "Unprintable exception")
 
391
        self.assertEqual(out, '')
 
392
 
 
393
    def test_log_unsupported_timezone(self):
 
394
        self.make_linear_branch()
 
395
        self.run_bzr_error(['bzr: ERROR: Unsupported timezone format "foo", '
 
396
                            'options are "utc", "original", "local".'],
 
397
                           ['log', '--timezone', 'foo'])
 
398
 
 
399
    def test_log_exclude_ancestry_no_range(self):
 
400
        self.make_linear_branch()
 
401
        self.run_bzr_error(['bzr: ERROR: --exclude-common-ancestry'
 
402
                            ' requires -r with two revisions'],
 
403
                           ['log', '--exclude-common-ancestry'])
 
404
 
 
405
    def test_log_exclude_ancestry_single_revision(self):
 
406
        self.make_merged_branch()
 
407
        self.run_bzr_error(['bzr: ERROR: --exclude-common-ancestry'
 
408
                            ' requires two different revisions'],
 
409
                           ['log', '--exclude-common-ancestry',
 
410
                            '-r1.1.1..1.1.1'])
 
411
 
 
412
class TestLogTags(TestLog):
191
413
 
192
414
    def test_log_with_tags(self):
193
 
        tree = self._prepare(format='dirstate-tags')
 
415
        tree = self.make_linear_branch(format='dirstate-tags')
194
416
        branch = tree.branch
195
417
        branch.tags.set_tag('tag1', branch.get_rev_id(1))
196
418
        branch.tags.set_tag('tag1.1', branch.get_rev_id(1))
205
427
        self.assertContainsRe(log, r'tags: (tag1, tag1\.1|tag1\.1, tag1)')
206
428
 
207
429
    def test_merged_log_with_tags(self):
208
 
        branch1_tree = self._prepare(path='branch1', format='dirstate-tags')
 
430
        branch1_tree = self.make_linear_branch('branch1',
 
431
                                               format='dirstate-tags')
209
432
        branch1 = branch1_tree.branch
210
433
        branch2_tree = branch1_tree.bzrdir.sprout('branch2').open_workingtree()
211
434
        branch1_tree.commit(message='foobar', allow_pointless=True)
212
435
        branch1.tags.set_tag('tag1', branch1.last_revision())
213
 
        os.chdir('branch2')
214
 
        self.run_bzr('merge ../branch1') # tags don't propagate otherwise
 
436
        # tags don't propagate if we don't merge
 
437
        self.run_bzr('merge ../branch1', working_dir='branch2')
215
438
        branch2_tree.commit(message='merge branch 1')
216
 
        log = self.run_bzr("log -r-1")[0]
 
439
        log = self.run_bzr("log -n0 -r-1", working_dir='branch2')[0]
217
440
        self.assertContainsRe(log, r'    tags: tag1')
218
 
        log = self.run_bzr("log -r3.1.1")[0]
 
441
        log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
219
442
        self.assertContainsRe(log, r'tags: tag1')
220
443
 
221
 
    def test_log_limit(self):
222
 
        tree = self.make_branch_and_tree('.')
223
 
        # We want more commits than our batch size starts at
224
 
        for pos in range(10):
225
 
            tree.commit("%s" % pos)
226
 
        log = self.run_bzr("log --limit 2")[0]
227
 
        self.assertNotContainsRe(log, r'revno: 1\n')
228
 
        self.assertNotContainsRe(log, r'revno: 2\n')
229
 
        self.assertNotContainsRe(log, r'revno: 3\n')
230
 
        self.assertNotContainsRe(log, r'revno: 4\n')
231
 
        self.assertNotContainsRe(log, r'revno: 5\n')
232
 
        self.assertNotContainsRe(log, r'revno: 6\n')
233
 
        self.assertNotContainsRe(log, r'revno: 7\n')
234
 
        self.assertNotContainsRe(log, r'revno: 8\n')
235
 
        self.assertContainsRe(log, r'revno: 9\n')
236
 
        self.assertContainsRe(log, r'revno: 10\n')
237
 
 
238
 
    def test_log_limit_short(self):
239
 
        self._prepare()
240
 
        log = self.run_bzr("log -l 2")[0]
241
 
        self.assertNotContainsRe(log, r'revno: 1\n')
242
 
        self.assertContainsRe(log, r'revno: 2\n')
243
 
        self.assertContainsRe(log, r'revno: 3\n')
244
 
 
245
 
    def test_log_bad_message_re(self):
246
 
        """Bad --message argument gives a sensible message
247
 
        
248
 
        See https://bugs.launchpad.net/bzr/+bug/251352
249
 
        """
250
 
        self._prepare()
251
 
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
252
 
        self.assertEqual("bzr: ERROR: Invalid regular expression"
253
 
            " in log message filter"
254
 
            ": '*'"
255
 
            ": nothing to repeat\n", err)
256
 
        self.assertEqual('', out)
257
 
 
258
 
 
259
 
class TestLogVerbose(TestCaseWithTransport):
 
444
 
 
445
class TestLogVerbose(TestLog):
260
446
 
261
447
    def setUp(self):
262
448
        super(TestLogVerbose, self).setUp()
263
 
        tree = self.make_branch_and_tree('.')
264
 
        self.build_tree(['hello.txt'])
265
 
        tree.add('hello.txt')
266
 
        tree.commit(message='message1')
 
449
        self.make_minimal_branch()
267
450
 
268
451
    def assertUseShortDeltaFormat(self, cmd):
269
452
        log = self.run_bzr(cmd)[0]
294
477
        self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
295
478
 
296
479
 
297
 
class TestLogMerges(TestCaseWithoutPropsHandler):
298
 
 
299
 
    def _prepare(self):
300
 
        parent_tree = self.make_branch_and_tree('parent')
301
 
        parent_tree.commit(message='first post', allow_pointless=True)
302
 
        child_tree = parent_tree.bzrdir.sprout('child').open_workingtree()
303
 
        child_tree.commit(message='branch 1', allow_pointless=True)
304
 
        smaller_tree = \
305
 
                child_tree.bzrdir.sprout('smallerchild').open_workingtree()
306
 
        smaller_tree.commit(message='branch 2', allow_pointless=True)
307
 
        child_tree.merge_from_branch(smaller_tree.branch)
308
 
        child_tree.commit(message='merge branch 2')
309
 
        parent_tree.merge_from_branch(child_tree.branch)
310
 
        parent_tree.commit(message='merge branch 1')
311
 
        os.chdir('parent')
312
 
 
313
 
    def _prepare_short(self):
314
 
        parent_tree = self.make_branch_and_tree('parent')
315
 
        parent_tree.commit(message='first post',
316
 
            timestamp=1132586700, timezone=36000,
317
 
            committer='Joe Foo <joe@foo.com>')
318
 
        child_tree = parent_tree.bzrdir.sprout('child').open_workingtree()
319
 
        child_tree.commit(message='branch 1',
320
 
            timestamp=1132586800, timezone=36000,
321
 
            committer='Joe Foo <joe@foo.com>')
322
 
        smaller_tree = \
323
 
                child_tree.bzrdir.sprout('smallerchild').open_workingtree()
324
 
        smaller_tree.commit(message='branch 2',
325
 
            timestamp=1132586900, timezone=36000,
326
 
            committer='Joe Foo <joe@foo.com>')
327
 
        child_tree.merge_from_branch(smaller_tree.branch)
328
 
        child_tree.commit(message='merge branch 2',
329
 
            timestamp=1132587000, timezone=36000,
330
 
            committer='Joe Foo <joe@foo.com>')
331
 
        parent_tree.merge_from_branch(child_tree.branch)
332
 
        parent_tree.commit(message='merge branch 1',
333
 
            timestamp=1132587100, timezone=36000,
334
 
            committer='Joe Foo <joe@foo.com>')
335
 
        os.chdir('parent')
 
480
class TestLogMerges(TestLogWithLogCatcher):
 
481
 
 
482
    def setUp(self):
 
483
        super(TestLogMerges, self).setUp()
 
484
        self.make_branches_with_merges()
 
485
 
 
486
    def make_branches_with_merges(self):
 
487
        level0 = self.make_branch_and_tree('level0')
 
488
        self.wt_commit(level0, 'in branch level0')
 
489
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
 
490
        self.wt_commit(level1, 'in branch level1')
 
491
        level2 = level1.bzrdir.sprout('level2').open_workingtree()
 
492
        self.wt_commit(level2, 'in branch level2')
 
493
        level1.merge_from_branch(level2.branch)
 
494
        self.wt_commit(level1, 'merge branch level2')
 
495
        level0.merge_from_branch(level1.branch)
 
496
        self.wt_commit(level0, 'merge branch level1')
336
497
 
337
498
    def test_merges_are_indented_by_level(self):
338
 
        self._prepare()
339
 
        out,err = self.run_bzr('log')
340
 
        self.assertEqual('', err)
341
 
        log = normalize_log(out)
342
 
        self.assertEqualDiff(log, """\
343
 
------------------------------------------------------------
344
 
revno: 2
345
 
committer: Lorem Ipsum <test@example.com>
346
 
branch nick: parent
347
 
timestamp: Just now
348
 
message:
349
 
  merge branch 1
350
 
    ------------------------------------------------------------
351
 
    revno: 1.1.2
352
 
    committer: Lorem Ipsum <test@example.com>
353
 
    branch nick: child
354
 
    timestamp: Just now
355
 
    message:
356
 
      merge branch 2
357
 
        ------------------------------------------------------------
358
 
        revno: 1.2.1
359
 
        committer: Lorem Ipsum <test@example.com>
360
 
        branch nick: smallerchild
361
 
        timestamp: Just now
362
 
        message:
363
 
          branch 2
364
 
    ------------------------------------------------------------
365
 
    revno: 1.1.1
366
 
    committer: Lorem Ipsum <test@example.com>
367
 
    branch nick: child
368
 
    timestamp: Just now
369
 
    message:
370
 
      branch 1
371
 
------------------------------------------------------------
372
 
revno: 1
373
 
committer: Lorem Ipsum <test@example.com>
374
 
branch nick: parent
375
 
timestamp: Just now
376
 
message:
377
 
  first post
378
 
""")
 
499
        self.run_bzr(['log', '-n0'], working_dir='level0')
 
500
        revnos_and_depth = [(r.revno, r.merge_depth)
 
501
                            for r in self.get_captured_revisions()]
 
502
        self.assertEqual([('2', 0), ('1.1.2', 1), ('1.2.1', 2), ('1.1.1', 1),
 
503
                          ('1', 0)],
 
504
                         [(r.revno, r.merge_depth)
 
505
                            for r in self.get_captured_revisions()])
379
506
 
380
507
    def test_force_merge_revisions_off(self):
381
 
        self._prepare()
382
 
        out,err = self.run_bzr('log --long -n1')
383
 
        self.assertEqual('', err)
384
 
        log = normalize_log(out)
385
 
        self.assertEqualDiff(log, """\
386
 
------------------------------------------------------------
387
 
revno: 2
388
 
committer: Lorem Ipsum <test@example.com>
389
 
branch nick: parent
390
 
timestamp: Just now
391
 
message:
392
 
  merge branch 1
393
 
------------------------------------------------------------
394
 
revno: 1
395
 
committer: Lorem Ipsum <test@example.com>
396
 
branch nick: parent
397
 
timestamp: Just now
398
 
message:
399
 
  first post
400
 
""")
 
508
        self.assertLogRevnos(['-n1'], ['2', '1'], working_dir='level0')
401
509
 
402
510
    def test_force_merge_revisions_on(self):
403
 
        self._prepare_short()
404
 
        out,err = self.run_bzr('log --short -n0')
405
 
        self.assertEqual('', err)
406
 
        log = normalize_log(out)
407
 
        self.assertEqualDiff(log, """\
408
 
    2 Joe Foo\t2005-11-22 [merge]
409
 
      merge branch 1
410
 
 
411
 
          1.1.2 Joe Foo\t2005-11-22 [merge]
412
 
                merge branch 2
413
 
 
414
 
              1.2.1 Joe Foo\t2005-11-22
415
 
                    branch 2
416
 
 
417
 
          1.1.1 Joe Foo\t2005-11-22
418
 
                branch 1
419
 
 
420
 
    1 Joe Foo\t2005-11-22
421
 
      first post
422
 
 
423
 
""")
 
511
        self.assertLogRevnos(['-n0'], ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
 
512
                             working_dir='level0')
 
513
 
 
514
    def test_include_merges(self):
 
515
        # Confirm --include-merges gives the same output as -n0
 
516
        self.assertLogRevnos(['--include-merges'],
 
517
                             ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
 
518
                             working_dir='level0')
 
519
        self.assertLogRevnos(['--include-merges'],
 
520
                             ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
 
521
                             working_dir='level0')
 
522
        out_im, err_im = self.run_bzr('log --include-merges',
 
523
                                      working_dir='level0')
 
524
        out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
 
525
        self.assertEqual('', err_im)
 
526
        self.assertEqual('', err_n0)
 
527
        self.assertEqual(out_im, out_n0)
424
528
 
425
529
    def test_force_merge_revisions_N(self):
426
 
        self._prepare_short()
427
 
        out,err = self.run_bzr('log --short -n2')
428
 
        self.assertEqual('', err)
429
 
        log = normalize_log(out)
430
 
        self.assertEqualDiff(log, """\
431
 
    2 Joe Foo\t2005-11-22 [merge]
432
 
      merge branch 1
433
 
 
434
 
          1.1.2 Joe Foo\t2005-11-22 [merge]
435
 
                merge branch 2
436
 
 
437
 
          1.1.1 Joe Foo\t2005-11-22
438
 
                branch 1
439
 
 
440
 
    1 Joe Foo\t2005-11-22
441
 
      first post
442
 
 
443
 
""")
 
530
        self.assertLogRevnos(['-n2'],
 
531
                             ['2', '1.1.2', '1.1.1', '1'],
 
532
                             working_dir='level0')
444
533
 
445
534
    def test_merges_single_merge_rev(self):
446
 
        self._prepare()
447
 
        out,err = self.run_bzr('log -r1.1.2')
448
 
        self.assertEqual('', err)
449
 
        log = normalize_log(out)
450
 
        self.assertEqualDiff(log, """\
451
 
------------------------------------------------------------
452
 
revno: 1.1.2
453
 
committer: Lorem Ipsum <test@example.com>
454
 
branch nick: child
455
 
timestamp: Just now
456
 
message:
457
 
  merge branch 2
458
 
    ------------------------------------------------------------
459
 
    revno: 1.2.1
460
 
    committer: Lorem Ipsum <test@example.com>
461
 
    branch nick: smallerchild
462
 
    timestamp: Just now
463
 
    message:
464
 
      branch 2
465
 
""")
 
535
        self.assertLogRevnosAndDepths(['-n0', '-r1.1.2'],
 
536
                                      [('1.1.2', 0), ('1.2.1', 1)],
 
537
                                      working_dir='level0')
466
538
 
467
539
    def test_merges_partial_range(self):
468
 
        self._prepare()
469
 
        out, err = self.run_bzr('log -r1.1.1..1.1.2')
470
 
        self.assertEqual('', err)
471
 
        log = normalize_log(out)
472
 
        self.assertEqualDiff(log, """\
473
 
------------------------------------------------------------
474
 
revno: 1.1.2
475
 
committer: Lorem Ipsum <test@example.com>
476
 
branch nick: child
477
 
timestamp: Just now
478
 
message:
479
 
  merge branch 2
480
 
    ------------------------------------------------------------
481
 
    revno: 1.2.1
482
 
    committer: Lorem Ipsum <test@example.com>
483
 
    branch nick: smallerchild
484
 
    timestamp: Just now
485
 
    message:
486
 
      branch 2
487
 
------------------------------------------------------------
488
 
revno: 1.1.1
489
 
committer: Lorem Ipsum <test@example.com>
490
 
branch nick: child
491
 
timestamp: Just now
492
 
message:
493
 
  branch 1
494
 
""")
495
 
 
496
 
    def test_merges_nonsupporting_formatter(self):
497
 
        # This "feature" of log formatters is madness. If a log
498
 
        # formatter cannot display a dotted-revno, it ought to ignore it.
499
 
        # Otherwise, a linear sequence is always expected to be handled now.
500
 
        raise KnownFailure('log formatters must support linear sequences now')
501
 
        self._prepare()
502
 
        err_msg = 'Selected log formatter only supports mainline revisions.'
503
 
        # The single revision case is tested in the core tests
504
 
        # since all standard formatters support single merge revisions.
505
 
        out,err = self.run_bzr('log --short -r1..1.1.2', retcode=3)
506
 
        self.assertContainsRe(err, err_msg)
507
 
        out,err = self.run_bzr('log --short -r1.1.1..1.1.2', retcode=3)
508
 
        self.assertContainsRe(err, err_msg)
509
 
 
510
 
 
511
 
def subst_dates(string):
512
 
    """Replace date strings with constant values."""
513
 
    return re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-\+]\d{4}',
514
 
                  'YYYY-MM-DD HH:MM:SS +ZZZZ', string)
515
 
 
516
 
 
517
 
class TestLogDiff(TestCaseWithoutPropsHandler):
518
 
 
519
 
    def _prepare(self):
520
 
        parent_tree = self.make_branch_and_tree('parent')
521
 
        self.build_tree(['parent/file1', 'parent/file2'])
522
 
        parent_tree.add('file1')
523
 
        parent_tree.add('file2')
524
 
        parent_tree.commit(message='first post',
525
 
            timestamp=1132586655, timezone=36000,
526
 
            committer='Lorem Ipsum <test@example.com>')
527
 
        child_tree = parent_tree.bzrdir.sprout('child').open_workingtree()
528
 
        self.build_tree_contents([('child/file2', 'hello\n')])
529
 
        child_tree.commit(message='branch 1',
530
 
            timestamp=1132586700, timezone=36000,
531
 
            committer='Lorem Ipsum <test@example.com>')
532
 
        parent_tree.merge_from_branch(child_tree.branch)
533
 
        parent_tree.commit(message='merge branch 1',
534
 
            timestamp=1132586800, timezone=36000,
535
 
            committer='Lorem Ipsum <test@example.com>')
536
 
        os.chdir('parent')
537
 
 
538
 
    def test_log_show_diff_long(self):
539
 
        self._prepare()
540
 
        out,err = self.run_bzr('log -p')
541
 
        self.assertEqual('', err)
542
 
        log = normalize_log(out)
543
 
        self.assertEqualDiff(subst_dates(log), """\
544
 
------------------------------------------------------------
545
 
revno: 2
546
 
committer: Lorem Ipsum <test@example.com>
547
 
branch nick: parent
548
 
timestamp: Just now
549
 
message:
550
 
  merge branch 1
551
 
diff:
552
 
=== modified file 'file2'
553
 
--- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
554
 
+++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
555
 
@@ -1,1 +1,1 @@
556
 
-contents of parent/file2
557
 
+hello
558
 
    ------------------------------------------------------------
559
 
    revno: 1.1.1
560
 
    committer: Lorem Ipsum <test@example.com>
561
 
    branch nick: child
562
 
    timestamp: Just now
563
 
    message:
564
 
      branch 1
565
 
    diff:
566
 
    === modified file 'file2'
567
 
    --- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
568
 
    +++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
569
 
    @@ -1,1 +1,1 @@
570
 
    -contents of parent/file2
571
 
    +hello
572
 
------------------------------------------------------------
573
 
revno: 1
574
 
committer: Lorem Ipsum <test@example.com>
575
 
branch nick: parent
576
 
timestamp: Just now
577
 
message:
578
 
  first post
579
 
diff:
580
 
=== added file 'file1'
581
 
--- file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
582
 
+++ file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
583
 
@@ -0,0 +1,1 @@
584
 
+contents of parent/file1
585
 
 
586
 
=== added file 'file2'
587
 
--- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
588
 
+++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
589
 
@@ -0,0 +1,1 @@
590
 
+contents of parent/file2
591
 
""")
592
 
 
593
 
    def test_log_show_diff_short(self):
594
 
        self._prepare()
595
 
        out,err = self.run_bzr('log -p --short')
596
 
        self.assertEqual('', err)
597
 
        log = normalize_log(out)
598
 
        self.assertEqualDiff(subst_dates(log), """\
599
 
    2 Lorem Ipsum\t2005-11-22 [merge]
600
 
      merge branch 1
601
 
      === modified file 'file2'
602
 
      --- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
603
 
      +++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
604
 
      @@ -1,1 +1,1 @@
605
 
      -contents of parent/file2
606
 
      +hello
607
 
 
608
 
    1 Lorem Ipsum\t2005-11-22
609
 
      first post
610
 
      === added file 'file1'
611
 
      --- file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
612
 
      +++ file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
613
 
      @@ -0,0 +1,1 @@
614
 
      +contents of parent/file1
615
 
\x20\x20\x20\x20\x20\x20
616
 
      === added file 'file2'
617
 
      --- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
618
 
      +++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
619
 
      @@ -0,0 +1,1 @@
620
 
      +contents of parent/file2
621
 
 
622
 
""")
623
 
 
624
 
    def test_log_show_diff_line(self):
625
 
        self._prepare()
626
 
        out,err = self.run_bzr('log -p --line')
627
 
        self.assertEqual('', err)
628
 
        log = normalize_log(out)
629
 
        # Not supported by this formatter so expect plain output
630
 
        self.assertEqualDiff(subst_dates(log), """\
631
 
2: Lorem Ipsum 2005-11-22 [merge] merge branch 1
632
 
1: Lorem Ipsum 2005-11-22 first post
633
 
""")
634
 
 
635
 
    def test_log_show_diff_file(self):
636
 
        """Only the diffs for the given file are to be shown"""
637
 
        self._prepare()
638
 
        out,err = self.run_bzr('log -p --short file2')
639
 
        self.assertEqual('', err)
640
 
        log = normalize_log(out)
641
 
        self.assertEqualDiff(subst_dates(log), """\
642
 
    2 Lorem Ipsum\t2005-11-22 [merge]
643
 
      merge branch 1
644
 
      === modified file 'file2'
645
 
      --- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
646
 
      +++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
647
 
      @@ -1,1 +1,1 @@
648
 
      -contents of parent/file2
649
 
      +hello
650
 
 
651
 
    1 Lorem Ipsum\t2005-11-22
652
 
      first post
653
 
      === added file 'file2'
654
 
      --- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
655
 
      +++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
656
 
      @@ -0,0 +1,1 @@
657
 
      +contents of parent/file2
658
 
 
659
 
""")
660
 
        out,err = self.run_bzr('log -p --short file1')
661
 
        self.assertEqual('', err)
662
 
        log = normalize_log(out)
663
 
        self.assertEqualDiff(subst_dates(log), """\
664
 
    1 Lorem Ipsum\t2005-11-22
665
 
      first post
666
 
      === added file 'file1'
667
 
      --- file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
668
 
      +++ file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
669
 
      @@ -0,0 +1,1 @@
670
 
      +contents of parent/file1
671
 
 
672
 
""")
 
540
        self.assertLogRevnosAndDepths(
 
541
                ['-n0', '-r1.1.1..1.1.2'],
 
542
                [('1.1.2', 0), ('1.2.1', 1), ('1.1.1', 0)],
 
543
                working_dir='level0')
 
544
 
 
545
    def test_merges_partial_range_ignore_before_lower_bound(self):
 
546
        """Dont show revisions before the lower bound's merged revs"""
 
547
        self.assertLogRevnosAndDepths(
 
548
                ['-n0', '-r1.1.2..2'],
 
549
                [('2', 0), ('1.1.2', 1), ('1.2.1', 2)],
 
550
                working_dir='level0')
 
551
 
 
552
 
 
553
class TestLogDiff(TestLogWithLogCatcher):
 
554
 
 
555
    # FIXME: We need specific tests for each LogFormatter about how the diffs
 
556
    # are displayed: --long indent them by depth, --short use a fixed
 
557
    # indent and --line does't display them. -- vila 10019
 
558
 
 
559
    def setUp(self):
 
560
        super(TestLogDiff, self).setUp()
 
561
        self.make_branch_with_diffs()
 
562
 
 
563
    def make_branch_with_diffs(self):
 
564
        level0 = self.make_branch_and_tree('level0')
 
565
        self.build_tree(['level0/file1', 'level0/file2'])
 
566
        level0.add('file1')
 
567
        level0.add('file2')
 
568
        self.wt_commit(level0, 'in branch level0')
 
569
 
 
570
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
 
571
        self.build_tree_contents([('level1/file2', 'hello\n')])
 
572
        self.wt_commit(level1, 'in branch level1')
 
573
        level0.merge_from_branch(level1.branch)
 
574
        self.wt_commit(level0, 'merge branch level1')
 
575
 
 
576
    def _diff_file1_revno1(self):
 
577
        return """=== added file 'file1'
 
578
--- file1\t1970-01-01 00:00:00 +0000
 
579
+++ file1\t2005-11-22 00:00:00 +0000
 
580
@@ -0,0 +1,1 @@
 
581
+contents of level0/file1
 
582
 
 
583
"""
 
584
 
 
585
    def _diff_file2_revno2(self):
 
586
        return """=== modified file 'file2'
 
587
--- file2\t2005-11-22 00:00:00 +0000
 
588
+++ file2\t2005-11-22 00:00:01 +0000
 
589
@@ -1,1 +1,1 @@
 
590
-contents of level0/file2
 
591
+hello
 
592
 
 
593
"""
 
594
 
 
595
    def _diff_file2_revno1_1_1(self):
 
596
        return """=== modified file 'file2'
 
597
--- file2\t2005-11-22 00:00:00 +0000
 
598
+++ file2\t2005-11-22 00:00:01 +0000
 
599
@@ -1,1 +1,1 @@
 
600
-contents of level0/file2
 
601
+hello
 
602
 
 
603
"""
 
604
 
 
605
    def _diff_file2_revno1(self):
 
606
        return """=== added file 'file2'
 
607
--- file2\t1970-01-01 00:00:00 +0000
 
608
+++ file2\t2005-11-22 00:00:00 +0000
 
609
@@ -0,0 +1,1 @@
 
610
+contents of level0/file2
 
611
 
 
612
"""
 
613
 
 
614
    def assertLogRevnosAndDiff(self, args, expected,
 
615
                            working_dir='.'):
 
616
        self.run_bzr(['log', '-p'] + args, working_dir=working_dir)
 
617
        expected_revnos_and_depths = [
 
618
            (revno, depth) for revno, depth, diff in expected]
 
619
        # Check the revnos and depths first to make debugging easier
 
620
        self.assertEqual(expected_revnos_and_depths,
 
621
                         [(r.revno, r.merge_depth)
 
622
                           for r in self.get_captured_revisions()])
 
623
        # Now check the diffs, adding the revno  in case of failure
 
624
        fmt = 'In revno %s\n%s'
 
625
        for expected_rev, actual_rev in izip(expected,
 
626
                                             self.get_captured_revisions()):
 
627
            revno, depth, expected_diff = expected_rev
 
628
            actual_diff = actual_rev.diff
 
629
            self.assertEqualDiff(fmt % (revno, expected_diff),
 
630
                                 fmt % (revno, actual_diff))
 
631
 
 
632
    def test_log_diff_with_merges(self):
 
633
        self.assertLogRevnosAndDiff(
 
634
            ['-n0'],
 
635
            [('2', 0, self._diff_file2_revno2()),
 
636
             ('1.1.1', 1, self._diff_file2_revno1_1_1()),
 
637
             ('1', 0, self._diff_file1_revno1()
 
638
              + self._diff_file2_revno1())],
 
639
            working_dir='level0')
 
640
 
 
641
 
 
642
    def test_log_diff_file1(self):
 
643
        self.assertLogRevnosAndDiff(['-n0', 'file1'],
 
644
                                    [('1', 0, self._diff_file1_revno1())],
 
645
                                    working_dir='level0')
 
646
 
 
647
    def test_log_diff_file2(self):
 
648
        self.assertLogRevnosAndDiff(['-n1', 'file2'],
 
649
                                    [('2', 0, self._diff_file2_revno2()),
 
650
                                     ('1', 0, self._diff_file2_revno1())],
 
651
                                    working_dir='level0')
 
652
 
 
653
 
 
654
class TestLogUnicodeDiff(TestLog):
673
655
 
674
656
    def test_log_show_diff_non_ascii(self):
675
657
        # Smoke test for bug #328007 UnicodeDecodeError on 'log -p'
692
674
        self.assertEqual('', err)
693
675
 
694
676
 
695
 
class TestLogEncodings(TestCaseInTempDir):
 
677
class TestLogEncodings(tests.TestCaseInTempDir):
696
678
 
697
679
    _mu = u'\xb5'
698
680
    _message = u'Message with \xb5'
714
696
    ]
715
697
 
716
698
    def setUp(self):
717
 
        TestCaseInTempDir.setUp(self)
718
 
        self.user_encoding = osutils._cached_user_encoding
719
 
 
720
 
    def tearDown(self):
721
 
        osutils._cached_user_encoding = self.user_encoding
722
 
        TestCaseInTempDir.tearDown(self)
 
699
        super(TestLogEncodings, self).setUp()
 
700
        self.overrideAttr(osutils, '_cached_user_encoding')
723
701
 
724
702
    def create_branch(self):
725
703
        bzr = self.run_bzr
726
704
        bzr('init')
727
 
        open('a', 'wb').write('some stuff\n')
 
705
        self.build_tree_contents([('a', 'some stuff\n')])
728
706
        bzr('add a')
729
707
        bzr(['commit', '-m', self._message])
730
708
 
793
771
        self.assertEquals(-1, stdout.find(test_in_cp1251))
794
772
 
795
773
 
796
 
class TestLogFile(TestCaseWithTransport):
 
774
class TestLogFile(TestLogWithLogCatcher):
797
775
 
798
776
    def test_log_local_branch_file(self):
799
777
        """We should be able to log files in local treeless branches"""
828
806
            tree.commit('remove file1')
829
807
        os.chdir('parent')
830
808
 
831
 
    def test_log_file(self):
832
 
        """The log for a particular file should only list revs for that file"""
833
 
        self.prepare_tree()
834
 
        log = self.run_bzr('log file1')[0]
835
 
        self.assertContainsRe(log, 'revno: 1\n')
836
 
        self.assertNotContainsRe(log, 'revno: 2\n')
837
 
        self.assertNotContainsRe(log, 'revno: 3\n')
838
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
839
 
        self.assertNotContainsRe(log, 'revno: 4\n')
840
 
        log = self.run_bzr('log file2')[0]
841
 
        self.assertNotContainsRe(log, 'revno: 1\n')
842
 
        self.assertContainsRe(log, 'revno: 2\n')
843
 
        self.assertNotContainsRe(log, 'revno: 3\n')
844
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
845
 
        self.assertContainsRe(log, 'revno: 4\n')
846
 
        log = self.run_bzr('log file3')[0]
847
 
        self.assertNotContainsRe(log, 'revno: 1\n')
848
 
        self.assertNotContainsRe(log, 'revno: 2\n')
849
 
        self.assertContainsRe(log, 'revno: 3\n')
850
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
851
 
        self.assertNotContainsRe(log, 'revno: 4\n')
852
 
        log = self.run_bzr('log -r3.1.1 file2')[0]
853
 
        self.assertNotContainsRe(log, 'revno: 1\n')
854
 
        self.assertNotContainsRe(log, 'revno: 2\n')
855
 
        self.assertNotContainsRe(log, 'revno: 3\n')
856
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
857
 
        self.assertNotContainsRe(log, 'revno: 4\n')
858
 
        log = self.run_bzr('log -r4 file2')[0]
859
 
        self.assertNotContainsRe(log, 'revno: 1\n')
860
 
        self.assertNotContainsRe(log, 'revno: 2\n')
861
 
        self.assertNotContainsRe(log, 'revno: 3\n')
862
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
863
 
        self.assertContainsRe(log, 'revno: 4\n')
864
 
        log = self.run_bzr('log -r3.. file2')[0]
865
 
        self.assertNotContainsRe(log, 'revno: 1\n')
866
 
        self.assertNotContainsRe(log, 'revno: 2\n')
867
 
        self.assertNotContainsRe(log, 'revno: 3\n')
868
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
869
 
        self.assertContainsRe(log, 'revno: 4\n')
870
 
        log = self.run_bzr('log -r..3 file2')[0]
871
 
        self.assertNotContainsRe(log, 'revno: 1\n')
872
 
        self.assertContainsRe(log, 'revno: 2\n')
873
 
        self.assertNotContainsRe(log, 'revno: 3\n')
874
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
875
 
        self.assertNotContainsRe(log, 'revno: 4\n')
 
809
    # FIXME: It would be good to parametrize the following tests against all
 
810
    # formatters. But the revisions selection is not *currently* part of the
 
811
    # LogFormatter contract, so using LogCatcher is sufficient -- vila 100118
 
812
    def test_log_file1(self):
 
813
        self.prepare_tree()
 
814
        self.assertLogRevnos(['-n0', 'file1'], ['1'])
 
815
 
 
816
    def test_log_file2(self):
 
817
        self.prepare_tree()
 
818
        # file2 full history
 
819
        self.assertLogRevnos(['-n0', 'file2'], ['4', '3.1.1', '2'])
 
820
        # file2 in a merge revision
 
821
        self.assertLogRevnos(['-n0', '-r3.1.1', 'file2'], ['3.1.1'])
 
822
        # file2 in a mainline revision
 
823
        self.assertLogRevnos(['-n0', '-r4', 'file2'], ['4', '3.1.1'])
 
824
        # file2 since a revision
 
825
        self.assertLogRevnos(['-n0', '-r3..', 'file2'], ['4', '3.1.1'])
 
826
        # file2 up to a revision
 
827
        self.assertLogRevnos(['-n0', '-r..3', 'file2'], ['2'])
 
828
 
 
829
    def test_log_file3(self):
 
830
        self.prepare_tree()
 
831
        self.assertLogRevnos(['-n0', 'file3'], ['3'])
876
832
 
877
833
    def test_log_file_historical_missing(self):
878
834
        # Check logging a deleted file gives an error if the
886
842
        # Check logging a deleted file is ok if the file existed
887
843
        # at the end the revision range
888
844
        self.prepare_tree(complex=True)
889
 
        log, err = self.run_bzr('log -r..4 file2')
890
 
        self.assertEquals('', err)
891
 
        self.assertNotContainsRe(log, 'revno: 1\n')
892
 
        self.assertContainsRe(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\n')
 
845
        self.assertLogRevnos(['-n0', '-r..4', 'file2'], ['4', '3.1.1', '2'])
896
846
 
897
847
    def test_log_file_historical_start(self):
898
848
        # Check logging a deleted file is ok if the file existed
899
849
        # at the start of the revision range
900
850
        self.prepare_tree(complex=True)
901
 
        log, err = self.run_bzr('log file1')
902
 
        self.assertEquals('', err)
903
 
        self.assertContainsRe(log, 'revno: 1\n')
904
 
        self.assertNotContainsRe(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\n')
 
851
        self.assertLogRevnos(['file1'], ['1'])
908
852
 
909
853
    def test_log_file_renamed(self):
910
854
        """File matched against revision range, not current tree."""
916
860
        self.assertContainsRe(err, err_msg)
917
861
 
918
862
        # Check we can see a renamed file if we give the right end revision
919
 
        log, err = self.run_bzr('log -r..4 file3')
920
 
        self.assertEquals('', err)
921
 
        self.assertNotContainsRe(log, 'revno: 1\n')
922
 
        self.assertNotContainsRe(log, 'revno: 2\n')
923
 
        self.assertContainsRe(log, 'revno: 3\n')
924
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
925
 
        self.assertNotContainsRe(log, 'revno: 4\n')
926
 
 
927
 
    def test_line_log_file(self):
928
 
        """The line log for a file should only list relevant mainline revs"""
929
 
        # Note: this also implicitly  covers the short logging case.
930
 
        # We test using --line in preference to --short because matching
931
 
        # revnos in the output of --line is more reliable.
932
 
        self.prepare_tree()
933
 
 
934
 
        # full history of file1
935
 
        log = self.run_bzr('log --line file1')[0]
936
 
        self.assertContainsRe(log, '^1:', re.MULTILINE)
937
 
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
938
 
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
939
 
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
940
 
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
941
 
 
942
 
        # full history of file2
943
 
        log = self.run_bzr('log --line file2')[0]
944
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
945
 
        self.assertContainsRe(log, '^2:', re.MULTILINE)
946
 
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
947
 
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
948
 
        self.assertContainsRe(log, '^4:', re.MULTILINE)
949
 
 
950
 
        # full history of file3
951
 
        log = self.run_bzr('log --line file3')[0]
952
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
953
 
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
954
 
        self.assertContainsRe(log, '^3:', re.MULTILINE)
955
 
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
956
 
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
957
 
 
958
 
        # file in a merge revision
959
 
        log = self.run_bzr('log --line -r3.1.1 file2')[0]
960
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
961
 
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
962
 
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
963
 
        self.assertContainsRe(log, '^3.1.1:', re.MULTILINE)
964
 
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
965
 
 
966
 
        # file in a mainline revision
967
 
        log = self.run_bzr('log --line -r4 file2')[0]
968
 
        self.assertNotContainsRe(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.assertContainsRe(log, '^4:', re.MULTILINE)
973
 
 
974
 
        # file since a revision
975
 
        log = self.run_bzr('log --line -r3.. file2')[0]
976
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
977
 
        self.assertNotContainsRe(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
 
        # file up to a revision
983
 
        log = self.run_bzr('log --line -r..3 file2')[0]
984
 
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
985
 
        self.assertContainsRe(log, '^2:', re.MULTILINE)
986
 
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
987
 
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
988
 
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
989
 
 
990
 
 
991
 
class TestLogMultiple(TestCaseWithTransport):
 
863
        self.assertLogRevnos(['-r..4', 'file3'], ['3'])
 
864
 
 
865
 
 
866
class TestLogMultiple(TestLogWithLogCatcher):
992
867
 
993
868
    def prepare_tree(self):
994
869
        tree = self.make_branch_and_tree('parent')
1017
892
        tree.commit(message='merge child branch')
1018
893
        os.chdir('parent')
1019
894
 
1020
 
    def assertRevnos(self, paths_str, expected_revnos):
1021
 
        # confirm the revision numbers in log --line output are those expected
1022
 
        out, err = self.run_bzr('log --line -n0 %s' % (paths_str,))
1023
 
        self.assertEqual('', err)
1024
 
        revnos = [s.split(':', 1)[0].lstrip() for s in out.splitlines()]
1025
 
        self.assertEqual(expected_revnos, revnos)
1026
 
 
1027
895
    def test_log_files(self):
1028
896
        """The log for multiple file should only list revs for those files"""
1029
897
        self.prepare_tree()
1030
 
        self.assertRevnos('file1 file2 dir1/dir2/file3',
1031
 
            ['6', '5.1.1', '3', '2', '1'])
 
898
        self.assertLogRevnos(['file1', 'file2', 'dir1/dir2/file3'],
 
899
                             ['6', '5.1.1', '3', '2', '1'])
1032
900
 
1033
901
    def test_log_directory(self):
1034
902
        """The log for a directory should show all nested files."""
1035
903
        self.prepare_tree()
1036
 
        self.assertRevnos('dir1', ['5', '3'])
 
904
        self.assertLogRevnos(['dir1'], ['5', '3'])
1037
905
 
1038
906
    def test_log_nested_directory(self):
1039
907
        """The log for a directory should show all nested files."""
1040
908
        self.prepare_tree()
1041
 
        self.assertRevnos('dir1/dir2', ['3'])
 
909
        self.assertLogRevnos(['dir1/dir2'], ['3'])
1042
910
 
1043
911
    def test_log_in_nested_directory(self):
1044
912
        """The log for a directory should show all nested files."""
1045
913
        self.prepare_tree()
1046
914
        os.chdir("dir1")
1047
 
        self.assertRevnos('.', ['5', '3'])
 
915
        self.assertLogRevnos(['.'], ['5', '3'])
1048
916
 
1049
917
    def test_log_files_and_directories(self):
1050
918
        """Logging files and directories together should be fine."""
1051
919
        self.prepare_tree()
1052
 
        self.assertRevnos('file4 dir1/dir2', ['4', '3'])
 
920
        self.assertLogRevnos(['file4', 'dir1/dir2'], ['4', '3'])
1053
921
 
1054
922
    def test_log_files_and_dirs_in_nested_directory(self):
1055
923
        """The log for a directory should show all nested files."""
1056
924
        self.prepare_tree()
1057
925
        os.chdir("dir1")
1058
 
        self.assertRevnos('dir2 file5', ['5', '3'])
 
926
        self.assertLogRevnos(['dir2', 'file5'], ['5', '3'])