~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 04:10:52 UTC
  • mfrom: (6616.1.1 fix-en-user-guide)
  • Revision ID: pqm@pqm.ubuntu.com-20160421041052-clcye7ns1qcl2n7w
(richard-wilbur) Ensure build of English use guide always uses English text
 even when user's locale specifies a different language. (Jelmer Vernooij)

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) 2006-2012, 2016 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
 
 
20
from itertools import izip
21
21
import os
22
22
 
23
 
import bzrlib
24
 
from bzrlib.tests.blackbox import ExternalBase
25
 
from bzrlib.tests import 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, test_log.TestCaseWithoutPropsHandler):
33
 
    pass
34
 
 
35
 
 
36
 
class TestLog(ExternalBase):
37
 
 
38
 
    def _prepare(self, path='.', format=None):
 
23
from bzrlib import (
 
24
    branchbuilder,
 
25
    errors,
 
26
    log,
 
27
    osutils,
 
28
    tests,
 
29
    )
 
30
from bzrlib.tests import (
 
31
    test_log,
 
32
    features,
 
33
    )
 
34
from bzrlib.tests.matchers import ContainsNoVfsCalls
 
35
 
 
36
 
 
37
class TestLog(tests.TestCaseWithTransport, test_log.TestLogMixin):
 
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
        # Break cycle with closure over self on cleanup by removing method
 
81
        self.addCleanup(setattr, MyLogFormatter, "__new__", None)
 
82
 
 
83
        def getme(branch):
 
84
                # Always return our own log formatter class hijacking the
 
85
                # default behavior (which requires setting up a config
 
86
                # variable)
 
87
            return MyLogFormatter
 
88
        self.overrideAttr(log.log_formatter_registry, 'get_default', getme)
 
89
 
 
90
    def get_captured_revisions(self):
 
91
        return self.log_catcher.revisions
 
92
 
 
93
    def assertLogRevnos(self, args, expected_revnos, working_dir='.',
 
94
                        out='', err=''):
 
95
        actual_out, actual_err = self.run_bzr(['log'] + args,
 
96
                                              working_dir=working_dir)
 
97
        self.assertEqual(out, actual_out)
 
98
        self.assertEqual(err, actual_err)
 
99
        self.assertEqual(expected_revnos,
 
100
                         [r.revno for r in self.get_captured_revisions()])
 
101
 
 
102
    def assertLogRevnosAndDepths(self, args, expected_revnos_and_depths,
 
103
                                working_dir='.'):
 
104
        self.run_bzr(['log'] + args, working_dir=working_dir)
 
105
        self.assertEqual(expected_revnos_and_depths,
 
106
                         [(r.revno, r.merge_depth)
 
107
                           for r in self.get_captured_revisions()])
 
108
 
 
109
 
 
110
class TestLogRevSpecs(TestLogWithLogCatcher):
 
111
 
 
112
    def test_log_no_revspec(self):
 
113
        self.make_linear_branch()
 
114
        self.assertLogRevnos([], ['3', '2', '1'])
50
115
 
51
116
    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)
 
117
        self.make_linear_branch()
 
118
        self.assertLogRevnos(['-r1..'], ['3', '2', '1'])
62
119
 
63
120
    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)
 
121
        self.make_linear_branch()
 
122
        self.assertLogRevnos(['-r..3'], ['3', '2', '1'])
67
123
 
68
124
    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)
 
125
        self.make_linear_branch()
 
126
        self.assertLogRevnos(['-r..'], ['3', '2', '1'])
 
127
 
 
128
    def test_log_negative_begin_revspec_full_log(self):
 
129
        self.make_linear_branch()
 
130
        self.assertLogRevnos(['-r-3..'], ['3', '2', '1'])
 
131
 
 
132
    def test_log_negative_both_revspec_full_log(self):
 
133
        self.make_linear_branch()
 
134
        self.assertLogRevnos(['-r-3..-1'], ['3', '2', '1'])
 
135
 
 
136
    def test_log_negative_both_revspec_partial(self):
 
137
        self.make_linear_branch()
 
138
        self.assertLogRevnos(['-r-3..-2'], ['2', '1'])
 
139
 
 
140
    def test_log_negative_begin_revspec(self):
 
141
        self.make_linear_branch()
 
142
        self.assertLogRevnos(['-r-2..'], ['3', '2'])
 
143
 
 
144
    def test_log_positive_revspecs(self):
 
145
        self.make_linear_branch()
 
146
        self.assertLogRevnos(['-r1..3'], ['3', '2', '1'])
 
147
 
 
148
    def test_log_dotted_revspecs(self):
 
149
        self.make_merged_branch()
 
150
        self.assertLogRevnos(['-n0', '-r1..1.1.1'], ['1.1.1', '1'])
 
151
 
 
152
    def test_log_limit(self):
 
153
        tree = self.make_branch_and_tree('.')
 
154
        # We want more commits than our batch size starts at
 
155
        for pos in range(10):
 
156
            tree.commit("%s" % pos)
 
157
        self.assertLogRevnos(['--limit', '2'], ['10', '9'])
 
158
 
 
159
    def test_log_limit_short(self):
 
160
        self.make_linear_branch()
 
161
        self.assertLogRevnos(['-l', '2'], ['3', '2'])
 
162
 
 
163
    def test_log_change_revno(self):
 
164
        self.make_linear_branch()
 
165
        self.assertLogRevnos(['-c1'], ['1'])
 
166
 
 
167
    def test_branch_revspec(self):
 
168
        foo = self.make_branch_and_tree('foo')
 
169
        bar = self.make_branch_and_tree('bar')
 
170
        self.build_tree(['foo/foo.txt', 'bar/bar.txt'])
 
171
        foo.add('foo.txt')
 
172
        bar.add('bar.txt')
 
173
        foo.commit(message='foo')
 
174
        bar.commit(message='bar')
 
175
        self.run_bzr('log -r branch:../bar', working_dir='foo')
 
176
        self.assertEqual([bar.branch.get_rev_id(1)],
 
177
                         [r.rev.revision_id
 
178
                          for r in self.get_captured_revisions()])
 
179
 
 
180
 
 
181
class TestLogExcludeCommonAncestry(TestLogWithLogCatcher):
 
182
 
 
183
    def test_exclude_common_ancestry_simple_revnos(self):
 
184
        self.make_linear_branch()
 
185
        self.assertLogRevnos(['-r1..3', '--exclude-common-ancestry'],
 
186
                             ['3', '2'])
 
187
 
 
188
 
 
189
class TestLogMergedLinearAncestry(TestLogWithLogCatcher):
 
190
 
 
191
    def setUp(self):
 
192
        super(TestLogMergedLinearAncestry, self).setUp()
 
193
        # FIXME: Using a MemoryTree would be even better here (but until we
 
194
        # stop calling run_bzr, there is no point) --vila 100118.
 
195
        builder = branchbuilder.BranchBuilder(self.get_transport())
 
196
        builder.start_series()
 
197
        # 1
 
198
        # | \
 
199
        # 2  1.1.1
 
200
        # | / |
 
201
        # 3  1.1.2
 
202
        # |   |
 
203
        # |  1.1.3
 
204
        # | / |
 
205
        # 4  1.1.4
 
206
        # | /
 
207
        # 5
 
208
        # | \
 
209
        # | 5.1.1
 
210
        # | /
 
211
        # 6
 
212
 
 
213
        # mainline
 
214
        builder.build_snapshot('1', None, [
 
215
            ('add', ('', 'root-id', 'directory', ''))])
 
216
        builder.build_snapshot('2', ['1'], [])
 
217
        # branch
 
218
        builder.build_snapshot('1.1.1', ['1'], [])
 
