/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

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

  • Committer: Martin von Gagern
  • Date: 2010-04-20 08:47:38 UTC
  • mfrom: (5167 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5195.
  • Revision ID: martin.vgagern@gmx.net-20100420084738-ygymnqmdllzrhpfn
merge trunk

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
 
 
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):
 
22
import re
 
23
 
 
24
from bzrlib import (
 
25
    branchbuilder,
 
26
    errors,
 
27
    log,
 
28
    osutils,
 
29
    tests,
 
30
    )
 
31
from bzrlib.tests import (
 
32
    script,
 
33
    test_log,
 
34
    )
 
35
 
 
36
 
 
37
class TestLog(tests.TestCaseWithTransport, test_log.TestLogMixin):
 
38
 
 
39
    def make_minimal_branch(self, path='.', format=None):
 
40
        tree = self.make_branch_and_tree(path, format=format)
 
41
        self.build_tree([path + '/hello.txt'])
 
42
        tree.add('hello.txt')
 
43
        tree.commit(message='message1')
 
44
        return tree
 
45
 
 
46
    def make_linear_branch(self, path='.', format=None):
39
47
        tree = self.make_branch_and_tree(path, format=format)
40
48
        self.build_tree(
41
49
            [path + '/hello.txt', path + '/goodbye.txt', path + '/meep.txt'])
45
53
        tree.commit(message='message2')
46
54
        tree.add('meep.txt')
47
55
        tree.commit(message='message3')
48
 
        self.full_log = self.run_bzr(["log", path])[0]
49
 
        return tree
 
56
        return tree
 
57
 
 
58
    def make_merged_branch(self, path='.', format=None):
 
59
        tree = self.make_linear_branch(path, format)
 
60
        tree2 = tree.bzrdir.sprout('tree2',
 
61
            revision_id=tree.branch.get_rev_id(1)).open_workingtree()
 
62
        tree2.commit(message='tree2 message2')
 
63
        tree2.commit(message='tree2 message3')
 
64
        tree.merge_from_branch(tree2.branch)
 
65
        tree.commit(message='merge')
 
66
        return tree
 
67
 
 
68
 
 
69
class TestLogWithLogCatcher(TestLog):
 
70
 
 
71
    def setUp(self):
 
72
        super(TestLogWithLogCatcher, self).setUp()
 
73
        # Capture log formatter creations
 
74
        class MyLogFormatter(test_log.LogCatcher):
 
75
 
 
76
            def __new__(klass, *args, **kwargs):
 
77
                self.log_catcher = test_log.LogCatcher(*args, **kwargs)
 
78
                # Always return our own log formatter
 
79
                return self.log_catcher
 
80
 
 
81
        def getme(branch):
 
82
                # Always return our own log formatter class hijacking the
 
83
                # default behavior (which requires setting up a config
 
84
                # variable)
 
85
            return MyLogFormatter
 
86
        self.overrideAttr(log.log_formatter_registry, 'get_default', getme)
 
87
 
 
88
    def get_captured_revisions(self):
 
89
        return self.log_catcher.revisions
 
90
 
 
91
    def assertLogRevnos(self, args, expected_revnos, working_dir='.'):
 
92
        self.run_bzr(['log'] + args, working_dir=working_dir)
 
93
        self.assertEqual(expected_revnos,
 
94
                         [r.revno for r in self.get_captured_revisions()])
 
95
 
 
96
    def assertLogRevnosAndDepths(self, args, expected_revnos_and_depths,
 
97
                                working_dir='.'):
 
98
        self.run_bzr(['log'] + args, working_dir=working_dir)
 
99
        self.assertEqual(expected_revnos_and_depths,
 
100
                         [(r.revno, r.merge_depth)
 
101
                           for r in self.get_captured_revisions()])
 
102
 
 
103
 
 
104
class TestLogRevSpecs(TestLogWithLogCatcher):
 
105
 
 
106
    def test_log_no_revspec(self):
 
107
        self.make_linear_branch()
 
108
        self.assertLogRevnos([], ['3', '2', '1'])
50
109
 
51
110
    def test_log_null_end_revspec(self):
52
 
        self._prepare()
53
 
        self.assertTrue('revno: 1\n' in self.full_log)
54
 
        self.assertTrue('revno: 2\n' in self.full_log)
55
 
        self.assertTrue('revno: 3\n' in self.full_log)
56
 
        self.assertTrue('message:\n  message1\n' in self.full_log)
57
 
        self.assertTrue('message:\n  message2\n' in self.full_log)
58
 
        self.assertTrue('message:\n  message3\n' in self.full_log)
59
 
 
60
 
        log = self.run_bzr("log -r 1..")[0]
61
 
        self.assertEqualDiff(log, self.full_log)
 
111
        self.make_linear_branch()
 
112
        self.assertLogRevnos(['-r1..'], ['3', '2', '1'])
62
113
 
63
114
    def test_log_null_begin_revspec(self):
64
 
        self._prepare()
65
 
        log = self.run_bzr("log -r ..3")[0]
66
 
        self.assertEqualDiff(self.full_log, log)
 
115
        self.make_linear_branch()
 
116
        self.assertLogRevnos(['-r..3'], ['3', '2', '1'])
67
117
 
68
118
    def test_log_null_both_revspecs(self):
69
 
        self._prepare()
70
 
        log = self.run_bzr("log -r ..")[0]
71
 
        self.assertEqualDiff(self.full_log, log)
 
119
        self.make_linear_branch()
 
120
        self.assertLogRevnos(['-r..'], ['3', '2', '1'])
 
121
 
 
122
    def test_log_negative_begin_revspec_full_log(self):
 
123
        self.make_linear_branch()
 
124
        self.assertLogRevnos(['-r-3..'], ['3', '2', '1'])
 
125
 
 
126
    def test_log_negative_both_revspec_full_log(self):
 
127
        self.make_linear_branch()
 
128
        self.assertLogRevnos(['-r-3..-1'], ['3', '2', '1'])
 
129
 
 
130
    def test_log_negative_both_revspec_partial(self):
 
131
        self.make_linear_branch()
 
132
        self.assertLogRevnos(['-r-3..-2'], ['2', '1'])
 
133
 
 
134
    def test_log_negative_begin_revspec(self):
 
135
        self.make_linear_branch()
 
136
        self.assertLogRevnos(['-r-2..'], ['3', '2'])
 
137
 
 
138
    def test_log_positive_revspecs(self):
 
139
        self.make_linear_branch()
 
140
        self.assertLogRevnos(['-r1..3'], ['3', '2', '1'])
 
141
 
 
142
    def test_log_dotted_revspecs(self):
 
