~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Ian Clatworthy
  • Date: 2009-07-22 14:07:56 UTC
  • mto: (4568.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4569.
  • Revision ID: ian.clatworthy@canonical.com-20090722140756-rx3dbtf3rlubfy4r
Improve the names and location of the quick reference cards

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
 
18
18
"""Black-box tests for bzr log."""
19
19
 
20
 
from itertools import izip
21
20
import os
 
21
import re
22
22
 
23
23
from bzrlib import (
24
 
    branchbuilder,
25
 
    errors,
26
 
    log,
27
24
    osutils,
28
25
    tests,
29
26
    )
30
 
from bzrlib.tests import (
31
 
    test_log,
32
 
    features,
33
 
    )
34
 
 
35
 
 
36
 
class TestLog(tests.TestCaseWithTransport, test_log.TestLogMixin):
 
27
from bzrlib.tests import test_log
 
28
 
 
29
 
 
30
class TestLog(tests.TestCaseWithTransport):
 
31
 
 
32
    def setUp(self):
 
33
        super(TestLog, self).setUp()
 
34
        self.timezone = 0 # UTC
 
35
        self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
37
36
 
38
37
    def make_minimal_branch(self, path='.', format=None):
39
38
        tree = self.make_branch_and_tree(path, format=format)
64
63
        tree.commit(message='merge')
65
64
        return tree
66
65
 
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'])
 
66
    def assertRevnos(self, log, must_have=(), must_not_have=()):
 
67
        """Check if revnos are in or not in the log output"""
 
68
        for revno in must_have:
 
69
            self.assertTrue(('revno: %s\n' % revno) in log,
 
70
                'Does not contain expected revno %s' % revno)
 
71
        for revno in must_not_have:
 
72
            self.assertFalse(('revno: %s\n' % revno) in log,
 
73
                'Contains unexpected revno %s' % revno)
 
74
 
 
75
    def commit_options(self):
 
76
        """Use some mostly fixed values for commits to simplify tests.
 
77
 
 
78
        Tests can use this function to get some commit attributes. The time
 
79
        stamp is incremented at each commit.
 
80
        """
 
81
        self.timestamp += 1 # 1 second between each commit
 
82
        return dict(committer='Lorem Ipsum <joe@foo.com>',
 
83
                 timezone=self.timezone,
 
84
                 timestamp=self.timestamp,
 
85
                 )
 
86
 
 
87
    def check_log(self, expected, args, working_dir='level0'):
 
88
        out, err = self.run_bzr(['log', '--timezone', 'utc'] + args,
 
89
                                working_dir=working_dir)
 
90
        self.assertEqual('', err)
 
91
        self.assertEqualDiff(expected, test_log.normalize_log(out))
 
92
 
 
93
 
 
94
class TestLogRevSpecs(TestLog):
114
95
 
115
96
    def test_log_null_end_revspec(self):
116
97
        self.make_linear_branch()
117
 
        self.assertLogRevnos(['-r1..'], ['3', '2', '1'])
 
98
        log = self.run_bzr(['log'])[0]
 
99
        self.assertTrue('revno: 1\n' in log)
 
100
        self.assertTrue('revno: 2\n' in log)
 
101
        self.assertTrue('revno: 3\n' in log)
 
102
        self.assertTrue('message:\n  message1\n' in log)
 
103
        self.assertTrue('message:\n  message2\n' in log)
 
104
        self.assertTrue('message:\n  message3\n' in log)
 
105
 
 
106
        full_log = self.run_bzr(['log'])[0]
 
107
        log = self.run_bzr("log -r 1..")[0]
 
108
        self.assertEqualDiff(log, full_log)
118
109
 
119
110
    def test_log_null_begin_revspec(self):
120
111
        self.make_linear_branch()
121
 
        self.assertLogRevnos(['-r..3'], ['3', '2', '1'])
 
112
        full_log = self.run_bzr(['log'])[0]
 
113
        log = self.run_bzr("log -r ..3")[0]
 
114
        self.assertEqualDiff(full_log, log)
122
115
 
123
116
    def test_log_null_both_revspecs(self):
124
117
        self.make_linear_branch()
125
 
        self.assertLogRevnos(['-r..'], ['3', '2', '1'])
 
118
        full_log = self.run_bzr(['log'])[0]
 
119
        log = self.run_bzr("log -r ..")[0]
 
120
        self.assertEqualDiff(full_log, log)
 
121
 
 
122
    def test_log_zero_revspec(self):
 
123
        self.make_minimal_branch()
 
124
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
125
                           ['log', '-r0'])
 
126
 
 
127
    def test_log_zero_begin_revspec(self):
 
128
        self.make_linear_branch()
 
129
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
130
                           ['log', '-r0..2'])
 
131
 
 
132
    def test_log_zero_end_revspec(self):
 
133
        self.make_linear_branch()
 
134
        self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
 
135
                           ['log', '-r-2..0'])
126
136
 
127
137
    def test_log_negative_begin_revspec_full_log(self):
128
138
        self.make_linear_branch()
129
 
        self.assertLogRevnos(['-r-3..'], ['3', '2', '1'])
 
