~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

Merge bzr.dev (and fix NEWS)

Show diffs side-by-side

added added

removed removed

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