143
        self.make_merged_branch()
 
144
        self.assertLogRevnos(['-n0', '-r1..1.1.1'], ['1.1.1', '1'])
 
145
 
 
146
    def test_log_limit(self):
 
147
        tree = self.make_branch_and_tree('.')
 
148
        # We want more commits than our batch size starts at
 
149
        for pos in range(10):
 
150
            tree.commit("%s" % pos)
 
151
        self.assertLogRevnos(['--limit', '2'], ['10', '9'])
 
152
 
 
153
    def test_log_limit_short(self):
 
154
        self.make_linear_branch()
 
155
        self.assertLogRevnos(['-l', '2'], ['3', '2'])
 
156
 
 
157
    def test_log_change_revno(self):
 
158
        self.make_linear_branch()
 
159
        self.assertLogRevnos(['-c1'], ['1'])
 
160
 
 
161
 
 
162
class TestLogMergedLinearAncestry(TestLogWithLogCatcher):
 
163
 
 
164
    def setUp(self):
 
165
        super(TestLogMergedLinearAncestry, self).setUp()
 
166
        # FIXME: Using a MemoryTree would be even better here (but until we
 
167
        # stop calling run_bzr, there is no point) --vila 100118.
 
168
        builder = branchbuilder.BranchBuilder(self.get_transport())
 
169
        builder.start_series()
 
170
        # mainline
 
171
        builder.build_snapshot('1', None, [
 
172
            ('add', ('', 'root-id', 'directory', ''))])
 
173
        builder.build_snapshot('2', ['1'], [])
 
174
        # branch
 
175
        builder.build_snapshot('1.1.1', ['1'], [])
 
176
        # merge branch into mainline
 
177
        builder.build_snapshot('3', ['2', '1.1.1'], [])
 
178
        # new commits in branch
 
179
        builder.build_snapshot('1.1.2', ['1.1.1'], [])
 
180
        builder.build_snapshot('1.1.3', ['1.1.2'], [])
 
181
        # merge branch into mainline
 
182
        builder.build_snapshot('4', ['3', '1.1.3'], [])
 
183
        # merge mainline into branch
 
184
        builder.build_snapshot('1.1.4', ['1.1.3', '4'], [])
 
185
        # merge branch into mainline
 
186
        builder.build_snapshot('5', ['4', '1.1.4'], [])
 
187
        builder.finish_series()
 
188
 
 
189
    def test_n0(self):
 
190
        self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4'],
 
191
                             ['1.1.4', '4', '1.1.3', '1.1.2', '3', '1.1.1'])
 
192
    def test_n0_forward(self):
 
193
        self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4', '--forward'],
 
194
                             ['3', '1.1.1', '4', '1.1.2', '1.1.3', '1.1.4'])
 
195
 
 
196
    def test_n1(self):
 
197
        # starting from 1.1.4 we follow the left-hand ancestry
 
198
        self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4'],
 
199
                             ['1.1.4', '1.1.3', '1.1.2', '1.1.1'])
 
200
 
 
201
    def test_n1_forward(self):
 
202
        self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4', '--forward'],
 
203
                             ['1.1.1', '1.1.2', '1.1.3', '1.1.4'])
 
204
 
 
205
 
 
206
class Test_GenerateAllRevisions(TestLogWithLogCatcher):
 
207
 
 
208
    def setUp(self):
 
209
        super(Test_GenerateAllRevisions, self).setUp()
 
210
        builder = self.make_branch_with_many_merges()
 
211
        b = builder.get_branch()
 
212
        b.lock_read()
 
213
        self.addCleanup(b.unlock)
 
214
        self.branch = b
 
215
 
 
216
    def make_branch_with_many_merges(self, path='.', format=None):
 
217
        builder = branchbuilder.BranchBuilder(self.get_transport())
 
218
        builder.start_series()
 
219
        # The graph below may look a bit complicated (and it may be but I've
 
220
        # banged my head enough on it) but the bug requires at least dotted
 
221
        # revnos *and* merged revisions below that.
 
222
        builder.build_snapshot('1', None, [
 
223
            ('add', ('', 'root-id', 'directory', ''))])
 
224
        builder.build_snapshot('2', ['1'], [])
 
225
        builder.build_snapshot('1.1.1', ['1'], [])
 
226
        builder.build_snapshot('2.1.1', ['2'], [])
 
227
        builder.build_snapshot('3', ['2', '1.1.1'], [])
 
228
        builder.build_snapshot('2.1.2', ['2.1.1'], [])
 
229
        builder.build_snapshot('2.2.1', ['2.1.1'], [])
 
230
        builder.build_snapshot('2.1.3', ['2.1.2', '2.2.1'], [])
 
231
        builder.build_snapshot('4', ['3', '2.1.3'], [])
 
232
        builder.build_snapshot('5', ['4', '2.1.2'], [])
 
233
        builder.finish_series()
 
234
        return builder
 
235
 
 
236
    def test_not_an_ancestor(self):
 
237
        self.assertRaises(errors.BzrCommandError,
 
238
                          log._generate_all_revisions,
 
239
                          self.branch, '1.1.1', '2.1.3', 'reverse',
 
240
                          delayed_graph_generation=True)
 
241
 
 
242
    def test_wrong_order(self):
 
243
        self.assertRaises(errors.BzrCommandError,
 
244
                          log._generate_all_revisions,
 
245
                          self.branch, '5', '2.1.3', 'reverse',
 
246
                          delayed_graph_generation=True)
 
247
 
 
248
    def test_no_start_rev_id_with_end_rev_id_being_a_merge(self):
 
249
        revs = log._generate_all_revisions(
 
250
            self.branch, None, '2.1.3',
 
251
            'reverse', delayed_graph_generation=True)
 
252
 
 
253
 
 
254
class TestLogRevSpecsWithPaths(TestLogWithLogCatcher):
 
255
 
 
256
    def test_log_revno_n_path_wrong_namespace(self):
 
257
        self.make_linear_branch('branch1')
 
258
        self.make_linear_branch('branch2')
 
259
        # There is no guarantee that a path exist between two arbitrary
 
260
        # revisions.
 
261
        self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)
 
262
 
 
263
    def test_log_revno_n_path_correct_order(self):
 
264
        self.make_linear_branch('branch2')
 
265
        self.assertLogRevnos(['-rrevno:1:branch2..revno:3:branch2'],
 
266
                             ['3', '2','1'])
 
267
 
 
268
    def test_log_revno_n_path(self):
 
269
        self.make_linear_branch('branch2')
 
270
        self.assertLogRevnos(['-rrevno:1:branch2'],
 
271
                             ['1'])
 