219
        # merge branch into mainline
 
220
        builder.build_snapshot('3', ['2', '1.1.1'], [])
 
221
        # new commits in branch
 
222
        builder.build_snapshot('1.1.2', ['1.1.1'], [])
 
223
        builder.build_snapshot('1.1.3', ['1.1.2'], [])
 
224
        # merge branch into mainline
 
225
        builder.build_snapshot('4', ['3', '1.1.3'], [])
 
226
        # merge mainline into branch
 
227
        builder.build_snapshot('1.1.4', ['1.1.3', '4'], [])
 
228
        # merge branch into mainline
 
229
        builder.build_snapshot('5', ['4', '1.1.4'], [])
 
230
        builder.build_snapshot('5.1.1', ['5'], [])
 
231
        builder.build_snapshot('6', ['5', '5.1.1'], [])
 
232
        builder.finish_series()
 
233
 
 
234
    def test_n0(self):
 
235
        self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4'],
 
236
                             ['1.1.4', '4', '1.1.3', '1.1.2', '3', '1.1.1'])
 
237
    def test_n0_forward(self):
 
238
        self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4', '--forward'],
 
239
                             ['3', '1.1.1', '4', '1.1.2', '1.1.3', '1.1.4'])
 
240
 
 
241
    def test_n1(self):
 
242
        # starting from 1.1.4 we follow the left-hand ancestry
 
243
        self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4'],
 
244
                             ['1.1.4', '1.1.3', '1.1.2', '1.1.1'])
 
245
 
 
246
    def test_n1_forward(self):
 
247
        self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4', '--forward'],
 
248
                             ['1.1.1', '1.1.2', '1.1.3', '1.1.4'])
 
249
 
 
250
    def test_fallback_when_end_rev_is_not_on_mainline(self):
 
251
        self.assertLogRevnos(['-n1', '-r1.1.1..5.1.1'],
 
252
                             # We don't get 1.1.1 because we say -n1
 
253
                             ['5.1.1', '5', '4', '3'])
 
254
 
 
255
 
 
256
class Test_GenerateAllRevisions(TestLogWithLogCatcher):
 
257
 
 
258
    def setUp(self):
 
259
        super(Test_GenerateAllRevisions, self).setUp()
 
260
        builder = self.make_branch_with_many_merges()
 
261
        b = builder.get_branch()
 
262
        b.lock_read()
 
263
        self.addCleanup(b.unlock)
 
264
        self.branch = b
 
265
 
 
266
    def make_branch_with_many_merges(self, path='.', format=None):
 
267
        builder = branchbuilder.BranchBuilder(self.get_transport())
 
268
        builder.start_series()
 
269
        # The graph below may look a bit complicated (and it may be but I've
 
270
        # banged my head enough on it) but the bug requires at least dotted
 
271
        # revnos *and* merged revisions below that.
 
272
        # 1
 
273
        # | \
 
274
        # 2  1.1.1
 
275
        # | X
 
276
        # 3  2.1.1
 
277
        # |   |    \
 
278
        # |  2.1.2  2.2.1
 
279
        # |   |    X
 
280
        # |  2.1.3  \
 
281
        # | /       /
 
282
        # 4        /
 
283
        # |       /
 
284
        # 5 -----/
 
285
        builder.build_snapshot('1', None, [
 
286
            ('add', ('', 'root-id', 'directory', ''))])
 
287
        builder.build_snapshot('2', ['1'], [])
 
288
        builder.build_snapshot('1.1.1', ['1'], [])
 
289
        builder.build_snapshot('2.1.1', ['2'], [])
 
290
        builder.build_snapshot('3', ['2', '1.1.1'], [])
 
291
        builder.build_snapshot('2.1.2', ['2.1.1'], [])
 
292
        builder.build_snapshot('2.2.1', ['2.1.1'], [])
 
293
        builder.build_snapshot('2.1.3', ['2.1.2', '2.2.1'], [])
 
294
        builder.build_snapshot('4', ['3', '2.1.3'], [])
 
295
        builder.build_snapshot('5', ['4', '2.1.2'], [])
 
296
        builder.finish_series()
 
297
        return builder
 
298
 
 
299
    def test_not_an_ancestor(self):
 
300
        self.assertRaises(errors.BzrCommandError,
 
301
                          log._generate_all_revisions,
 
302
                          self.branch, '1.1.1', '2.1.3', 'reverse',
 
303
                          delayed_graph_generation=True)
 
304
 
 
305
    def test_wrong_order(self):
 
306
        self.assertRaises(errors.BzrCommandError,
 
307
                          log._generate_all_revisions,
 
308
                          self.branch, '5', '2.1.3', 'reverse',
 
309
                          delayed_graph_generation=True)
 
310
 
 
311
    def test_no_start_rev_id_with_end_rev_id_being_a_merge(self):
 
312
        revs = log._generate_all_revisions(
 
313
            self.branch, None, '2.1.3',
 
314
            'reverse', delayed_graph_generation=True)
 
315
 
 
316
 
 
317
class TestLogRevSpecsWithPaths(TestLogWithLogCatcher):
 
318
 
 
319
    def test_log_revno_n_path_wrong_namespace(self):
 
320
        self.make_linear_branch('branch1')
 
321
        self.make_linear_branch('branch2')
 
322
        # There is no guarantee that a path exist between two arbitrary
 
323
        # revisions.
 
324
        self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)
 
325
 
 
326
    def test_log_revno_n_path_correct_order(self):
 
327
        self.make_linear_branch('branch2')
 
328
        self.assertLogRevnos(['-rrevno:1:branch2..revno:3:branch2'],
 
329
                             ['3', '2','1'])
 
330
 
 
331
    def test_log_revno_n_path(self):
 
332
        self.make_linear_branch('branch2')
 
333
        self.assertLogRevnos(['-rrevno:1:branch2'],
 
334
                             ['1'])
 
335
        rev_props = self.log_catcher.revisions[0].rev.properties
 
336
        self.assertEqual('branch2', rev_props['branch-nick'])
 
337
 
 
338
 
 
339
class TestLogErrors(TestLog):
72
340
 
73
341
    def test_log_zero_revspec(self):
74
 
        self._prepare()
75
 
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
342
        self.make_minimal_branch()
 
343
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
76
344
                           ['log', '-r0'])
77
345
 
78
346
    def test_log_zero_begin_revspec(self):
79
 
        self._prepare()
80
 
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
347
        self.make_linear_branch()
 
348
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
81
349
                           ['log', '-r0..2'])
82
350
 
83
351
    def test_log_zero_end_revspec(self):
84
 
        self._prepare()
85
 
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
352
        self.make_linear_branch()
 
353
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
86
354
                           ['log', '-r-2..0'])
87
355
 
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)
 
356
    def test_log_nonexistent_revno(self):
 
357
        self.make_minimal_branch()
 
358
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
 
359
                            "does not exist in branch:"],
 
360
                           ['log', '-r1234'])
 
361
 
 
362
    def test_log_nonexistent_dotted_revno(self):
 
363
        self.make_minimal_branch()
 
364
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
 
365
                            "does not exist in branch:"],
 
366
                           ['log',  '-r123.123'])
 
367
 
 
368
    def test_log_change_nonexistent_revno(self):
 
369
        self.make_minimal_branch()
 
370
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
 
371
                            "does not exist in branch:"],
 
372
                           ['log',  '-c1234'])
 
373
 
 
374
    def test_log_change_nonexistent_dotted_revno(self):
 
375
        self.make_minimal_branch()
 
376
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
 
377
                            "does not exist in branch:"],
 
378
                           ['log', '-c123.123'])
 
