~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

Major code cleanup.

Show diffs side-by-side

added added

removed removed

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