272
        rev_props = self.log_catcher.revisions[0].rev.properties
 
273
        self.assertEqual('branch2', rev_props['branch-nick'])
 
274
 
 
275
 
 
276
class TestLogErrors(TestLog):
72
277
 
73
278
    def test_log_zero_revspec(self):
74
 
        self._prepare()
75
 
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
279
        self.make_minimal_branch()
 
280
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
76
281
                           ['log', '-r0'])
77
282
 
78
283
    def test_log_zero_begin_revspec(self):
79
 
        self._prepare()
80
 
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
284
        self.make_linear_branch()
 
285
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
81
286
                           ['log', '-r0..2'])
82
287
 
83
288
    def test_log_zero_end_revspec(self):
84
 
        self._prepare()
85
 
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
289
        self.make_linear_branch()
 
290
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
86
291
                           ['log', '-r-2..0'])
87
292
 
88
 
    def test_log_unsupported_timezone(self):
89
 
        self._prepare()
90
 
        self.run_bzr_error('bzr: ERROR: Unsupported timezone format "foo", '
91
 
                           'options are "utc", "original", "local".',
92
 
                           ['log', '--timezone', 'foo'])
93
 
 
94
 
    def test_log_negative_begin_revspec_full_log(self):
95
 
        self._prepare()
96
 
        log = self.run_bzr("log -r -3..")[0]
97
 
        self.assertEqualDiff(self.full_log, log)
98
 
 
99
 
    def test_log_negative_both_revspec_full_log(self):
100
 
        self._prepare()
101
 
        log = self.run_bzr("log -r -3..-1")[0]
102
 
        self.assertEqualDiff(self.full_log, log)
103
 
 
104
 
    def test_log_negative_both_revspec_partial(self):
105
 
        self._prepare()
106
 
        log = self.run_bzr("log -r -3..-2")[0]
107
 
        self.assertTrue('revno: 1\n' in log)
108
 
        self.assertTrue('revno: 2\n' in log)
109
 
        self.assertTrue('revno: 3\n' not in log)
110
 
 
111
 
    def test_log_negative_begin_revspec(self):
112
 
        self._prepare()
113
 
        log = self.run_bzr("log -r -2..")[0]
114
 
        self.assertTrue('revno: 1\n' not in log)
115
 
        self.assertTrue('revno: 2\n' in log)
116
 
        self.assertTrue('revno: 3\n' in log)
117
 
 
118
 
    def test_log_positive_revspecs(self):
119
 
        self._prepare()
120
 
        log = self.run_bzr("log -r 1..3")[0]
121
 
        self.assertEqualDiff(self.full_log, log)
122
 
 
123
 
    def test_log_reversed_revspecs(self):
124
 
        self._prepare()
125
 
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
126
 
                            'the end revision.\n',),
127
 
                           ['log', '-r3..1'])
128
 
 
129
 
    def test_log_revno_n_path(self):
130
 
        self._prepare(path='branch1')
131
 
        self._prepare(path='branch2')
132
 
        log = self.run_bzr("log -r revno:2:branch1..revno:3:branch2",
133
 
                          retcode=3)[0]
134
 
        log = self.run_bzr("log -r revno:1:branch2..revno:3:branch2")[0]
135
 
        self.assertEqualDiff(self.full_log, log)
136
 
        log = self.run_bzr("log -r revno:1:branch2")[0]
137
 
        self.assertTrue('revno: 1\n' in log)
138
 
        self.assertTrue('revno: 2\n' not in log)
139
 
        self.assertTrue('branch nick: branch2\n' in log)
140
 
        self.assertTrue('branch nick: branch1\n' not in log)
141
 
 
142
 
    def test_log_change_revno(self):
143
 
        self._prepare()
144
 
        expected_log = self.run_bzr("log -r 1")[0]
145
 
        log = self.run_bzr("log -c 1")[0]
146
 
        self.assertEqualDiff(expected_log, log)
147
 
 
148
 
    def test_log_change_single_revno(self):
149
 
        self._prepare()
150
 
        self.run_bzr_error('bzr: ERROR: Option --change does not'
151
 
                           ' accept revision ranges',
 
293
    def test_log_nonexistent_revno(self):
 
294
        self.make_minimal_branch()
 
295
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
 
296
                            "does not exist in branch:"],
 
297
                           ['log', '-r1234'])
 
298
 
 
299
    def test_log_nonexistent_dotted_revno(self):
 
300
        self.make_minimal_branch()
 
301
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
 
302
                            "does not exist in branch:"],
 
303
                           ['log',  '-r123.123'])
 
304
 
 
305
    def test_log_change_nonexistent_revno(self):
 
306
        self.make_minimal_branch()
 
307
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
 
308
                            "does not exist in branch:"],
 
309
                           ['log',  '-c1234'])
 
310
 
 
311
    def test_log_change_nonexistent_dotted_revno(self):
 
312
        self.make_minimal_branch()
 
313
        self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
 
314
                            "does not exist in branch:"],
 
315
                           ['log', '-c123.123'])
 
316
 
 
317
    def test_log_change_single_revno_only(self):
 
318
        self.make_minimal_branch()
 
319
        self.run_bzr_error(['bzr: ERROR: Option --change does not'
 
320
                           ' accept revision ranges'],
152
321
                           ['log', '--change', '2..3'])
153
322
 
154
323
    def test_log_change_incompatible_with_revision(self):
155
 
        self._prepare()
156
 
        self.run_bzr_error('bzr: ERROR: --revision and --change'
157
 
                           ' are mutually exclusive',
 
324
        self.run_bzr_error(['bzr: ERROR: --revision and --change'
 
325
                           ' are mutually exclusive'],
158
326
                           ['log', '--change', '2', '--revision', '3'])
159
327
 
160
328
    def test_log_nonexistent_file(self):
 
329
        self.make_minimal_branch()
161
330
        # files that don't exist in either the basis tree or working tree
162
331
        # should give an error
163
 
        wt = self.make_branch_and_tree('.')
164
332
        out, err = self.run_bzr('log does-not-exist', retcode=3)
165
 
        self.assertContainsRe(
166
 
            err, 'Path does not have any revision history: does-not-exist')
 
333
        self.assertContainsRe(err,
 
334
                              'Path unknown at end or start of revision range: '
 
335
                              'does-not-exist')
 
336
 
 
337
    def test_log_reversed_revspecs(self):
 
338
        self.make_linear_branch()
 
339
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
 
340
                            'the end revision.\n',),
 
341
                           ['log', '-r3..1'])
 
