~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Vincent Ladeuil
  • Date: 2010-02-10 15:46:03 UTC
  • mfrom: (4985.3.21 update)
  • mto: This revision was merged to the branch mainline in revision 5021.
  • Revision ID: v.ladeuil+lp@free.fr-20100210154603-k4no1gvfuqpzrw7p
Update performs two merges in a more logical order but stop on conflicts

Show diffs side-by-side

added added

removed removed

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