/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

merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
 
# -*- coding: utf-8 -*-
 
1
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
3
2
#
4
3
# This program is free software; you can redistribute it and/or modify
5
4
# it under the terms of the GNU General Public License as published by
13
12
#
14
13
# You should have received a copy of the GNU General Public License
15
14
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
16
 
18
17
 
19
18
"""Black-box tests for bzr log."""
20
19
 
21
20
import os
22
 
 
23
 
import bzrlib
24
 
from bzrlib.tests.blackbox import ExternalBase
25
 
from bzrlib.tests import TestCaseInTempDir, TestCaseWithTransport
26
 
 
27
 
 
28
 
class TestLog(ExternalBase):
29
 
 
30
 
    def _prepare(self, format=None):
31
 
        if format:
32
 
            self.run_bzr(["init", "--format="+format])
33
 
        else:
34
 
            self.run_bzr("init")
35
 
        self.build_tree(['hello.txt', 'goodbye.txt', 'meep.txt'])
36
 
        self.run_bzr("add hello.txt")
37
 
        self.run_bzr("commit -m message1 hello.txt")
38
 
        self.run_bzr("add goodbye.txt")
39
 
        self.run_bzr("commit -m message2 goodbye.txt")
40
 
        self.run_bzr("add meep.txt")
41
 
        self.run_bzr("commit -m message3 meep.txt")
42
 
        self.full_log = self.run_bzr("log")[0]
 
21
import re
 
22
 
 
23
from bzrlib import (
 
24
    osutils,
 
25
    tests,
 
26
    )
 
27
from bzrlib.tests import test_log
 
28
 
 
29
 
 
30
class TestLog(tests.TestCaseWithTransport):
 
31
 
 
32
    def setUp(self):
 
33
        super(TestLog, self).setUp()
 
34
        self.timezone = 0 # UTC
 
35
        self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
 
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
    def assertRevnos(self, log, must_have=(), must_not_have=()):
 
67
        """Check if revnos are in or not in the log output"""
 
68
        for revno in must_have:
 
69
            self.assertTrue(('revno: %s\n' % revno) in log,
 
70
                'Does not contain expected revno %s' % revno)
 
71
        for revno in must_not_have:
 
72
            self.assertFalse(('revno: %s\n' % revno) in log,
 
73
                'Contains unexpected revno %s' % revno)
 
74
 
 
75
    def commit_options(self):
 
76
        """Use some mostly fixed values for commits to simplify tests.
 
77
 
 
78
        Tests can use this function to get some commit attributes. The time
 
79
        stamp is incremented at each commit.
 
80
        """
 
81
        self.timestamp += 1 # 1 second between each commit
 
82
        return dict(committer='Lorem Ipsum <joe@foo.com>',
 
83
                 timezone=self.timezone,
 
84
                 timestamp=self.timestamp,
 
85
                 )
 
86
 
 
87
    def check_log(self, expected, args, working_dir='level0'):
 
88
        out, err = self.run_bzr(['log', '--timezone', 'utc'] + args,
 
89
                                working_dir=working_dir)
 
90
        self.assertEqual('', err)
 
91
        self.assertEqualDiff(expected, test_log.normalize_log(out))
 
92
 
 
93
 
 
94
class TestLogRevSpecs(TestLog):
43
95
 
44
96
    def test_log_null_end_revspec(self):
45
 
        self._prepare()
46
 
        self.assertTrue('revno: 1\n' in self.full_log)
47
 
        self.assertTrue('revno: 2\n' in self.full_log)
48
 
        self.assertTrue('revno: 3\n' in self.full_log)
49
 
        self.assertTrue('message:\n  message1\n' in self.full_log)
50
 
        self.assertTrue('message:\n  message2\n' in self.full_log)
51
 
        self.assertTrue('message:\n  message3\n' in self.full_log)
 
97
        self.make_linear_branch()
 
98
        log = self.run_bzr(['log'])[0]
 
99
        self.assertTrue('revno: 1\n' in log)
 
100
        self.assertTrue('revno: 2\n' in log)
 
101
        self.assertTrue('revno: 3\n' in log)
 
102
        self.assertTrue('message:\n  message1\n' in log)
 
103
        self.assertTrue('message:\n  message2\n' in log)
 
104
        self.assertTrue('message:\n  message3\n' in log)
52
105
 
 
106
        full_log = self.run_bzr(['log'])[0]
53
107
        log = self.run_bzr("log -r 1..")[0]
54
 
        self.assertEqualDiff(log, self.full_log)
 
108
        self.assertEqualDiff(log, full_log)
55
109
 
56
110
    def test_log_null_begin_revspec(self):
57
 
        self._prepare()
 
111
        self.make_linear_branch()
 
112
        full_log = self.run_bzr(['log'])[0]
58
113
        log = self.run_bzr("log -r ..3")[0]
59
 
        self.assertEqualDiff(self.full_log, log)
 
114
        self.assertEqualDiff(full_log, log)
60
115
 
61
116
    def test_log_null_both_revspecs(self):
62
 
        self._prepare()
 
117
        self.make_linear_branch()
 
118
        full_log = self.run_bzr(['log'])[0]
63
119
        log = self.run_bzr("log -r ..")[0]
64
 
        self.assertEquals(self.full_log, log)
65
 
        self.assertEqualDiff(self.full_log, log)
 
120
        self.assertEqualDiff(full_log, log)
 
121
 
 
122
    def test_log_zero_revspec(self):
 
123
        self.make_minimal_branch()
 
124
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
125
                           ['log', '-r0'])
 
126
 
 
127
    def test_log_zero_begin_revspec(self):
 
128
        self.make_linear_branch()
 
129
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
130
                           ['log', '-r0..2'])
 
131
 
 
132
    def test_log_zero_end_revspec(self):
 
133
        self.make_linear_branch()
 
134
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
135
                           ['log', '-r-2..0'])
66
136
 
67
137
    def test_log_negative_begin_revspec_full_log(self):
68
 
        self._prepare()
 
138
        self.make_linear_branch()
 
139
        full_log = self.run_bzr(['log'])[0]
69
140
        log = self.run_bzr("log -r -3..")[0]
70
 
        self.assertEqualDiff(self.full_log, log)
 
141
        self.assertEqualDiff(full_log, log)
71
142
 
72
143
    def test_log_negative_both_revspec_full_log(self):
73
 
        self._prepare()
 
144
        self.make_linear_branch()
 
145
        full_log = self.run_bzr(['log'])[0]
74
146
        log = self.run_bzr("log -r -3..-1")[0]
75
 
        self.assertEqualDiff(self.full_log, log)
 
147
        self.assertEqualDiff(full_log, log)
76
148
 
77
149
    def test_log_negative_both_revspec_partial(self):
78
 
        self._prepare()
 
150
        self.make_linear_branch()
79
151
        log = self.run_bzr("log -r -3..-2")[0]
80
152
        self.assertTrue('revno: 1\n' in log)
81
153
        self.assertTrue('revno: 2\n' in log)
82
154
        self.assertTrue('revno: 3\n' not in log)
83
155
 
84
156
    def test_log_negative_begin_revspec(self):
85
 
        self._prepare()
 
157
        self.make_linear_branch()
86
158
        log = self.run_bzr("log -r -2..")[0]
87
159
        self.assertTrue('revno: 1\n' not in log)