139
        full_log = self.run_bzr(['log'])[0]
 
140
        log = self.run_bzr("log -r -3..")[0]
 
141
        self.assertEqualDiff(full_log, log)
130
142
 
131
143
    def test_log_negative_both_revspec_full_log(self):
132
144
        self.make_linear_branch()
133
 
        self.assertLogRevnos(['-r-3..-1'], ['3', '2', '1'])
 
145
        full_log = self.run_bzr(['log'])[0]
 
146
        log = self.run_bzr("log -r -3..-1")[0]
 
147
        self.assertEqualDiff(full_log, log)
134
148
 
135
149
    def test_log_negative_both_revspec_partial(self):
136
150
        self.make_linear_branch()
137
 
        self.assertLogRevnos(['-r-3..-2'], ['2', '1'])
 
151
        log = self.run_bzr("log -r -3..-2")[0]
 
152
        self.assertTrue('revno: 1\n' in log)
 
153
        self.assertTrue('revno: 2\n' in log)
 
154
        self.assertTrue('revno: 3\n' not in log)
138
155
 
139
156
    def test_log_negative_begin_revspec(self):
140
157
        self.make_linear_branch()
141
 
        self.assertLogRevnos(['-r-2..'], ['3', '2'])
 
158
        log = self.run_bzr("log -r -2..")[0]
 
159
        self.assertTrue('revno: 1\n' not in log)
 
160
        self.assertTrue('revno: 2\n' in log)
 
161
        self.assertTrue('revno: 3\n' in log)
142
162
 
143
163
    def test_log_positive_revspecs(self):
144
164
        self.make_linear_branch()
145
 
        self.assertLogRevnos(['-r1..3'], ['3', '2', '1'])
 
165
        full_log = self.run_bzr(['log'])[0]
 
166
        log = self.run_bzr("log -r 1..3")[0]
 
167
        self.assertEqualDiff(full_log, log)
146
168
 
147
169
    def test_log_dotted_revspecs(self):
148
170
        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):
 
171
        log = self.run_bzr("log -n0 -r 1..1.1.1")[0]
 
172
        self.assertRevnos(log, (1, '1.1.1'), (2, 3, '1.1.2', 4))
 
173
 
 
174
    def test_log_reversed_revspecs(self):
 
175
        self.make_linear_branch()
 
176
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
 
177
                            'the end revision.\n',),
 
178
                           ['log', '-r3..1'])
 
179
 
 
180
    def test_log_reversed_dotted_revspecs(self):
 
181
        self.make_merged_branch()
 
182
        self.run_bzr_error(('bzr: ERROR: Start revision not found in '
 
183
                            'left-hand history of end revision.\n',),
 
184
                           "log -r 1.1.1..1")
 
185
 
 
186
    def test_log_revno_n_path(self):
295
187
        self.make_linear_branch('branch1')
296
188
        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'])
 
189
        # Swapped revisions
 
190
        self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)[0]
 
191
        # Correct order
 
192
        log = self.run_bzr("log -r revno:1:branch2..revno:3:branch2")[0]
 
193
        full_log = self.run_bzr(['log'], working_dir='branch2')[0]
 
194
        self.assertEqualDiff(full_log, log)
 
195
        log = self.run_bzr("log -r revno:1:branch2")[0]
 
196
        self.assertTrue('revno: 1\n' in log)
 
197
        self.assertTrue('revno: 2\n' not in log)
 
198
        self.assertTrue('branch nick: branch2\n' in log)
 
199
        self.assertTrue('branch nick: branch1\n' not in log)
330
200
 
331
201
    def test_log_nonexistent_revno(self):
332
202
        self.make_minimal_branch()
333
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
334
 
                            "does not exist in branch:"],
335
 
                           ['log', '-r1234'])
 
203
        (out, err) = self.run_bzr_error(
 
204
            ["bzr: ERROR: Requested revision: '1234' "
 
205
             "does not exist in branch:"],
 
206
            ['log', '-r1234'])
336
207
 
337
208
    def test_log_nonexistent_dotted_revno(self):
338
209
        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'])
 
210
        (out, err) = self.run_bzr_error(
 
211
            ["bzr: ERROR: Requested revision: '123.123' "
 
212
             "does not exist in branch:"],
 
213
            ['log',  '-r123.123'])
 
214
 
 
215
    def test_log_change_revno(self):
 
216
        self.make_linear_branch()
 
217
        expected_log = self.run_bzr("log -r 1")[0]
 
218
        log = self.run_bzr("log -c 1")[0]
 
219
        self.assertEqualDiff(expected_log, log)
342
220
 
343
221
    def test_log_change_nonexistent_revno(self):
344
222
        self.make_minimal_branch()
345
 
        self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
346
 
                            "does not exist in branch:"],
347
 
                           ['log',  '-c1234'])
 
223
        (out, err) = self.run_bzr_error(
 
224
            ["bzr: ERROR: Requested revision: '1234' "
 
225
             "does not exist in branch:"],
 
226
            ['log',  '-c1234'])
348
227
 
349
228
    def test_log_change_nonexistent_dotted_revno(self):
350
229
        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'])
 