342
 
 
343
    def test_log_reversed_dotted_revspecs(self):
 
344
        self.make_merged_branch()
 
345
        self.run_bzr_error(('bzr: ERROR: Start revision not found in '
 
346
                            'left-hand history of end revision.\n',),
 
347
                           "log -r 1.1.1..1")
 
348
 
 
349
    def test_log_bad_message_re(self):
 
350
        """Bad --message argument gives a sensible message
 
351
        
 
352
        See https://bugs.launchpad.net/bzr/+bug/251352
 
353
        """
 
354
        self.make_minimal_branch()
 
355
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
 
356
        self.assertEqual("bzr: ERROR: Invalid regular expression"
 
357
            " in log message filter"
 
358
            ": '*'"
 
359
            ": nothing to repeat\n", err)
 
360
        self.assertEqual('', out)
 
361
 
 
362
    def test_log_unsupported_timezone(self):
 
363
        self.make_linear_branch()
 
364
        self.run_bzr_error(['bzr: ERROR: Unsupported timezone format "foo", '
 
365
                            'options are "utc", "original", "local".'],
 
366
                           ['log', '--timezone', 'foo'])
 
367
 
 
368
 
 
369
class TestLogTags(TestLog):
167
370
 
168
371
    def test_log_with_tags(self):
169
 
        tree = self._prepare(format='dirstate-tags')
 
372
        tree = self.make_linear_branch(format='dirstate-tags')
170
373
        branch = tree.branch
171
374
        branch.tags.set_tag('tag1', branch.get_rev_id(1))
172
375
        branch.tags.set_tag('tag1.1', branch.get_rev_id(1))
173
 
        branch.tags.set_tag('tag3', branch.last_revision()) 
174
 
        
 
376
        branch.tags.set_tag('tag3', branch.last_revision())
 
377
 
175
378
        log = self.run_bzr("log -r-1")[0]
176
379
        self.assertTrue('tags: tag3' in log)
177
380
 
181
384
        self.assertContainsRe(log, r'tags: (tag1, tag1\.1|tag1\.1, tag1)')
182
385
 
183
386
    def test_merged_log_with_tags(self):
184
 
        branch1_tree = self._prepare(path='branch1', format='dirstate-tags')
 
387
        branch1_tree = self.make_linear_branch('branch1',
 
388
                                               format='dirstate-tags')
185
389
        branch1 = branch1_tree.branch
186
390
        branch2_tree = branch1_tree.bzrdir.sprout('branch2').open_workingtree()
187
391
        branch1_tree.commit(message='foobar', allow_pointless=True)
188
392
        branch1.tags.set_tag('tag1', branch1.last_revision())
189
 
        os.chdir('branch2')
190
 
        self.run_bzr('merge ../branch1') # tags don't propagate otherwise
 
393
        # tags don't propagate if we don't merge
 
394
        self.run_bzr('merge ../branch1', working_dir='branch2')
191
395
        branch2_tree.commit(message='merge branch 1')
192
 
        log = self.run_bzr("log -r-1")[0]
 
396
        log = self.run_bzr("log -n0 -r-1", working_dir='branch2')[0]
193
397
        self.assertContainsRe(log, r'    tags: tag1')
194
 
        log = self.run_bzr("log -r3.1.1")[0]
 
398
        log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
195
399
        self.assertContainsRe(log, r'tags: tag1')
196
400
 
197
 
    def test_log_limit(self):
198
 
        tree = self.make_branch_and_tree('.')
199
 
        # We want more commits than our batch size starts at
200
 
        for pos in range(10):
201
 
            tree.commit("%s" % pos)
202
 
        log = self.run_bzr("log --limit 2")[0]
203
 
        self.assertNotContainsRe(log, r'revno: 1\n')
204
 
        self.assertNotContainsRe(log, r'revno: 2\n')
205
 
        self.assertNotContainsRe(log, r'revno: 3\n')
206
 
        self.assertNotContainsRe(log, r'revno: 4\n')
207
 
        self.assertNotContainsRe(log, r'revno: 5\n')
208
 
        self.assertNotContainsRe(log, r'revno: 6\n')
209
 
        self.assertNotContainsRe(log, r'revno: 7\n')
210
 
        self.assertNotContainsRe(log, r'revno: 8\n')
211
 
        self.assertContainsRe(log, r'revno: 9\n')
212
 
        self.assertContainsRe(log, r'revno: 10\n')
213
 
 
214
 
    def test_log_limit_short(self):
215
 
        self._prepare()
216
 
        log = self.run_bzr("log -l 2")[0]
217
 
        self.assertNotContainsRe(log, r'revno: 1\n')
218
 
        self.assertContainsRe(log, r'revno: 2\n')
219
 
        self.assertContainsRe(log, r'revno: 3\n')
220
 
 
221
 
 
222
 
class TestLogMerges(TestCaseWithoutPropsHandler):
223
 
 
224
 
    def _prepare(self):
225
 
        parent_tree = self.make_branch_and_tree('parent')
226
 
        parent_tree.commit(message='first post', allow_pointless=True)
227
 
        child_tree = parent_tree.bzrdir.sprout('child').open_workingtree()
228
 
        child_tree.commit(message='branch 1', allow_pointless=True)
229
 
        smaller_tree = \
230
 
                child_tree.bzrdir.sprout('smallerchild').open_workingtree()
231
 
        smaller_tree.commit(message='branch 2', allow_pointless=True)
232
 
        child_tree.merge_from_branch(smaller_tree.branch)
233
 
        child_tree.commit(message='merge branch 2')
234
 
        parent_tree.merge_from_branch(child_tree.branch)
235
 
        parent_tree.commit(message='merge branch 1')
236
 
        os.chdir('parent')
 
401
 
 
402
class TestLogVerbose(TestLog):
 
403
 
 
404
    def setUp(self):
 
405
        super(TestLogVerbose, self).setUp()
 
406
        self.make_minimal_branch()
 
407
 
 
408
    def assertUseShortDeltaFormat(self, cmd):
 
409
        log = self.run_bzr(cmd)[0]
 
410
        # Check that we use the short status format
 
411
        self.assertContainsRe(log, '(?m)^\s*A  hello.txt$')
 
412
        self.assertNotContainsRe(log, '(?m)^\s*added:$')
 
413
 
 
414
    def assertUseLongDeltaFormat(self, cmd):
 
415
        log = self.run_bzr(cmd)[0]
 
416
        # Check that we use the long status format
 
417
        self.assertNotContainsRe(log, '(?m)^\s*A  hello.txt$')
 
418
        self.assertContainsRe(log, '(?m)^\s*added:$')
 