379
 
 
380
    def test_log_change_single_revno_only(self):
 
381
        self.make_minimal_branch()
 
382
        self.run_bzr_error(['bzr: ERROR: Option --change does not'
 
383
                           ' accept revision ranges'],
 
384
                           ['log', '--change', '2..3'])
 
385
 
 
386
    def test_log_change_incompatible_with_revision(self):
 
387
        self.run_bzr_error(['bzr: ERROR: --revision and --change'
 
388
                           ' are mutually exclusive'],
 
389
                           ['log', '--change', '2', '--revision', '3'])
 
390
 
 
391
    def test_log_nonexistent_file(self):
 
392
        self.make_minimal_branch()
 
393
        # files that don't exist in either the basis tree or working tree
 
394
        # should give an error
 
395
        out, err = self.run_bzr('log does-not-exist', retcode=3)
 
396
        self.assertContainsRe(err,
 
397
                              'Path unknown at end or start of revision range: '
 
398
                              'does-not-exist')
122
399
 
123
400
    def test_log_reversed_revspecs(self):
124
 
        self._prepare()
 
401
        self.make_linear_branch()
125
402
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
126
403
                            'the end revision.\n',),
127
404
                           ['log', '-r3..1'])
128
405
 
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
 
    def test_log_nonexistent_file(self):
143
 
        # files that don't exist in either the basis tree or working tree
144
 
        # should give an error
145
 
        wt = self.make_branch_and_tree('.')
146
 
        out, err = self.run_bzr('log does-not-exist', retcode=3)
147
 
        self.assertContainsRe(
148
 
            err, 'Path does not have any revision history: does-not-exist')
 
406
    def test_log_reversed_dotted_revspecs(self):
 
407
        self.make_merged_branch()
 
408
        self.run_bzr_error(('bzr: ERROR: Start revision not found in '
 
409
                            'history of end revision.\n',),
 
410
                           "log -r 1.1.1..1")
 
411
 
 
412
    def test_log_bad_message_re(self):
 
413
        """Bad --message argument gives a sensible message
 
414
 
 
415
        See https://bugs.launchpad.net/bzr/+bug/251352
 
416
        """
 
417
        self.make_minimal_branch()
 
418
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
 
419
        self.assertContainsRe(err, "ERROR.*Invalid pattern.*nothing to repeat")
 
420
        self.assertNotContainsRe(err, "Unprintable exception")
 
421
        self.assertEqual(out, '')
 
422
 
 
423
    def test_log_unsupported_timezone(self):
 
424
        self.make_linear_branch()
 
425
        self.run_bzr_error(['bzr: ERROR: Unsupported timezone format "foo", '
 
426
                            'options are "utc", "original", "local".'],
 
427
                           ['log', '--timezone', 'foo'])
 
428
 
 
429
    def test_log_exclude_ancestry_no_range(self):
 
430
        self.make_linear_branch()
 
431
        self.run_bzr_error(['bzr: ERROR: --exclude-common-ancestry'
 
432
                            ' requires -r with two revisions'],
 
433
                           ['log', '--exclude-common-ancestry'])
 
434
 
 
435
    def test_log_exclude_ancestry_single_revision(self):
 
436
        self.make_merged_branch()
 
437
        self.run_bzr_error(['bzr: ERROR: --exclude-common-ancestry'
 
438
                            ' requires two different revisions'],
 
439
                           ['log', '--exclude-common-ancestry',
 
440
                            '-r1.1.1..1.1.1'])
 
441
 
 
442
class TestLogTags(TestLog):
149
443
 
150
444
    def test_log_with_tags(self):
151
 
        tree = self._prepare(format='dirstate-tags')
 
445
        tree = self.make_linear_branch(format='dirstate-tags')
152
446
        branch = tree.branch
153
447
        branch.tags.set_tag('tag1', branch.get_rev_id(1))
154
448
        branch.tags.set_tag('tag1.1', branch.get_rev_id(1))
155
 
        branch.tags.set_tag('tag3', branch.last_revision()) 
156
 
        
 
449
        branch.tags.set_tag('tag3', branch.last_revision())
 
450
 
157
451
        log = self.run_bzr("log -r-1")[0]
158
452
        self.assertTrue('tags: tag3' in log)
159
453
 
163
457
        self.assertContainsRe(log, r'tags: (tag1, tag1\.1|tag1\.1, tag1)')
164
458
 
165
459
    def test_merged_log_with_tags(self):
166
 
        branch1_tree = self._prepare(path='branch1', format='dirstate-tags')
 
460
        branch1_tree = self.make_linear_branch('branch1',
 
461
                                               format='dirstate-tags')
167
462
        branch1 = branch1_tree.branch
168
463
        branch2_tree = branch1_tree.bzrdir.sprout('branch2').open_workingtree()
169
464
        branch1_tree.commit(message='foobar', allow_pointless=True)
170
465
        branch1.tags.set_tag('tag1', branch1.last_revision())
171
 
        os.chdir('branch2')
172
 
        self.run_bzr('merge ../branch1') # tags don't propagate otherwise
 
466
        # tags don't propagate if we don't merge
 
467
        self.run_bzr('merge ../branch1', working_dir='branch2')
173
468
        branch2_tree.commit(message='merge branch 1')
174
 
        log = self.run_bzr("log -r-1")[0]
 
469
        log = self.run_bzr("log -n0 -r-1", working_dir='branch2')[0]
175
470
        self.assertContainsRe(log, r'    tags: tag1')
176
 
        log = self.run_bzr("log -r3.1.1")[0]
 
471
        log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
177
472
        self.assertContainsRe(log, r'tags: tag1')
178
473
 
179
 
    def test_log_limit(self):
180
 
        tree = self.make_branch_and_tree('.')
181
 
        # We want more commits than our batch size starts at
182
 
        for pos in range(10):
183
 
            tree.commit("%s" % pos)
184
 
        log = self.run_bzr("log --limit 2")[0]
185
 
        self.assertNotContainsRe(log, r'revno: 1\n')
186
 
        self.assertNotContainsRe(log, r'revno: 2\n')
187
 
        self.assertNotContainsRe(log, r'revno: 3\n')
188
 
        self.assertNotContainsRe(log, r'revno: 4\n')
189
 
        self.assertNotContainsRe(log, r'revno: 5\n')
190
 
        self.assertNotContainsRe(log, r'revno: 6\n')
191
 
        self.assertNotContainsRe(log, r'revno: 7\n')
192
 
        self.assertNotContainsRe(log, r'revno: 8\n')
193
 
        self.assertContainsRe(log, r'revno: 9\n')
194
 
        self.assertContainsRe(log, r'revno: 10\n')
195
 
 
196
 
    def test_log_limit_short(self):
197
 
        self._prepare()
198
 
        log = self.run_bzr("log -l 2")[0]
199
 
        self.assertNotContainsRe(log, r'revno: 1\n')
200
 
        self.assertContainsRe(log, r'revno: 2\n')
201
 
        self.assertContainsRe(log, r'revno: 3\n')
202
 
 
203
 
 
204
 
class TestLogMerges(TestCaseWithoutPropsHandler):
205
 
 
206
 
    def _prepare(self):
207
 
        parent_tree = self.make_branch_and_tree('parent')
208
 
        parent_tree.commit(message='first post', allow_pointless=True)
209
 
        child_tree = parent_tree.bzrdir.sprout('child').open_workingtree()
210
 
        child_tree.commit(message='branch 1', allow_pointless=True)
211
 
        smaller_tree = \
212
 
                child_tree.bzrdir.sprout('smallerchild').open_workingtree()
213
 
        smaller_tree.commit(message='branch 2', allow_pointless=True)