230
        (out, err) = self.run_bzr_error(
 
231
            ["bzr: ERROR: Requested revision: '123.123' "
 
232
             "does not exist in branch:"],
 
233
            ['log', '-c123.123'])
354
234
 
355
235
    def test_log_change_single_revno_only(self):
356
236
        self.make_minimal_branch()
372
252
                              'Path unknown at end or start of revision range: '
373
253
                              'does-not-exist')
374
254
 
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
255
    def test_log_with_tags(self):
420
256
        tree = self.make_linear_branch(format='dirstate-tags')
421
257
        branch = tree.branch
446
282
        log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
447
283
        self.assertContainsRe(log, r'tags: tag1')
448
284
 
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)
 
285
    def test_log_limit(self):
 
286
        tree = self.make_branch_and_tree('.')
 
287
        # We want more commits than our batch size starts at
 
288
        for pos in range(10):
 
289
            tree.commit("%s" % pos)
 
290
        log = self.run_bzr("log --limit 2")[0]
 
291
        self.assertNotContainsRe(log, r'revno: 1\n')
 
292
        self.assertNotContainsRe(log, r'revno: 2\n')
 
293
        self.assertNotContainsRe(log, r'revno: 3\n')
 
294
        self.assertNotContainsRe(log, r'revno: 4\n')
 
295
        self.assertNotContainsRe(log, r'revno: 5\n')
 
296
        self.assertNotContainsRe(log, r'revno: 6\n')
 
297
        self.assertNotContainsRe(log, r'revno: 7\n')
 
298
        self.assertNotContainsRe(log, r'revno: 8\n')
 
299
        self.assertContainsRe(log, r'revno: 9\n')
 
300
        self.assertContainsRe(log, r'revno: 10\n')
 
301
 
 
302
    def test_log_limit_short(self):
 
303
        self.make_linear_branch()
 
304
        log = self.run_bzr("log -l 2")[0]
 
305
        self.assertNotContainsRe(log, r'revno: 1\n')
 
306
        self.assertContainsRe(log, r'revno: 2\n')
 
307
        self.assertContainsRe(log, r'revno: 3\n')
 
308
 
 
309
    def test_log_bad_message_re(self):
 
310
        """Bad --message argument gives a sensible message
 
311
        
 
312
        See https://bugs.launchpad.net/bzr/+bug/251352
 
313
        """
 
314
        self.make_minimal_branch()
 
315
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
 
316
        self.assertEqual("bzr: ERROR: Invalid regular expression"
 
317
            " in log message filter"
 
318
            ": '*'"
 
319
            ": nothing to repeat\n", err)
 
320
        self.assertEqual('', out)
 
321
 
 
322
 
 
323
class TestLogTimeZone(TestLog):
 
324
 
 
325
    def test_log_unsupported_timezone(self):
 
326
        self.make_linear_branch()
 
327
        self.run_bzr_error(['bzr: ERROR: Unsupported timezone format "foo", '
 
328
                            'options are "utc", "original", "local".'],
 
329
                           ['log', '--timezone', 'foo'])
467
330
 
468
331
 
469
332
class TestLogVerbose(TestLog):
487
350
    def test_log_short_verbose(self):
488
351
        self.assertUseShortDeltaFormat(['log', '--short', '-v'])
489
352
 
490
 
    def test_log_s_verbose(self):
491
 
        self.assertUseShortDeltaFormat(['log', '-S', '-v'])
492
 
 
493
353
    def test_log_short_verbose_verbose(self):
494
354
        self.assertUseLongDeltaFormat(['log', '--short', '-vv'])
495
355
 
504
364
        self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
505
365
 
506
366
 
507
 
class TestLogMerges(TestLogWithLogCatcher):
 
367
class TestLogMerges(TestLog):
508
368
 
509
369
    def setUp(self):
510
370
        super(TestLogMerges, self).setUp()
512
372
 
513
373
    def make_branches_with_merges(self):
514
374
        level0 = self.make_branch_and_tree('level0')
515
 
        self.wt_commit(level0, 'in branch level0')
 
375
        level0.commit(message='in branch level0', **self.commit_options())
 
376
 
516
377
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
517
 
        self.wt_commit(level1, 'in branch level1')
 
378
        level1.commit(message='in branch level1', **self.commit_options())
 
379
 
518
380
        level2 = level1.bzrdir.sprout('level2').open_workingtree()
519
 
        self.wt_commit(level2, 'in branch level2')
 
381
        level2.commit(message='in branch level2', **self.commit_options())
 
382
 
520
383
        level1.merge_from_branch(level2.branch)
521
 
        self.wt_commit(level1, 'merge branch level2')
 
384
        level1.commit(message='merge branch level2', **self.commit_options())
 
385
 
522
386
        level0.merge_from_branch(level1.branch)
523
 
        self.wt_commit(level0, 'merge branch level1')
 
387
        level0.commit(message='merge branch level1', **self.commit_options())
524
388
 
525
389
    def test_merges_are_indented_by_level(self):
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()])
 