419
 
 
420
    def test_log_short_verbose(self):
 
421
        self.assertUseShortDeltaFormat(['log', '--short', '-v'])
 
422
 
 
423
    def test_log_short_verbose_verbose(self):
 
424
        self.assertUseLongDeltaFormat(['log', '--short', '-vv'])
 
425
 
 
426
    def test_log_long_verbose(self):
 
427
        # Check that we use the long status format, ignoring the verbosity
 
428
        # level
 
429
        self.assertUseLongDeltaFormat(['log', '--long', '-v'])
 
430
 
 
431
    def test_log_long_verbose_verbose(self):
 
432
        # Check that we use the long status format, ignoring the verbosity
 
433
        # level
 
434
        self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
 
435
 
 
436
 
 
437
class TestLogMerges(TestLogWithLogCatcher):
 
438
 
 
439
    def setUp(self):
 
440
        super(TestLogMerges, self).setUp()
 
441
        self.make_branches_with_merges()
 
442
 
 
443
    def make_branches_with_merges(self):
 
444
        level0 = self.make_branch_and_tree('level0')
 
445
        self.wt_commit(level0, 'in branch level0')
 
446
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
 
447
        self.wt_commit(level1, 'in branch level1')
 
448
        level2 = level1.bzrdir.sprout('level2').open_workingtree()
 
449
        self.wt_commit(level2, 'in branch level2')
 
450
        level1.merge_from_branch(level2.branch)
 
451
        self.wt_commit(level1, 'merge branch level2')
 
452
        level0.merge_from_branch(level1.branch)
 
453
        self.wt_commit(level0, 'merge branch level1')
237
454
 
238
455
    def test_merges_are_indented_by_level(self):
239
 
        self._prepare()
240
 
        out,err = self.run_bzr('log')
241
 
        self.assertEqual('', err)
242
 
        log = normalize_log(out)
243
 
        self.assertEqualDiff(log, """\
244
 
------------------------------------------------------------
245
 
revno: 2
246
 
committer: Lorem Ipsum <test@example.com>
247
 
branch nick: parent
248
 
timestamp: Just now
249
 
message:
250
 
  merge branch 1
251
 
    ------------------------------------------------------------
252
 
    revno: 1.1.2
253
 
    committer: Lorem Ipsum <test@example.com>
254
 
    branch nick: child
255
 
    timestamp: Just now
256
 
    message:
257
 
      merge branch 2
258
 
        ------------------------------------------------------------
259
 
        revno: 1.2.1
260
 
        committer: Lorem Ipsum <test@example.com>
261
 
        branch nick: smallerchild
262
 
        timestamp: Just now
263
 
        message:
264
 
          branch 2
265
 
    ------------------------------------------------------------
266
 
    revno: 1.1.1
267
 
    committer: Lorem Ipsum <test@example.com>
268
 
    branch nick: child
269
 
    timestamp: Just now
270
 
    message:
271
 
      branch 1
272
 
------------------------------------------------------------
273
 
revno: 1
274
 
committer: Lorem Ipsum <test@example.com>
275
 
branch nick: parent
276
 
timestamp: Just now
277
 
message:
278
 
  first post
279
 
""")
 
456
        self.run_bzr(['log', '-n0'], working_dir='level0')
 
457
        revnos_and_depth = [(r.revno, r.merge_depth)
 
458
                            for r in self.get_captured_revisions()]
 
459
        self.assertEqual([('2', 0), ('1.1.2', 1), ('1.2.1', 2), ('1.1.1', 1),
 
460
                          ('1', 0)],
 
461
                         [(r.revno, r.merge_depth)
 
462
                            for r in self.get_captured_revisions()])
 
463
 
 
464
    def test_force_merge_revisions_off(self):
 
465
        self.assertLogRevnos(['-n1'], ['2', '1'], working_dir='level0')
 
466
 
 
467
    def test_force_merge_revisions_on(self):
 
468
        self.assertLogRevnos(['-n0'], ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
 
469
                             working_dir='level0')
 
470
 
 
471
    def test_include_merges(self):
 
472
        # Confirm --include-merges gives the same output as -n0
 
473
        self.assertLogRevnos(['--include-merges'],
 
474
                             ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
 
475
                             working_dir='level0')
 
476
        self.assertLogRevnos(['--include-merges'],
 
477
                             ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
 
478
                             working_dir='level0')
 
479
        out_im, err_im = self.run_bzr('log --include-merges',
 
480
                                      working_dir='level0')
 
481
        out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
 
482
        self.assertEqual('', err_im)
 
483
        self.assertEqual('', err_n0)
 
484
        self.assertEqual(out_im, out_n0)
 
485
 
 
486
    def test_force_merge_revisions_N(self):
 
487
        self.assertLogRevnos(['-n2'],
 
488
                             ['2', '1.1.2', '1.1.1', '1'],
 
489
                             working_dir='level0')
280
490
 
281
491
    def test_merges_single_merge_rev(self):
282
 
        self._prepare()
283
 
        out,err = self.run_bzr('log -r1.1.2')
284
 
        self.assertEqual('', err)
285
 
        log = normalize_log(out)
286
 
        self.assertEqualDiff(log, """\
287
 
------------------------------------------------------------
288
 
revno: 1.1.2
289
 
committer: Lorem Ipsum <test@example.com>
290
 
branch nick: child
291
 
timestamp: Just now
292
 
message:
293
 
  merge branch 2
294
 
    ------------------------------------------------------------
295
 
    revno: 1.2.1
296
 
    committer: Lorem Ipsum <test@example.com>
297
 
    branch nick: smallerchild
298
 
    timestamp: Just now
299
 
    message:
300
 
      branch 2
301
 
""")
 
492
        self.assertLogRevnosAndDepths(['-n0', '-r1.1.2'],
 
493
                                      [('1.1.2', 0), ('1.2.1', 1)],
 
494
                                      working_dir='level0')
302
495
 
303
496
    def test_merges_partial_range(self):
304
 
        self._prepare()
305
 
        out,err = self.run_bzr('log -r1.1.1..1.1.2')
306
 
        self.assertEqual('', err)
307
 
        log = normalize_log(out)
308
 
        self.assertEqualDiff(log, """\
309
 
------------------------------------------------------------
310
 
revno: 1.1.2
311
 
committer: Lorem Ipsum <test@example.com>
312
 
branch nick: child
313
 
timestamp: Just now
314
 
message:
315
 
  merge branch 2
316
 
    ------------------------------------------------------------
317
 
    revno: 1.2.1
318
 
    committer: Lorem Ipsum <test@example.com>
319
 
    branch nick: smallerchild
320
 
    timestamp: Just now
321
 
    message:
322
 
      branch 2
323
 
------------------------------------------------------------
324
 
revno: 1.1.1
325
 
committer: Lorem Ipsum <test@example.com>
326
 
branch nick: child
327
 
timestamp: Just now
328
 
message:
329
 
  branch 1
330
 
""")
331
 
 
332
 
    def test_merges_nonsupporting_formatter(self):