214
 
        child_tree.merge_from_branch(smaller_tree.branch)
215
 
        child_tree.commit(message='merge branch 2')
216
 
        parent_tree.merge_from_branch(child_tree.branch)
217
 
        parent_tree.commit(message='merge branch 1')
218
 
        os.chdir('parent')
 
474
 
 
475
class TestLogSignatures(TestLog):
 
476
 
 
477
    def test_log_with_signatures(self):
 
478
        self.requireFeature(features.gpgme)
 
479
 
 
480
        tree = self.make_linear_branch(format='dirstate-tags')
 
481
 
 
482
        log = self.run_bzr("log --signatures")[0]
 
483
        self.assertTrue('signature: no signature' in log)
 
484
 
 
485
    def test_log_without_signatures(self):
 
486
        self.requireFeature(features.gpgme)
 
487
 
 
488
        tree = self.make_linear_branch(format='dirstate-tags')
 
489
 
 
490
        log = self.run_bzr("log")[0]
 
491
        self.assertFalse('signature: no signature' in log)
 
492
 
 
493
 
 
494
class TestLogVerbose(TestLog):
 
495
 
 
496
    def setUp(self):
 
497
        super(TestLogVerbose, self).setUp()
 
498
        self.make_minimal_branch()
 
499
 
 
500
    def assertUseShortDeltaFormat(self, cmd):
 
501
        log = self.run_bzr(cmd)[0]
 
502
        # Check that we use the short status format
 
503
        self.assertContainsRe(log, '(?m)^\s*A  hello.txt$')
 
504
        self.assertNotContainsRe(log, '(?m)^\s*added:$')
 
505
 
 
506
    def assertUseLongDeltaFormat(self, cmd):
 
507
        log = self.run_bzr(cmd)[0]
 
508
        # Check that we use the long status format
 
509
        self.assertNotContainsRe(log, '(?m)^\s*A  hello.txt$')
 
510
        self.assertContainsRe(log, '(?m)^\s*added:$')
 
511
 
 
512
    def test_log_short_verbose(self):
 
513
        self.assertUseShortDeltaFormat(['log', '--short', '-v'])
 
514
 
 
515
    def test_log_s_verbose(self):
 
516
        self.assertUseShortDeltaFormat(['log', '-S', '-v'])
 
517
 
 
518
    def test_log_short_verbose_verbose(self):
 
519
        self.assertUseLongDeltaFormat(['log', '--short', '-vv'])
 
520
 
 
521
    def test_log_long_verbose(self):
 
522
        # Check that we use the long status format, ignoring the verbosity
 
523
        # level
 
524
        self.assertUseLongDeltaFormat(['log', '--long', '-v'])
 
525
 
 
526
    def test_log_long_verbose_verbose(self):
 
527
        # Check that we use the long status format, ignoring the verbosity
 
528
        # level
 
529
        self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
 
530
 
 
531
 
 
532
class TestLogMerges(TestLogWithLogCatcher):
 
533
 
 
534
    def setUp(self):
 
535
        super(TestLogMerges, self).setUp()
 
536
        self.make_branches_with_merges()
 
537
 
 
538
    def make_branches_with_merges(self):
 
539
        level0 = self.make_branch_and_tree('level0')
 
540
        self.wt_commit(level0, 'in branch level0')
 
541
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
 
542
        self.wt_commit(level1, 'in branch level1')
 
543
        level2 = level1.bzrdir.sprout('level2').open_workingtree()
 
544
        self.wt_commit(level2, 'in branch level2')
 
545
        level1.merge_from_branch(level2.branch)
 
546
        self.wt_commit(level1, 'merge branch level2')
 
547
        level0.merge_from_branch(level1.branch)
 
548
        self.wt_commit(level0, 'merge branch level1')
219
549
 
220
550
    def test_merges_are_indented_by_level(self):
221
 
        self._prepare()
222
 
        out,err = self.run_bzr('log')
223
 
        self.assertEqual('', err)
224
 
        log = normalize_log(out)
225
 
        self.assertEqualDiff(log, """\
226
 
------------------------------------------------------------
227
 
revno: 2
228
 
committer: Lorem Ipsum <test@example.com>
229
 
branch nick: parent
230
 
timestamp: Just now
231
 
message:
232
 
  merge branch 1
233
 
    ------------------------------------------------------------
234
 
    revno: 1.1.2
235
 
    committer: Lorem Ipsum <test@example.com>
236
 
    branch nick: child
237
 
    timestamp: Just now
238
 
    message:
239
 
      merge branch 2
240
 
        ------------------------------------------------------------
241
 
        revno: 1.2.1
242
 
        committer: Lorem Ipsum <test@example.com>
243
 
        branch nick: smallerchild
244
 
        timestamp: Just now
245
 
        message:
246
 
          branch 2
247
 
    ------------------------------------------------------------
248
 
    revno: 1.1.1
249
 
    committer: Lorem Ipsum <test@example.com>
250
 
    branch nick: child
251
 
    timestamp: Just now
252
 
    message:
253
 
      branch 1
254
 
------------------------------------------------------------
255
 
revno: 1
256
 
committer: Lorem Ipsum <test@example.com>
257
 
branch nick: parent
258
 
timestamp: Just now
259
 
message:
260
 
  first post
261
 
""")
 
551
        self.run_bzr(['log', '-n0'], working_dir='level0')
 
552
        revnos_and_depth = [(r.revno, r.merge_depth)
 
553
                            for r in self.get_captured_revisions()]
 
554
        self.assertEqual([('2', 0), ('1.1.2', 1), ('1.2.1', 2), ('1.1.1', 1),
 
555
                          ('1', 0)],
 
556
                         [(r.revno, r.merge_depth)
 
557
                            for r in self.get_captured_revisions()])
 
558
 
 
559
    def test_force_merge_revisions_off(self):
 
560
        self.assertLogRevnos(['-n1'], ['2', '1'], working_dir='level0')
 
561
 
 
562
    def test_force_merge_revisions_on(self):
 
563
        self.assertLogRevnos(['-n0'], ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
 
564
                             working_dir='level0')
 
565
 
 
566
    def test_include_merges(self):
 
567
        # Confirm --include-merges gives the same output as -n0
 
568
        msg = ("The option '--include-merges' to 'bzr log' "
 
569
               "has been deprecated in bzr 2.5. "
 
570
               "Please use '--include-merged' instead.\n")
 
571
        self.assertLogRevnos(['--include-merges'],
 
572
                             ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
 
573
                             working_dir='level0', err=msg)
 
574
        self.assertLogRevnos(['--include-merges'],
 
575
                             ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
 
576
                             working_dir='level0', err=msg)
 
577
        out_im, err_im = self.run_bzr('log --include-merges',
 
578
                                      working_dir='level0')
 
579
        out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
 
580
        self.assertEqual(msg, err_im)
 
581
        self.assertEqual('', err_n0)
 
582
        self.assertEqual(out_im, out_n0)
 
583
 
 
584
    def test_include_merged(self):
 
585
        # Confirm --include-merged gives the same output as -n0
 
586
        expected = ['2', '1.1.2', '1.2.1', '1.1.1', '1']
 
587
        self.assertLogRevnos(['--include-merged'],
 
588
                             expected, working_dir='level0')
 
589
        self.assertLogRevnos(['--include-merged'],
 
590
                             expected, working_dir='level0')
 
591
 
 
592
    def test_force_merge_revisions_N(self):
 
593
        self.assertLogRevnos(['-n2'],
 
594
                             ['2', '1.1.2', '1.1.1', '1'],
 
595
                             working_dir='level0')
262
596
 
263
597
    def test_merges_single_merge_rev(self):
