~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Patch Queue Manager
  • Date: 2011-09-22 14:12:18 UTC
  • mfrom: (6155.3.1 jam)
  • Revision ID: pqm@pqm.ubuntu.com-20110922141218-86s4uu6nqvourw4f
(jameinel) Cleanup comments bzrlib/smart/__init__.py (John A Meinel)

Show diffs side-by-side

added added

removed removed

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