88
160
        self.assertTrue('revno: 2\n' in log)
89
161
        self.assertTrue('revno: 3\n' in log)
90
162
 
91
 
    def test_log_postive_revspecs(self):
92
 
        self._prepare()
 
163
    def test_log_positive_revspecs(self):
 
164
        self.make_linear_branch()
 
165
        full_log = self.run_bzr(['log'])[0]
93
166
        log = self.run_bzr("log -r 1..3")[0]
94
 
        self.assertEqualDiff(self.full_log, log)
 
167
        self.assertEqualDiff(full_log, log)
 
168
 
 
169
    def test_log_dotted_revspecs(self):
 
170
        self.make_merged_branch()
 
171
        log = self.run_bzr("log -n0 -r 1..1.1.1")[0]
 
172
        self.assertRevnos(log, (1, '1.1.1'), (2, 3, '1.1.2', 4))
95
173
 
96
174
    def test_log_reversed_revspecs(self):
97
 
        self._prepare()
 
175
        self.make_linear_branch()
98
176
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
99
177
                            'the end revision.\n',),
100
178
                           ['log', '-r3..1'])
101
179
 
 
180
    def test_log_reversed_dotted_revspecs(self):
 
181
        self.make_merged_branch()
 
182
        self.run_bzr_error(('bzr: ERROR: Start revision not found in '
 
183
                            'left-hand history of end revision.\n',),
 
184
                           "log -r 1.1.1..1")
 
185
 
102
186
    def test_log_revno_n_path(self):
103
 
        os.mkdir('branch1')
104
 
        os.chdir('branch1')
105
 
        self._prepare()
106
 
        os.chdir('..')
107
 
        os.mkdir('branch2')
108
 
        os.chdir('branch2')
109
 
        self._prepare()
110
 
        os.chdir('..')
111
 
        log = self.run_bzr("log -r revno:2:branch1..revno:3:branch2",
112
 
                          retcode=3)[0]
 
187
        self.make_linear_branch('branch1')
 
188
        self.make_linear_branch('branch2')
 
189
        # Swapped revisions
 
190
        self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)[0]
 
191
        # Correct order
113
192
        log = self.run_bzr("log -r revno:1:branch2..revno:3:branch2")[0]
114
 
        self.assertEqualDiff(self.full_log, log)
 
193
        full_log = self.run_bzr(['log'], working_dir='branch2')[0]
 
194
        self.assertEqualDiff(full_log, log)
115
195
        log = self.run_bzr("log -r revno:1:branch2")[0]
116
196
        self.assertTrue('revno: 1\n' in log)
117
197
        self.assertTrue('revno: 2\n' not in log)
118
198
        self.assertTrue('branch nick: branch2\n' in log)
119
199
        self.assertTrue('branch nick: branch1\n' not in log)
120
 
        
 
200
 
 
201
    def test_log_nonexistent_revno(self):
 
202
        self.make_minimal_branch()
 
203
        (out, err) = self.run_bzr_error(
 
204
            ["bzr: ERROR: Requested revision: '1234' "
 
205
             "does not exist in branch:"],
 
206
            ['log', '-r1234'])
 
207
 
 
208
    def test_log_nonexistent_dotted_revno(self):
 
209
        self.make_minimal_branch()
 
210
        (out, err) = self.run_bzr_error(
 
211
            ["bzr: ERROR: Requested revision: '123.123' "
 
212
             "does not exist in branch:"],
 
213
            ['log',  '-r123.123'])
 
214
 
 
215
    def test_log_change_revno(self):
 
216
        self.make_linear_branch()
 
217
        expected_log = self.run_bzr("log -r 1")[0]
 
218
        log = self.run_bzr("log -c 1")[0]
 
219
        self.assertEqualDiff(expected_log, log)
 
220
 
 
221
    def test_log_change_nonexistent_revno(self):
 
222
        self.make_minimal_branch()
 
223
        (out, err) = self.run_bzr_error(
 
224
            ["bzr: ERROR: Requested revision: '1234' "
 
225
             "does not exist in branch:"],
 
226
            ['log',  '-c1234'])
 
227
 
 
228
    def test_log_change_nonexistent_dotted_revno(self):
 
229
        self.make_minimal_branch()
 
230
        (out, err) = self.run_bzr_error(
 
231
            ["bzr: ERROR: Requested revision: '123.123' "
 
232
             "does not exist in branch:"],
 
233
            ['log', '-c123.123'])
 
234
 
 
235
    def test_log_change_single_revno_only(self):
 
236
        self.make_minimal_branch()
 
237
        self.run_bzr_error(['bzr: ERROR: Option --change does not'
 
238
                           ' accept revision ranges'],
 
239
                           ['log', '--change', '2..3'])
 
240
 
 
241
    def test_log_change_incompatible_with_revision(self):
 
242
        self.run_bzr_error(['bzr: ERROR: --revision and --change'
 
243
                           ' are mutually exclusive'],
 
244
                           ['log', '--change', '2', '--revision', '3'])
 
245
 
121
246
    def test_log_nonexistent_file(self):
 
247
        self.make_minimal_branch()
122
248
        # files that don't exist in either the basis tree or working tree
123
249
        # should give an error
124
 
        wt = self.make_branch_and_tree('.')
125
250
        out, err = self.run_bzr('log does-not-exist', retcode=3)
126
 
        self.assertContainsRe(
127
 
            err, 'Path does not have any revision history: does-not-exist')
 
251
        self.assertContainsRe(err,
 
252
                              'Path unknown at end or start of revision range: '
 
253
                              'does-not-exist')
128
254
 
129
255
    def test_log_with_tags(self):
130
 
        self._prepare(format='dirstate-tags')
131
 
        self.run_bzr('tag -r1 tag1')
132
 
        self.run_bzr('tag -r1 tag1.1')
133
 
        self.run_bzr('tag tag3')
134
 
        
 
256
        tree = self.make_linear_branch(format='dirstate-tags')
 
257
        branch = tree.branch
 
258
        branch.tags.set_tag('tag1', branch.get_rev_id(1))
 
259
        branch.tags.set_tag('tag1.1', branch.get_rev_id(1))
 
260
        branch.tags.set_tag('tag3', branch.last_revision())
 
261
 
135
262
        log = self.run_bzr("log -r-1")[0]
136
263
        self.assertTrue('tags: tag3' in log)
137
264
 
141
268
        self.assertContainsRe(log, r'tags: (tag1, tag1\.1|tag1\.1, tag1)')
142
269
 
143
270
    def test_merged_log_with_tags(self):
144
 
        os.mkdir('branch1')
145
 
        os.chdir('branch1')
146
 
        self._prepare(format='dirstate-tags')
147
 
        os.chdir('..')
148
 
        self.run_bzr('branch branch1 branch2')
149
 
        os.chdir('branch1')
150
 
        self.run_bzr('commit -m foobar --unchanged')
151
 
        self.run_bzr('tag tag1')
152
 
        os.chdir('../branch2')
153
 
        self.run_bzr('merge ../branch1')
154
 
        self.run_bzr(['commit', '-m', 'merge branch 1'])
155
 
        log = self.run_bzr("log -r-1")[0]
 
271
        branch1_tree = self.make_linear_branch('branch1',
 
272
                                               format='dirstate-tags')
 
273
        branch1 = branch1_tree.branch
 