264
 
        self._prepare()
265
 
        out,err = self.run_bzr('log -r1.1.2')
266
 
        self.assertEqual('', err)
267
 
        log = normalize_log(out)
268
 
        self.assertEqualDiff(log, """\
269
 
------------------------------------------------------------
270
 
revno: 1.1.2
271
 
committer: Lorem Ipsum <test@example.com>
272
 
branch nick: child
273
 
timestamp: Just now
274
 
message:
275
 
  merge branch 2
276
 
    ------------------------------------------------------------
277
 
    revno: 1.2.1
278
 
    committer: Lorem Ipsum <test@example.com>
279
 
    branch nick: smallerchild
280
 
    timestamp: Just now
281
 
    message:
282
 
      branch 2
283
 
""")
 
598
        self.assertLogRevnosAndDepths(['-n0', '-r1.1.2'],
 
599
                                      [('1.1.2', 0), ('1.2.1', 1)],
 
600
                                      working_dir='level0')
284
601
 
285
602
    def test_merges_partial_range(self):
286
 
        self._prepare()
287
 
        out,err = self.run_bzr('log -r1.1.1..1.1.2')
288
 
        self.assertEqual('', err)
289
 
        log = normalize_log(out)
290
 
        self.assertEqualDiff(log, """\
291
 
------------------------------------------------------------
292
 
revno: 1.1.2
293
 
committer: Lorem Ipsum <test@example.com>
294
 
branch nick: child
295
 
timestamp: Just now
296
 
message:
297
 
  merge branch 2
298
 
    ------------------------------------------------------------
299
 
    revno: 1.2.1
300
 
    committer: Lorem Ipsum <test@example.com>
301
 
    branch nick: smallerchild
302
 
    timestamp: Just now
303
 
    message:
304
 
      branch 2
305
 
------------------------------------------------------------
306
 
revno: 1.1.1
307
 
committer: Lorem Ipsum <test@example.com>
308
 
branch nick: child
309
 
timestamp: Just now
310
 
message:
311
 
  branch 1
312
 
""")
313
 
 
314
 
    def test_merges_nonsupporting_formatter(self):
315
 
        self._prepare()
316
 
        err_msg = 'Selected log formatter only supports mainline revisions.'
317
 
        # The single revision case is tested in the core tests
318
 
        # since all standard formatters support single merge revisions.
319
 
        out,err = self.run_bzr('log --short -r1..1.1.2', retcode=3)
320
 
        self.assertContainsRe(err, err_msg)
321
 
        out,err = self.run_bzr('log --short -r1.1.1..1.1.2', retcode=3)
322
 
        self.assertContainsRe(err, err_msg)
323
 
 
324
 
 
325
 
class TestLogEncodings(TestCaseInTempDir):
 
603
        self.assertLogRevnosAndDepths(
 
604
                ['-n0', '-r1.1.1..1.1.2'],
 
605
                [('1.1.2', 0), ('1.2.1', 1), ('1.1.1', 0)],
 
606
                working_dir='level0')
 
607
 
 
608
    def test_merges_partial_range_ignore_before_lower_bound(self):
 
609
        """Dont show revisions before the lower bound's merged revs"""
 
610
        self.assertLogRevnosAndDepths(
 
611
                ['-n0', '-r1.1.2..2'],
 
612
                [('2', 0), ('1.1.2', 1), ('1.2.1', 2)],
 
613
                working_dir='level0')
 
614
 
 
615
    def test_omit_merges_with_sidelines(self):
 
616
        self.assertLogRevnos(['--omit-merges', '-n0'], ['1.2.1', '1.1.1', '1'],
 
617
                             working_dir='level0')
 
618
 
 
619
    def test_omit_merges_without_sidelines(self):
 
620
        self.assertLogRevnos(['--omit-merges', '-n1'], ['1'],
 
621
                             working_dir='level0')
 
622
 
 
623
 
 
624
class TestLogDiff(TestLogWithLogCatcher):
 
625
 
 
626
    # FIXME: We need specific tests for each LogFormatter about how the diffs
 
627
    # are displayed: --long indent them by depth, --short use a fixed
 
628
    # indent and --line does't display them. -- vila 10019
 
629
 
 
630
    def setUp(self):
 
631
        super(TestLogDiff, self).setUp()
 
632
        self.make_branch_with_diffs()
 
633
 
 
634
    def make_branch_with_diffs(self):
 
635
        level0 = self.make_branch_and_tree('level0')
 
636
        self.build_tree(['level0/file1', 'level0/file2'])
 
637
        level0.add('file1')
 
638
        level0.add('file2')
 
639
        self.wt_commit(level0, 'in branch level0')
 
640
 
 
641
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
 
642
        self.build_tree_contents([('level1/file2', 'hello\n')])
 
643
        self.wt_commit(level1, 'in branch level1')
 
644
        level0.merge_from_branch(level1.branch)
 
645
        self.wt_commit(level0, 'merge branch level1')
 
646
 
 
647
    def _diff_file1_revno1(self):
 
648
        return """=== added file 'file1'
 
649
--- file1\t1970-01-01 00:00:00 +0000
 
650
+++ file1\t2005-11-22 00:00:00 +0000
 
651
@@ -0,0 +1,1 @@
 
652
+contents of level0/file1
 
653
 
 
654
"""
 
655
 
 
656
    def _diff_file2_revno2(self):
 
657
        return """=== modified file 'file2'
 
658
--- file2\t2005-11-22 00:00:00 +0000
 
659
+++ file2\t2005-11-22 00:00:01 +0000
 
660
@@ -1,1 +1,1 @@
 
661
-contents of level0/file2
 
662
+hello
 
663
 
 
664
"""
 
665
 
 
666
    def _diff_file2_revno1_1_1(self):
 
667
        return """=== modified file 'file2'
 
668
--- file2\t2005-11-22 00:00:00 +0000
 
669
+++ file2\t2005-11-22 00:00:01 +0000
 
670
@@ -1,1 +1,1 @@
 
671
-contents of level0/file2
 
672
+hello
 
673
 
 
674
"""
 
675
 
 
676
    def _diff_file2_revno1(self):
 
677
        return """=== added file 'file2'
 
678
--- file2\t1970-01-01 00:00:00 +0000
 
679
+++ file2\t2005-11-22 00:00:00 +0000
 
680
@@ -0,0 +1,1 @@
 
681
+contents of level0/file2
 
682
 
 
683
"""
 
684
 
 
685
    def assertLogRevnosAndDiff(self, args, expected,
 
686
                            working_dir='.'):
 
687
        self.run_bzr(['log', '-p'] + args, working_dir=working_dir)
 
688
        expected_revnos_and_depths = [
 
689
            (revno, depth) for revno, depth, diff in expected]
 
690
        # Check the revnos and depths first to make debugging easier
 
691
        self.assertEqual(expected_revnos_and_depths,
 
692
                         [(r.revno, r.merge_depth)
 
693
                           for r in self.get_captured_revisions()])
 
694
        # Now check the diffs, adding the revno  in case of failure
 
695
        fmt = 'In revno %s\n%s'
 
696
        for expected_rev, actual_rev in izip(expected,
 
697
                                             self.get_captured_revisions()):
 
698
            revno, depth, expected_diff = expected_rev
 
699
            actual_diff = actual_rev.diff
 
700
            self.assertEqualDiff(fmt % (revno, expected_diff),
 
701
                                 fmt % (revno, actual_diff))
 
702
 
 
703
    def test_log_diff_with_merges(self):
 