333
 
        self._prepare()
334
 
        err_msg = 'Selected log formatter only supports mainline revisions.'
335
 
        # The single revision case is tested in the core tests
336
 
        # since all standard formatters support single merge revisions.
337
 
        out,err = self.run_bzr('log --short -r1..1.1.2', retcode=3)
338
 
        self.assertContainsRe(err, err_msg)
339
 
        out,err = self.run_bzr('log --short -r1.1.1..1.1.2', retcode=3)
340
 
        self.assertContainsRe(err, err_msg)
341
 
 
342
 
 
343
 
class TestLogEncodings(TestCaseInTempDir):
 
497
        self.assertLogRevnosAndDepths(
 
498
                ['-n0', '-r1.1.1..1.1.2'],
 
499
                [('1.1.2', 0), ('1.2.1', 1), ('1.1.1', 0)],
 
500
                working_dir='level0')
 
501
 
 
502
    def test_merges_partial_range_ignore_before_lower_bound(self):
 
503
        """Dont show revisions before the lower bound's merged revs"""
 
504
        self.assertLogRevnosAndDepths(
 
505
                ['-n0', '-r1.1.2..2'],
 
506
                [('2', 0), ('1.1.2', 1), ('1.2.1', 2)],
 
507
                working_dir='level0')
 
508
 
 
509
 
 
510
class TestLogDiff(TestLogWithLogCatcher):
 
511
 
 
512
    # FIXME: We need specific tests for each LogFormatter about how the diffs
 
513
    # are displayed: --long indent them by depth, --short use a fixed
 
514
    # indent and --line does't display them. -- vila 10019
 
515
 
 
516
    def setUp(self):
 
517
        super(TestLogDiff, self).setUp()
 
518
        self.make_branch_with_diffs()
 
519
 
 
520
    def make_branch_with_diffs(self):
 
521
        level0 = self.make_branch_and_tree('level0')
 
522
        self.build_tree(['level0/file1', 'level0/file2'])
 
523
        level0.add('file1')
 
524
        level0.add('file2')
 
525
        self.wt_commit(level0, 'in branch level0')
 
526
 
 
527
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
 
528
        self.build_tree_contents([('level1/file2', 'hello\n')])
 
529
        self.wt_commit(level1, 'in branch level1')
 
530
        level0.merge_from_branch(level1.branch)
 
531
        self.wt_commit(level0, 'merge branch level1')
 
532
 
 
533
    def _diff_file1_revno1(self):
 
534
        return """=== added file 'file1'
 
535
--- file1\t1970-01-01 00:00:00 +0000
 
536
+++ file1\t2005-11-22 00:00:00 +0000
 
537
@@ -0,0 +1,1 @@
 
538
+contents of level0/file1
 
539
 
 
540
"""
 
541
 
 
542
    def _diff_file2_revno2(self):
 
543
        return """=== modified file 'file2'
 
544
--- file2\t2005-11-22 00:00:00 +0000
 
545
+++ file2\t2005-11-22 00:00:01 +0000
 
546
@@ -1,1 +1,1 @@
 
547
-contents of level0/file2
 
548
+hello
 
549
 
 
550
"""
 
551
 
 
552
    def _diff_file2_revno1_1_1(self):
 
553
        return """=== modified file 'file2'
 
554
--- file2\t2005-11-22 00:00:00 +0000
 
555
+++ file2\t2005-11-22 00:00:01 +0000
 
556
@@ -1,1 +1,1 @@
 
557
-contents of level0/file2
 
558
+hello
 
559
 
 
560
"""
 
561
 
 
562
    def _diff_file2_revno1(self):
 
563
        return """=== added file 'file2'
 
564
--- file2\t1970-01-01 00:00:00 +0000
 
565
+++ file2\t2005-11-22 00:00:00 +0000
 
566
@@ -0,0 +1,1 @@
 
567
+contents of level0/file2
 
568
 
 
569
"""
 
570
 
 
571
    def assertLogRevnosAndDiff(self, args, expected,
 
572
                            working_dir='.'):
 
573
        self.run_bzr(['log', '-p'] + args, working_dir=working_dir)
 
574
        expected_revnos_and_depths = [
 
575
            (revno, depth) for revno, depth, diff in expected]
 
576
        # Check the revnos and depths first to make debugging easier
 
577
        self.assertEqual(expected_revnos_and_depths,
 
578
                         [(r.revno, r.merge_depth)
 
579
                           for r in self.get_captured_revisions()])
 
580
        # Now check the diffs, adding the revno  in case of failure
 
581
        fmt = 'In revno %s\n%s'
 
582
        for expected_rev, actual_rev in izip(expected,
 
583
                                             self.get_captured_revisions()):
 
584
            revno, depth, expected_diff = expected_rev
 
585
            actual_diff = actual_rev.diff
 
586
            self.assertEqualDiff(fmt % (revno, expected_diff),
 
587
                                 fmt % (revno, actual_diff))
 
588
 
 
589
    def test_log_diff_with_merges(self):
 
590
        self.assertLogRevnosAndDiff(
 
591
            ['-n0'],
 
592
            [('2', 0, self._diff_file2_revno2()),
 
593
             ('1.1.1', 1, self._diff_file2_revno1_1_1()),
 
594
             ('1', 0, self._diff_file1_revno1()
 
595
              + self._diff_file2_revno1())],
 
596
            working_dir='level0')
 
597
 
 
598
 
 
599
    def test_log_diff_file1(self):
 
600
        self.assertLogRevnosAndDiff(['-n0', 'file1'],
 
601
                                    [('1', 0, self._diff_file1_revno1())],
 
602
                                    working_dir='level0')
 
603
 
 
604
    def test_log_diff_file2(self):
 
605
        self.assertLogRevnosAndDiff(['-n1', 'file2'],
 
606
                                    [('2', 0, self._diff_file2_revno2()),
 
607
                                     ('1', 0, self._diff_file2_revno1())],
 
608
                                    working_dir='level0')
 
609
 
 
610
 
 
611
class TestLogUnicodeDiff(TestLog):
 
612
 
 
613
    def test_log_show_diff_non_ascii(self):
 
614
        # Smoke test for bug #328007 UnicodeDecodeError on 'log -p'
 