274
        branch2_tree = branch1_tree.bzrdir.sprout('branch2').open_workingtree()
 
275
        branch1_tree.commit(message='foobar', allow_pointless=True)
 
276
        branch1.tags.set_tag('tag1', branch1.last_revision())
 
277
        # tags don't propagate if we don't merge
 
278
        self.run_bzr('merge ../branch1', working_dir='branch2')
 
279
        branch2_tree.commit(message='merge branch 1')
 
280
        log = self.run_bzr("log -n0 -r-1", working_dir='branch2')[0]
156
281
        self.assertContainsRe(log, r'    tags: tag1')
157
 
        log = self.run_bzr("log -r3.1.1")[0]
 
282
        log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
158
283
        self.assertContainsRe(log, r'tags: tag1')
159
284
 
160
285
    def test_log_limit(self):
161
 
        self._prepare()
 
286
        tree = self.make_branch_and_tree('.')
 
287
        # We want more commits than our batch size starts at
 
288
        for pos in range(10):
 
289
            tree.commit("%s" % pos)
162
290
        log = self.run_bzr("log --limit 2")[0]
163
 
        self.assertTrue('revno: 1\n' not in log)
164
 
        self.assertTrue('revno: 2\n' in log)
165
 
        self.assertTrue('revno: 3\n' in log)
166
 
 
167
 
class TestLogMerges(ExternalBase):
168
 
 
169
 
    def _prepare(self):
170
 
        self.build_tree(['parent/'])
171
 
        self.run_bzr('init parent')
172
 
        self.run_bzr(['commit', '-m', 'first post', '--unchanged', 'parent'])
173
 
        self.run_bzr('branch parent child')
174
 
        self.run_bzr(['commit', '-m', 'branch 1', '--unchanged', 'child'])
175
 
        self.run_bzr('branch child smallerchild')
176
 
        self.run_bzr(['commit', '-m', 'branch 2', '--unchanged',
177
 
                      'smallerchild'])
178
 
        os.chdir('child')
179
 
        self.run_bzr('merge ../smallerchild')
180
 
        self.run_bzr(['commit', '-m', 'merge branch 2'])
181
 
        os.chdir('../parent')
182
 
        self.run_bzr('merge ../child')
183
 
        self.run_bzr(['commit', '-m', 'merge branch 1'])
 
291
        self.assertNotContainsRe(log, r'revno: 1\n')
 
292
        self.assertNotContainsRe(log, r'revno: 2\n')
 
293
        self.assertNotContainsRe(log, r'revno: 3\n')
 
294
        self.assertNotContainsRe(log, r'revno: 4\n')
 
295
        self.assertNotContainsRe(log, r'revno: 5\n')
 
296
        self.assertNotContainsRe(log, r'revno: 6\n')
 
297
        self.assertNotContainsRe(log, r'revno: 7\n')
 
298
        self.assertNotContainsRe(log, r'revno: 8\n')
 
299
        self.assertContainsRe(log, r'revno: 9\n')
 
300
        self.assertContainsRe(log, r'revno: 10\n')
 
301
 
 
302
    def test_log_limit_short(self):
 
303
        self.make_linear_branch()
 
304
        log = self.run_bzr("log -l 2")[0]
 
305
        self.assertNotContainsRe(log, r'revno: 1\n')
 
306
        self.assertContainsRe(log, r'revno: 2\n')
 
307
        self.assertContainsRe(log, r'revno: 3\n')
 
308
 
 
309
    def test_log_bad_message_re(self):
 
310
        """Bad --message argument gives a sensible message
 
311
        
 
312
        See https://bugs.launchpad.net/bzr/+bug/251352
 
313
        """
 
314
        self.make_minimal_branch()
 
315
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
 
316
        self.assertEqual("bzr: ERROR: Invalid regular expression"
 
317
            " in log message filter"
 
318
            ": '*'"
 
319
            ": nothing to repeat\n", err)
 
320
        self.assertEqual('', out)
 
321
 
 
322
 
 
323
class TestLogTimeZone(TestLog):
 
324
 
 
325
    def test_log_unsupported_timezone(self):
 
326
        self.make_linear_branch()
 
327
        self.run_bzr_error(['bzr: ERROR: Unsupported timezone format "foo", '
 
328
                            'options are "utc", "original", "local".'],
 
329
                           ['log', '--timezone', 'foo'])
 
330
 
 
331
 
 
332
class TestLogVerbose(TestLog):
 
333
 
 
334
    def setUp(self):
 
335
        super(TestLogVerbose, self).setUp()
 
336
        self.make_minimal_branch()
 
337
 
 
338
    def assertUseShortDeltaFormat(self, cmd):
 
339
        log = self.run_bzr(cmd)[0]
 
340
        # Check that we use the short status format
 
341
        self.assertContainsRe(log, '(?m)^\s*A  hello.txt$')
 
342
        self.assertNotContainsRe(log, '(?m)^\s*added:$')
 
343
 
 
344
    def assertUseLongDeltaFormat(self, cmd):
 
345
        log = self.run_bzr(cmd)[0]
 
346
        # Check that we use the long status format
 
347
        self.assertNotContainsRe(log, '(?m)^\s*A  hello.txt$')
 
348
        self.assertContainsRe(log, '(?m)^\s*added:$')
 
349
 
 
350
    def test_log_short_verbose(self):
 
351
        self.assertUseShortDeltaFormat(['log', '--short', '-v'])
 
352
 
 
353
    def test_log_short_verbose_verbose(self):
 
354
        self.assertUseLongDeltaFormat(['log', '--short', '-vv'])
 
355
 
 
356
    def test_log_long_verbose(self):
 
357
        # Check that we use the long status format, ignoring the verbosity
 
358
        # level
 
359
        self.assertUseLongDeltaFormat(['log', '--long', '-v'])
 
360
 
 
361
    def test_log_long_verbose_verbose(self):
 
362
        # Check that we use the long status format, ignoring the verbosity
 
363
        # level
 
364
        self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
 
365
 
 
366
 
 
367
class TestLogMerges(TestLog):
 
368
 
 
369
    def setUp(self):
 
370
        super(TestLogMerges, self).setUp()
 
371
        self.make_branches_with_merges()
 
372
 
 
373
    def make_branches_with_merges(self):
 
374
        level0 = self.make_branch_and_tree('level0')
 
375
        level0.commit(message='in branch level0', **self.commit_options())
 
376
 
 
377
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
 
378
        level1.commit(message='in branch level1', **self.commit_options())
 
379
 
 
380
        level2 = level1.bzrdir.sprout('level2').open_workingtree()
 
381
        level2.commit(message='in branch level2', **self.commit_options())
 
382
 
 
383
        level1.merge_from_branch(level2.branch)
 
384
        level1.commit(message='merge branch level2', **self.commit_options())
 
385
 
 
386
        level0.merge_from_branch(level1.branch)
 
387
        level0.commit(message='merge branch level1', **self.commit_options())
184
388
 
185
389
    def test_merges_are_indented_by_level(self):
186
 
        self._prepare()
187
 
        out,err = self.run_bzr('log')
188
 
        # the log will look something like:
189
 
#        self.assertEqual("""\
190
 
#------------------------------------------------------------
191
 
#revno: 2
192
 
#committer: Robert Collins <foo@example.com>
193
 
