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."""
24
from bzrlib.tests.blackbox import ExternalBase
25
from bzrlib.tests import TestCaseInTempDir, TestCaseWithTransport
28
class TestLog(ExternalBase):
30
def _prepare(self, format=None):
32
self.run_bzr(["init", "--format="+format])
35
self.build_tree(['hello.txt', 'goodbye.txt', 'meep.txt'])
36
self.run_bzr("add hello.txt")
37
self.run_bzr("commit -m message1 hello.txt")
38
self.run_bzr("add goodbye.txt")
39
self.run_bzr("commit -m message2 goodbye.txt")
40
self.run_bzr("add meep.txt")
41
self.run_bzr("commit -m message3 meep.txt")
42
self.full_log = self.run_bzr("log")[0]
27
from bzrlib.tests import test_log
30
class TestLog(tests.TestCaseWithTransport):
33
super(TestLog, self).setUp()
34
self.timezone = 0 # UTC
35
self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
37
def make_minimal_branch(self, path='.', format=None):
38
tree = self.make_branch_and_tree(path, format=format)
39
self.build_tree([path + '/hello.txt'])
41
tree.commit(message='message1')
44
def make_linear_branch(self, path='.', format=None):
45
tree = self.make_branch_and_tree(path, format=format)
47
[path + '/hello.txt', path + '/goodbye.txt', path + '/meep.txt'])
49
tree.commit(message='message1')
50
tree.add('goodbye.txt')
51
tree.commit(message='message2')
53
tree.commit(message='message3')
56
def make_merged_branch(self, path='.', format=None):
57
tree = self.make_linear_branch(path, format)
58
tree2 = tree.bzrdir.sprout('tree2',
59
revision_id=tree.branch.get_rev_id(1)).open_workingtree()
60
tree2.commit(message='tree2 message2')
61
tree2.commit(message='tree2 message3')
62
tree.merge_from_branch(tree2.branch)
63
tree.commit(message='merge')
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)
75
def commit_options(self):
76
"""Use some mostly fixed values for commits to simplify tests.
78
Tests can use this function to get some commit attributes. The time
79
stamp is incremented at each commit.
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,
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))
94
class TestLogRevSpecs(TestLog):
44
96
def test_log_null_end_revspec(self):
46
self.assertTrue('revno: 1\n' in self.full_log)
47
self.assertTrue('revno: 2\n' in self.full_log)
48
self.assertTrue('revno: 3\n' in self.full_log)
49
self.assertTrue('message:\n message1\n' in self.full_log)
50
self.assertTrue('message:\n message2\n' in self.full_log)
51
self.assertTrue('message:\n message3\n' in self.full_log)
97
self.make_linear_branch()
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)
106
full_log = self.run_bzr(['log'])[0]
53
107
log = self.run_bzr("log -r 1..")[0]
54
self.assertEqualDiff(log, self.full_log)
108
self.assertEqualDiff(log, full_log)
56
110
def test_log_null_begin_revspec(self):
111
self.make_linear_branch()
112
full_log = self.run_bzr(['log'])[0]
58
113
log = self.run_bzr("log -r ..3")[0]
59
self.assertEqualDiff(self.full_log, log)
114
self.assertEqualDiff(full_log, log)
61
116
def test_log_null_both_revspecs(self):
117
self.make_linear_branch()
118
full_log = self.run_bzr(['log'])[0]
63
119
log = self.run_bzr("log -r ..")[0]
64
self.assertEquals(self.full_log, log)
65
self.assertEqualDiff(self.full_log, log)
120
self.assertEqualDiff(full_log, log)
122
def test_log_zero_revspec(self):
123
self.make_minimal_branch()
124
self.run_bzr_error(['bzr: ERROR: Logging revision 0 is invalid.'],
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.'],
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.'],
67
137
def test_log_negative_begin_revspec_full_log(self):
138
self.make_linear_branch()
139
full_log = self.run_bzr(['log'])[0]
69
140
log = self.run_bzr("log -r -3..")[0]
70
self.assertEqualDiff(self.full_log, log)
141
self.assertEqualDiff(full_log, log)
72
143
def test_log_negative_both_revspec_full_log(self):
144
self.make_linear_branch()
145
full_log = self.run_bzr(['log'])[0]
74
146
log = self.run_bzr("log -r -3..-1")[0]
75
self.assertEqualDiff(self.full_log, log)
147
self.assertEqualDiff(full_log, log)
77
149
def test_log_negative_both_revspec_partial(self):
150
self.make_linear_branch()
79
151
log = self.run_bzr("log -r -3..-2")[0]
80
152
self.assertTrue('revno: 1\n' in log)
81
153
self.assertTrue('revno: 2\n' in log)
82
154
self.assertTrue('revno: 3\n' not in log)
84
156
def test_log_negative_begin_revspec(self):
157
self.make_linear_branch()
86
158
log = self.run_bzr("log -r -2..")[0]
87
159
self.assertTrue('revno: 1\n' not in log)
88
160
self.assertTrue('revno: 2\n' in log)
89
161
self.assertTrue('revno: 3\n' in log)
91
def test_log_postive_revspecs(self):
163
def test_log_positive_revspecs(self):
164
self.make_linear_branch()
165
full_log = self.run_bzr(['log'])[0]
93
166
log = self.run_bzr("log -r 1..3")[0]
94
self.assertEqualDiff(self.full_log, log)
167
self.assertEqualDiff(full_log, log)
169
def test_log_dotted_revspecs(self):
170
self.make_merged_branch()
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))
96
174
def test_log_reversed_revspecs(self):
175
self.make_linear_branch()
98
176
self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
99
177
'the end revision.\n',),
100
178
['log', '-r3..1'])
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',),
102
186
def test_log_revno_n_path(self):
111
log = self.run_bzr("log -r revno:2:branch1..revno:3:branch2",
187
self.make_linear_branch('branch1')
188
self.make_linear_branch('branch2')
190
self.run_bzr("log -r revno:2:branch1..revno:3:branch2", retcode=3)[0]
113
192
log = self.run_bzr("log -r revno:1:branch2..revno:3:branch2")[0]
114
self.assertEqualDiff(self.full_log, log)
193
full_log = self.run_bzr(['log'], working_dir='branch2')[0]
194
self.assertEqualDiff(full_log, log)
115
195
log = self.run_bzr("log -r revno:1:branch2")[0]
116
196
self.assertTrue('revno: 1\n' in log)
117
197
self.assertTrue('revno: 2\n' not in log)
118
198
self.assertTrue('branch nick: branch2\n' in log)
119
199
self.assertTrue('branch nick: branch1\n' not in log)
201
def test_log_nonexistent_revno(self):
202
self.make_minimal_branch()
203
(out, err) = self.run_bzr_error(
204
["bzr: ERROR: Requested revision: '1234' "
205
"does not exist in branch:"],
208
def test_log_nonexistent_dotted_revno(self):
209
self.make_minimal_branch()
210
(out, err) = self.run_bzr_error(
211
["bzr: ERROR: Requested revision: '123.123' "
212
"does not exist in branch:"],
213
['log', '-r123.123'])
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)
221
def test_log_change_nonexistent_revno(self):
222
self.make_minimal_branch()
223
(out, err) = self.run_bzr_error(
224
["bzr: ERROR: Requested revision: '1234' "
225
"does not exist in branch:"],
228
def test_log_change_nonexistent_dotted_revno(self):
229
self.make_minimal_branch()
230
(out, err) = self.run_bzr_error(
231
["bzr: ERROR: Requested revision: '123.123' "
232
"does not exist in branch:"],
233
['log', '-c123.123'])
235
def test_log_change_single_revno_only(self):
236
self.make_minimal_branch()
237
self.run_bzr_error(['bzr: ERROR: Option --change does not'
238
' accept revision ranges'],
239
['log', '--change', '2..3'])
241
def test_log_change_incompatible_with_revision(self):
242
self.run_bzr_error(['bzr: ERROR: --revision and --change'
243
' are mutually exclusive'],
244
['log', '--change', '2', '--revision', '3'])
121
246
def test_log_nonexistent_file(self):
247
self.make_minimal_branch()
122
248
# files that don't exist in either the basis tree or working tree
123
249
# should give an error
124
wt = self.make_branch_and_tree('.')
125
250
out, err = self.run_bzr('log does-not-exist', retcode=3)
126
self.assertContainsRe(
127
err, 'Path does not have any revision history: does-not-exist')
251
self.assertContainsRe(err,
252
'Path unknown at end or start of revision range: '
129
255
def test_log_with_tags(self):
130
self._prepare(format='dirstate-tags')
131
self.run_bzr('tag -r1 tag1')
132
self.run_bzr('tag -r1 tag1.1')
133
self.run_bzr('tag tag3')
256
tree = self.make_linear_branch(format='dirstate-tags')
258
branch.tags.set_tag('tag1', branch.get_rev_id(1))
259
branch.tags.set_tag('tag1.1', branch.get_rev_id(1))
260
branch.tags.set_tag('tag3', branch.last_revision())
135
262
log = self.run_bzr("log -r-1")[0]
136
263
self.assertTrue('tags: tag3' in log)
141
268
self.assertContainsRe(log, r'tags: (tag1, tag1\.1|tag1\.1, tag1)')
143
270
def test_merged_log_with_tags(self):
146
self._prepare(format='dirstate-tags')
148
self.run_bzr('branch branch1 branch2')
150
self.run_bzr('commit -m foobar --unchanged')
151
self.run_bzr('tag tag1')
152
os.chdir('../branch2')
153
self.run_bzr('merge ../branch1')
154
self.run_bzr(['commit', '-m', 'merge branch 1'])
155
log = self.run_bzr("log -r-1")[0]
271
branch1_tree = self.make_linear_branch('branch1',
272
format='dirstate-tags')
273
branch1 = branch1_tree.branch
274
branch2_tree = branch1_tree.bzrdir.sprout('branch2').open_workingtree()
275
branch1_tree.commit(message='foobar', allow_pointless=True)
276
branch1.tags.set_tag('tag1', branch1.last_revision())
277
# tags don't propagate if we don't merge
278
self.run_bzr('merge ../branch1', working_dir='branch2')
279
branch2_tree.commit(message='merge branch 1')
280
log = self.run_bzr("log -n0 -r-1", working_dir='branch2')[0]
156
281
self.assertContainsRe(log, r' tags: tag1')
157
log = self.run_bzr("log -r3.1.1")[0]
282
log = self.run_bzr("log -n0 -r3.1.1", working_dir='branch2')[0]
158
283
self.assertContainsRe(log, r'tags: tag1')
160
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)
162
290
log = self.run_bzr("log --limit 2")[0]
163
self.assertTrue('revno: 1\n' not in log)
164
self.assertTrue('revno: 2\n' in log)
165
self.assertTrue('revno: 3\n' in log)
167
class TestLogMerges(ExternalBase):
170
self.build_tree(['parent/'])
171
self.run_bzr('init parent')
172
self.run_bzr(['commit', '-m', 'first post', '--unchanged', 'parent'])
173
self.run_bzr('branch parent child')
174
self.run_bzr(['commit', '-m', 'branch 1', '--unchanged', 'child'])
175
self.run_bzr('branch child smallerchild')
176
self.run_bzr(['commit', '-m', 'branch 2', '--unchanged',
179
self.run_bzr('merge ../smallerchild')
180
self.run_bzr(['commit', '-m', 'merge branch 2'])
181
os.chdir('../parent')
182
self.run_bzr('merge ../child')
183
self.run_bzr(['commit', '-m', 'merge branch 1'])
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')
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')
309
def test_log_bad_message_re(self):
310
"""Bad --message argument gives a sensible message
312
See https://bugs.launchpad.net/bzr/+bug/251352
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"
319
": nothing to repeat\n", err)
320
self.assertEqual('', out)
323
class TestLogTimeZone(TestLog):
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'])
332
class TestLogVerbose(TestLog):
335
super(TestLogVerbose, self).setUp()
336
self.make_minimal_branch()
338
def assertUseShortDeltaFormat(self, cmd):
339
log = self.run_bzr(cmd)[0]
340
# Check that we use the short status format
341
self.assertContainsRe(log, '(?m)^\s*A hello.txt$')
342
self.assertNotContainsRe(log, '(?m)^\s*added:$')
344
def assertUseLongDeltaFormat(self, cmd):
345
log = self.run_bzr(cmd)[0]
346
# Check that we use the long status format
347
self.assertNotContainsRe(log, '(?m)^\s*A hello.txt$')
348
self.assertContainsRe(log, '(?m)^\s*added:$')
350
def test_log_short_verbose(self):
351
self.assertUseShortDeltaFormat(['log', '--short', '-v'])
353
def test_log_short_verbose_verbose(self):
354
self.assertUseLongDeltaFormat(['log', '--short', '-vv'])
356
def test_log_long_verbose(self):
357
# Check that we use the long status format, ignoring the verbosity
359
self.assertUseLongDeltaFormat(['log', '--long', '-v'])
361
def test_log_long_verbose_verbose(self):
362
# Check that we use the long status format, ignoring the verbosity
364
self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
367
class TestLogMerges(TestLog):
370
super(TestLogMerges, self).setUp()
371
self.make_branches_with_merges()
373
def make_branches_with_merges(self):
374
level0 = self.make_branch_and_tree('level0')
375
level0.commit(message='in branch level0', **self.commit_options())
377
level1 = level0.bzrdir.sprout('level1').open_workingtree()
378
level1.commit(message='in branch level1', **self.commit_options())
380
level2 = level1.bzrdir.sprout('level2').open_workingtree()
381
level2.commit(message='in branch level2', **self.commit_options())
383
level1.merge_from_branch(level2.branch)
384
level1.commit(message='merge branch level2', **self.commit_options())
386
level0.merge_from_branch(level1.branch)
387
level0.commit(message='merge branch level1', **self.commit_options())
185
389
def test_merges_are_indented_by_level(self):
187
out,err = self.run_bzr('log')
188
# the log will look something like:
189
# self.assertEqual("""\
190
#------------------------------------------------------------
192
#committer: Robert Collins <foo@example.com>
194
#timestamp: Tue 2006-03-28 22:31:40 +1100
197
# ------------------------------------------------------------
199
# committer: Robert Collins <foo@example.com>
201
# timestamp: Tue 2006-03-28 22:31:40 +1100
204
# ------------------------------------------------------------
206
# committer: Robert Collins <foo@example.com>
207
# branch nick: smallerchild
208
# timestamp: Tue 2006-03-28 22:31:40 +1100
211
# ------------------------------------------------------------
213
# committer: Robert Collins <foo@example.com>
215
# timestamp: Tue 2006-03-28 22:31:40 +1100
218
#------------------------------------------------------------
220
#committer: Robert Collins <foo@example.com>
222
#timestamp: Tue 2006-03-28 22:31:39 +1100
226
# but we dont have a nice pattern matcher hooked up yet, so:
227
# we check for the indenting of the commit message and the
229
self.assertTrue('revno: 2' in out)
230
self.assertTrue(' merge branch 1' in out)
231
self.assertTrue(' revno: 1.1.2' in out)
232
self.assertTrue(' merge branch 2' in out)
233
self.assertTrue(' revno: 1.1.1.1' in out)
234
self.assertTrue(' branch 2' in out)
235
self.assertTrue(' revno: 1.1.1' in out)
236
self.assertTrue(' branch 1' in out)
237
self.assertTrue('revno: 1\n' in out)
238
self.assertTrue(' first post' in out)
239
self.assertEqual('', err)
391
------------------------------------------------------------
393
committer: Lorem Ipsum <test@example.com>
398
------------------------------------------------------------
400
committer: Lorem Ipsum <test@example.com>
405
------------------------------------------------------------
407
committer: Lorem Ipsum <test@example.com>
412
------------------------------------------------------------
414
committer: Lorem Ipsum <test@example.com>
419
------------------------------------------------------------
421
committer: Lorem Ipsum <test@example.com>
427
self.check_log(expected, ['-n0'])
429
def test_force_merge_revisions_off(self):
431
------------------------------------------------------------
433
committer: Lorem Ipsum <test@example.com>
438
------------------------------------------------------------
440
committer: Lorem Ipsum <test@example.com>
446
self.check_log(expected, ['--long', '-n1'])
448
def test_force_merge_revisions_on(self):
450
2 Lorem Ipsum\t2005-11-22 [merge]
453
1.1.2 Lorem Ipsum\t2005-11-22 [merge]
456
1.2.1 Lorem Ipsum\t2005-11-22
459
1.1.1 Lorem Ipsum\t2005-11-22
462
1 Lorem Ipsum\t2005-11-22
466
self.check_log(expected, ['--short', '-n0'])
468
def test_include_merges(self):
469
# Confirm --include-merges gives the same output as -n0
470
out_im, err_im = self.run_bzr('log --include-merges',
471
working_dir='level0')
472
out_n0, err_n0 = self.run_bzr('log -n0', working_dir='level0')
473
self.assertEqual('', err_im)
474
self.assertEqual('', err_n0)
475
self.assertEqual(out_im, out_n0)
477
def test_force_merge_revisions_N(self):
479
2 Lorem Ipsum\t2005-11-22 [merge]
482
1.1.2 Lorem Ipsum\t2005-11-22 [merge]
485
1.1.1 Lorem Ipsum\t2005-11-22
488
1 Lorem Ipsum\t2005-11-22
492
self.check_log(expected, ['--short', '-n2'])
241
494
def test_merges_single_merge_rev(self):
243
out,err = self.run_bzr('log -r1.1.2')
244
# the log will look something like:
245
# self.assertEqual("""\
246
#------------------------------------------------------------
248
#committer: Robert Collins <foo@example.com>
250
#timestamp: Tue 2006-03-28 22:31:40 +1100
253
# ------------------------------------------------------------
255
# committer: Robert Collins <foo@example.com>
256
# branch nick: smallerchild
257
# timestamp: Tue 2006-03-28 22:31:40 +1100
261
# but we dont have a nice pattern matcher hooked up yet, so:
262
# we check for the indenting of the commit message and the
264
self.assertTrue('revno: 2' not in out)
265
self.assertTrue(' merge branch 1' not in out)
266
self.assertTrue('revno: 1.1.2' in out)
267
self.assertTrue(' merge branch 2' in out)
268
self.assertTrue(' revno: 1.1.1.1' in out)
269
self.assertTrue(' branch 2' in out)
270
self.assertTrue('revno: 1.1.1\n' not in out)
271
self.assertTrue(' branch 1' not in out)
272
self.assertTrue('revno: 1\n' not in out)
273
self.assertTrue(' first post' not in out)
274
self.assertEqual('', err)
496
------------------------------------------------------------
498
committer: Lorem Ipsum <test@example.com>
503
------------------------------------------------------------
505
committer: Lorem Ipsum <test@example.com>
511
self.check_log(expected, ['-n0', '-r1.1.2'])
276
513
def test_merges_partial_range(self):
278
out,err = self.run_bzr('log -r1.1.1..1.1.2')
279
# the log will look something like:
280
# self.assertEqual("""\
281
#------------------------------------------------------------
283
#committer: Robert Collins <foo@example.com>
285
#timestamp: Tue 2006-03-28 22:31:40 +1100
288
# ------------------------------------------------------------
290
# committer: Robert Collins <foo@example.com>
291
# branch nick: smallerchild
292
# timestamp: Tue 2006-03-28 22:31:40 +1100
295
#------------------------------------------------------------
297
#committer: Robert Collins <foo@example.com>
299
#timestamp: Tue 2006-03-28 22:31:40 +1100
303
# but we dont have a nice pattern matcher hooked up yet, so:
304
# we check for the indenting of the commit message and the
306
self.assertTrue('revno: 2' not in out)
307
self.assertTrue(' merge branch 1' not in out)
308
self.assertTrue('revno: 1.1.2' in out)
309
self.assertTrue(' merge branch 2' in out)
310
self.assertTrue(' revno: 1.1.1.1' in out)
311
self.assertTrue(' branch 2' in out)
312
self.assertTrue('revno: 1.1.1' in out)
313
self.assertTrue(' branch 1' in out)
314
self.assertTrue('revno: 1\n' not in out)
315
self.assertTrue(' first post' not in out)
316
self.assertEqual('', err)
319
class TestLogEncodings(TestCaseInTempDir):
515
------------------------------------------------------------
517
committer: Lorem Ipsum <test@example.com>
522
------------------------------------------------------------
524
committer: Lorem Ipsum <test@example.com>
529
------------------------------------------------------------
531
committer: Lorem Ipsum <test@example.com>
537
self.check_log(expected, ['-n0', '-r1.1.1..1.1.2'])
540
class TestLogDiff(TestLog):
543
super(TestLogDiff, self).setUp()
544
self.make_branch_with_diffs()
546
def make_branch_with_diffs(self):
547
level0 = self.make_branch_and_tree('level0')
548
self.build_tree(['level0/file1', 'level0/file2'])
551
level0.commit(message='in branch level0', **self.commit_options())
553
level1 = level0.bzrdir.sprout('level1').open_workingtree()
554
self.build_tree_contents([('level1/file2', 'hello\n')])
555
level1.commit(message='in branch level1', **self.commit_options())
556
level0.merge_from_branch(level1.branch)
557
level0.commit(message='merge branch level1', **self.commit_options())
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)
564
------------------------------------------------------------
566
committer: Lorem Ipsum <test@example.com>
572
=== modified file 'file2'
573
--- file2\t2005-11-22 00:00:01 +0000
574
+++ file2\t2005-11-22 00:00:02 +0000
576
-contents of level0/file2
578
------------------------------------------------------------
580
committer: Lorem Ipsum <test@example.com>
586
=== modified file 'file2'
587
--- file2\t2005-11-22 00:00:01 +0000
588
+++ file2\t2005-11-22 00:00:02 +0000
590
-contents of level0/file2
592
------------------------------------------------------------
594
committer: Lorem Ipsum <test@example.com>
600
=== added file 'file1'
601
--- file1\t1970-01-01 00:00:00 +0000
602
+++ file1\t2005-11-22 00:00:01 +0000
604
+contents of level0/file1
606
=== added file 'file2'
607
--- file2\t1970-01-01 00:00:00 +0000
608
+++ file2\t2005-11-22 00:00:01 +0000
610
+contents of level0/file2
612
self.check_log(expected, ['-p', '-n0'])
614
def test_log_show_diff_short(self):
616
2 Lorem Ipsum\t2005-11-22 [merge]
618
=== modified file 'file2'
619
--- file2\t2005-11-22 00:00:01 +0000
620
+++ file2\t2005-11-22 00:00:02 +0000
622
-contents of level0/file2
625
1 Lorem Ipsum\t2005-11-22
627
=== added file 'file1'
628
--- file1\t1970-01-01 00:00:00 +0000
629
+++ file1\t2005-11-22 00:00:01 +0000
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
637
+contents of level0/file2
639
Use --include-merges or -n0 to see merged revisions.
641
self.check_log(expected, ['-p', '--short'])
643
def test_log_show_diff_line(self):
644
# Not supported by this formatter so expect plain output
646
2: Lorem Ipsum 2005-11-22 [merge] merge branch level1
647
1: Lorem Ipsum 2005-11-22 in branch level0
649
self.check_log(expected, ['-p', '--line'])
651
def test_log_show_diff_file1(self):
652
"""Only the diffs for the given file are to be shown"""
654
1 Lorem Ipsum\t2005-11-22
656
=== added file 'file1'
657
--- file1\t1970-01-01 00:00:00 +0000
658
+++ file1\t2005-11-22 00:00:01 +0000
660
+contents of level0/file1
663
self.check_log(expected, ['-p', '--short', 'file1'])
665
def test_log_show_diff_file2(self):
666
"""Only the diffs for the given file are to be shown"""
668
2 Lorem Ipsum\t2005-11-22 [merge]
670
=== modified file 'file2'
671
--- file2\t2005-11-22 00:00:01 +0000
672
+++ file2\t2005-11-22 00:00:02 +0000
674
-contents of level0/file2
677
1 Lorem Ipsum\t2005-11-22
679
=== added file 'file2'
680
--- file2\t1970-01-01 00:00:00 +0000
681
+++ file2\t2005-11-22 00:00:01 +0000
683
+contents of level0/file2
685
Use --include-merges or -n0 to see merged revisions.
687
self.check_log(expected, ['-p', '--short', 'file2'])
690
class TestLogUnicodeDiff(TestLog):
692
def test_log_show_diff_non_ascii(self):
693
# Smoke test for bug #328007 UnicodeDecodeError on 'log -p'
694
message = u'Message with \xb5'
695
body = 'Body with \xb5\n'
696
wt = self.make_branch_and_tree('.')
697
self.build_tree_contents([('foo', body)])
699
wt.commit(message=message)
700
# check that command won't fail with unicode error
701
# don't care about exact output because we have other tests for this
702
out,err = self.run_bzr('log -p --long')
703
self.assertNotEqual('', out)
704
self.assertEqual('', err)
705
out,err = self.run_bzr('log -p --short')
706
self.assertNotEqual('', out)
707
self.assertEqual('', err)
708
out,err = self.run_bzr('log -p --line')
709
self.assertNotEqual('', out)
710
self.assertEqual('', err)
713
class TestLogEncodings(tests.TestCaseInTempDir):
322
716
_message = u'Message with \xb5'
428
821
tree.bzrdir.destroy_workingtree()
429
822
self.run_bzr('log tree/file')
824
def prepare_tree(self, complex=False):
825
# The complex configuration includes deletes and renames
826
tree = self.make_branch_and_tree('parent')
827
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
829
tree.commit('add file1')
831
tree.commit('add file2')
833
tree.commit('add file3')
834
child_tree = tree.bzrdir.sprout('child').open_workingtree()
835
self.build_tree_contents([('child/file2', 'hello')])
836
child_tree.commit(message='branch 1')
837
tree.merge_from_branch(child_tree.branch)
838
tree.commit(message='merge child branch')
841
tree.commit('remove file2')
842
tree.rename_one('file3', 'file4')
843
tree.commit('file3 is now called file4')
845
tree.commit('remove file1')
431
848
def test_log_file(self):
432
849
"""The log for a particular file should only list revs for that file"""
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 ')
894
def test_log_file_historical_missing(self):
895
# Check logging a deleted file gives an error if the
896
# file isn't found at the end or start of the revision range
897
self.prepare_tree(complex=True)
898
err_msg = "Path unknown at end or start of revision range: file2"
899
err = self.run_bzr('log file2', retcode=3)[1]
900
self.assertContainsRe(err, err_msg)
902
def test_log_file_historical_end(self):
903
# Check logging a deleted file is ok if the file existed
904
# at the end the revision range
905
self.prepare_tree(complex=True)
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 ')
914
def test_log_file_historical_start(self):
915
# Check logging a deleted file is ok if the file existed
916
# at the start of the revision range
917
self.prepare_tree(complex=True)
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 ')
926
def test_log_file_renamed(self):
927
"""File matched against revision range, not current tree."""
928
self.prepare_tree(complex=True)
930
# Check logging a renamed file gives an error by default
931
err_msg = "Path unknown at end or start of revision range: file3"
932
err = self.run_bzr('log file3', retcode=3)[1]
933
self.assertContainsRe(err, err_msg)
935
# Check we can see a renamed file if we give the right end revision
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 ')
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.
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)
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)
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)
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)
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)
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)
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)
1008
class TestLogMultiple(tests.TestCaseWithTransport):
1010
def prepare_tree(self):
433
1011
tree = self.make_branch_and_tree('parent')
434
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
1016
'parent/dir1/file5',
1017
'parent/dir1/dir2/',
1018
'parent/dir1/dir2/file3',
435
1020
tree.add('file1')
436
1021
tree.commit('add file1')
437
1022
tree.add('file2')
438
1023
tree.commit('add file2')
1024
tree.add(['dir1', 'dir1/dir2', 'dir1/dir2/file3'])
440
1025
tree.commit('add file3')
441
self.run_bzr('branch parent child')
442
print >> file('child/file2', 'wb'), 'hello'
443
self.run_bzr(['commit', '-m', 'branch 1', 'child'])
1027
tree.commit('add file4')
1028
tree.add('dir1/file5')
1029
tree.commit('add file5')
1030
child_tree = tree.bzrdir.sprout('child').open_workingtree()
1031
self.build_tree_contents([('child/file2', 'hello')])
1032
child_tree.commit(message='branch 1')
1033
tree.merge_from_branch(child_tree.branch)
1034
tree.commit(message='merge child branch')
444
1035
os.chdir('parent')
445
self.run_bzr('merge ../child')
446
self.run_bzr(['commit', '-m', 'merge child branch'])
447
log = self.run_bzr('log file1')[0]
448
self.assertContainsRe(log, 'revno: 1\n')
449
self.assertNotContainsRe(log, 'revno: 2\n')
450
self.assertNotContainsRe(log, 'revno: 3\n')
451
self.assertNotContainsRe(log, 'revno: 3.1.1\n')
452
self.assertNotContainsRe(log, 'revno: 4\n')
453
log = self.run_bzr('log file2')[0]
454
self.assertNotContainsRe(log, 'revno: 1\n')
455
self.assertContainsRe(log, 'revno: 2\n')
456
self.assertNotContainsRe(log, 'revno: 3\n')
457
self.assertContainsRe(log, 'revno: 3.1.1\n')
458
self.assertContainsRe(log, 'revno: 4\n')
459
log = self.run_bzr('log file3')[0]
460
self.assertNotContainsRe(log, 'revno: 1\n')
461
self.assertNotContainsRe(log, 'revno: 2\n')
462
self.assertContainsRe(log, 'revno: 3\n')
463
self.assertNotContainsRe(log, 'revno: 3.1.1\n')
464
self.assertNotContainsRe(log, 'revno: 4\n')
465
log = self.run_bzr('log -r3.1.1 file2')[0]
466
self.assertNotContainsRe(log, 'revno: 1\n')
467
self.assertNotContainsRe(log, 'revno: 2\n')
468
self.assertNotContainsRe(log, 'revno: 3\n')
469
self.assertContainsRe(log, 'revno: 3.1.1\n')
470
self.assertNotContainsRe(log, 'revno: 4\n')
471
log = self.run_bzr('log -r4 file2')[0]
472
self.assertNotContainsRe(log, 'revno: 1\n')
473
self.assertNotContainsRe(log, 'revno: 2\n')
474
self.assertNotContainsRe(log, 'revno: 3\n')
475
self.assertContainsRe(log, 'revno: 3.1.1\n')
476
self.assertContainsRe(log, 'revno: 4\n')
477
log = self.run_bzr('log -r3.. file2')[0]
478
self.assertNotContainsRe(log, 'revno: 1\n')
479
self.assertNotContainsRe(log, 'revno: 2\n')
480
self.assertNotContainsRe(log, 'revno: 3\n')
481
self.assertContainsRe(log, 'revno: 3.1.1\n')
482
self.assertContainsRe(log, 'revno: 4\n')
483
log = self.run_bzr('log -r..3 file2')[0]
484
self.assertNotContainsRe(log, 'revno: 1\n')
485
self.assertContainsRe(log, 'revno: 2\n')
486
self.assertNotContainsRe(log, 'revno: 3\n')
487
self.assertNotContainsRe(log, 'revno: 3.1.1\n')
488
self.assertNotContainsRe(log, 'revno: 4\n')
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)
1044
def test_log_files(self):
1045
"""The log for multiple file should only list revs for those files"""
1047
self.assertRevnos('file1 file2 dir1/dir2/file3',
1048
['6', '5.1.1', '3', '2', '1'])
1050
def test_log_directory(self):
1051
"""The log for a directory should show all nested files."""
1053
self.assertRevnos('dir1', ['5', '3'])
1055
def test_log_nested_directory(self):
1056
"""The log for a directory should show all nested files."""
1058
self.assertRevnos('dir1/dir2', ['3'])
1060
def test_log_in_nested_directory(self):
1061
"""The log for a directory should show all nested files."""
1064
self.assertRevnos('.', ['5', '3'])
1066
def test_log_files_and_directories(self):
1067
"""Logging files and directories together should be fine."""
1069
self.assertRevnos('file4 dir1/dir2', ['4', '3'])
1071
def test_log_files_and_dirs_in_nested_directory(self):
1072
"""The log for a directory should show all nested files."""
1075
self.assertRevnos('dir2 file5', ['5', '3'])