390
        expected = """\
 
391
------------------------------------------------------------
 
392
revno: 2 [merge]
 
393
committer: Lorem Ipsum <test@example.com>
 
394
branch nick: level0
 
395
timestamp: Just now
 
396
message:
 
397
  merge branch level1
 
398
    ------------------------------------------------------------
 
399
    revno: 1.1.2 [merge]
 
400
    committer: Lorem Ipsum <test@example.com>
 
401
    branch nick: level1
 
402
    timestamp: Just now
 
403
    message:
 
404
      merge branch level2
 
405
        ------------------------------------------------------------
 
406
        revno: 1.2.1
 
407
        committer: Lorem Ipsum <test@example.com>
 
408
        branch nick: level2
 
409
        timestamp: Just now
 
410
        message:
 
411
          in branch level2
 
412
    ------------------------------------------------------------
 
413
    revno: 1.1.1
 
414
    committer: Lorem Ipsum <test@example.com>
 
415
    branch nick: level1
 
416
    timestamp: Just now
 
417
    message:
 
418
      in branch level1
 
419
------------------------------------------------------------
 
420
revno: 1
 
421
committer: Lorem Ipsum <test@example.com>
 
422
branch nick: level0
 
423
timestamp: Just now
 
424
message:
 
425
  in branch level0
 
426
"""
 
427
        self.check_log(expected, ['-n0'])
533
428
 
534
429
    def test_force_merge_revisions_off(self):
535
 
        self.assertLogRevnos(['-n1'], ['2', '1'], working_dir='level0')
 
430
        expected = """\
 
431
------------------------------------------------------------
 
432
revno: 2 [merge]
 
433
committer: Lorem Ipsum <test@example.com>
 
434
branch nick: level0
 
435
timestamp: Just now
 
436
message:
 
437
  merge branch level1
 
438
------------------------------------------------------------
 
439
revno: 1
 
440
committer: Lorem Ipsum <test@example.com>
 
441
branch nick: level0
 
442
timestamp: Just now
 
443
message:
 
444
  in branch level0
 
445
"""
 
446
        self.check_log(expected, ['--long', '-n1'])
536
447
 
537
448
    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')
 
449
        expected = """\
 
450
    2 Lorem Ipsum\t2005-11-22 [merge]
 
451
      merge branch level1
 
452
 
 
453
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
454
                merge branch level2
 
455
 
 
456
              1.2.1 Lorem Ipsum\t2005-11-22
 
457
                    in branch level2
 
458
 
 
459
          1.1.1 Lorem Ipsum\t2005-11-22
 
460
                in branch level1
 
461
 
 
462
    1 Lorem Ipsum\t2005-11-22
 
463
      in branch level0
 
464
 
 
465
"""
 
466
        self.check_log(expected, ['--short', '-n0'])
540
467
 
541
468
    def test_include_merges(self):
542
469
        # 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
470
        out_im, err_im = self.run_bzr('log --include-merges',
553
471
                                      working_dir='level0')
554
472
        out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
555
 
        self.assertEqual(msg, err_im)
 
473
        self.assertEqual('', err_im)
556
474
        self.assertEqual('', err_n0)
557
475
        self.assertEqual(out_im, out_n0)
558
476
 
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
477
    def test_force_merge_revisions_N(self):
568
 
        self.assertLogRevnos(['-n2'],
569
 
                             ['2', '1.1.2', '1.1.1', '1'],
570
 
                             working_dir='level0')
 
478
        expected = """\
 
479
    2 Lorem Ipsum\t2005-11-22 [merge]
 
480
      merge branch level1
 
481
 
 
482
          1.1.2 Lorem Ipsum\t2005-11-22 [merge]
 
483
                merge branch level2
 
484
 
 
485
          1.1.1 Lorem Ipsum\t2005-11-22
 
486
                in branch level1
 
487
 
 
488
    1 Lorem Ipsum\t2005-11-22
 
489
      in branch level0
 
490
 
 
491
"""
 
492
        self.check_log(expected, ['--short', '-n2'])
571
493
 
572
494
    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')
 
495
        expected = """\
 
496
------------------------------------------------------------
 
497
revno: 1.1.2 [merge]
 
498
committer: Lorem Ipsum <test@example.com>
 
499
branch nick: level1
 
500
timestamp: Just now
 
501
message:
 
502
  merge branch level2
 
503
    ------------------------------------------------------------
 
504
    revno: 1.2.1
 
505
    committer: Lorem Ipsum <test@example.com>
 
506
    branch nick: level2
 
507
    timestamp: Just now
 
508
    message:
 
509
      in branch level2
 
510
"""
 
511
        self.check_log(expected, ['-n0', '-r1.1.2'])
576
512
 
577
513
    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
 
514
        expected = """\
 
515
------------------------------------------------------------
 
516
revno: 1.1.2 [merge]
 
517
committer: Lorem Ipsum <test@example.com>
 
518
branch nick: level1
 
519
timestamp: Just now
 
520
message:
 
521
  merge branch level2
 
522
    ------------------------------------------------------------
 
523
    revno: 1.2.1
 
524
    committer: Lorem Ipsum <test@example.com>
 
525
    branch nick: level2
 
526
    timestamp: Just now
 
527
    message:
 
528
      in branch level2
 
529
------------------------------------------------------------
 
530
revno: 1.1.1
 
531
committer: Lorem Ipsum <test@example.com>
 
532
branch nick: level1
 
533
timestamp: Just now
 
534
message:
 
535
  in branch level1
 
536
"""
 
