~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Martin Packman
  • Date: 2011-12-23 19:38:22 UTC
  • mto: This revision was merged to the branch mainline in revision 6405.
  • Revision ID: martin.packman@canonical.com-20111223193822-hesheea4o8aqwexv
Accept and document passing the medium rather than transport for smart connections

Show diffs side-by-side

added added

removed removed

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