704
        self.assertLogRevnosAndDiff(
 
705
            ['-n0'],
 
706
            [('2', 0, self._diff_file2_revno2()),
 
707
             ('1.1.1', 1, self._diff_file2_revno1_1_1()),
 
708
             ('1', 0, self._diff_file1_revno1()
 
709
              + self._diff_file2_revno1())],
 
710
            working_dir='level0')
 
711
 
 
712
 
 
713
    def test_log_diff_file1(self):
 
714
        self.assertLogRevnosAndDiff(['-n0', 'file1'],
 
715
                                    [('1', 0, self._diff_file1_revno1())],
 
716
                                    working_dir='level0')
 
717
 
 
718
    def test_log_diff_file2(self):
 
719
        self.assertLogRevnosAndDiff(['-n1', 'file2'],
 
720
                                    [('2', 0, self._diff_file2_revno2()),
 
721
                                     ('1', 0, self._diff_file2_revno1())],
 
722
                                    working_dir='level0')
 
723
 
 
724
 
 
725
class TestLogUnicodeDiff(TestLog):
 
726
 
 
727
    def test_log_show_diff_non_ascii(self):
 
728
        # Smoke test for bug #328007 UnicodeDecodeError on 'log -p'
 
729
        message = u'Message with \xb5'
 
730
        body = 'Body with \xb5\n'
 
731
        wt = self.make_branch_and_tree('.')
 
732
        self.build_tree_contents([('foo', body)])
 
733
        wt.add('foo')
 
734
        wt.commit(message=message)
 
735
        # check that command won't fail with unicode error
 
736
        # don't care about exact output because we have other tests for this
 
737
        out,err = self.run_bzr('log -p --long')
 
738
        self.assertNotEqual('', out)
 
739
        self.assertEqual('', err)
 
740
        out,err = self.run_bzr('log -p --short')
 
741
        self.assertNotEqual('', out)
 
742
        self.assertEqual('', err)
 
743
        out,err = self.run_bzr('log -p --line')
 
744
        self.assertNotEqual('', out)
 
745
        self.assertEqual('', err)
 
746
 
 
747
 
 
748
class TestLogEncodings(tests.TestCaseInTempDir):
326
749
 
327
750
    _mu = u'\xb5'
328
751
    _message = u'Message with \xb5'
333
756
        'latin-1',
334
757
        'iso-8859-1',
335
758
        'cp437', # Common windows encoding
336
 
        'cp1251', # Alexander Belchenko's windows encoding
 
759
        'cp1251', # Russian windows encoding
337
760
        'cp1258', # Common windows encoding
338
761
    ]
339
762
    # Encodings which cannot encode mu
344
767
    ]
345
768
 
346
769
    def setUp(self):
347
 
        TestCaseInTempDir.setUp(self)
348
 
        self.user_encoding = bzrlib.user_encoding
349
 
 
350
 
    def tearDown(self):
351
 
        bzrlib.user_encoding = self.user_encoding
352
 
        TestCaseInTempDir.tearDown(self)
 
770
        super(TestLogEncodings, self).setUp()
 
771
        self.overrideAttr(osutils, '_cached_user_encoding')
353
772
 
354
773
    def create_branch(self):
355
774
        bzr = self.run_bzr
356
775
        bzr('init')
357
 
        open('a', 'wb').write('some stuff\n')
 
776
        self.build_tree_contents([('a', 'some stuff\n')])
358
777
        bzr('add a')
359
778
        bzr(['commit', '-m', self._message])
360
779
 
367
786
        else:
368
787
            encoded_msg = self._message.encode(encoding)
369
788
 
370
 
        old_encoding = bzrlib.user_encoding
 
789
        old_encoding = osutils._cached_user_encoding
371
790
        # This test requires that 'run_bzr' uses the current
372
791
        # bzrlib, because we override user_encoding, and expect
373
792
        # it to be used
374
793
        try:
375
 
            bzrlib.user_encoding = 'ascii'
 
794
            osutils._cached_user_encoding = 'ascii'
376
795
            # We should be able to handle any encoding
377
796
            out, err = bzr('log', encoding=encoding)
378
797
            if not fail:
383
802
            else:
384
803
                self.assertNotEqual(-1, out.find('Message with ?'))
385
804
        finally:
386
 
            bzrlib.user_encoding = old_encoding
 
805
            osutils._cached_user_encoding = old_encoding
387
806
 
388
807
    def test_log_handles_encoding(self):
389
808
        self.create_branch()
399
818
 
400
819
    def test_stdout_encoding(self):
401
820
        bzr = self.run_bzr
402
 
        bzrlib.user_encoding = "cp1251"
 
821
        osutils._cached_user_encoding = "cp1251"
403
822
 
404
823
        bzr('init')
405
824
        self.build_tree(['a'])
418
837
        test_in_cp866 = '\x92\xa5\xe1\xe2'
419
838
        test_in_cp1251 = '\xd2\xe5\xf1\xf2'
420
839
        # Make sure the log string is encoded in cp866
421
 
        self.assertEquals(test_in_cp866, message[2:])
 
840
        self.assertEqual(test_in_cp866, message[2:])
422
841
        # Make sure the cp1251 string is not found anywhere
423
 
        self.assertEquals(-1, stdout.find(test_in_cp1251))
424
 
 
425
 
 
426
 
class TestLogFile(TestCaseWithTransport):
 
842
        self.assertEqual(-1, stdout.find(test_in_cp1251))
 
843
 
 
844
 
 
845
class TestLogFile(TestLogWithLogCatcher):
427
846
 
428
847
    def test_log_local_branch_file(self):
429
848
        """We should be able to log files in local treeless branches"""
434
853
        tree.bzrdir.destroy_workingtree()
435
854
        self.run_bzr('log tree/file')
436
855
 
437
 
    def test_log_file(self):
438
 
        """The log for a particular file should only list revs for that file"""
 
856
    def prepare_tree(self, complex=False):
 
857
        # The complex configuration includes deletes and renames
439
858
        tree = self.make_branch_and_tree('parent')
440
859
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
441
860
        tree.add('file1')
449
868
        child_tree.commit(message='branch 1')
450
869
        tree.merge_from_branch(child_tree.branch)
451
870
        tree.commit(message='merge child branch')
452
 
        os.chdir('parent')
453
 
        log = self.run_bzr('log file1')[0]
454
 
        self.assertContainsRe(log, 'revno: 1\n')
455
 
        self.assertNotContainsRe(log, 'revno: 2\n')
456
 
        self.assertNotContainsRe(log, 'revno: 3\n')
457
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
458
 
        self.assertNotContainsRe(log, 'revno: 4\n')
459
 
        log = self.run_bzr('log file2')[0]
460
 
        self.assertNotContainsRe(log, 'revno: 1\n')
461
 
        self.assertContainsRe(log, 'revno: 2\n')
462
 
        self.assertNotContainsRe(log, 'revno: 3\n')
463
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
464
 
        self.assertContainsRe(log, 'revno: 4\n')
465
 
        log = self.run_bzr('log file3')[0]
466
 
        self.assertNotContainsRe(log, 'revno: 1\n')
467
 
        self.assertNotContainsRe(log, 'revno: 2\n')
468
 
        self.assertContainsRe(log, 'revno: 3\n')
469
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
470
 
        self.assertNotContainsRe(log, 'revno: 4\n')
471
 
        log = self.run_bzr('log -r3.1.1 file2')[0]
472
 
        self.assertNotContainsRe(log, 'revno: 1\n')
473
 
        self.assertNotContainsRe(log, 'revno: 2\n')
474
 
        self.assertNotContainsRe(log, 'revno: 3\n')
475
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
476
 
        self.assertNotContainsRe(log, 'revno: 4\n')
477
 
        log = self.run_bzr('log -r4 file2')[0]
