~bzr/ubuntu/lucid/bzr/beta-ppa

« back to all changes in this revision

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

  • Committer: Martin Pool
  • Date: 2010-07-02 07:29:40 UTC
  • mfrom: (129.1.7 packaging-karmic)
  • Revision ID: mbp@sourcefrog.net-20100702072940-hpzq5elg8wjve8rh
* PPA rebuild.
* PPA rebuild for Karmic.
* PPA rebuild for Jaunty.
* PPA rebuild for Hardy.
* From postinst, actually remove the example bash completion scripts.
  (LP: #249452)
* New upstream release.
* New upstream release.
* New upstream release.
* Revert change to Build-depends: Dapper does not have python-central.
  Should be python-support..
* Target ppa..
* Target ppa..
* Target ppa..
* Target ppa..
* New upstream release.
* Switch to dpkg-source 3.0 (quilt) format.
* Bump standards version to 3.8.4.
* Remove embedded copy of python-configobj. Closes: #555336
* Remove embedded copy of python-elementtree. Closes: #555343
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* debian/control: Fix obsolete-relation-form-in-source
  lintian warning. 
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Split out docs into bzr-doc package.
* New upstream release.
* Added John Francesco Ferlito to Uploaders.
* Fix install path to quick-reference guide
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to doc paths changing
* New upstream release.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to doc paths changing
* New upstream release.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to doc paths changing
* New upstream release.
* Fix FTBFS due to path changes, again, again.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to path changes.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Bump standards version to 3.8.3.
* Remove unused patch system.
* New upstream release.
* New upstream release.
* New upstream release.
* Fix copy and paste tab error in .install file
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
 + Fixes compatibility with Python 2.4. Closes: #537708
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream version.
* Bump standards version to 3.8.2.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Add python-pyrex to build-deps to ensure C extensions are always build.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Split documentation into bzr-doc package. ((LP: #385074)
* Multiple packaging changes to make us more linitan clean.
* New upstream release.
* Split documentation into bzr-doc package. ((LP: #385074)
* Multiple packaging changes to make us more linitan clean.
* New upstream release.
* Split documentation into bzr-doc package. ((LP: #385074)
* Multiple packaging changes to make us more linitan clean.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Fix API compatibility version. (Closes: #526233)
* New upstream release.
  + Fixes default format for upgrade command. (Closes: #464688)
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Add missing dependency on zlib development library. (Closes:
  #523595)
* Add zlib build-depends.
* Add zlib build-depends.
* Add zlib build-depends.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Move to section vcs.
* Bump standards version to 3.8.1.
* New upstream release.
* Remove temporary patch for missing .c files from distribution
* New upstream release.
* Remove temporary patch for missing .c files from distribution
* New upstream release.
* Remove temporary patch for missing .c files from distribution
* Add temporary patch for missing .c files from distribution
* Add temporary patch for missing .c files from distribution
* Add temporary patch for missing .c files from distribution
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Recommend ca-certificates. (Closes: #452024)
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Update watch file. bazaar now uses launchpad to host its sources.
* Remove patch for inventory root revision copy, applied upstream.
* New upstream release.
* New upstream release.
* New upstream release
* Force removal of files installed in error to /etc/bash_completion.d/
  (LP: #249452)
* New upstream release.
* New upstream release
* New upstream release.
* Bump standards version.
* Include patch for inventory root revision copy, required for bzr-svn.
* New upstream release.
* Remove unused lintian overrides.
* Correct the package version not to be native.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Final 1.5 release.
* New upstream release.
* New upstream release.
* New upstream release.
* Add myself as a co-maintainer.
* Add a Dm-Upload-Allowed: yes header.
* New upstream bugfix release.
* New upstream release.
* Final 1.3 release.
* New upstream release.
* First release candidate of the upcoming 1.3 release.
* Rebuild to fix the problem caused by a build with a broken python-central.
* New upstream release.
* Rebuild for dapper PPA.
* Apply Lamont's patches to fix build-dependencies on dapper.
  (See: https://bugs.launchpad.net/bzr/+bug/189915)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2010 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
 
 
18
"""Black-box tests for bzr log."""
 
19
 
 
20
import os
 
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):
 
95
 
 
96
    def test_log_null_end_revspec(self):
 
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)
 
105
 
 
106
        full_log = self.run_bzr(['log'])[0]
 
107
        log = self.run_bzr("log -r 1..")[0]
 
108
        self.assertEqualDiff(log, full_log)
 
109
 
 
110
    def test_log_null_begin_revspec(self):
 
111
        self.make_linear_branch()
 
112
        full_log = self.run_bzr(['log'])[0]
 
113
        log = self.run_bzr("log -r ..3")[0]
 
114
        self.assertEqualDiff(full_log, log)
 
115
 
 
116
    def test_log_null_both_revspecs(self):
 
117
        self.make_linear_branch()
 
118
        full_log = self.run_bzr(['log'])[0]
 
119
        log = self.run_bzr("log -r ..")[0]
 
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'])
 
136
 
 
137
    def test_log_negative_begin_revspec_full_log(self):
 
138
        self.make_linear_branch()
 
139
        full_log = self.run_bzr(['log'])[0]
 
140
        log = self.run_bzr("log -r -3..")[0]
 
141
        self.assertEqualDiff(full_log, log)
 
142
 
 
143
    def test_log_negative_both_revspec_full_log(self):
 
144
        self.make_linear_branch()
 
145
        full_log = self.run_bzr(['log'])[0]
 
146
        log = self.run_bzr("log -r -3..-1")[0]
 
147
        self.assertEqualDiff(full_log, log)
 
148
 
 
149
    def test_log_negative_both_revspec_partial(self):
 
150
        self.make_linear_branch()
 
151
        log = self.run_bzr("log -r -3..-2")[0]
 
152
        self.assertTrue('revno: 1\n' in log)
 
153
        self.assertTrue('revno: 2\n' in log)
 
154
        self.assertTrue('revno: 3\n' not in log)
 
155
 
 
156
    def test_log_negative_begin_revspec(self):
 
157
        self.make_linear_branch()
 
158
        log = self.run_bzr("log -r -2..")[0]
 
159
        self.assertTrue('revno: 1\n' not in log)
 
160
        self.assertTrue('revno: 2\n' in log)
 
161
        self.assertTrue('revno: 3\n' in log)
 
162
 
 
163
    def test_log_positive_revspecs(self):
 
164
        self.make_linear_branch()
 
165
        full_log = self.run_bzr(['log'])[0]
 
166
        log = self.run_bzr("log -r 1..3")[0]
 
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))
 
173
 
 
174
    def test_log_reversed_revspecs(self):
 
175
        self.make_linear_branch()
 
176
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
 
177
                            'the end revision.\n',),
 
178
                           ['log', '-r3..1'])
 
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
 
 
186
    def test_log_revno_n_path(self):
 
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
 
192
        log = self.run_bzr("log -r revno:1:branch2..revno:3:branch2")[0]
 
193
        full_log = self.run_bzr(['log'], working_dir='branch2')[0]
 
194
        self.assertEqualDiff(full_log, log)
 
195
        log = self.run_bzr("log -r revno:1:branch2")[0]
 
196
        self.assertTrue('revno: 1\n' in log)
 
197
        self.assertTrue('revno: 2\n' not in log)
 
198
        self.assertTrue('branch nick: branch2\n' in log)
 
199
        self.assertTrue('branch nick: branch1\n' not in log)
 
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
 
 
246
    def test_log_nonexistent_file(self):
 
247
        self.make_minimal_branch()
 
248
        # files that don't exist in either the basis tree or working tree
 
249
        # should give an error
 
250
        out, err = self.run_bzr('log does-not-exist', retcode=3)
 
251
        self.assertContainsRe(err,
 
252
                              'Path unknown at end or start of revision range: '
 
253
                              'does-not-exist')
 
254
 
 
255
    def test_log_with_tags(self):
 
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
 
 
262
        log = self.run_bzr("log -r-1")[0]
 
263
        self.assertTrue('tags: tag3' in log)
 
264
 
 
265
        log = self.run_bzr("log -r1")[0]
 
266
        # I guess that we can't know the order of tags in the output
 
267
        # since dicts are unordered, need to check both possibilities
 
268
        self.assertContainsRe(log, r'tags: (tag1, tag1\.1|tag1\.1, tag1)')
 
269
 
 
270
    def test_merged_log_with_tags(self):
 
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]
 
281
        self.assertContainsRe(log, r'    tags: tag1')
 
282
        log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
 
283
        self.assertContainsRe(log, r'tags: tag1')
 
284
 
 
285
    def test_log_limit(self):
 
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)
 
290
        log = self.run_bzr("log --limit 2")[0]
 
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())
 
388
 
 
389
    def test_merges_are_indented_by_level(self):
 
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'])
 
493
 
 
494
    def test_merges_single_merge_rev(self):
 
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'])
 
512
 
 
513
    def test_merges_partial_range(self):
 
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
    def test_merges_partial_range_ignore_before_lower_bound(self):
 
540
        """Dont show revisions before the lower bound's merged revs"""
 
541
        expected = """\
 
542
    2 Lorem Ipsum\t2005-11-22 [merge]
 
543
      merge branch level1
 
544
 
 
545
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
546
                merge branch level2
 
547
 
 
548
              1.2.1 Lorem Ipsum\t2005-11-22
 
549
                    in branch level2
 
550
 
 
551
"""
 
552
        self.check_log(expected, ['--short', '-n0', '-r1.1.2..2'])
 
553
 
 
554
 
 
555
class TestLogDiff(TestLog):
 
556
 
 
557
    def setUp(self):
 
558
        super(TestLogDiff, self).setUp()
 
559
        self.make_branch_with_diffs()
 
560
 
 
561
    def make_branch_with_diffs(self):
 
562
        level0 = self.make_branch_and_tree('level0')
 
563
        self.build_tree(['level0/file1', 'level0/file2'])
 
564
        level0.add('file1')
 
565
        level0.add('file2')
 
566
        level0.commit(message='in branch level0', **self.commit_options())
 
567
 
 
568
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
 
569
        self.build_tree_contents([('level1/file2', 'hello\n')])
 
570
        level1.commit(message='in branch level1', **self.commit_options())
 
571
        level0.merge_from_branch(level1.branch)
 
572
        level0.commit(message='merge branch level1', **self.commit_options())
 
573
 
 
574
    def test_log_show_diff_long_with_merges(self):
 
575
        out,err = self.run_bzr('log -p -n0')
 
576
        self.assertEqual('', err)
 
577
        log = test_log.normalize_log(out)
 
578
        expected = """\
 
579
------------------------------------------------------------
 
580
revno: 2 [merge]
 
581
committer: Lorem Ipsum <test@example.com>
 
582
branch nick: level0
 
583
timestamp: Just now
 
584
message:
 
585
  merge branch level1
 
586
diff:
 
587
=== modified file 'file2'
 
588
--- file2\t2005-11-22 00:00:01 +0000
 
589
+++ file2\t2005-11-22 00:00:02 +0000
 
590
@@ -1,1 +1,1 @@
 
591
-contents of level0/file2
 
592
+hello
 
593
    ------------------------------------------------------------
 
594
    revno: 1.1.1
 
595
    committer: Lorem Ipsum <test@example.com>
 
596
    branch nick: level1
 
597
    timestamp: Just now
 
598
    message:
 
599
      in branch level1
 
600
    diff:
 
601
    === modified file 'file2'
 
602
    --- file2\t2005-11-22 00:00:01 +0000
 
603
    +++ file2\t2005-11-22 00:00:02 +0000
 
604
    @@ -1,1 +1,1 @@
 
605
    -contents of level0/file2
 
606
    +hello
 
607
------------------------------------------------------------
 
608
revno: 1
 
609
committer: Lorem Ipsum <test@example.com>
 
610
branch nick: level0
 
611
timestamp: Just now
 
612
message:
 
613
  in branch level0
 
614
diff:
 
615
=== added file 'file1'
 
616
--- file1\t1970-01-01 00:00:00 +0000
 
617
+++ file1\t2005-11-22 00:00:01 +0000
 
618
@@ -0,0 +1,1 @@
 
619
+contents of level0/file1
 
620
 
 
621
=== added file 'file2'
 
622
--- file2\t1970-01-01 00:00:00 +0000
 
623
+++ file2\t2005-11-22 00:00:01 +0000
 
624
@@ -0,0 +1,1 @@
 
625
+contents of level0/file2
 
626
"""
 
627
        self.check_log(expected, ['-p', '-n0'])
 
628
 
 
629
    def test_log_show_diff_short(self):
 
630
        expected = """\
 
631
    2 Lorem Ipsum\t2005-11-22 [merge]
 
632
      merge branch level1
 
633
      === modified file 'file2'
 
634
      --- file2\t2005-11-22 00:00:01 +0000
 
635
      +++ file2\t2005-11-22 00:00:02 +0000
 
636
      @@ -1,1 +1,1 @@
 
637
      -contents of level0/file2
 
638
      +hello
 
639
 
 
640
    1 Lorem Ipsum\t2005-11-22
 
641
      in branch level0
 
642
      === added file 'file1'
 
643
      --- file1\t1970-01-01 00:00:00 +0000
 
644
      +++ file1\t2005-11-22 00:00:01 +0000
 
645
      @@ -0,0 +1,1 @@
 
646
      +contents of level0/file1
 
647
\x20\x20\x20\x20\x20\x20
 
648
      === added file 'file2'
 
649
      --- file2\t1970-01-01 00:00:00 +0000
 
650
      +++ file2\t2005-11-22 00:00:01 +0000
 
651
      @@ -0,0 +1,1 @@
 
652
      +contents of level0/file2
 
653
 
 
654
Use --include-merges or -n0 to see merged revisions.
 
655
"""
 
656
        self.check_log(expected, ['-p', '--short'])
 
657
 
 
658
    def test_log_show_diff_line(self):
 
659
        # Not supported by this formatter so expect plain output
 
660
        expected = """\
 
661
2: Lorem Ipsum 2005-11-22 [merge] merge branch level1
 
662
1: Lorem Ipsum 2005-11-22 in branch level0
 
663
"""
 
664
        self.check_log(expected, ['-p', '--line'])
 
665
 
 
666
    def test_log_show_diff_file1(self):
 
667
        """Only the diffs for the given file are to be shown"""
 
668
        expected = """\
 
669
    1 Lorem Ipsum\t2005-11-22
 
670
      in branch level0
 
671
      === added file 'file1'
 
672
      --- file1\t1970-01-01 00:00:00 +0000
 
673
      +++ file1\t2005-11-22 00:00:01 +0000
 
674
      @@ -0,0 +1,1 @@
 
675
      +contents of level0/file1
 
676
 
 
677
"""
 
678
        self.check_log(expected, ['-p', '--short', 'file1'])
 
679
 
 
680
    def test_log_show_diff_file2(self):
 
681
        """Only the diffs for the given file are to be shown"""
 
682
        expected = """\
 
683
    2 Lorem Ipsum\t2005-11-22 [merge]
 
684
      merge branch level1
 
685
      === modified file 'file2'
 
686
      --- file2\t2005-11-22 00:00:01 +0000
 
687
      +++ file2\t2005-11-22 00:00:02 +0000
 
688
      @@ -1,1 +1,1 @@
 
689
      -contents of level0/file2
 
690
      +hello
 
691
 
 
692
    1 Lorem Ipsum\t2005-11-22
 
693
      in branch level0
 
694
      === added file 'file2'
 
695
      --- file2\t1970-01-01 00:00:00 +0000
 
696
      +++ file2\t2005-11-22 00:00:01 +0000
 
697
      @@ -0,0 +1,1 @@
 
698
      +contents of level0/file2
 
699
 
 
700
Use --include-merges or -n0 to see merged revisions.
 
701
"""
 
702
        self.check_log(expected, ['-p', '--short', 'file2'])
 
703
 
 
704
 
 
705
class TestLogUnicodeDiff(TestLog):
 
706
 
 
707
    def test_log_show_diff_non_ascii(self):
 
708
        # Smoke test for bug #328007 UnicodeDecodeError on 'log -p'
 
709
        message = u'Message with \xb5'
 
710
        body = 'Body with \xb5\n'
 
711
        wt = self.make_branch_and_tree('.')
 
712
        self.build_tree_contents([('foo', body)])
 
713
        wt.add('foo')
 
714
        wt.commit(message=message)
 
715
        # check that command won't fail with unicode error
 
716
        # don't care about exact output because we have other tests for this
 
717
        out,err = self.run_bzr('log -p --long')
 
718
        self.assertNotEqual('', out)
 
719
        self.assertEqual('', err)
 
720
        out,err = self.run_bzr('log -p --short')
 
721
        self.assertNotEqual('', out)
 
722
        self.assertEqual('', err)
 
723
        out,err = self.run_bzr('log -p --line')
 
724
        self.assertNotEqual('', out)
 
725
        self.assertEqual('', err)
 
726
 
 
727
 
 
728
class TestLogEncodings(tests.TestCaseInTempDir):
 
729
 
 
730
    _mu = u'\xb5'
 
731
    _message = u'Message with \xb5'
 
732
 
 
733
    # Encodings which can encode mu
 
734
    good_encodings = [
 
735
        'utf-8',
 
736
        'latin-1',
 
737
        'iso-8859-1',
 
738
        'cp437', # Common windows encoding
 
739
        'cp1251', # Russian windows encoding
 
740
        'cp1258', # Common windows encoding
 
741
    ]
 
742
    # Encodings which cannot encode mu
 
743
    bad_encodings = [
 
744
        'ascii',
 
745
        'iso-8859-2',
 
746
        'koi8_r',
 
747
    ]
 
748
 
 
749
    def setUp(self):
 
750
        super(TestLogEncodings, self).setUp()
 
751
        self.user_encoding = osutils._cached_user_encoding
 
752
        def restore():
 
753
            osutils._cached_user_encoding = self.user_encoding
 
754
        self.addCleanup(restore)
 
755
 
 
756
    def create_branch(self):
 
757
        bzr = self.run_bzr
 
758
        bzr('init')
 
759
        open('a', 'wb').write('some stuff\n')
 
760
        bzr('add a')
 
761
        bzr(['commit', '-m', self._message])
 
762
 
 
763
    def try_encoding(self, encoding, fail=False):
 
764
        bzr = self.run_bzr
 
765
        if fail:
 
766
            self.assertRaises(UnicodeEncodeError,
 
767
                self._mu.encode, encoding)
 
768
            encoded_msg = self._message.encode(encoding, 'replace')
 
769
        else:
 
770
            encoded_msg = self._message.encode(encoding)
 
771
 
 
772
        old_encoding = osutils._cached_user_encoding
 
773
        # This test requires that 'run_bzr' uses the current
 
774
        # bzrlib, because we override user_encoding, and expect
 
775
        # it to be used
 
776
        try:
 
777
            osutils._cached_user_encoding = 'ascii'
 
778
            # We should be able to handle any encoding
 
779
            out, err = bzr('log', encoding=encoding)
 
780
            if not fail:
 
781
                # Make sure we wrote mu as we expected it to exist
 
782
                self.assertNotEqual(-1, out.find(encoded_msg))
 
783
                out_unicode = out.decode(encoding)
 
784
                self.assertNotEqual(-1, out_unicode.find(self._message))
 
785
            else:
 
786
                self.assertNotEqual(-1, out.find('Message with ?'))
 
787
        finally:
 
788
            osutils._cached_user_encoding = old_encoding
 
789
 
 
790
    def test_log_handles_encoding(self):
 
791
        self.create_branch()
 
792
 
 
793
        for encoding in self.good_encodings:
 
794
            self.try_encoding(encoding)
 
795
 
 
796
    def test_log_handles_bad_encoding(self):
 
797
        self.create_branch()
 
798
 
 
799
        for encoding in self.bad_encodings:
 
800
            self.try_encoding(encoding, fail=True)
 
801
 
 
802
    def test_stdout_encoding(self):
 
803
        bzr = self.run_bzr
 
804
        osutils._cached_user_encoding = "cp1251"
 
805
 
 
806
        bzr('init')
 
807
        self.build_tree(['a'])
 
808
        bzr('add a')
 
809
        bzr(['commit', '-m', u'\u0422\u0435\u0441\u0442'])
 
810
        stdout, stderr = self.run_bzr('log', encoding='cp866')
 
811
 
 
812
        message = stdout.splitlines()[-1]
 
813
 
 
814
        # explanation of the check:
 
815
        # u'\u0422\u0435\u0441\u0442' is word 'Test' in russian
 
816
        # in cp866  encoding this is string '\x92\xa5\xe1\xe2'
 
817
        # in cp1251 encoding this is string '\xd2\xe5\xf1\xf2'
 
818
        # This test should check that output of log command
 
819
        # encoded to sys.stdout.encoding
 
820
        test_in_cp866 = '\x92\xa5\xe1\xe2'
 
821
        test_in_cp1251 = '\xd2\xe5\xf1\xf2'
 
822
        # Make sure the log string is encoded in cp866
 
823
        self.assertEquals(test_in_cp866, message[2:])
 
824
        # Make sure the cp1251 string is not found anywhere
 
825
        self.assertEquals(-1, stdout.find(test_in_cp1251))
 
826
 
 
827
 
 
828
class TestLogFile(tests.TestCaseWithTransport):
 
829
 
 
830
    def test_log_local_branch_file(self):
 
831
        """We should be able to log files in local treeless branches"""
 
832
        tree = self.make_branch_and_tree('tree')
 
833
        self.build_tree(['tree/file'])
 
834
        tree.add('file')
 
835
        tree.commit('revision 1')
 
836
        tree.bzrdir.destroy_workingtree()
 
837
        self.run_bzr('log tree/file')
 
838
 
 
839
    def prepare_tree(self, complex=False):
 
840
        # The complex configuration includes deletes and renames
 
841
        tree = self.make_branch_and_tree('parent')
 
842
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
 
843
        tree.add('file1')
 
844
        tree.commit('add file1')
 
845
        tree.add('file2')
 
846
        tree.commit('add file2')
 
847
        tree.add('file3')
 
848
        tree.commit('add file3')
 
849
        child_tree = tree.bzrdir.sprout('child').open_workingtree()
 
850
        self.build_tree_contents([('child/file2', 'hello')])
 
851
        child_tree.commit(message='branch 1')
 
852
        tree.merge_from_branch(child_tree.branch)
 
853
        tree.commit(message='merge child branch')
 
854
        if complex:
 
855
            tree.remove('file2')
 
856
            tree.commit('remove file2')
 
857
            tree.rename_one('file3', 'file4')
 
858
            tree.commit('file3 is now called file4')
 
859
            tree.remove('file1')
 
860
            tree.commit('remove file1')
 
861
        os.chdir('parent')
 
862
 
 
863
    def test_log_file(self):
 
864
        """The log for a particular file should only list revs for that file"""
 
865
        self.prepare_tree()
 
866
        log = self.run_bzr('log -n0 file1')[0]
 
867
        self.assertContainsRe(log, 'revno: 1\n')
 
868
        self.assertNotContainsRe(log, 'revno: 2\n')
 
869
        self.assertNotContainsRe(log, 'revno: 3\n')
 
870
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
871
        self.assertNotContainsRe(log, 'revno: 4 ')
 
872
        log = self.run_bzr('log -n0 file2')[0]
 
873
        self.assertNotContainsRe(log, 'revno: 1\n')
 
874
        self.assertContainsRe(log, 'revno: 2\n')
 
875
        self.assertNotContainsRe(log, 'revno: 3\n')
 
876
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
877
        self.assertContainsRe(log, 'revno: 4 ')
 
878
        log = self.run_bzr('log -n0 file3')[0]
 
879
        self.assertNotContainsRe(log, 'revno: 1\n')
 
880
        self.assertNotContainsRe(log, 'revno: 2\n')
 
881
        self.assertContainsRe(log, 'revno: 3\n')
 
882
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
883
        self.assertNotContainsRe(log, 'revno: 4 ')
 
884
        log = self.run_bzr('log -n0 -r3.1.1 file2')[0]
 
885
        self.assertNotContainsRe(log, 'revno: 1\n')
 
886
        self.assertNotContainsRe(log, 'revno: 2\n')
 
887
        self.assertNotContainsRe(log, 'revno: 3\n')
 
888
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
889
        self.assertNotContainsRe(log, 'revno: 4 ')
 
890
        log = self.run_bzr('log -n0 -r4 file2')[0]
 
891
        self.assertNotContainsRe(log, 'revno: 1\n')
 
892
        self.assertNotContainsRe(log, 'revno: 2\n')
 
893
        self.assertNotContainsRe(log, 'revno: 3\n')
 
894
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
895
        self.assertContainsRe(log, 'revno: 4 ')
 
896
        log = self.run_bzr('log -n0 -r3.. file2')[0]
 
897
        self.assertNotContainsRe(log, 'revno: 1\n')
 
898
        self.assertNotContainsRe(log, 'revno: 2\n')
 
899
        self.assertNotContainsRe(log, 'revno: 3\n')
 
900
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
901
        self.assertContainsRe(log, 'revno: 4 ')
 
902
        log = self.run_bzr('log -n0 -r..3 file2')[0]
 
903
        self.assertNotContainsRe(log, 'revno: 1\n')
 
904
        self.assertContainsRe(log, 'revno: 2\n')
 
905
        self.assertNotContainsRe(log, 'revno: 3\n')
 
906
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
907
        self.assertNotContainsRe(log, 'revno: 4 ')
 
908
 
 
909
    def test_log_file_historical_missing(self):
 
910
        # Check logging a deleted file gives an error if the
 
911
        # file isn't found at the end or start of the revision range
 
912
        self.prepare_tree(complex=True)
 
913
        err_msg = "Path unknown at end or start of revision range: file2"
 
914
        err = self.run_bzr('log file2', retcode=3)[1]
 
915
        self.assertContainsRe(err, err_msg)
 
916
 
 
917
    def test_log_file_historical_end(self):
 
918
        # Check logging a deleted file is ok if the file existed
 
919
        # at the end the revision range
 
920
        self.prepare_tree(complex=True)
 
921
        log, err = self.run_bzr('log -n0 -r..4 file2')
 
922
        self.assertEquals('', err)
 
923
        self.assertNotContainsRe(log, 'revno: 1\n')
 
924
        self.assertContainsRe(log, 'revno: 2\n')
 
925
        self.assertNotContainsRe(log, 'revno: 3\n')
 
926
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
927
        self.assertContainsRe(log, 'revno: 4 ')
 
928
 
 
929
    def test_log_file_historical_start(self):
 
930
        # Check logging a deleted file is ok if the file existed
 
931
        # at the start of the revision range
 
932
        self.prepare_tree(complex=True)
 
933
        log, err = self.run_bzr('log file1')
 
934
        self.assertEquals('', err)
 
935
        self.assertContainsRe(log, 'revno: 1\n')
 
936
        self.assertNotContainsRe(log, 'revno: 2\n')
 
937
        self.assertNotContainsRe(log, 'revno: 3\n')
 
938
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
939
        self.assertNotContainsRe(log, 'revno: 4 ')
 
940
 
 
941
    def test_log_file_renamed(self):
 
942
        """File matched against revision range, not current tree."""
 
943
        self.prepare_tree(complex=True)
 
944
 
 
945
        # Check logging a renamed file gives an error by default
 
946
        err_msg = "Path unknown at end or start of revision range: file3"
 
947
        err = self.run_bzr('log file3', retcode=3)[1]
 
948
        self.assertContainsRe(err, err_msg)
 
949
 
 
950
        # Check we can see a renamed file if we give the right end revision
 
951
        log, err = self.run_bzr('log -r..4 file3')
 
952
        self.assertEquals('', err)
 
953
        self.assertNotContainsRe(log, 'revno: 1\n')
 
954
        self.assertNotContainsRe(log, 'revno: 2\n')
 
955
        self.assertContainsRe(log, 'revno: 3\n')
 
956
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
957
        self.assertNotContainsRe(log, 'revno: 4 ')
 
958
 
 
959
    def test_line_log_file(self):
 
960
        """The line log for a file should only list relevant mainline revs"""
 
961
        # Note: this also implicitly  covers the short logging case.
 
962
        # We test using --line in preference to --short because matching
 
963
        # revnos in the output of --line is more reliable.
 
964
        self.prepare_tree()
 
965
 
 
966
        # full history of file1
 
967
        log = self.run_bzr('log --line file1')[0]
 
968
        self.assertContainsRe(log, '^1:', re.MULTILINE)
 
969
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
970
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
971
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
972
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
973
 
 
974
        # full history of file2
 
975
        log = self.run_bzr('log --line file2')[0]
 
976
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
977
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
978
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
979
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
980
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
981
 
 
982
        # full history of file3
 
983
        log = self.run_bzr('log --line file3')[0]
 
984
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
985
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
986
        self.assertContainsRe(log, '^3:', re.MULTILINE)
 
987
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
988
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
989
 
 
990
        # file in a merge revision
 
991
        log = self.run_bzr('log --line -r3.1.1 file2')[0]
 
992
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
993
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
994
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
995
        self.assertContainsRe(log, '^3.1.1:', re.MULTILINE)
 
996
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
997
 
 
998
        # file in a mainline revision
 
999
        log = self.run_bzr('log --line -r4 file2')[0]
 
1000
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
1001
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
1002
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
1003
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
1004
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
1005
 
 
1006
        # file since a revision
 
1007
        log = self.run_bzr('log --line -r3.. file2')[0]
 
1008
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
1009
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
1010
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
1011
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
1012
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
1013
 
 
1014
        # file up to a revision
 
1015
        log = self.run_bzr('log --line -r..3 file2')[0]
 
1016
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
1017
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
1018
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
1019
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
1020
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
1021
 
 
1022
 
 
1023
class TestLogMultiple(tests.TestCaseWithTransport):
 
1024
 
 
1025
    def prepare_tree(self):
 
1026
        tree = self.make_branch_and_tree('parent')
 
1027
        self.build_tree([
 
1028
            'parent/file1',
 
1029
            'parent/file2',
 
1030
            'parent/dir1/',
 
1031
            'parent/dir1/file5',
 
1032
            'parent/dir1/dir2/',
 
1033
            'parent/dir1/dir2/file3',
 
1034
            'parent/file4'])
 
1035
        tree.add('file1')
 
1036
        tree.commit('add file1')
 
1037
        tree.add('file2')
 
1038
        tree.commit('add file2')
 
1039
        tree.add(['dir1', 'dir1/dir2', 'dir1/dir2/file3'])
 
1040
        tree.commit('add file3')
 
1041
        tree.add('file4')
 
1042
        tree.commit('add file4')
 
1043
        tree.add('dir1/file5')
 
1044
        tree.commit('add file5')
 
1045
        child_tree = tree.bzrdir.sprout('child').open_workingtree()
 
1046
        self.build_tree_contents([('child/file2', 'hello')])
 
1047
        child_tree.commit(message='branch 1')
 
1048
        tree.merge_from_branch(child_tree.branch)
 
1049
        tree.commit(message='merge child branch')
 
1050
        os.chdir('parent')
 
1051
 
 
1052
    def assertRevnos(self, paths_str, expected_revnos):
 
1053
        # confirm the revision numbers in log --line output are those expected
 
1054
        out, err = self.run_bzr('log --line -n0 %s' % (paths_str,))
 
1055
        self.assertEqual('', err)
 
1056
        revnos = [s.split(':', 1)[0].lstrip() for s in out.splitlines()]
 
1057
        self.assertEqual(expected_revnos, revnos)
 
1058
 
 
1059
    def test_log_files(self):
 
1060
        """The log for multiple file should only list revs for those files"""
 
1061
        self.prepare_tree()
 
1062
        self.assertRevnos('file1 file2 dir1/dir2/file3',
 
1063
            ['6', '5.1.1', '3', '2', '1'])
 
1064
 
 
1065
    def test_log_directory(self):
 
1066
        """The log for a directory should show all nested files."""
 
1067
        self.prepare_tree()
 
1068
        self.assertRevnos('dir1', ['5', '3'])
 
1069
 
 
1070
    def test_log_nested_directory(self):
 
1071
        """The log for a directory should show all nested files."""
 
1072
        self.prepare_tree()
 
1073
        self.assertRevnos('dir1/dir2', ['3'])
 
1074
 
 
1075
    def test_log_in_nested_directory(self):
 
1076
        """The log for a directory should show all nested files."""
 
1077
        self.prepare_tree()
 
1078
        os.chdir("dir1")
 
1079
        self.assertRevnos('.', ['5', '3'])
 
1080
 
 
1081
    def test_log_files_and_directories(self):
 
1082
        """Logging files and directories together should be fine."""
 
1083
        self.prepare_tree()
 
1084
        self.assertRevnos('file4 dir1/dir2', ['4', '3'])
 
1085
 
 
1086
    def test_log_files_and_dirs_in_nested_directory(self):
 
1087
        """The log for a directory should show all nested files."""
 
1088
        self.prepare_tree()
 
1089
        os.chdir("dir1")
 
1090
        self.assertRevnos('dir2 file5', ['5', '3'])