615
        message = u'Message with \xb5'
 
616
        body = 'Body with \xb5\n'
 
617
        wt = self.make_branch_and_tree('.')
 
618
        self.build_tree_contents([('foo', body)])
 
619
        wt.add('foo')
 
620
        wt.commit(message=message)
 
621
        # check that command won't fail with unicode error
 
622
        # don't care about exact output because we have other tests for this
 
623
        out,err = self.run_bzr('log -p --long')
 
624
        self.assertNotEqual('', out)
 
625
        self.assertEqual('', err)
 
626
        out,err = self.run_bzr('log -p --short')
 
627
        self.assertNotEqual('', out)
 
628
        self.assertEqual('', err)
 
629
        out,err = self.run_bzr('log -p --line')
 
630
        self.assertNotEqual('', out)
 
631
        self.assertEqual('', err)
 
632
 
 
633
 
 
634
class TestLogEncodings(tests.TestCaseInTempDir):
344
635
 
345
636
    _mu = u'\xb5'
346
637
    _message = u'Message with \xb5'
351
642
        'latin-1',
352
643
        'iso-8859-1',
353
644
        'cp437', # Common windows encoding
354
 
        'cp1251', # Alexander Belchenko's windows encoding
 
645
        'cp1251', # Russian windows encoding
355
646
        'cp1258', # Common windows encoding
356
647
    ]
357
648
    # Encodings which cannot encode mu
362
653
    ]
363
654
 
364
655
    def setUp(self):
365
 
        TestCaseInTempDir.setUp(self)
366
 
        self.user_encoding = bzrlib.user_encoding
367
 
 
368
 
    def tearDown(self):
369
 
        bzrlib.user_encoding = self.user_encoding
370
 
        TestCaseInTempDir.tearDown(self)
 
656
        super(TestLogEncodings, self).setUp()
 
657
        self.overrideAttr(osutils, '_cached_user_encoding')
371
658
 
372
659
    def create_branch(self):
373
660
        bzr = self.run_bzr
374
661
        bzr('init')
375
 
        open('a', 'wb').write('some stuff\n')
 
662
        self.build_tree_contents([('a', 'some stuff\n')])
376
663
        bzr('add a')
377
664
        bzr(['commit', '-m', self._message])
378
665
 
385
672
        else:
386
673
            encoded_msg = self._message.encode(encoding)
387
674
 
388
 
        old_encoding = bzrlib.user_encoding
 
675
        old_encoding = osutils._cached_user_encoding
389
676
        # This test requires that 'run_bzr' uses the current
390
677
        # bzrlib, because we override user_encoding, and expect
391
678
        # it to be used
392
679
        try:
393
 
            bzrlib.user_encoding = 'ascii'
 
680
            osutils._cached_user_encoding = 'ascii'
394
681
            # We should be able to handle any encoding
395
682
            out, err = bzr('log', encoding=encoding)
396
683
            if not fail:
401
688
            else:
402
689
                self.assertNotEqual(-1, out.find('Message with ?'))
403
690
        finally:
404
 
            bzrlib.user_encoding = old_encoding
 
691
            osutils._cached_user_encoding = old_encoding
405
692
 
406
693
    def test_log_handles_encoding(self):
407
694
        self.create_branch()
417
704
 
418
705
    def test_stdout_encoding(self):
419
706
        bzr = self.run_bzr
420
 
        bzrlib.user_encoding = "cp1251"
 
707
        osutils._cached_user_encoding = "cp1251"
421
708
 
422
709
        bzr('init')
423
710
        self.build_tree(['a'])
441
728
        self.assertEquals(-1, stdout.find(test_in_cp1251))
442
729
 
443
730
 
444
 
class TestLogFile(TestCaseWithTransport):
 
731
class TestLogFile(TestLogWithLogCatcher):
445
732
 
446
733
    def test_log_local_branch_file(self):
447
734
        """We should be able to log files in local treeless branches"""
452
739
        tree.bzrdir.destroy_workingtree()
453
740
        self.run_bzr('log tree/file')
454
741
 
455
 
    def test_log_file(self):
456
 
        """The log for a particular file should only list revs for that file"""
 
742
    def prepare_tree(self, complex=False):
 
743
        # The complex configuration includes deletes and renames
457
744
        tree = self.make_branch_and_tree('parent')
458
745
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
459
746
        tree.add('file1')
467
754
        child_tree.commit(message='branch 1')
468
755
        tree.merge_from_branch(child_tree.branch)
469
756
        tree.commit(message='merge child branch')
470
 
        os.chdir('parent')
471
 
        log = self.run_bzr('log file1')[0]
472
 
        self.assertContainsRe(log, 'revno: 1\n')
473
 
        self.assertNotContainsRe(log, 'revno: 2\n')
474
 
        self.assertNotContainsRe(log, 'revno: 3\n')
475
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
476
 
        self.assertNotContainsRe(log, 'revno: 4\n')
477
 
        log = self.run_bzr('log file2')[0]
478
 
        self.assertNotContainsRe(log, 'revno: 1\n')