#branch nick: parent
194
 
#timestamp: Tue 2006-03-28 22:31:40 +1100
195
 
#message:
196
 
#  merge branch 1
197
 
#    ------------------------------------------------------------
198
 
#    revno: 1.1.2  
199
 
#    committer: Robert Collins <foo@example.com>
200
 
#    branch nick: child
201
 
#    timestamp: Tue 2006-03-28 22:31:40 +1100
202
 
#    message:
203
 
#      merge branch 2
204
 
#        ------------------------------------------------------------
205
 
#        revno: 1.1.1.1
206
 
#        committer: Robert Collins <foo@example.com>
207
 
#        branch nick: smallerchild
208
 
#        timestamp: Tue 2006-03-28 22:31:40 +1100
209
 
#        message:
210
 
#          branch 2
211
 
#    ------------------------------------------------------------
212
 
#    revno: 1.1.1
213
 
#    committer: Robert Collins <foo@example.com>
214
 
#    branch nick: child
215
 
#    timestamp: Tue 2006-03-28 22:31:40 +1100
216
 
#    message:
217
 
#      branch 1
218
 
#------------------------------------------------------------
219
 
#revno: 1
220
 
#committer: Robert Collins <foo@example.com>
221
 
#branch nick: parent
222
 
#timestamp: Tue 2006-03-28 22:31:39 +1100
223
 
#message:
224
 
#  first post
225
 
#""", out)
226
 
        # but we dont have a nice pattern matcher hooked up yet, so:
227
 
        # we check for the indenting of the commit message and the 
228
 
        # revision numbers 
229
 
        self.assertTrue('revno: 2' in out)
230
 
        self.assertTrue('  merge branch 1' in out)
231
 
        self.assertTrue('    revno: 1.1.2' in out)
232
 
        self.assertTrue('      merge branch 2' in out)
233
 
        self.assertTrue('        revno: 1.1.1.1' in out)
234
 
        self.assertTrue('          branch 2' in out)
235
 
        self.assertTrue('    revno: 1.1.1' in out)
236
 
        self.assertTrue('      branch 1' in out)
237
 
        self.assertTrue('revno: 1\n' in out)
238
 
        self.assertTrue('  first post' in out)
239
 
        self.assertEqual('', err)
 
390
        expected = """\
 
391
------------------------------------------------------------
 
392
revno: 2 [merge]
 
393
committer: Lorem Ipsum <test@example.com>
 
394
branch nick: level0
 
395
timestamp: Just now
 
396
message:
 
397
  merge branch level1
 
398
    ------------------------------------------------------------
 
399
    revno: 1.1.2 [merge]
 
400
    committer: Lorem Ipsum <test@example.com>
 
401
    branch nick: level1
 
402
    timestamp: Just now
 
403
    message:
 
404
      merge branch level2
 
405
        ------------------------------------------------------------
 
406
        revno: 1.2.1
 
407
        committer: Lorem Ipsum <test@example.com>
 
408
        branch nick: level2
 
409
        timestamp: Just now
 
410
        message:
 
411
          in branch level2
 
412
    ------------------------------------------------------------
 
413
    revno: 1.1.1
 
414
    committer: Lorem Ipsum <test@example.com>
 
415
    branch nick: level1
 
416
    timestamp: Just now
 
417
    message:
 
418
      in branch level1
 
419
------------------------------------------------------------
 
420
revno: 1
 
421
committer: Lorem Ipsum <test@example.com>
 
422
branch nick: level0
 
423
timestamp: Just now
 
424
message:
 
425
  in branch level0
 
426
"""
 
427
        self.check_log(expected, ['-n0'])
 
428
 
 
429
    def test_force_merge_revisions_off(self):
 
430
        expected = """\
 
431
------------------------------------------------------------
 
432
revno: 2 [merge]
 
433
committer: Lorem Ipsum <test@example.com>
 
434
branch nick: level0
 
435
timestamp: Just now
 
436
message:
 
437
  merge branch level1
 
438
------------------------------------------------------------
 
439
revno: 1
 
440
committer: Lorem Ipsum <test@example.com>
 
441
branch nick: level0
 
442
timestamp: Just now
 
443
message:
 
444
  in branch level0
 
445
"""
 
446
        self.check_log(expected, ['--long', '-n1'])
 
447
 
 
448
    def test_force_merge_revisions_on(self):
 
449
        expected = """\
 
450
    2 Lorem Ipsum\t2005-11-22 [merge]
 
451
      merge branch level1
 
452
 
 
453
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
454
                merge branch level2
 
455
 
 
456
              1.2.1 Lorem Ipsum\t2005-11-22
 
457
                    in branch level2
 
458
 
 
459
          1.1.1 Lorem Ipsum\t2005-11-22
 
460
                in branch level1
 
461
 
 
462
    1 Lorem Ipsum\t2005-11-22
 
463
      in branch level0
 
464
 
 
465
"""
 
466
        self.check_log(expected, ['--short', '-n0'])
 
467
 
 
468
    def test_include_merges(self):
 
469
        # Confirm --include-merges gives the same output as -n0
 
470
        out_im, err_im = self.run_bzr('log --include-merges',
 
471
                                      working_dir='level0')
 
472
        out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
 
473
        self.assertEqual('', err_im)
 
474
        self.assertEqual('', err_n0)
 
475
        self.assertEqual(out_im, out_n0)
 
476
 
 
477
    def test_force_merge_revisions_N(self):
 
478
        expected = """\
 
479
    2 Lorem Ipsum\t2005-11-22 [merge]
 
480
      merge branch level1
 
481
 
 
482
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
483
                merge branch level2
 
484
 
 
485
          1.1.1 Lorem Ipsum\t2005-11-22
 
486
                in branch level1
 
487
 
 
488
    1 Lorem Ipsum\t2005-11-22
 
489
      in branch level0
 
490
 
 
491
"""
 
492
        self.check_log(expected, ['--short', '-n2'])
240
493
 
241
494
    def test_merges_single_merge_rev(self):
242
 
        self._prepare()
243
 
        out,err = self.run_bzr('log -r1.1.2')
244
 
        # the log will look something like:
245
 
#        self.assertEqual("""\
246
 
#------------------------------------------------------------
247
 
#revno: 1.1.2  
248
 
#committer: Robert Collins <foo@example.com>
249
 
#branch nick: child
250
 
#timestamp: Tue 2006-03-28 22:31:40 +1100
251
 
#message:
252
 
#  merge branch 2
253
 
#    ------------------------------------------------------------
254
 
#    revno: 1.1.1.1
255
 
#    committer: Robert Collins <foo@example.com>
256
 
#    branch nick: smallerchild
257
 
#    timestamp: Tue 2006-03-28 22:31:40 +1100
258
 
#    message:
259
 
#      branch 2
260
 
#""", out)
261
 
        # but we dont have a nice pattern matcher hooked up yet, so:
262
 
        # we check for the indenting of the commit message and the 
263
 
        # revision numbers 
264
 
        self.assertTrue('revno: 2' not in out)
265
 
        self.assertTrue('  merge branch 1' not in out)
266
 
        self.assertTrue('revno: 1.1.2' in out)
267
 
        self.assertTrue('  merge branch 2' in out)
268
 
        self.assertTrue('    revno: 1.1.1.1' in out)
269
 
        self.assertTrue('      branch 2' in out)