478
 
        self.assertNotContainsRe(log, 'revno: 1\n')
479
 
        self.assertNotContainsRe(log, 'revno: 2\n')
480
 
        self.assertNotContainsRe(log, 'revno: 3\n')
481
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
482
 
        self.assertContainsRe(log, 'revno: 4\n')
483
 
        log = self.run_bzr('log -r3.. file2')[0]
484
 
        self.assertNotContainsRe(log, 'revno: 1\n')
485
 
        self.assertNotContainsRe(log, 'revno: 2\n')
486
 
        self.assertNotContainsRe(log, 'revno: 3\n')
487
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
488
 
        self.assertContainsRe(log, 'revno: 4\n')
489
 
        log = self.run_bzr('log -r..3 file2')[0]
490
 
        self.assertNotContainsRe(log, 'revno: 1\n')
491
 
        self.assertContainsRe(log, 'revno: 2\n')
492
 
        self.assertNotContainsRe(log, 'revno: 3\n')
493
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
494
 
        self.assertNotContainsRe(log, 'revno: 4\n')
 
871
        if complex:
 
872
            tree.remove('file2')
 
873
            tree.commit('remove file2')
 
874
            tree.rename_one('file3', 'file4')
 
875
            tree.commit('file3 is now called file4')
 
876
            tree.remove('file1')
 
877
            tree.commit('remove file1')
 
878
        os.chdir('parent')
 
879
 
 
880
    # FIXME: It would be good to parametrize the following tests against all
 
881
    # formatters. But the revisions selection is not *currently* part of the
 
882
    # LogFormatter contract, so using LogCatcher is sufficient -- vila 100118
 
883
    def test_log_file1(self):
 
884
        self.prepare_tree()
 
885
        self.assertLogRevnos(['-n0', 'file1'], ['1'])
 
886
 
 
887
    def test_log_file2(self):
 
888
        self.prepare_tree()
 
889
        # file2 full history
 
890
        self.assertLogRevnos(['-n0', 'file2'], ['4', '3.1.1', '2'])
 
891
        # file2 in a merge revision
 
892
        self.assertLogRevnos(['-n0', '-r3.1.1', 'file2'], ['3.1.1'])
 
893
        # file2 in a mainline revision
 
894
        self.assertLogRevnos(['-n0', '-r4', 'file2'], ['4', '3.1.1'])
 
895
        # file2 since a revision
 
896
        self.assertLogRevnos(['-n0', '-r3..', 'file2'], ['4', '3.1.1'])
 
897
        # file2 up to a revision
 
898
        self.assertLogRevnos(['-n0', '-r..3', 'file2'], ['2'])
 
899
 
 
900
    def test_log_file3(self):
 
901
        self.prepare_tree()
 
902
        self.assertLogRevnos(['-n0', 'file3'], ['3'])
 
903
 
 
904
    def test_log_file_historical_missing(self):
 
905
        # Check logging a deleted file gives an error if the
 
906
        # file isn't found at the end or start of the revision range
 
907
        self.prepare_tree(complex=True)
 
908
        err_msg = "Path unknown at end or start of revision range: file2"
 
909
        err = self.run_bzr('log file2', retcode=3)[1]
 
910
        self.assertContainsRe(err, err_msg)
 
911
 
 
912
    def test_log_file_historical_end(self):
 
913
        # Check logging a deleted file is ok if the file existed
 
914
        # at the end the revision range
 
915
        self.prepare_tree(complex=True)
 
916
        self.assertLogRevnos(['-n0', '-r..4', 'file2'], ['4', '3.1.1', '2'])
 
917
 
 
918
    def test_log_file_historical_start(self):
 
919
        # Check logging a deleted file is ok if the file existed
 
920
        # at the start of the revision range
 
921
        self.prepare_tree(complex=True)
 
922
        self.assertLogRevnos(['file1'], ['1'])
 
923
 
 
924
    def test_log_file_renamed(self):
 
925
        """File matched against revision range, not current tree."""
 
926
        self.prepare_tree(complex=True)
 
927
 
 
928
        # Check logging a renamed file gives an error by default
 
929
        err_msg = "Path unknown at end or start of revision range: file3"
 
930
        err = self.run_bzr('log file3', retcode=3)[1]
 
931
        self.assertContainsRe(err, err_msg)
 
932
 
 
933
        # Check we can see a renamed file if we give the right end revision
 
934
        self.assertLogRevnos(['-r..4', 'file3'], ['3'])
 
935
 
 
936
 
 
937
class TestLogMultiple(TestLogWithLogCatcher):
 
938
 
 
939
    def prepare_tree(self):
 
940
        tree = self.make_branch_and_tree('parent')
 
941
        self.build_tree([
 
942
            'parent/file1',
 
943
            'parent/file2',
 
944
            'parent/dir1/',
 
945
            'parent/dir1/file5',
 
946
            'parent/dir1/dir2/',
 
947
            'parent/dir1/dir2/file3',
 
948
            'parent/file4'])
 
949
        tree.add('file1')
 
950
        tree.commit('add file1')
 
951
        tree.add('file2')
 
952
        tree.commit('add file2')
 
953
        tree.add(['dir1', 'dir1/dir2', 'dir1/dir2/file3'])
 
954
        tree.commit('add file3')
 
955
        tree.add('file4')
 
956
        tree.commit('add file4')
 
957
        tree.add('dir1/file5')
 
958
        tree.commit('add file5')
 
959
        child_tree = tree.bzrdir.sprout('child').open_workingtree()
 
960
        self.build_tree_contents([('child/file2', 'hello')])
 
961
        child_tree.commit(message='branch 1')
 
962
        tree.merge_from_branch(child_tree.branch)
 
963
        tree.commit(message='merge child branch')
 
964
        os.chdir('parent')
 
965
 
 
966
    def test_log_files(self):
 
967
        """The log for multiple file should only list revs for those files"""
 
968
        self.prepare_tree()
 
969
        self.assertLogRevnos(['file1', 'file2', 'dir1/dir2/file3'],
 
970
                             ['6', '5.1.1', '3', '2', '1'])
 
971
 
 
972
    def test_log_directory(self):
 
973
        """The log for a directory should show all nested files."""
 
974
        self.prepare_tree()
 
975
        self.assertLogRevnos(['dir1'], ['5', '3'])
 
976
 
 
977
    def test_log_nested_directory(self):
 
978
        """The log for a directory should show all nested files."""
 
979
        self.prepare_tree()
 
980
        self.assertLogRevnos(['dir1/dir2'], ['3'])
 
981
 
 
982
    def test_log_in_nested_directory(self):
 
983
        """The log for a directory should show all nested files."""
 
984
        self.prepare_tree()
 
985
        os.chdir("dir1")
 
986
        self.assertLogRevnos(['.'], ['5', '3'])
 
987
 
 
988
    def test_log_files_and_directories(self):
 
989
        """Logging files and directories together should be fine."""
 
990
        self.prepare_tree()
 
991
        self.assertLogRevnos(['file4', 'dir1/dir2'], ['4', '3'])
 
992
 
 
993
    def test_log_files_and_dirs_in_nested_directory(self):
 
994
        """The log for a directory should show all nested files."""
 
995
        self.prepare_tree()
 
996
        os.chdir("dir1")
 
997
        self.assertLogRevnos(['dir2', 'file5'], ['5', '3'])
 
998
 
 
999
 
 
1000
class MainlineGhostTests(TestLogWithLogCatcher):
 
1001
 
 
1002
    def setUp(self):
 
1003
        super(MainlineGhostTests, self).setUp()
 
1004
        tree = self.make_branch_and_tree('')
 
