14
13
# You should have received a copy of the GNU General Public License
15
14
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
18
"""Black-box tests for bzr log."""
20
from itertools import izip
24
from bzrlib.tests.blackbox import ExternalBase
25
from bzrlib.tests import TestCaseInTempDir
28
class TestLog(ExternalBase):
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]
30
from bzrlib.tests import (
36
class TestLog(tests.TestCaseWithTransport, test_log.TestLogMixin):
38
def make_minimal_branch(self, path='.', format=None):
39
tree = self.make_branch_and_tree(path, format=format)
40
self.build_tree([path + '/hello.txt'])
42
tree.commit(message='message1')
45
def make_linear_branch(self, path='.', format=None):
46
tree = self.make_branch_and_tree(path, format=format)
48
[path + '/hello.txt', path + '/goodbye.txt', path + '/meep.txt'])
50
tree.commit(message='message1')
51
tree.add('goodbye.txt')
52
tree.commit(message='message2')
54
tree.commit(message='message3')
57
def make_merged_branch(self, path='.', format=None):
58
tree = self.make_linear_branch(path, format)
59
tree2 = tree.bzrdir.sprout('tree2',
60
revision_id=tree.branch.get_rev_id(1)).open_workingtree()
61
tree2.commit(message='tree2 message2')
62
tree2.commit(message='tree2 message3')
63
tree.merge_from_branch(tree2.branch)
64
tree.commit(message='merge')
68
class TestLogWithLogCatcher(TestLog):
71
super(TestLogWithLogCatcher, self).setUp()
72
# Capture log formatter creations
73
class MyLogFormatter(test_log.LogCatcher):
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
81
# Always return our own log formatter class hijacking the
82
# default behavior (which requires setting up a config
85
self.overrideAttr(log.log_formatter_registry, 'get_default', getme)
87
def get_captured_revisions(self):
88
return self.log_catcher.revisions
90
def assertLogRevnos(self, args, expected_revnos, working_dir='.'):
91
self.run_bzr(['log'] + args, working_dir=working_dir)
92
self.assertEqual(expected_revnos,
93
[r.revno for r in self.get_captured_revisions()])
95
def assertLogRevnosAndDepths(self, args, expected_revnos_and_depths,
97
self.run_bzr(['log'] + args, working_dir=working_dir)
98
self.assertEqual(expected_revnos_and_depths,
99
[(r.revno, r.merge_depth)
100
for r in self.get_captured_revisions()])
103
class TestLogRevSpecs(TestLogWithLogCatcher):
105
def test_log_no_revspec(self):
106
self.make_linear_branch()
107
self.assertLogRevnos([], ['3', '2', '1'])
41
109
def test_log_null_end_revspec(self):
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)
50
log = self.runbzr("log -r 1..")[0]
51
self.assertEquals(log, self.full_log)
110
self.make_linear_branch()
111
self.assertLogRevnos(['-r1..'], ['3', '2', '1'])
53
113
def test_log_null_begin_revspec(self):
55
log = self.runbzr("log -r ..3")[0]
56
self.assertEquals(self.full_log, log)
114
self.make_linear_branch()
115
self.assertLogRevnos(['-r..3'], ['3', '2', '1'])
58
117
def test_log_null_both_revspecs(self):
60
log = self.runbzr("log -r ..")[0]
61
self.assertEquals(self.full_log, log)
118
self.make_linear_branch()
119
self.assertLogRevnos(['-r..'], ['3', '2', '1'])
63
121
def test_log_negative_begin_revspec_full_log(self):
65
log = self.runbzr("log -r -3..")[0]
66
self.assertEquals(self.full_log, log)
122
self.make_linear_branch()
123
self.assertLogRevnos(['-r-3..'], ['3', '2', '1'])
68
125
def test_log_negative_both_revspec_full_log(self):
70
log = self.runbzr("log -r -3..-1")[0]
71
self.assertEquals(self.full_log, log)
126
self.make_linear_branch()
127
self.assertLogRevnos(['-r-3..-1'], ['3', '2', '1'])
73
129
def test_log_negative_both_revspec_partial(self):
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)
130
self.make_linear_branch()
131
self.assertLogRevnos(['-r-3..-2'], ['2', '1'])
80
133
def test_log_negative_begin_revspec(self):
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)
87
def test_log_postive_revspecs(self):
89
log = self.runbzr("log -r 1..3")[0]
90
self.assertEquals(self.full_log, log)
93
class TestLogMerges(ExternalBase):
134
self.make_linear_branch()
135
self.assertLogRevnos(['-r-2..'], ['3', '2'])
137
def test_log_positive_revspecs(self):
138
self.make_linear_branch()
139
self.assertLogRevnos(['-r1..3'], ['3', '2', '1'])
141
def test_log_dotted_revspecs(self):
142
self.make_merged_branch()
143
self.assertLogRevnos(['-n0', '-r1..1.1.1'], ['1.1.1', '1'])
145
def test_log_limit(self):
146
tree = self.make_branch_and_tree('.')
147
# We want more commits than our batch size starts at
148
for pos in range(10):
149
tree.commit("%s" % pos)
150
self.assertLogRevnos(['--limit', '2'], ['10', '9'])
152
def test_log_limit_short(self):
153
self.make_linear_branch()
154
self.assertLogRevnos(['-l', '2'], ['3', '2'])
156
def test_log_change_revno(self):
157
self.make_linear_branch()
158
self.assertLogRevnos(['-c1'], ['1'])
161
class TestBug474807(TestLogWithLogCatcher):
164
super(TestBug474807, self).setUp()
165
# FIXME: Using a MemoryTree would be even better here (but until we
166
# stop calling run_bzr, there is no point) --vila 100118.
167
builder = branchbuilder.BranchBuilder(self.get_transport())
168
builder.start_series()
170
builder.build_snapshot('1', None, [
171
('add', ('', 'root-id', 'directory', ''))])
172
builder.build_snapshot('2', ['1'], [])
174
builder.build_snapshot('1.1.1', ['1'], [])
175
# merge branch into mainline
176
builder.build_snapshot('3', ['2', '1.1.1'], [])
177
# new commits in branch
178
builder.build_snapshot('1.1.2', ['1.1.1'], [])
179
builder.build_snapshot('1.1.3', ['1.1.2'], [])
180
# merge branch into mainline
181
builder.build_snapshot('4', ['3', '1.1.3'], [])
182
# merge mainline into branch
183
builder.build_snapshot('1.1.4', ['1.1.3', '4'], [])
184
# merge branch into mainline
185
builder.build_snapshot('5', ['4', '1.1.4'], [])
186
builder.finish_series()
189
self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4'],
190
['1.1.4', '4', '1.1.3', '1.1.2', '3', '1.1.1'])
191
def test_n0_forward(self):
192
self.assertLogRevnos(['-n0', '-r1.1.1..1.1.4', '--forward'],
193
['3', '1.1.1', '4', '1.1.2', '1.1.3', '1.1.4'])
196
# starting from 1.1.4 we follow the left-hand ancestry
197
self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4'],
198
['1.1.4', '1.1.3', '1.1.2', '1.1.1'])
200
def test_n1_forward(self):
201
self.assertLogRevnos(['-n1', '-r1.1.1..1.1.4', '--forward'],
202
['1.1.1', '1.1.2', '1.1.3', '1.1.4'])
205
class TestLogRevSpecsWithPaths(TestLogWithLogCatcher):
207
def test_log_revno_n_path_wrong_namespace(self):
208
self.make_linear_branch('branch1')
209
self.make_linear_branch('branch2')
210
# There is no guarantee that a path exist between two arbitrary
212
self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)
213
# But may be it's worth trying though ? -- vila 100115
215
def test_log_revno_n_path_correct_order(self):
216
self.make_linear_branch('branch2')
217
self.assertLogRevnos(['-rrevno:1:branch2..revno:3:branch2'],
220
def test_log_revno_n_path(self):
221
self.make_linear_branch('branch2')
222
self.assertLogRevnos(['-rrevno:1:branch2'],
224
rev_props = self.log_catcher.revisions[0].rev.properties
225
self.assertEqual('branch2', rev_props['branch-nick'])
228
class TestLogErrors(TestLog):
230
def test_log_zero_revspec(self):
231
self.make_minimal_branch()
232
self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
235
def test_log_zero_begin_revspec(self):
236
self.make_linear_branch()
237
self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
240
def test_log_zero_end_revspec(self):
241
self.make_linear_branch()
242
self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
245
def test_log_nonexistent_revno(self):
246
self.make_minimal_branch()
247
self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
248
"does not exist in branch:"],
251
def test_log_nonexistent_dotted_revno(self):
252
self.make_minimal_branch()
253
self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
254
"does not exist in branch:"],
255
['log', '-r123.123'])
257
def test_log_change_nonexistent_revno(self):
258
self.make_minimal_branch()
259
self.run_bzr_error(["bzr: ERROR: Requested revision: '1234' "
260
"does not exist in branch:"],
263
def test_log_change_nonexistent_dotted_revno(self):
264
self.make_minimal_branch()
265
self.run_bzr_error(["bzr: ERROR: Requested revision: '123.123' "
266
"does not exist in branch:"],
267
['log', '-c123.123'])
269
def test_log_change_single_revno_only(self):
270
self.make_minimal_branch()
271
self.run_bzr_error(['bzr: ERROR: Option --change does not'
272
' accept revision ranges'],
273
['log', '--change', '2..3'])
275
def test_log_change_incompatible_with_revision(self):
276
self.run_bzr_error(['bzr: ERROR: --revision and --change'
277
' are mutually exclusive'],
278
['log', '--change', '2', '--revision', '3'])
280
def test_log_nonexistent_file(self):
281
self.make_minimal_branch()
282
# files that don't exist in either the basis tree or working tree
283
# should give an error
284
out, err = self.run_bzr('log does-not-exist', retcode=3)
285
self.assertContainsRe(err,
286
'Path unknown at end or start of revision range: '
289
def test_log_reversed_revspecs(self):
290
self.make_linear_branch()
291
self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
292
'the end revision.\n',),
295
def test_log_reversed_dotted_revspecs(self):
296
self.make_merged_branch()
297
self.run_bzr_error(('bzr: ERROR: Start revision not found in '
298
'left-hand history of end revision.\n',),
301
def test_log_bad_message_re(self):
302
"""Bad --message argument gives a sensible message
304
See https://bugs.launchpad.net/bzr/+bug/251352
306
self.make_minimal_branch()
307
out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
308
self.assertEqual("bzr: ERROR: Invalid regular expression"
309
" in log message filter"
311
": nothing to repeat\n", err)
312
self.assertEqual('', out)
314
def test_log_unsupported_timezone(self):
315
self.make_linear_branch()
316
self.run_bzr_error(['bzr: ERROR: Unsupported timezone format "foo", '
317
'options are "utc", "original", "local".'],
318
['log', '--timezone', 'foo'])
321
class TestLogTags(TestLog):
323
def test_log_with_tags(self):
324
tree = self.make_linear_branch(format='dirstate-tags')
326
branch.tags.set_tag('tag1', branch.get_rev_id(1))
327
branch.tags.set_tag('tag1.1', branch.get_rev_id(1))
328
branch.tags.set_tag('tag3', branch.last_revision())
330
log = self.run_bzr("log -r-1")[0]
331
self.assertTrue('tags: tag3' in log)
333
log = self.run_bzr("log -r1")[0]
334
# I guess that we can't know the order of tags in the output
335
# since dicts are unordered, need to check both possibilities
336
self.assertContainsRe(log, r'tags: (tag1, tag1\.1|tag1\.1, tag1)')
338
def test_merged_log_with_tags(self):
339
branch1_tree = self.make_linear_branch('branch1',
340
format='dirstate-tags')
341
branch1 = branch1_tree.branch
342
branch2_tree = branch1_tree.bzrdir.sprout('branch2').open_workingtree()
343
branch1_tree.commit(message='foobar', allow_pointless=True)
344
branch1.tags.set_tag('tag1', branch1.last_revision())
345
# tags don't propagate if we don't merge
346
self.run_bzr('merge ../branch1', working_dir='branch2')
347
branch2_tree.commit(message='merge branch 1')
348
log = self.run_bzr("log -n0 -r-1", working_dir='branch2')[0]
349
self.assertContainsRe(log, r' tags: tag1')
350
log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
351
self.assertContainsRe(log, r'tags: tag1')
354
class TestLogVerbose(TestLog):
357
super(TestLogVerbose, self).setUp()
358
self.make_minimal_branch()
360
def assertUseShortDeltaFormat(self, cmd):
361
log = self.run_bzr(cmd)[0]
362
# Check that we use the short status format
363
self.assertContainsRe(log, '(?m)^\s*A hello.txt$')
364
self.assertNotContainsRe(log, '(?m)^\s*added:$')
366
def assertUseLongDeltaFormat(self, cmd):
367
log = self.run_bzr(cmd)[0]
368
# Check that we use the long status format
369
self.assertNotContainsRe(log, '(?m)^\s*A hello.txt$')
370
self.assertContainsRe(log, '(?m)^\s*added:$')
372
def test_log_short_verbose(self):
373
self.assertUseShortDeltaFormat(['log', '--short', '-v'])
375
def test_log_short_verbose_verbose(self):
376
self.assertUseLongDeltaFormat(['log', '--short', '-vv'])
378
def test_log_long_verbose(self):
379
# Check that we use the long status format, ignoring the verbosity
381
self.assertUseLongDeltaFormat(['log', '--long', '-v'])
383
def test_log_long_verbose_verbose(self):
384
# Check that we use the long status format, ignoring the verbosity
386
self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
389
class TestLogMerges(TestLogWithLogCatcher):
392
super(TestLogMerges, self).setUp()
393
self.make_branches_with_merges()
395
def make_branches_with_merges(self):
396
level0 = self.make_branch_and_tree('level0')
397
self.wt_commit(level0, 'in branch level0')
398
level1 = level0.bzrdir.sprout('level1').open_workingtree()
399
self.wt_commit(level1, 'in branch level1')
400
level2 = level1.bzrdir.sprout('level2').open_workingtree()
401
self.wt_commit(level2, 'in branch level2')
402
level1.merge_from_branch(level2.branch)
403
self.wt_commit(level1, 'merge branch level2')
404
level0.merge_from_branch(level1.branch)
405
self.wt_commit(level0, 'merge branch level1')
95
407
def test_merges_are_indented_by_level(self):
96
self.build_tree(['parent/'])
97
self.run_bzr('init', 'parent')
98
self.run_bzr('commit', '-m', 'first post', '--unchanged', 'parent')
99
self.run_bzr('branch', 'parent', 'child')
100
self.run_bzr('commit', '-m', 'branch 1', '--unchanged', 'child')
101
self.run_bzr('branch', 'child', 'smallerchild')
102
self.run_bzr('commit', '-m', 'branch 2', '--unchanged', 'smallerchild')
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
#------------------------------------------------------------
114
#committer: Robert Collins <foo@example.com>
116
#timestamp: Tue 2006-03-28 22:31:40 +1100
119
# ------------------------------------------------------------
120
# merged: foo@example.com-20060328113140-91f43cfb46dc2863
121
# committer: Robert Collins <foo@example.com>
123
# timestamp: Tue 2006-03-28 22:31:40 +1100
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
133
# ------------------------------------------------------------
134
# merged: foo@example.com-20060328113140-5749a4757a8ac792
135
# committer: Robert Collins <foo@example.com>
137
# timestamp: Tue 2006-03-28 22:31:40 +1100
140
#------------------------------------------------------------
142
#committer: Robert Collins <foo@example.com>
144
#timestamp: Tue 2006-03-28 22:31:39 +1100
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)
158
class TestLogEncodings(TestCaseInTempDir):
408
self.run_bzr(['log', '-n0'], working_dir='level0')
409
revnos_and_depth = [(r.revno, r.merge_depth)
410
for r in self.get_captured_revisions()]
411
self.assertEqual([('2', 0), ('1.1.2', 1), ('1.2.1', 2), ('1.1.1', 1),
413
[(r.revno, r.merge_depth)
414
for r in self.get_captured_revisions()])
416
def test_force_merge_revisions_off(self):
417
self.assertLogRevnos(['-n1'], ['2', '1'], working_dir='level0')
419
def test_force_merge_revisions_on(self):
420
self.assertLogRevnos(['-n0'], ['2', '1.1.2', '1.2.1', '1.1.1', '1'],
421
working_dir='level0')
423
def test_include_merges(self):
424
# Confirm --include-merges gives the same output as -n0
425
self.assertLogRevnos(['--include-merges'],
426
['2', '1.1.2', '1.2.1', '1.1.1', '1'],
427
working_dir='level0')
428
self.assertLogRevnos(['--include-merges'],
429
['2', '1.1.2', '1.2.1', '1.1.1', '1'],
430
working_dir='level0')
431
out_im, err_im = self.run_bzr('log --include-merges',
432
working_dir='level0')
433
out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
434
self.assertEqual('', err_im)
435
self.assertEqual('', err_n0)
436
self.assertEqual(out_im, out_n0)
438
def test_force_merge_revisions_N(self):
439
self.assertLogRevnos(['-n2'],
440
['2', '1.1.2', '1.1.1', '1'],
441
working_dir='level0')
443
def test_merges_single_merge_rev(self):
444
self.assertLogRevnosAndDepths(['-n0', '-r1.1.2'],
445
[('1.1.2', 0), ('1.2.1', 1)],
446
working_dir='level0')
448
def test_merges_partial_range(self):
449
self.assertLogRevnosAndDepths(
450
['-n0', '-r1.1.1..1.1.2'],
451
[('1.1.2', 0), ('1.2.1', 1), ('1.1.1', 0)],
452
working_dir='level0')
454
def test_merges_partial_range_ignore_before_lower_bound(self):
455
"""Dont show revisions before the lower bound's merged revs"""
456
self.assertLogRevnosAndDepths(
457
['-n0', '-r1.1.2..2'],
458
[('2', 0), ('1.1.2', 1), ('1.2.1', 2)],
459
working_dir='level0')
462
class TestLogDiff(TestLogWithLogCatcher):
464
# FIXME: We need specific tests for each LogFormatter about how the diffs
465
# are displayed: --long indent them by depth, --short use a fixed
466
# indent and --line does't display them. -- vila 10019
469
super(TestLogDiff, self).setUp()
470
self.make_branch_with_diffs()
472
def make_branch_with_diffs(self):
473
level0 = self.make_branch_and_tree('level0')
474
self.build_tree(['level0/file1', 'level0/file2'])
477
self.wt_commit(level0, 'in branch level0')
479
level1 = level0.bzrdir.sprout('level1').open_workingtree()
480
self.build_tree_contents([('level1/file2', 'hello\n')])
481
self.wt_commit(level1, 'in branch level1')
482
level0.merge_from_branch(level1.branch)
483
self.wt_commit(level0, 'merge branch level1')
485
def _diff_file1_revno1(self):
486
return """=== added file 'file1'
487
--- file1\t1970-01-01 00:00:00 +0000
488
+++ file1\t2005-11-22 00:00:00 +0000
490
+contents of level0/file1
494
def _diff_file2_revno2(self):
495
return """=== modified file 'file2'
496
--- file2\t2005-11-22 00:00:00 +0000
497
+++ file2\t2005-11-22 00:00:01 +0000
499
-contents of level0/file2
504
def _diff_file2_revno1_1_1(self):
505
return """=== modified file 'file2'
506
--- file2\t2005-11-22 00:00:00 +0000
507
+++ file2\t2005-11-22 00:00:01 +0000
509
-contents of level0/file2
514
def _diff_file2_revno1(self):
515
return """=== added file 'file2'
516
--- file2\t1970-01-01 00:00:00 +0000
517
+++ file2\t2005-11-22 00:00:00 +0000
519
+contents of level0/file2
523
def assertLogRevnosAndDiff(self, args, expected,
525
self.run_bzr(['log', '-p'] + args, working_dir=working_dir)
526
expected_revnos_and_depths = [
527
(revno, depth) for revno, depth, diff in expected]
528
# Check the revnos and depths first to make debugging easier
529
self.assertEqual(expected_revnos_and_depths,
530
[(r.revno, r.merge_depth)
531
for r in self.get_captured_revisions()])
532
# Now check the diffs, adding the revno in case of failure
533
fmt = 'In revno %s\n%s'
534
for expected_rev, actual_rev in izip(expected,
535
self.get_captured_revisions()):
536
revno, depth, expected_diff = expected_rev
537
actual_diff = actual_rev.diff
538
self.assertEqualDiff(fmt % (revno, expected_diff),
539
fmt % (revno, actual_diff))
541
def test_log_diff_with_merges(self):
542
self.assertLogRevnosAndDiff(
544
[('2', 0, self._diff_file2_revno2()),
545
('1.1.1', 1, self._diff_file2_revno1_1_1()),
546
('1', 0, self._diff_file1_revno1()
547
+ self._diff_file2_revno1())],
548
working_dir='level0')
551
def test_log_diff_file1(self):
552
self.assertLogRevnosAndDiff(['-n0', 'file1'],
553
[('1', 0, self._diff_file1_revno1())],
554
working_dir='level0')
556
def test_log_diff_file2(self):
557
self.assertLogRevnosAndDiff(['-n1', 'file2'],
558
[('2', 0, self._diff_file2_revno2()),
559
('1', 0, self._diff_file2_revno1())],
560
working_dir='level0')
563
class TestLogUnicodeDiff(TestLog):
565
def test_log_show_diff_non_ascii(self):
566
# Smoke test for bug #328007 UnicodeDecodeError on 'log -p'
567
message = u'Message with \xb5'
568
body = 'Body with \xb5\n'
569
wt = self.make_branch_and_tree('.')
570
self.build_tree_contents([('foo', body)])
572
wt.commit(message=message)
573
# check that command won't fail with unicode error
574
# don't care about exact output because we have other tests for this
575
out,err = self.run_bzr('log -p --long')
576
self.assertNotEqual('', out)
577
self.assertEqual('', err)
578
out,err = self.run_bzr('log -p --short')
579
self.assertNotEqual('', out)
580
self.assertEqual('', err)
581
out,err = self.run_bzr('log -p --line')
582
self.assertNotEqual('', out)
583
self.assertEqual('', err)
586
class TestLogEncodings(tests.TestCaseInTempDir):
161
589
_message = u'Message with \xb5'
255
679
# Make sure the cp1251 string is not found anywhere
256
680
self.assertEquals(-1, stdout.find(test_in_cp1251))
683
class TestLogFile(TestLogWithLogCatcher):
685
def test_log_local_branch_file(self):
686
"""We should be able to log files in local treeless branches"""
687
tree = self.make_branch_and_tree('tree')
688
self.build_tree(['tree/file'])
690
tree.commit('revision 1')
691
tree.bzrdir.destroy_workingtree()
692
self.run_bzr('log tree/file')
694
def prepare_tree(self, complex=False):
695
# The complex configuration includes deletes and renames
696
tree = self.make_branch_and_tree('parent')
697
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
699
tree.commit('add file1')
701
tree.commit('add file2')
703
tree.commit('add file3')
704
child_tree = tree.bzrdir.sprout('child').open_workingtree()
705
self.build_tree_contents([('child/file2', 'hello')])
706
child_tree.commit(message='branch 1')
707
tree.merge_from_branch(child_tree.branch)
708
tree.commit(message='merge child branch')
711
tree.commit('remove file2')
712
tree.rename_one('file3', 'file4')
713
tree.commit('file3 is now called file4')
715
tree.commit('remove file1')
718
# FIXME: It would be good to parametrize the following tests against all
719
# formatters. But the revisions selection is not *currently* part of the
720
# LogFormatter contract, so using LogCatcher is sufficient -- vila 100118
721
def test_log_file1(self):
723
self.assertLogRevnos(['-n0', 'file1'], ['1'])
725
def test_log_file2(self):
728
self.assertLogRevnos(['-n0', 'file2'], ['4', '3.1.1', '2'])
729
# file2 in a merge revision
730
self.assertLogRevnos(['-n0', '-r3.1.1', 'file2'], ['3.1.1'])
731
# file2 in a mainline revision
732
self.assertLogRevnos(['-n0', '-r4', 'file2'], ['4', '3.1.1'])
733
# file2 since a revision
734
self.assertLogRevnos(['-n0', '-r3..', 'file2'], ['4', '3.1.1'])
735
# file2 up to a revision
736
self.assertLogRevnos(['-n0', '-r..3', 'file2'], ['2'])
738
def test_log_file3(self):
740
self.assertLogRevnos(['-n0', 'file3'], ['3'])
742
def test_log_file_historical_missing(self):
743
# Check logging a deleted file gives an error if the
744
# file isn't found at the end or start of the revision range
745
self.prepare_tree(complex=True)
746
err_msg = "Path unknown at end or start of revision range: file2"
747
err = self.run_bzr('log file2', retcode=3)[1]
748
self.assertContainsRe(err, err_msg)
750
def test_log_file_historical_end(self):
751
# Check logging a deleted file is ok if the file existed
752
# at the end the revision range
753
self.prepare_tree(complex=True)
754
self.assertLogRevnos(['-n0', '-r..4', 'file2'], ['4', '3.1.1', '2'])
756
def test_log_file_historical_start(self):
757
# Check logging a deleted file is ok if the file existed
758
# at the start of the revision range
759
self.prepare_tree(complex=True)
760
self.assertLogRevnos(['file1'], ['1'])
762
def test_log_file_renamed(self):
763
"""File matched against revision range, not current tree."""
764
self.prepare_tree(complex=True)
766
# Check logging a renamed file gives an error by default
767
err_msg = "Path unknown at end or start of revision range: file3"
768
err = self.run_bzr('log file3', retcode=3)[1]
769
self.assertContainsRe(err, err_msg)
771
# Check we can see a renamed file if we give the right end revision
772
self.assertLogRevnos(['-r..4', 'file3'], ['3'])
775
class TestLogMultiple(TestLogWithLogCatcher):
777
def prepare_tree(self):
778
tree = self.make_branch_and_tree('parent')
785
'parent/dir1/dir2/file3',
788
tree.commit('add file1')
790
tree.commit('add file2')
791
tree.add(['dir1', 'dir1/dir2', 'dir1/dir2/file3'])
792
tree.commit('add file3')
794
tree.commit('add file4')
795
tree.add('dir1/file5')
796
tree.commit('add file5')
797
child_tree = tree.bzrdir.sprout('child').open_workingtree()
798
self.build_tree_contents([('child/file2', 'hello')])
799
child_tree.commit(message='branch 1')
800
tree.merge_from_branch(child_tree.branch)
801
tree.commit(message='merge child branch')
804
def test_log_files(self):
805
"""The log for multiple file should only list revs for those files"""
807
self.assertLogRevnos(['file1', 'file2', 'dir1/dir2/file3'],
808
['6', '5.1.1', '3', '2', '1'])
810
def test_log_directory(self):
811
"""The log for a directory should show all nested files."""
813
self.assertLogRevnos(['dir1'], ['5', '3'])
815
def test_log_nested_directory(self):
816
"""The log for a directory should show all nested files."""
818
self.assertLogRevnos(['dir1/dir2'], ['3'])
820
def test_log_in_nested_directory(self):
821
"""The log for a directory should show all nested files."""
824
self.assertLogRevnos(['.'], ['5', '3'])
826
def test_log_files_and_directories(self):
827
"""Logging files and directories together should be fine."""
829
self.assertLogRevnos(['file4', 'dir1/dir2'], ['4', '3'])
831
def test_log_files_and_dirs_in_nested_directory(self):
832
"""The log for a directory should show all nested files."""
835
self.assertLogRevnos(['dir2', 'file5'], ['5', '3'])