270
 
        self.assertTrue('revno: 1.1.1\n' not in out)
271
 
        self.assertTrue('  branch 1' not in out)
272
 
        self.assertTrue('revno: 1\n' not in out)
273
 
        self.assertTrue('  first post' not in out)
274
 
        self.assertEqual('', err)
 
495
        expected = """\
 
496
------------------------------------------------------------
 
497
revno: 1.1.2 [merge]
 
498
committer: Lorem Ipsum <test@example.com>
 
499
branch nick: level1
 
500
timestamp: Just now
 
501
message:
 
502
  merge branch level2
 
503
    ------------------------------------------------------------
 
504
    revno: 1.2.1
 
505
    committer: Lorem Ipsum <test@example.com>
 
506
    branch nick: level2
 
507
    timestamp: Just now
 
508
    message:
 
509
      in branch level2
 
510
"""
 
511
        self.check_log(expected, ['-n0', '-r1.1.2'])
275
512
 
276
513
    def test_merges_partial_range(self):
277
 
        self._prepare()
278
 
        out,err = self.run_bzr('log -r1.1.1..1.1.2')
279
 
        # the log will look something like:
280
 
#        self.assertEqual("""\
281
 
#------------------------------------------------------------
282
 
#revno: 1.1.2  
283
 
#committer: Robert Collins <foo@example.com>
284
 
#branch nick: child
285
 
#timestamp: Tue 2006-03-28 22:31:40 +1100
286
 
#message:
287
 
#  merge branch 2
288
 
#    ------------------------------------------------------------
289
 
#    revno: 1.1.1.1
290
 
#    committer: Robert Collins <foo@example.com>
291
 
#    branch nick: smallerchild
292
 
#    timestamp: Tue 2006-03-28 22:31:40 +1100
293
 
#    message:
294
 
#      branch 2
295
 
#------------------------------------------------------------
296
 
#revno: 1.1.1
297
 
#committer: Robert Collins <foo@example.com>
298
 
#branch nick: child
299
 
#timestamp: Tue 2006-03-28 22:31:40 +1100
300
 
#message:
301
 
#  branch 1
302
 
#""", out)
303
 
        # but we dont have a nice pattern matcher hooked up yet, so:
304
 
        # we check for the indenting of the commit message and the 
305
 
        # revision numbers 
306
 
        self.assertTrue('revno: 2' not in out)
307
 
        self.assertTrue('  merge branch 1' not in out)
308
 
        self.assertTrue('revno: 1.1.2' in out)
309
 
        self.assertTrue('  merge branch 2' in out)
310
 
        self.assertTrue('    revno: 1.1.1.1' in out)
311
 
        self.assertTrue('      branch 2' in out)
312
 
        self.assertTrue('revno: 1.1.1' in out)
313
 
        self.assertTrue('  branch 1' in out)
314
 
        self.assertTrue('revno: 1\n' not in out)
315
 
        self.assertTrue('  first post' not in out)
316
 
        self.assertEqual('', err)
317
 
 
318
 
 
319
 
class TestLogEncodings(TestCaseInTempDir):
 
514
        expected = """\
 
515
------------------------------------------------------------
 
516
revno: 1.1.2 [merge]
 
517
committer: Lorem Ipsum <test@example.com>
 
518
branch nick: level1
 
519
timestamp: Just now
 
520
message:
 
521
  merge branch level2
 
522
    ------------------------------------------------------------
 
523
    revno: 1.2.1
 
524
    committer: Lorem Ipsum <test@example.com>
 
525
    branch nick: level2
 
526
    timestamp: Just now
 
527
    message:
 
528
      in branch level2
 
529
------------------------------------------------------------
 
530
revno: 1.1.1
 
531
committer: Lorem Ipsum <test@example.com>
 
532
branch nick: level1
 
533
timestamp: Just now
 
534
message:
 
535
  in branch level1
 
536
"""
 
537
        self.check_log(expected, ['-n0', '-r1.1.1..1.1.2'])
 
538
 
 
539
 
 
540
class TestLogDiff(TestLog):
 
541
 
 
542
    def setUp(self):
 
543
        super(TestLogDiff, self).setUp()
 
544
        self.make_branch_with_diffs()
 
545
 
 
546
    def make_branch_with_diffs(self):
 
547
        level0 = self.make_branch_and_tree('level0')
 
548
        self.build_tree(['level0/file1', 'level0/file2'])
 
549
        level0.add('file1')
 
550
        level0.add('file2')
 
551
        level0.commit(message='in branch level0', **self.commit_options())
 
552
 
 
553
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
 
554
        self.build_tree_contents([('level1/file2', 'hello\n')])
 
555
        level1.commit(message='in branch level1', **self.commit_options())
 
556
        level0.merge_from_branch(level1.branch)
 
557
        level0.commit(message='merge branch level1', **self.commit_options())
 
558
 
 
559
    def test_log_show_diff_long_with_merges(self):
 
560
        out,err = self.run_bzr('log -p -n0')
 
561
        self.assertEqual('', err)
 
562
        log = test_log.normalize_log(out)
 
563
        expected = """\
 
564
------------------------------------------------------------
 
565
revno: 2 [merge]
 
566
committer: Lorem Ipsum <test@example.com>
 
567
branch nick: level0
 
568
timestamp: Just now
 
569
message:
 
570
  merge branch level1
 
571
diff:
 
572
=== modified file 'file2'
 
573
--- file2\t2005-11-22 00:00:01 +0000
 
574
+++ file2\t2005-11-22 00:00:02 +0000
 
575
@@ -1,1 +1,1 @@
 
576
-contents of level0/file2
 
577
+hello
 
578
    ------------------------------------------------------------
 
579
    revno: 1.1.1
 
580
    committer: Lorem Ipsum <test@example.com>
 
581
    branch nick: level1
 
582
    timestamp: Just now
 
583
    message:
 
584
      in branch level1
 
585
    diff:
 
586
    === modified file 'file2'
 
587
    --- file2\t2005-11-22 00:00:01 +0000
 
588
    +++ file2\t2005-11-22 00:00:02 +0000
 
589
    @@ -1,1 +1,1 @@
 
590
    -contents of level0/file2
 
591
    +hello
 
592
------------------------------------------------------------
 
593
revno: 1
 
594
committer: Lorem Ipsum <test@example.com>
 
595
branch nick: level0
 
596
timestamp: Just now
 
597
message:
 
598
  in branch level0
 
599
diff:
 
600
=== added file 'file1'
 
601
--- file1\t1970-01-01 00:00:00 +0000
 
602
+++ file1\t2005-11-22 00:00:01 +0000
 
603
@@ -0,0 +1,1 @@
 
604
+contents of level0/file1
 
605
 
 
606
=== added file 'file2'
 
607
--- file2\t1970-01-01 00:00:00 +0000
 
608
+++ file2\t2005-11-22 00:00:01 +0000
 
609
@@ -0,0 +1,1 @@
 
610
+contents of level0/file2
 
611
"""
 
612
        self.check_log(expected, ['-p', '-n0'])
 
613
 
 
614
    def test_log_show_diff_short(self):
 