479
 
        self.assertContainsRe(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 file3')[0]
484
 
        self.assertNotContainsRe(log, 'revno: 1\n')
485
 
        self.assertNotContainsRe(log, 'revno: 2\n')
486
 
        self.assertContainsRe(log, 'revno: 3\n')
487
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
488
 
        self.assertNotContainsRe(log, 'revno: 4\n')
489
 
        log = self.run_bzr('log -r3.1.1 file2')[0]
490
 
        self.assertNotContainsRe(log, 'revno: 1\n')
491
 
        self.assertNotContainsRe(log, 'revno: 2\n')
492
 
        self.assertNotContainsRe(log, 'revno: 3\n')
493
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
494
 
        self.assertNotContainsRe(log, 'revno: 4\n')
495
 
        log = self.run_bzr('log -r4 file2')[0]
496
 
        self.assertNotContainsRe(log, 'revno: 1\n')
497
 
        self.assertNotContainsRe(log, 'revno: 2\n')
498
 
        self.assertNotContainsRe(log, 'revno: 3\n')
499
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
500
 
        self.assertContainsRe(log, 'revno: 4\n')
501
 
        log = self.run_bzr('log -r3.. file2')[0]
502
 
        self.assertNotContainsRe(log, 'revno: 1\n')
503
 
        self.assertNotContainsRe(log, 'revno: 2\n')
504
 
        self.assertNotContainsRe(log, 'revno: 3\n')
505
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
506
 
        self.assertContainsRe(log, 'revno: 4\n')
507
 
        log = self.run_bzr('log -r..3 file2')[0]
508
 
        self.assertNotContainsRe(log, 'revno: 1\n')
509
 
        self.assertContainsRe(log, 'revno: 2\n')
510
 
        self.assertNotContainsRe(log, 'revno: 3\n')
511
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
512
 
        self.assertNotContainsRe(log, 'revno: 4\n')
 
757
        if complex:
 
758
            tree.remove('file2')
 
759
            tree.commit('remove file2')
 
760
            tree.rename_one('file3', 'file4')
 
761
            tree.commit('file3 is now called file4')
 
762
            tree.remove('file1')
 
763
            tree.commit('remove file1')
 
764
        os.chdir('parent')
 
765
 
 
766
    # FIXME: It would be good to parametrize the following tests against all
 
767
    # formatters. But the revisions selection is not *currently* part of the
 
768
    # LogFormatter contract, so using LogCatcher is sufficient -- vila 100118
 
769
    def test_log_file1(self):
 
770
        self.prepare_tree()
 
771
        self.assertLogRevnos(['-n0', 'file1'], ['1'])
 
772
 
 
773
    def test_log_file2(self):
 
774
        self.prepare_tree()
 
775
        # file2 full history
 
776
        self.assertLogRevnos(['-n0', 'file2'], ['4', '3.1.1', '2'])
 
777
        # file2 in a merge revision
 
778
        self.assertLogRevnos(['-n0', '-r3.1.1', 'file2'], ['3.1.1'])
 
779
        # file2 in a mainline revision
 
780
        self.assertLogRevnos(['-n0', '-r4', 'file2'], ['4', '3.1.1'])
 
781
        # file2 since a revision
 
782
        self.assertLogRevnos(['-n0', '-r3..', 'file2'], ['4', '3.1.1'])
 
783
        # file2 up to a revision
 
784
        self.assertLogRevnos(['-n0', '-r..3', 'file2'], ['2'])
 
785
 
 
786
    def test_log_file3(self):
 
787
        self.prepare_tree()
 
788
        self.assertLogRevnos(['-n0', 'file3'], ['3'])
 
789
 
 
790
    def test_log_file_historical_missing(self):
 
791
        # Check logging a deleted file gives an error if the
 
792
        # file isn't found at the end or start of the revision range
 
793
        self.prepare_tree(complex=True)
 
794
        err_msg = "Path unknown at end or start of revision range: file2"
 
795
        err = self.run_bzr('log file2', retcode=3)[1]
 
796
        self.assertContainsRe(err, err_msg)
 
797
 
 
798
    def test_log_file_historical_end(self):
 
799
        # Check logging a deleted file is ok if the file existed
 
800
        # at the end the revision range
 
801
        self.prepare_tree(complex=True)
 
802
        self.assertLogRevnos(['-n0', '-r..4', 'file2'], ['4', '3.1.1', '2'])
 
803
 
 
804
    def test_log_file_historical_start(self):
 
805
        # Check logging a deleted file is ok if the file existed
 
806
        # at the start of the revision range
 
807
        self.prepare_tree(complex=True)
 
808
        self.assertLogRevnos(['file1'], ['1'])
 
809
 
 
810
    def test_log_file_renamed(self):
 
811
        """File matched against revision range, not current tree."""
 
812
        self.prepare_tree(complex=True)
 
813
 
 
814
        # Check logging a renamed file gives an error by default
 
815
        err_msg = "Path unknown at end or start of revision range: file3"
 
816
        err = self.run_bzr('log file3', retcode=3)[1]
 
817
        self.assertContainsRe(err, err_msg)
 
818
 
 
819
        # Check we can see a renamed file if we give the right end revision
 
820
        self.assertLogRevnos(['-r..4', 'file3'], ['3'])
 
821
 
 
822
 
 
823
class TestLogMultiple(TestLogWithLogCatcher):
 
824
 
 
825
    def prepare_tree(self):
 
826
        tree = self.make_branch_and_tree('parent')
 
827
        self.build_tree([
 
828
            'parent/file1',
 
829
            'parent/file2',
 
830
            'parent/dir1/',
 
831
            'parent/dir1/file5',
 
832
            'parent/dir1/dir2/',
 
833
            'parent/dir1/dir2/file3',
 
834
            'parent/file4'])
 
835
        tree.add('file1')
 
836
        tree.commit('add file1')
 
837
        tree.add('file2')
 
838
        tree.commit('add file2')
 
839
        tree.add(['dir1', 'dir1/dir2', 'dir1/dir2/file3'])
 
840
        tree.commit('add file3')
 
841
        tree.add('file4')
 
842
        tree.commit('add file4')
 
843
        tree.add('dir1/file5')
 
844
        tree.commit('add file5')
 
845
        child_tree = tree.bzrdir.sprout('child').open_workingtree()
 
846
        self.build_tree_contents([('child/file2', 'hello')])
 
847
        child_tree.commit(message='branch 1')
 
848
        tree.merge_from_branch(child_tree.branch)
 
849
        tree.commit(message='merge child branch')
 
850
        os.chdir('parent')
 
851
 
 
852
    def test_log_files(self):
 
853
        """The log for multiple file should only list revs for those files"""
 
854
        self.prepare_tree()
 
855
        self.assertLogRevnos(['file1', 'file2', 'dir1/dir2/file3'],
 
856
                             ['6', '5.1.1', '3', '2', '1'])
 
857
 
 
858
    def test_log_directory(self):
 
859
        """The log for a directory should show all nested files."""
 
860
        self.prepare_tree()
 
861
        self.assertLogRevnos(['dir1'], ['5', '3'])
 
862
 
 
863
    def test_log_nested_directory(self):
 
864
        """The log for a directory should show all nested files."""
 
865
        self.prepare_tree()
 
866
        self.assertLogRevnos(['dir1/dir2'], ['3'])
 
867
 
 
868
    def test_log_in_nested_directory(self):
 
869
        """The log for a directory should show all nested files."""
 
870
        self.prepare_tree()
 
871
        os.chdir("dir1")
 
872
        self.assertLogRevnos(['.'], ['5', '3'])
 
873
 
 
874
    def test_log_files_and_directories(self):
 
875
        """Logging files and directories together should be fine."""
 
876
        self.prepare_tree()
 
877
        self.assertLogRevnos(['file4', 'dir1/dir2'], ['4', '3'])
 
878
 
 
879
    def test_log_files_and_dirs_in_nested_directory(self):
 
880
        """The log for a directory should show all nested files."""
 
881
        self.prepare_tree()
 
882
        os.chdir("dir1")
 
883
        self.assertLogRevnos(['dir2', 'file5'], ['5', '3'])