1005
        tree.set_parent_ids(["spooky"], allow_leftmost_as_ghost=True)
 
1006
        tree.add('')
 
1007
        tree.commit('msg1', rev_id='rev1')
 
1008
        tree.commit('msg2', rev_id='rev2')
 
1009
 
 
1010
    def test_log_range(self):
 
1011
        self.assertLogRevnos(["-r1..2"], ["2", "1"])
 
1012
 
 
1013
    def test_log_norange(self):
 
1014
        self.assertLogRevnos([], ["2", "1"])
 
1015
 
 
1016
    def test_log_range_open_begin(self):
 
1017
        self.knownFailure("log with ghosts fails. bug #726466")
 
1018
        (stdout, stderr) = self.run_bzr(['log', '-r..2'], retcode=3)
 
1019
        self.assertEqual(["2", "1"],
 
1020
                         [r.revno for r in self.get_captured_revisions()])
 
1021
        self.assertEqual("bzr: ERROR: Further revision history missing.", stderr)
 
1022
 
 
1023
    def test_log_range_open_end(self):
 
1024
        self.assertLogRevnos(["-r1.."], ["2", "1"])
 
1025
 
 
1026
class TestLogMatch(TestLogWithLogCatcher):
 
1027
    def prepare_tree(self):
 
1028
        tree = self.make_branch_and_tree('')
 
1029
        self.build_tree(
 
1030
            ['/hello.txt', '/goodbye.txt'])
 
1031
        tree.add('hello.txt')
 
1032
        tree.commit(message='message1', committer='committer1', authors=['author1'])
 
1033
        tree.add('goodbye.txt')
 
1034
        tree.commit(message='message2', committer='committer2', authors=['author2'])
 
1035
    
 
1036
    def test_message(self):
 
1037
        self.prepare_tree()
 
1038
        self.assertLogRevnos(["-m", "message1"], ["1"])
 
1039
        self.assertLogRevnos(["-m", "message2"], ["2"])
 
1040
        self.assertLogRevnos(["-m", "message"], ["2", "1"])
 
1041
        self.assertLogRevnos(["-m", "message1", "-m", "message2"], ["2", "1"])
 
1042
        self.assertLogRevnos(["--match-message", "message1"], ["1"])
 
1043
        self.assertLogRevnos(["--match-message", "message2"], ["2"])
 
1044
        self.assertLogRevnos(["--match-message", "message"], ["2", "1"])
 
1045
        self.assertLogRevnos(["--match-message", "message1", 
 
1046
                              "--match-message", "message2"], ["2", "1"])
 
1047
        self.assertLogRevnos(["--message", "message1"], ["1"])
 
1048
        self.assertLogRevnos(["--message", "message2"], ["2"])
 
1049
        self.assertLogRevnos(["--message", "message"], ["2", "1"])
 
1050
        self.assertLogRevnos(["--match-message", "message1", 
 
1051
                              "--message", "message2"], ["2", "1"])
 
1052
        self.assertLogRevnos(["--message", "message1", 
 
1053
                              "--match-message", "message2"], ["2", "1"])
 
1054
 
 
1055
    def test_committer(self):
 
1056
        self.prepare_tree()
 
1057
        self.assertLogRevnos(["-m", "committer1"], ["1"])
 
1058
        self.assertLogRevnos(["-m", "committer2"], ["2"])
 
1059
        self.assertLogRevnos(["-m", "committer"], ["2", "1"])
 
1060
        self.assertLogRevnos(["-m", "committer1", "-m", "committer2"], 
 
1061
                             ["2", "1"])
 
1062
        self.assertLogRevnos(["--match-committer", "committer1"], ["1"])
 
1063
        self.assertLogRevnos(["--match-committer", "committer2"], ["2"])
 
1064
        self.assertLogRevnos(["--match-committer", "committer"], ["2", "1"])
 
1065
        self.assertLogRevnos(["--match-committer", "committer1", 
 
1066
                              "--match-committer", "committer2"], ["2", "1"])
 
1067
 
 
1068
    def test_author(self):
 
1069
        self.prepare_tree()
 
1070
        self.assertLogRevnos(["-m", "author1"], ["1"])
 
1071
        self.assertLogRevnos(["-m", "author2"], ["2"])
 
1072
        self.assertLogRevnos(["-m", "author"], ["2", "1"])
 
1073
        self.assertLogRevnos(["-m", "author1", "-m", "author2"], 
 
1074
                             ["2", "1"])
 
1075
        self.assertLogRevnos(["--match-author", "author1"], ["1"])
 
1076
        self.assertLogRevnos(["--match-author", "author2"], ["2"])
 
1077
        self.assertLogRevnos(["--match-author", "author"], ["2", "1"])
 
1078
        self.assertLogRevnos(["--match-author", "author1", 
 
1079
                              "--match-author", "author2"], ["2", "1"])
 
1080
 
 
1081
 
 
1082
class TestSmartServerLog(tests.TestCaseWithTransport):
 
1083
 
 
1084
    def test_standard_log(self):
 
1085
        self.setup_smart_server_with_call_log()
 
1086
        t = self.make_branch_and_tree('branch')
 
1087
        self.build_tree_contents([('branch/foo', 'thecontents')])
 
1088
        t.add("foo")
 
1089
        t.commit("message")
 
1090
        self.reset_smart_call_log()
 
1091
        out, err = self.run_bzr(['log', self.get_url('branch')])
 
1092
        # This figure represent the amount of work to perform this use case. It
 
1093
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
1094
        # being too low. If rpc_count increases, more network roundtrips have
 
1095
        # become necessary for this use case. Please do not adjust this number
 
1096
        # upwards without agreement from bzr's network support maintainers.
 
1097
        self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
 
1098
        self.assertLength(1, self.hpss_connections)
 
1099
        self.assertLength(9, self.hpss_calls)
 
1100
 
 
1101
    def test_verbose_log(self):
 
1102
        self.setup_smart_server_with_call_log()
 
1103
        t = self.make_branch_and_tree('branch')
 
1104
        self.build_tree_contents([('branch/foo', 'thecontents')])
 
1105
        t.add("foo")
 
1106
        t.commit("message")
 
1107
        self.reset_smart_call_log()
 
1108
        out, err = self.run_bzr(['log', '-v', self.get_url('branch')])
 
1109
        # This figure represent the amount of work to perform this use case. It
 
1110
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
1111
        # being too low. If rpc_count increases, more network roundtrips have
 
1112
        # become necessary for this use case. Please do not adjust this number
 
1113
        # upwards without agreement from bzr's network support maintainers.
 
1114
        self.assertLength(10, self.hpss_calls)
 
1115
        self.assertLength(1, self.hpss_connections)
 
1116
        self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
 
1117
 
 
1118
    def test_per_file(self):
 
1119
        self.setup_smart_server_with_call_log()
 
1120
        t = self.make_branch_and_tree('branch')
 
1121
        self.build_tree_contents([('branch/foo', 'thecontents')])
 
1122
        t.add("foo")
 
1123
        t.commit("message")
 
1124
        self.reset_smart_call_log()
 
1125
        out, err = self.run_bzr(['log', '-v', self.get_url('branch') + "/foo"])
 
1126
        # This figure represent the amount of work to perform this use case. It
 
1127
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
1128
        # being too low. If rpc_count increases, more network roundtrips have
 
1129
        # become necessary for this use case. Please do not adjust this number
 
1130
        # upwards without agreement from bzr's network support maintainers.
 
1131
        self.assertLength(14, self.hpss_calls)
 
1132
        self.assertLength(1, self.hpss_connections)
 
1133
        self.assertThat(self.hpss_calls, ContainsNoVfsCalls)