615
        expected = """\
 
616
    2 Lorem Ipsum\t2005-11-22 [merge]
 
617
      merge branch level1
 
618
      === modified file 'file2'
 
619
      --- file2\t2005-11-22 00:00:01 +0000
 
620
      +++ file2\t2005-11-22 00:00:02 +0000
 
621
      @@ -1,1 +1,1 @@
 
622
      -contents of level0/file2
 
623
      +hello
 
624
 
 
625
    1 Lorem Ipsum\t2005-11-22
 
626
      in branch level0
 
627
      === added file 'file1'
 
628
      --- file1\t1970-01-01 00:00:00 +0000
 
629
      +++ file1\t2005-11-22 00:00:01 +0000
 
630
      @@ -0,0 +1,1 @@
 
631
      +contents of level0/file1
 
632
\x20\x20\x20\x20\x20\x20
 
633
      === added file 'file2'
 
634
      --- file2\t1970-01-01 00:00:00 +0000
 
635
      +++ file2\t2005-11-22 00:00:01 +0000
 
636
      @@ -0,0 +1,1 @@
 
637
      +contents of level0/file2
 
638
 
 
639
Use --include-merges or -n0 to see merged revisions.
 
640
"""
 
641
        self.check_log(expected, ['-p', '--short'])
 
642
 
 
643
    def test_log_show_diff_line(self):
 
644
        # Not supported by this formatter so expect plain output
 
645
        expected = """\
 
646
2: Lorem Ipsum 2005-11-22 [merge] merge branch level1
 
647
1: Lorem Ipsum 2005-11-22 in branch level0
 
648
"""
 
649
        self.check_log(expected, ['-p', '--line'])
 
650
 
 
651
    def test_log_show_diff_file1(self):
 
652
        """Only the diffs for the given file are to be shown"""
 
653
        expected = """\
 
654
    1 Lorem Ipsum\t2005-11-22
 
655
      in branch level0
 
656
      === added file 'file1'
 
657
      --- file1\t1970-01-01 00:00:00 +0000
 
658
      +++ file1\t2005-11-22 00:00:01 +0000
 
659
      @@ -0,0 +1,1 @@
 
660
      +contents of level0/file1
 
661
 
 
662
"""
 
663
        self.check_log(expected, ['-p', '--short', 'file1'])
 
664
 
 
665
    def test_log_show_diff_file2(self):
 
666
        """Only the diffs for the given file are to be shown"""
 
667
        expected = """\
 
668
    2 Lorem Ipsum\t2005-11-22 [merge]
 
669
      merge branch level1
 
670
      === modified file 'file2'
 
671
      --- file2\t2005-11-22 00:00:01 +0000
 
672
      +++ file2\t2005-11-22 00:00:02 +0000
 
673
      @@ -1,1 +1,1 @@
 
674
      -contents of level0/file2
 
675
      +hello
 
676
 
 
677
    1 Lorem Ipsum\t2005-11-22
 
678
      in branch level0
 
679
      === added file 'file2'
 
680
      --- file2\t1970-01-01 00:00:00 +0000
 
681
      +++ file2\t2005-11-22 00:00:01 +0000
 
682
      @@ -0,0 +1,1 @@
 
683
      +contents of level0/file2
 
684
 
 
685
Use --include-merges or -n0 to see merged revisions.
 
686
"""
 
687
        self.check_log(expected, ['-p', '--short', 'file2'])
 
688
 
 
689
 
 
690
class TestLogUnicodeDiff(TestLog):
 
691
 
 
692
    def test_log_show_diff_non_ascii(self):
 
693
        # Smoke test for bug #328007 UnicodeDecodeError on 'log -p'
 
694
        message = u'Message with \xb5'
 
695
        body = 'Body with \xb5\n'
 
696
        wt = self.make_branch_and_tree('.')
 
697
        self.build_tree_contents([('foo', body)])
 
698
        wt.add('foo')
 
699
        wt.commit(message=message)
 
700
        # check that command won't fail with unicode error
 
701
        # don't care about exact output because we have other tests for this
 
702
        out,err = self.run_bzr('log -p --long')
 
703
        self.assertNotEqual('', out)
 
704
        self.assertEqual('', err)
 
705
        out,err = self.run_bzr('log -p --short')
 
706
        self.assertNotEqual('', out)
 
707
        self.assertEqual('', err)
 
708
        out,err = self.run_bzr('log -p --line')
 
709
        self.assertNotEqual('', out)
 
710
        self.assertEqual('', err)
 
711
 
 
712
 
 
713
class TestLogEncodings(tests.TestCaseInTempDir):
320
714
 
321
715
    _mu = u'\xb5'
322
716
    _message = u'Message with \xb5'
327
721
        'latin-1',
328
722
        'iso-8859-1',
329
723
        'cp437', # Common windows encoding
330
 
        'cp1251', # Alexander Belchenko's windows encoding
 
724
        'cp1251', # Russian windows encoding
331
725
        'cp1258', # Common windows encoding
332
726
    ]
333
727
    # Encodings which cannot encode mu
338
732
    ]
339
733
 
340
734
    def setUp(self):
341
 
        TestCaseInTempDir.setUp(self)
342
 
        self.user_encoding = bzrlib.user_encoding
343
 
 
344
 
    def tearDown(self):
345
 
        bzrlib.user_encoding = self.user_encoding
346
 
        TestCaseInTempDir.tearDown(self)
 
735
        super(TestLogEncodings, self).setUp()
 
736
        self.user_encoding = osutils._cached_user_encoding
 
737
        def restore():
 
738
            osutils._cached_user_encoding = self.user_encoding
 
739
        self.addCleanup(restore)
347
740
 
348
741
    def create_branch(self):
349
742
        bzr = self.run_bzr
361
754
        else:
362
755
            encoded_msg = self._message.encode(encoding)
363
756
 
364
 
        old_encoding = bzrlib.user_encoding
 
757
        old_encoding = osutils._cached_user_encoding
365
758
        # This test requires that 'run_bzr' uses the current
366
759
        # bzrlib, because we override user_encoding, and expect
367
760
        # it to be used
368
761
        try:
369
 
            bzrlib.user_encoding = 'ascii'
 
762
            osutils._cached_user_encoding = 'ascii'
370
763
            # We should be able to handle any encoding
371
764
            out, err = bzr('log', encoding=encoding)
372
765
            if not fail:
377
770
            else:
378
771
                self.assertNotEqual(-1, out.find('Message with ?'))
379
772
        finally:
380
 
            bzrlib.user_encoding = old_encoding
 
773
            osutils._cached_user_encoding = old_encoding
381
774
 
382
775
    def test_log_handles_encoding(self):
383
776
        self.create_branch()
393
786
 
394
787
    def test_stdout_encoding(self):
395
788
        bzr = self.run_bzr
396
 
        bzrlib.user_encoding = "cp1251"
 
789
        osutils._cached_user_encoding = "cp1251"
397
790
 
398
791
        bzr('init')
399
792
        self.build_tree(['a'])
417
810
        self.assertEquals(-1, stdout.find(test_in_cp1251))
418
811
 
419
812
 
420
 
class TestLogFile(TestCaseWithTransport):
 
813
class TestLogFile(tests.TestCaseWithTransport):
421
814
 
422
815
    def test_log_local_branch_file(self):
423
816
        """We should be able to log files in local treeless branches"""
428
821
        tree.bzrdir.destroy_workingtree()
429
822
        self.run_bzr('log tree/file')
430
823
 
 
824
    def prepare_tree(self, complex=False):
 