537
        self.check_log(expected, ['-n0', '-r1.1.1..1.1.2'])
 
538
 
 
539
 
 
540
class TestLogDiff(TestLog):
604
541
 
605
542
    def setUp(self):
606
543
        super(TestLogDiff, self).setUp()
611
548
        self.build_tree(['level0/file1', 'level0/file2'])
612
549
        level0.add('file1')
613
550
        level0.add('file2')
614
 
        self.wt_commit(level0, 'in branch level0')
 
551
        level0.commit(message='in branch level0', **self.commit_options())
615
552
 
616
553
        level1 = level0.bzrdir.sprout('level1').open_workingtree()
617
554
        self.build_tree_contents([('level1/file2', 'hello\n')])
618
 
        self.wt_commit(level1, 'in branch level1')
 
555
        level1.commit(message='in branch level1', **self.commit_options())
619
556
        level0.merge_from_branch(level1.branch)
620
 
        self.wt_commit(level0, 'merge branch level1')
 
557
        level0.commit(message='merge branch level1', **self.commit_options())
621
558
 
622
 
    def _diff_file1_revno1(self):
623
 
        return """=== added file 'file1'
 
559
    def test_log_show_diff_long_with_merges(self):
 
560
        out,err = self.run_bzr('log -p -n0')
 
561
        self.assertEqual('', err)
 
562
        log = test_log.normalize_log(out)
 
563
        expected = """\
 
564
------------------------------------------------------------
 
565
revno: 2 [merge]
 
566
committer: Lorem Ipsum <test@example.com>
 
567
branch nick: level0
 
568
timestamp: Just now
 
569
message:
 
570
  merge branch level1
 
571
diff:
 
572
=== modified file 'file2'
 
573
--- file2\t2005-11-22 00:00:01 +0000
 
574
+++ file2\t2005-11-22 00:00:02 +0000
 
575
@@ -1,1 +1,1 @@
 
576
-contents of level0/file2
 
577
+hello
 
578
    ------------------------------------------------------------
 
579
    revno: 1.1.1
 
580
    committer: Lorem Ipsum <test@example.com>
 
581
    branch nick: level1
 
582
    timestamp: Just now
 
583
    message:
 
584
      in branch level1
 
585
    diff:
 
586
    === modified file 'file2'
 
587
    --- file2\t2005-11-22 00:00:01 +0000
 
588
    +++ file2\t2005-11-22 00:00:02 +0000
 
589
    @@ -1,1 +1,1 @@
 
590
    -contents of level0/file2
 
591
    +hello
 
592
------------------------------------------------------------
 
593
revno: 1
 
594
committer: Lorem Ipsum <test@example.com>
 
595
branch nick: level0
 
596
timestamp: Just now
 
597
message:
 
598
  in branch level0
 
599
diff:
 
600
=== added file 'file1'
624
601
--- file1\t1970-01-01 00:00:00 +0000
625
 
+++ file1\t2005-11-22 00:00:00 +0000
 
602
+++ file1\t2005-11-22 00:00:01 +0000
626
603
@@ -0,0 +1,1 @@
627
604
+contents of level0/file1
628
605
 
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'
 
606
=== added file 'file2'
653
607
--- file2\t1970-01-01 00:00:00 +0000
654
 
+++ file2\t2005-11-22 00:00:00 +0000
 
608
+++ file2\t2005-11-22 00:00:01 +0000
655
609
@@ -0,0 +1,1 @@
656
610
+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')
 
611
"""
 
612
        self.check_log(expected, ['-p', '-n0'])
 
613
 
 
614
    def test_log_show_diff_short(self):
 
615
        expected = """\
 
616
    2 Lorem Ipsum\t2005-11-22 [merge]
 
617
      merge branch level1
 
618
      === modified file 'file2'
 
619
      --- file2\t2005-11-22 00:00:01 +0000
 
620
      +++ file2\t2005-11-22 00:00:02 +0000
 
621
      @@ -1,1 +1,1 @@
 
622
      -contents of level0/file2
 
623
      +hello
 
624
 
 
625
    1 Lorem Ipsum\t2005-11-22
 
626
      in branch level0
 
627
      === added file 'file1'
 
628
      --- file1\t1970-01-01 00:00:00 +0000
 
629
      +++ file1\t2005-11-22 00:00:01 +0000
 
630
      @@ -0,0 +1,1 @@
 
631
      +contents of level0/file1
 
632
\x20\x20\x20\x20\x20\x20
 
633
      === added file 'file2'
 
634
      --- file2\t1970-01-01 00:00:00 +0000
 
635
      +++ file2\t2005-11-22 00:00:01 +0000
 
636
      @@ -0,0 +1,1 @@
 
637
      +contents of level0/file2
 
638
 
 
639
Use --include-merges or -n0 to see merged revisions.
 
640
"""
 
641
        self.check_log(expected, ['-p', '--short'])
 
642
 
 
643
    def test_log_show_diff_line(self):
 
644
        # Not supported by this formatter so expect plain output
 
645
        expected = """\
 
