~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

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