825
        # The complex configuration includes deletes and renames
 
826
        tree = self.make_branch_and_tree('parent')
 
827
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
 
828
        tree.add('file1')
 
829
        tree.commit('add file1')
 
830
        tree.add('file2')
 
831
        tree.commit('add file2')
 
832
        tree.add('file3')
 
833
        tree.commit('add file3')
 
834
        child_tree = tree.bzrdir.sprout('child').open_workingtree()
 
835
        self.build_tree_contents([('child/file2', 'hello')])
 
836
        child_tree.commit(message='branch 1')
 
837
        tree.merge_from_branch(child_tree.branch)
 
838
        tree.commit(message='merge child branch')
 
839
        if complex:
 
840
            tree.remove('file2')
 
841
            tree.commit('remove file2')
 
842
            tree.rename_one('file3', 'file4')
 
843
            tree.commit('file3 is now called file4')
 
844
            tree.remove('file1')
 
845
            tree.commit('remove file1')
 
846
        os.chdir('parent')
 
847
 
431
848
    def test_log_file(self):
432
849
        """The log for a particular file should only list revs for that file"""
 
850
        self.prepare_tree()
 
851
        log = self.run_bzr('log -n0 file1')[0]
 
852
        self.assertContainsRe(log, 'revno: 1\n')
 
853
        self.assertNotContainsRe(log, 'revno: 2\n')
 
854
        self.assertNotContainsRe(log, 'revno: 3\n')
 
855
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
856
        self.assertNotContainsRe(log, 'revno: 4 ')
 
857
        log = self.run_bzr('log -n0 file2')[0]
 
858
        self.assertNotContainsRe(log, 'revno: 1\n')
 
859
        self.assertContainsRe(log, 'revno: 2\n')
 
860
        self.assertNotContainsRe(log, 'revno: 3\n')
 
861
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
862
        self.assertContainsRe(log, 'revno: 4 ')
 
863
        log = self.run_bzr('log -n0 file3')[0]
 
864
        self.assertNotContainsRe(log, 'revno: 1\n')
 
865
        self.assertNotContainsRe(log, 'revno: 2\n')
 
866
        self.assertContainsRe(log, 'revno: 3\n')
 
867
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
868
        self.assertNotContainsRe(log, 'revno: 4 ')
 
869
        log = self.run_bzr('log -n0 -r3.1.1 file2')[0]
 
870
        self.assertNotContainsRe(log, 'revno: 1\n')
 
871
        self.assertNotContainsRe(log, 'revno: 2\n')
 
872
        self.assertNotContainsRe(log, 'revno: 3\n')
 
873
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
874
        self.assertNotContainsRe(log, 'revno: 4 ')
 
875
        log = self.run_bzr('log -n0 -r4 file2')[0]
 
876
        self.assertNotContainsRe(log, 'revno: 1\n')
 
877
        self.assertNotContainsRe(log, 'revno: 2\n')
 
878
        self.assertNotContainsRe(log, 'revno: 3\n')
 
879
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
880
        self.assertContainsRe(log, 'revno: 4 ')
 
881
        log = self.run_bzr('log -n0 -r3.. file2')[0]
 
882
        self.assertNotContainsRe(log, 'revno: 1\n')
 
883
        self.assertNotContainsRe(log, 'revno: 2\n')
 
884
        self.assertNotContainsRe(log, 'revno: 3\n')
 
885
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
886
        self.assertContainsRe(log, 'revno: 4 ')
 
887
        log = self.run_bzr('log -n0 -r..3 file2')[0]
 
888
        self.assertNotContainsRe(log, 'revno: 1\n')
 
889
        self.assertContainsRe(log, 'revno: 2\n')
 
890
        self.assertNotContainsRe(log, 'revno: 3\n')
 
891
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
892
        self.assertNotContainsRe(log, 'revno: 4 ')
 
893
 
 
894
    def test_log_file_historical_missing(self):
 
895
        # Check logging a deleted file gives an error if the
 
896
        # file isn't found at the end or start of the revision range
 
897
        self.prepare_tree(complex=True)
 
898
        err_msg = "Path unknown at end or start of revision range: file2"
 
899
        err = self.run_bzr('log file2', retcode=3)[1]
 
900
        self.assertContainsRe(err, err_msg)
 
901
 
 
902
    def test_log_file_historical_end(self):
 
903
        # Check logging a deleted file is ok if the file existed
 
904
        # at the end the revision range
 
905
        self.prepare_tree(complex=True)
 
906
        log, err = self.run_bzr('log -n0 -r..4 file2')
 
907
        self.assertEquals('', err)
 
908
        self.assertNotContainsRe(log, 'revno: 1\n')
 
909
        self.assertContainsRe(log, 'revno: 2\n')
 
910
        self.assertNotContainsRe(log, 'revno: 3\n')
 
911
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
912
        self.assertContainsRe(log, 'revno: 4 ')
 
913
 
 
914
    def test_log_file_historical_start(self):
 
915
        # Check logging a deleted file is ok if the file existed
 
916
        # at the start of the revision range
 
917
        self.prepare_tree(complex=True)
 
918
        log, err = self.run_bzr('log file1')
 
919
        self.assertEquals('', err)
 
920
        self.assertContainsRe(log, 'revno: 1\n')
 
921
        self.assertNotContainsRe(log, 'revno: 2\n')
 
922
        self.assertNotContainsRe(log, 'revno: 3\n')
 
923
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
924
        self.assertNotContainsRe(log, 'revno: 4 ')
 
925
 
 
926
    def test_log_file_renamed(self):
 
927
        """File matched against revision range, not current tree."""
 
928
        self.prepare_tree(complex=True)
 
929
 
 
930
        # Check logging a renamed file gives an error by default
 
931
        err_msg = "Path unknown at end or start of revision range: file3"
 
932
        err = self.run_bzr('log file3', retcode=3)[1]
 
933
        self.assertContainsRe(err, err_msg)
 
934
 
 
935
        # Check we can see a renamed file if we give the right end revision
 
936
        log, err = self.run_bzr('log -r..4 file3')
 
937
        self.assertEquals('', err)
 
938
        self.assertNotContainsRe(log, 'revno: 1\n')
 
939
        self.assertNotContainsRe(log, 'revno: 2\n')
 
940
        self.assertContainsRe(log, 'revno: 3\n')
 
941
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
942
        self.assertNotContainsRe(log, 'revno: 4 ')
 
943
 
 
944
    def test_line_log_file(self):
 
945
        """The line log for a file should only list relevant mainline revs"""
 
946
        # Note: this also implicitly  covers the short logging case.
 
947
        # We test using --line in preference to --short because matching
 
948
        # revnos in the output of --line is more reliable.
 
949
        self.prepare_tree()
 
950
 
 
951
        # full history of file1
 
952
        log = self.run_bzr('log --line file1')[0]
 
953
        self.assertContainsRe(log, '^1:', re.MULTILINE)
 
954
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
955
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
956
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
957
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
958
 
 
959
        # full history of file2
 
960
        log = self.run_bzr('log --line file2')[0]
 
961
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
962
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
963
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
964
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
965
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
966
 
 
967
        # full history of file3
 
968
        log = self.run_bzr('log --line file3')[0]
 
969
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
970
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
971
        self.assertContainsRe(log, '^3:', re.MULTILINE)
 