646
2: Lorem Ipsum 2005-11-22 [merge] merge branch level1
 
647
1: Lorem Ipsum 2005-11-22 in branch level0
 
648
"""
 
649
        self.check_log(expected, ['-p', '--line'])
 
650
 
 
651
    def test_log_show_diff_file1(self):
 
652
        """Only the diffs for the given file are to be shown"""
 
653
        expected = """\
 
654
    1 Lorem Ipsum\t2005-11-22
 
655
      in branch level0
 
656
      === added file 'file1'
 
657
      --- file1\t1970-01-01 00:00:00 +0000
 
658
      +++ file1\t2005-11-22 00:00:01 +0000
 
659
      @@ -0,0 +1,1 @@
 
660
      +contents of level0/file1
 
661
 
 
662
"""
 
663
        self.check_log(expected, ['-p', '--short', 'file1'])
 
664
 
 
665
    def test_log_show_diff_file2(self):
 
666
        """Only the diffs for the given file are to be shown"""
 
667
        expected = """\
 
668
    2 Lorem Ipsum\t2005-11-22 [merge]
 
669
      merge branch level1
 
670
      === modified file 'file2'
 
671
      --- file2\t2005-11-22 00:00:01 +0000
 
672
      +++ file2\t2005-11-22 00:00:02 +0000
 
673
      @@ -1,1 +1,1 @@
 
674
      -contents of level0/file2
 
675
      +hello
 
676
 
 
677
    1 Lorem Ipsum\t2005-11-22
 
678
      in branch level0
 
679
      === added file 'file2'
 
680
      --- file2\t1970-01-01 00:00:00 +0000
 
681
      +++ file2\t2005-11-22 00:00:01 +0000
 
682
      @@ -0,0 +1,1 @@
 
683
      +contents of level0/file2
 
684
 
 
685
Use --include-merges or -n0 to see merged revisions.
 