972
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
973
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
974
 
 
975
        # file in a merge revision
 
976
        log = self.run_bzr('log --line -r3.1.1 file2')[0]
 
977
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
978
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
979
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
980
        self.assertContainsRe(log, '^3.1.1:', re.MULTILINE)
 
981
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
982
 
 
983
        # file in a mainline revision
 
984
        log = self.run_bzr('log --line -r4 file2')[0]
 
985
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
986
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
987
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
988
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
989
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
990
 
 
991
        # file since a revision
 
992
        log = self.run_bzr('log --line -r3.. file2')[0]
 
993
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
994
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
995
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
996
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
997
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
998
 
 
999
        # file up to a revision
 
1000
        log = self.run_bzr('log --line -r..3 file2')[0]
 
1001
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
1002
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
1003
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
1004
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
1005
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
1006
 
 
1007
 
 
1008
class TestLogMultiple(tests.TestCaseWithTransport):
 
1009
 
 
1010
    def prepare_tree(self):
433
1011
        tree = self.make_branch_and_tree('parent')
434
 
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
 
1012
        self.build_tree([
 
1013
            'parent/file1',
 
1014
            'parent/file2',
 
1015
            'parent/dir1/',
 
1016
            'parent/dir1/file5',
 
1017
            'parent/dir1/dir2/',
 
1018
            'parent/dir1/dir2/file3',
 
1019
            'parent/file4'])
435
1020
        tree.add('file1')
436
1021
        tree.commit('add file1')
437
1022
        tree.add('file2')
438
1023
        tree.commit('add file2')
439
 
        tree.add('file3')
 
1024
        tree.add(['dir1', 'dir1/dir2', 'dir1/dir2/file3'])
440
1025
        tree.commit('add file3')
441
 
        self.run_bzr('branch parent child')
442
 
        print >> file('child/file2', 'wb'), 'hello'
443
 
        self.run_bzr(['commit', '-m', 'branch 1', 'child'])
 
1026
        tree.add('file4')
 
1027
        tree.commit('add file4')
 
1028
        tree.add('dir1/file5')
 
1029
        tree.commit('add file5')
 
1030
        child_tree = tree.bzrdir.sprout('child').open_workingtree()
 
1031
        self.build_tree_contents([('child/file2', 'hello')])
 
1032
        child_tree.commit(message='branch 1')
 
1033
        tree.merge_from_branch(child_tree.branch)
 
1034
        tree.commit(message='merge child branch')
444
1035
        os.chdir('parent')
445
 
        self.run_bzr('merge ../child')
446
 
        self.run_bzr(['commit', '-m', 'merge child branch'])
447
 
        log = self.run_bzr('log file1')[0]
448
 
        self.assertContainsRe(log, 'revno: 1\n')
449
 
        self.assertNotContainsRe(log, 'revno: 2\n')
450
 
        self.assertNotContainsRe(log, 'revno: 3\n')
451
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
452
 
        self.assertNotContainsRe(log, 'revno: 4\n')
453
 
        log = self.run_bzr('log file2')[0]
454
 
        self.assertNotContainsRe(log, 'revno: 1\n')
455
 
        self.assertContainsRe(log, 'revno: 2\n')
456
 
        self.assertNotContainsRe(log, 'revno: 3\n')
457
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
458
 
        self.assertContainsRe(log, 'revno: 4\n')
459
 
        log = self.run_bzr('log file3')[0]
460
 
        self.assertNotContainsRe(log, 'revno: 1\n')
461
 
        self.assertNotContainsRe(log, 'revno: 2\n')
462
 
        self.assertContainsRe(log, 'revno: 3\n')
463
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
464
 
        self.assertNotContainsRe(log, 'revno: 4\n')
465
 
        log = self.run_bzr('log -r3.1.1 file2')[0]
466
 
        self.assertNotContainsRe(log, 'revno: 1\n')
467
 
        self.assertNotContainsRe(log, 'revno: 2\n')
468
 
        self.assertNotContainsRe(log, 'revno: 3\n')
469
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
470
 
        self.assertNotContainsRe(log, 'revno: 4\n')
471
 
        log = self.run_bzr('log -r4 file2')[0]
472
 
        self.assertNotContainsRe(log, 'revno: 1\n')
473
 
        self.assertNotContainsRe(log, 'revno: 2\n')
474
 
        self.assertNotContainsRe(log, 'revno: 3\n')
475
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
476
 
        self.assertContainsRe(log, 'revno: 4\n')
477
 
        log = self.run_bzr('log -r3.. file2')[0]
478
 
        self.assertNotContainsRe(log, 'revno: 1\n')
479
 
        self.assertNotContainsRe(log, 'revno: 2\n')
480
 
        self.assertNotContainsRe(log, 'revno: 3\n')
481
 
        self.assertContainsRe(log, 'revno: 3.1.1\n')
482
 
        self.assertContainsRe(log, 'revno: 4\n')
483
 
        log = self.run_bzr('log -r..3 file2')[0]
484
 
        self.assertNotContainsRe(log, 'revno: 1\n')
485
 
        self.assertContainsRe(log, 'revno: 2\n')
486
 
        self.assertNotContainsRe(log, 'revno: 3\n')
487
 
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
488
 
        self.assertNotContainsRe(log, 'revno: 4\n')
 
1036
 
 
1037
    def assertRevnos(self, paths_str, expected_revnos):
 
1038
        # confirm the revision numbers in log --line output are those expected
 
1039
        out, err = self.run_bzr('log --line -n0 %s' % (paths_str,))
 
1040
        self.assertEqual('', err)
 
1041
        revnos = [s.split(':', 1)[0].lstrip() for s in out.splitlines()]
 
1042
        self.assertEqual(expected_revnos, revnos)
 
1043
 
 
1044
    def test_log_files(self):
 
1045
        """The log for multiple file should only list revs for those files"""
 
1046
        self.prepare_tree()
 
1047
        self.assertRevnos('file1 file2 dir1/dir2/file3',
 
1048
            ['6', '5.1.1', '3', '2', '1'])
 
1049
 
 
1050
    def test_log_directory(self):
 
1051
        """The log for a directory should show all nested files."""
 
1052
        self.prepare_tree()
 
1053
        self.assertRevnos('dir1', ['5', '3'])
 
1054
 
 
1055
    def test_log_nested_directory(self):
 
1056
        """The log for a directory should show all nested files."""
 
1057
        self.prepare_tree()
 
1058
        self.assertRevnos('dir1/dir2', ['3'])
 
1059
 
 
1060
    def test_log_in_nested_directory(self):
 
1061
        """The log for a directory should show all nested files."""
 
1062
        self.prepare_tree()
 
1063
        os.chdir("dir1")
 
1064
        self.assertRevnos('.', ['5', '3'])
 
1065
 
 
1066
    def test_log_files_and_directories(self):
 
1067
        """Logging files and directories together should be fine."""
 
1068
        self.prepare_tree()
 
1069
        self.assertRevnos('file4 dir1/dir2', ['4', '3'])
 
1070
 
 
1071
    def test_log_files_and_dirs_in_nested_directory(self):
 
1072
        """The log for a directory should show all nested files."""
 
1073
        self.prepare_tree()
 
1074
        os.chdir("dir1")
 
1075
        self.assertRevnos('dir2 file5', ['5', '3'])