686
"""
 
687
        self.check_log(expected, ['-p', '--short', 'file2'])
698
688
 
699
689
 
700
690
class TestLogUnicodeDiff(TestLog):
743
733
 
744
734
    def setUp(self):
745
735
        super(TestLogEncodings, self).setUp()
746
 
        self.overrideAttr(osutils, '_cached_user_encoding')
 
736
        self.user_encoding = osutils._cached_user_encoding
 
737
        def restore():
 
738
            osutils._cached_user_encoding = self.user_encoding
 
739
        self.addCleanup(restore)
747
740
 
748
741
    def create_branch(self):
749
742
        bzr = self.run_bzr
750
743
        bzr('init')
751
 
        self.build_tree_contents([('a', 'some stuff\n')])
 
744
        open('a', 'wb').write('some stuff\n')
752
745
        bzr('add a')
753
746
        bzr(['commit', '-m', self._message])
754
747
 
817
810
        self.assertEquals(-1, stdout.find(test_in_cp1251))
818
811
 
819
812
 
820
 
class TestLogFile(TestLogWithLogCatcher):
 
813
class TestLogFile(tests.TestCaseWithTransport):
821
814
 
822
815
    def test_log_local_branch_file(self):
823
816
        """We should be able to log files in local treeless branches"""
852
845
            tree.commit('remove file1')
853
846
        os.chdir('parent')
854
847
 
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'])
 
848
    def test_log_file(self):
 
849
        """The log for a particular file should only list revs for that file"""
 
850
        self.prepare_tree()
 
851
        log = self.run_bzr('log -n0 file1')[0]
 
852
        self.assertContainsRe(log, 'revno: 1\n')
 
853
        self.assertNotContainsRe(log, 'revno: 2\n')
 
854
        self.assertNotContainsRe(log, 'revno: 3\n')
 
855
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
856
        self.assertNotContainsRe(log, 'revno: 4 ')
 
857
        log = self.run_bzr('log -n0 file2')[0]
 
858
        self.assertNotContainsRe(log, 'revno: 1\n')
 
859
        self.assertContainsRe(log, 'revno: 2\n')
 
860
        self.assertNotContainsRe(log, 'revno: 3\n')
 
861
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
862
        self.assertContainsRe(log, 'revno: 4 ')
 
863
        log = self.run_bzr('log -n0 file3')[0]
 
864
        self.assertNotContainsRe(log, 'revno: 1\n')
 
865
        self.assertNotContainsRe(log, 'revno: 2\n')
 
866
        self.assertContainsRe(log, 'revno: 3\n')
 
867
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
868
        self.assertNotContainsRe(log, 'revno: 4 ')
 
869
        log = self.run_bzr('log -n0 -r3.1.1 file2')[0]
 
870
        self.assertNotContainsRe(log, 'revno: 1\n')
 
871
        self.assertNotContainsRe(log, 'revno: 2\n')
 
872
        self.assertNotContainsRe(log, 'revno: 3\n')
 
873
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
874
        self.assertNotContainsRe(log, 'revno: 4 ')
 
875
        log = self.run_bzr('log -n0 -r4 file2')[0]
 
876
        self.assertNotContainsRe(log, 'revno: 1\n')
 
877
        self.assertNotContainsRe(log, 'revno: 2\n')
 
878
        self.assertNotContainsRe(log, 'revno: 3\n')
 
879
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
880
        self.assertContainsRe(log, 'revno: 4 ')
 
881
        log = self.run_bzr('log -n0 -r3.. file2')[0]
 
882
        self.assertNotContainsRe(log, 'revno: 1\n')
 
883
        self.assertNotContainsRe(log, 'revno: 2\n')
 
884
        self.assertNotContainsRe(log, 'revno: 3\n')
 
885
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
886
        self.assertContainsRe(log, 'revno: 4 ')
 
887
        log = self.run_bzr('log -n0 -r..3 file2')[0]
 
888
        self.assertNotContainsRe(log, 'revno: 1\n')
 
889
        self.assertContainsRe(log, 'revno: 2\n')
 
890
        self.assertNotContainsRe(log, 'revno: 3\n')
 
891
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
892
        self.assertNotContainsRe(log, 'revno: 4 ')
878
893
 
879
894
    def test_log_file_historical_missing(self):
880
895
        # Check logging a deleted file gives an error if the
888
903
        # Check logging a deleted file is ok if the file existed
889
904
        # at the end the revision range
890
905
        self.prepare_tree(complex=True)
891
 
        self.assertLogRevnos(['-n0', '-r..4', 'file2'], ['4', '3.1.1', '2'])
 
906
        log, err = self.run_bzr('log -n0 -r..4 file2')
 
907
        self.assertEquals('', err)
 
908
        self.assertNotContainsRe(log, 'revno: 1\n')
 
909
        self.assertContainsRe(log, 'revno: 2\n')
 
910
        self.assertNotContainsRe(log, 'revno: 3\n')
 
911
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
912
        self.assertContainsRe(log, 'revno: 4 ')
892
913
 
893
914
    def test_log_file_historical_start(self):
894
915
        # Check logging a deleted file is ok if the file existed
895
916
        # at the start of the revision range
896
917
        self.prepare_tree(complex=True)
897
 
        self.assertLogRevnos(['file1'], ['1'])
 
918
        log, err = self.run_bzr('log file1')
 
919
        self.assertEquals('', err)
 
920
        self.assertContainsRe(log, 'revno: 1\n')
 
921
        self.assertNotContainsRe(log, 'revno: 2\n')
 
922
        self.assertNotContainsRe(log, 'revno: 3\n')
 
923
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
924
        self.assertNotContainsRe(log, 'revno: 4 ')
898
925
 
899
926
    def test_log_file_renamed(self):
900
927
        """File matched against revision range, not current tree."""
906
933
        self.assertContainsRe(err, err_msg)
907
934
 
908
935
        # 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):
 
936
        log, err = self.run_bzr('log -r..4 file3')
 
937
        self.assertEquals('', err)
 
938
        self.assertNotContainsRe(log, 'revno: 1\n')
 
939
        self.assertNotContainsRe(log, 'revno: 2\n')
 
940
        self.assertContainsRe(log, 'revno: 3\n')
 
941
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
942
        self.assertNotContainsRe(log, 'revno: 4 ')
 
943
 
 
944
    def test_line_log_file(self):
 
945
        """The line log for a file should only list relevant mainline revs"""
 
946
        # Note: this also implicitly  covers the short logging case.
 
947
        # We test using --line in preference to --short because matching
 
948
        # revnos in the output of --line is more reliable.
 
949
        self.prepare_tree()
 
950
 
 
951
        # full history of file1
 
952
        log = self.run_bzr('log --line file1')[0]
 
953
        self.assertContainsRe(log, '^1:', re.MULTILINE)
 
954
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
955
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
956
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
957
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
958
 
 
959
        # full history of file2
 
960
        log = self.run_bzr('log --line file2')[0]
 
961
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
962
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
963
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
964
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
965
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
966
 
 
967
        # full history of file3
 
968
        log = self.run_bzr('log --line file3')[0]
 
969
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
970
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
971
        self.assertContainsRe(log, '^3:', re.MULTILINE)
 
972
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
973
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
974
 
 
975
        # file in a merge revision
 
976
        log = self.run_bzr('log --line -r3.1.1 file2')[0]
 
977
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
978
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
979
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
980
        self.assertContainsRe(log, '^3.1.1:', re.MULTILINE)
 
981
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
982
 
 
983
        # file in a mainline revision
 
984
        log = self.run_bzr('log --line -r4 file2')[0]
 
985
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
986
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
987
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
988
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
989
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
990
 
 
991
        # file since a revision
 
992
        log = self.run_bzr('log --line -r3.. file2')[0]
 
993
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
994
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
995
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
996
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
997
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
998
 
 
999
        # file up to a revision
 
1000
        log = self.run_bzr('log --line -r..3 file2')[0]
 
1001
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
1002
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
1003
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
1004
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
1005
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
1006
 
 
1007
 
 
1008
class TestLogMultiple(tests.TestCaseWithTransport):
913
1009
 
914
1010
    def prepare_tree(self):
915
1011
        tree = self.make_branch_and_tree('parent')
938
1034
        tree.commit(message='merge child branch')
939
1035
        os.chdir('parent')
940
1036
 
 
1037
    def assertRevnos(self, paths_str, expected_revnos):
 
1038
        # confirm the revision numbers in log --line output are those expected
 
1039
        out, err = self.run_bzr('log --line -n0 %s' % (paths_str,))
 
1040
        self.assertEqual('', err)
 
1041
        revnos = [s.split(':', 1)[0].lstrip() for s in out.splitlines()]
 
1042
        self.assertEqual(expected_revnos, revnos)
 
1043
 
941
1044
    def test_log_files(self):
942
1045
        """The log for multiple file should only list revs for those files"""
943
1046
        self.prepare_tree()
944
 
        self.assertLogRevnos(['file1', 'file2', 'dir1/dir2/file3'],
945
 
                             ['6', '5.1.1', '3', '2', '1'])
 
1047
        self.assertRevnos('file1 file2 dir1/dir2/file3',
 
1048
            ['6', '5.1.1', '3', '2', '1'])
946
1049
 
947
1050
    def test_log_directory(self):
948
1051
        """The log for a directory should show all nested files."""
949
1052
        self.prepare_tree()
950
 
        self.assertLogRevnos(['dir1'], ['5', '3'])
 
1053
        self.assertRevnos('dir1', ['5', '3'])
951
1054
 
952
1055
    def test_log_nested_directory(self):
953
1056
        """The log for a directory should show all nested files."""
954
1057
        self.prepare_tree()
955
 
        self.assertLogRevnos(['dir1/dir2'], ['3'])
 
1058
        self.assertRevnos('dir1/dir2', ['3'])
956
1059
 
957
1060
    def test_log_in_nested_directory(self):
958
1061
        """The log for a directory should show all nested files."""
959
1062
        self.prepare_tree()
960
1063
        os.chdir("dir1")
961
 
        self.assertLogRevnos(['.'], ['5', '3'])
 
1064
        self.assertRevnos('.', ['5', '3'])
962
1065
 
963
1066
    def test_log_files_and_directories(self):
964
1067
        """Logging files and directories together should be fine."""
965
1068
        self.prepare_tree()
966
 
        self.assertLogRevnos(['file4', 'dir1/dir2'], ['4', '3'])
 
1069
        self.assertRevnos('file4 dir1/dir2', ['4', '3'])
967
1070
 
968
1071
    def test_log_files_and_dirs_in_nested_directory(self):
969
1072
        """The log for a directory should show all nested files."""
970
1073
        self.prepare_tree()
971
1074
        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"])
1055
 
 
1056
 
 
1057
 
class TestSmartServerLog(tests.TestCaseWithTransport):
1058
 
 
1059
 
    def test_standard_log(self):
1060
 
        self.setup_smart_server_with_call_log()
1061
 
        t = self.make_branch_and_tree('branch')
1062
 
        self.build_tree_contents([('branch/foo', 'thecontents')])
1063
 
        t.add("foo")
1064
 
        t.commit("message")
1065
 
        self.reset_smart_call_log()
1066
 
        out, err = self.run_bzr(['log', self.get_url('branch')])
1067
 
        # This figure represent the amount of work to perform this use case. It
1068
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
1069
 
        # being too low. If rpc_count increases, more network roundtrips have
1070
 
        # become necessary for this use case. Please do not adjust this number
1071
 
        # upwards without agreement from bzr's network support maintainers.
1072
 
        self.assertLength(10, self.hpss_calls)
1073
 
 
1074
 
    def test_verbose_log(self):
1075
 
        self.setup_smart_server_with_call_log()
1076
 
        t = self.make_branch_and_tree('branch')
1077
 
        self.build_tree_contents([('branch/foo', 'thecontents')])
1078
 
        t.add("foo")
1079
 
        t.commit("message")
1080
 
        self.reset_smart_call_log()
1081
 
        out, err = self.run_bzr(['log', '-v', self.get_url('branch')])
1082
 
        # This figure represent the amount of work to perform this use case. It
1083
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
1084
 
        # being too low. If rpc_count increases, more network roundtrips have
1085
 
        # become necessary for this use case. Please do not adjust this number
1086
 
        # upwards without agreement from bzr's network support maintainers.
1087
 
        self.assertLength(19, self.hpss_calls)
1088
 
 
1089
 
    def test_per_file(self):
1090
 
        self.setup_smart_server_with_call_log()
1091
 
        t = self.make_branch_and_tree('branch')
1092
 
        self.build_tree_contents([('branch/foo', 'thecontents')])
1093
 
        t.add("foo")
1094
 
        t.commit("message")
1095
 
        self.reset_smart_call_log()
1096
 
        out, err = self.run_bzr(['log', '-v', self.get_url('branch') + "/foo"])
1097
 
        # This figure represent the amount of work to perform this use case. It
1098
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
1099
 
        # being too low. If rpc_count increases, more network roundtrips have
1100
 
        # become necessary for this use case. Please do not adjust this number
1101
 
        # upwards without agreement from bzr's network support maintainers.
1102
 
        self.assertLength(21, self.hpss_calls)
 
1075
        self.assertRevnos('dir2 file5', ['5', '3'])