13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
18
from cStringIO import StringIO
30
class TestCaseWithoutPropsHandler(tests.TestCaseWithTransport):
33
super(TestCaseWithoutPropsHandler, self).setUp()
34
# keep a reference to the "current" custom prop. handler registry
35
self.properties_handler_registry = log.properties_handler_registry
36
# Use a clean registry for log
37
log.properties_handler_registry = registry.Registry()
40
log.properties_handler_registry = self.properties_handler_registry
41
self.addCleanup(restore)
44
class LogCatcher(log.LogFormatter):
45
"""Pull log messages into a list rather than displaying them.
47
To simplify testing we save logged revisions here rather than actually
48
formatting anything, so that we can precisely check the result without
49
being dependent on the formatting.
20
from bzrlib import log
21
from bzrlib.tests import TestCase, TestCaseWithTransport
22
from bzrlib.log import (show_log,
29
from bzrlib.branch import Branch
30
from bzrlib.errors import (
32
InvalidRevisionNumber,
34
from bzrlib.revision import Revision
35
from bzrlib.revisionspec import (
41
class LogCatcher(LogFormatter):
42
"""Pull log messages into list rather than displaying them.
44
For ease of testing we save log messages here rather than actually
45
formatting them, so that we can precisely check the result without
46
being too dependent on the exact formatting.
48
We should also test the LogFormatter.
52
51
supports_delta = True
54
53
def __init__(self):
55
54
super(LogCatcher, self).__init__(to_file=None)
58
57
def log_revision(self, revision):
59
self.revisions.append(revision)
62
class TestShowLog(tests.TestCaseWithTransport):
58
self.logs.append(revision)
61
class TestShowLog(TestCaseWithTransport):
64
63
def checkDelta(self, delta, **kw):
65
"""Check the filenames touched by a delta are as expected.
67
Caller only have to pass in the list of files for each part, all
68
unspecified parts are considered empty (and checked as such).
64
"""Check the filenames touched by a delta are as expected."""
70
65
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
71
# By default we expect an empty list
72
66
expected = kw.get(n, [])
73
67
# strip out only the path components
74
68
got = [x[0] for x in getattr(delta, n)]
75
self.assertEqual(expected, got)
77
def assertInvalidRevisonNumber(self, br, start, end):
79
self.assertRaises(errors.InvalidRevisionNumber,
81
start_revision=start, end_revision=end)
69
self.assertEquals(expected, got)
83
71
def test_cur_revno(self):
84
72
wt = self.make_branch_and_tree('.')
88
76
wt.commit('empty commit')
89
log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
91
# Since there is a single revision in the branch all the combinations
93
self.assertInvalidRevisonNumber(b, 2, 1)
94
self.assertInvalidRevisonNumber(b, 1, 2)
95
self.assertInvalidRevisonNumber(b, 0, 2)
96
self.assertInvalidRevisonNumber(b, 1, 0)
97
self.assertInvalidRevisonNumber(b, -1, 1)
98
self.assertInvalidRevisonNumber(b, 1, -1)
100
def test_empty_branch(self):
77
show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
78
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
79
start_revision=2, end_revision=1)
80
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
81
start_revision=1, end_revision=2)
82
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
83
start_revision=0, end_revision=2)
84
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
85
start_revision=1, end_revision=0)
86
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
87
start_revision=-1, end_revision=1)
88
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
89
start_revision=1, end_revision=-1)
91
def test_simple_log(self):
92
eq = self.assertEquals
101
94
wt = self.make_branch_and_tree('.')
104
log.show_log(wt.branch, lf)
106
self.assertEqual([], lf.revisions)
108
def test_empty_commit(self):
109
wt = self.make_branch_and_tree('.')
111
102
wt.commit('empty commit')
112
103
lf = LogCatcher()
113
log.show_log(wt.branch, lf, verbose=True)
115
self.assertEqual(1, len(revs))
116
self.assertEqual('1', revs[0].revno)
117
self.assertEqual('empty commit', revs[0].rev.message)
118
self.checkDelta(revs[0].delta)
104
show_log(b, lf, verbose=True)
106
eq(lf.logs[0].revno, '1')
107
eq(lf.logs[0].rev.message, 'empty commit')
109
self.log('log delta: %r' % d)
120
def test_simple_commit(self):
121
wt = self.make_branch_and_tree('.')
122
wt.commit('empty commit')
123
112
self.build_tree(['hello'])
125
114
wt.commit('add one file',
126
115
committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
127
116
u'<test@example.com>')
118
lf = self.make_utf8_encoded_stringio()
119
# log using regular thing
120
show_log(b, LongLogFormatter(lf))
122
for l in lf.readlines():
125
# get log as data structure
128
126
lf = LogCatcher()
129
log.show_log(wt.branch, lf, verbose=True)
130
self.assertEqual(2, len(lf.revisions))
127
show_log(b, lf, verbose=True)
129
self.log('log entries:')
130
for logentry in lf.logs:
131
self.log('%4s %s' % (logentry.revno, logentry.rev.message))
131
133
# first one is most recent
132
log_entry = lf.revisions[0]
133
self.assertEqual('2', log_entry.revno)
134
self.assertEqual('add one file', log_entry.rev.message)
135
self.checkDelta(log_entry.delta, added=['hello'])
137
def test_commit_message_with_control_chars(self):
138
wt = self.make_branch_and_tree('.')
139
msg = u"All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
140
msg = msg.replace(u'\r', u'\n')
134
logentry = lf.logs[0]
135
eq(logentry.revno, '2')
136
eq(logentry.rev.message, 'add one file')
138
self.log('log 2 delta: %r' % d)
139
self.checkDelta(d, added=['hello'])
141
# commit a log message with control characters
142
msg = "All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
143
self.log("original commit message: %r", msg)
142
145
lf = LogCatcher()
143
log.show_log(wt.branch, lf, verbose=True)
144
committed_msg = lf.revisions[0].rev.message
145
if wt.branch.repository._serializer.squashes_xml_invalid_characters:
146
self.assertNotEqual(msg, committed_msg)
147
self.assertTrue(len(committed_msg) > len(msg))
149
self.assertEqual(msg, committed_msg)
146
show_log(b, lf, verbose=True)
147
committed_msg = lf.logs[0].rev.message
148
self.log("escaped commit message: %r", committed_msg)
149
self.assert_(msg != committed_msg)
150
self.assert_(len(committed_msg) > len(msg))
151
def test_commit_message_without_control_chars(self):
152
wt = self.make_branch_and_tree('.')
152
# Check that log message with only XML-valid characters isn't
153
153
# escaped. As ElementTree apparently does some kind of
154
154
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
155
155
# included in the test commit message, even though they are
156
156
# valid XML 1.0 characters.
157
157
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
158
self.log("original commit message: %r", msg)
159
160
lf = LogCatcher()
160
log.show_log(wt.branch, lf, verbose=True)
161
committed_msg = lf.revisions[0].rev.message
162
self.assertEqual(msg, committed_msg)
161
show_log(b, lf, verbose=True)
162
committed_msg = lf.logs[0].rev.message
163
self.log("escaped commit message: %r", committed_msg)
164
self.assert_(msg == committed_msg)
164
166
def test_deltas_in_merge_revisions(self):
165
167
"""Check deltas created for both mainline and merge revisions"""
168
eq = self.assertEquals
166
169
wt = self.make_branch_and_tree('parent')
167
170
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
181
184
lf = LogCatcher()
182
185
lf.supports_merge_revisions = True
183
log.show_log(b, lf, verbose=True)
186
self.assertEqual(3, len(revs))
189
self.assertEqual('2', logentry.revno)
190
self.assertEqual('merge child branch', logentry.rev.message)
191
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
194
self.assertEqual('1.1.1', logentry.revno)
195
self.assertEqual('remove file1 and modify file2', logentry.rev.message)
196
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
199
self.assertEqual('1', logentry.revno)
200
self.assertEqual('add file1 and file2', logentry.rev.message)
201
self.checkDelta(logentry.delta, added=['file1', 'file2'])
186
show_log(b, lf, verbose=True)
188
logentry = lf.logs[0]
189
eq(logentry.revno, '2')
190
eq(logentry.rev.message, 'merge child branch')
192
self.checkDelta(d, removed=['file1'], modified=['file2'])
193
logentry = lf.logs[1]
194
eq(logentry.revno, '1.1.1')
195
eq(logentry.rev.message, 'remove file1 and modify file2')
197
self.checkDelta(d, removed=['file1'], modified=['file2'])
198
logentry = lf.logs[2]
199
eq(logentry.revno, '1')
200
eq(logentry.rev.message, 'add file1 and file2')
202
self.checkDelta(d, added=['file1', 'file2'])
204
def test_merges_nonsupporting_formatter(self):
205
"""Tests that show_log will raise if the formatter doesn't
206
support merge revisions."""
207
wt = self.make_branch_and_memory_tree('.')
211
wt.commit('rev-1', rev_id='rev-1',
212
timestamp=1132586655, timezone=36000,
213
committer='Joe Foo <joe@foo.com>')
214
wt.commit('rev-merged', rev_id='rev-2a',
215
timestamp=1132586700, timezone=36000,
216
committer='Joe Foo <joe@foo.com>')
217
wt.set_parent_ids(['rev-1', 'rev-2a'])
218
wt.branch.set_last_revision_info(1, 'rev-1')
219
wt.commit('rev-2', rev_id='rev-2b',
220
timestamp=1132586800, timezone=36000,
221
committer='Joe Foo <joe@foo.com>')
222
logfile = self.make_utf8_encoded_stringio()
223
formatter = ShortLogFormatter(to_file=logfile)
226
revspec = RevisionSpec.from_string('1.1.1')
227
rev = revspec.in_history(wtb)
228
self.assertRaises(BzrCommandError, show_log, wtb, lf,
229
start_revision=rev, end_revision=rev)
204
234
def make_commits_with_trailing_newlines(wt):
205
"""Helper method for LogFormatter tests"""
235
"""Helper method for LogFormatter tests"""
208
238
open('a', 'wb').write('hello moto\n')
262
292
1 Joe Foo\t2005-11-21
263
293
simple log message
268
def _prepare_tree_with_merges(self, with_tags=False):
269
wt = self.make_branch_and_memory_tree('.')
271
self.addCleanup(wt.unlock)
273
wt.commit('rev-1', rev_id='rev-1',
274
timestamp=1132586655, timezone=36000,
275
committer='Joe Foo <joe@foo.com>')
276
wt.commit('rev-merged', rev_id='rev-2a',
277
timestamp=1132586700, timezone=36000,
278
committer='Joe Foo <joe@foo.com>')
279
wt.set_parent_ids(['rev-1', 'rev-2a'])
280
wt.branch.set_last_revision_info(1, 'rev-1')
281
wt.commit('rev-2', rev_id='rev-2b',
282
timestamp=1132586800, timezone=36000,
283
committer='Joe Foo <joe@foo.com>')
286
branch.tags.set_tag('v0.2', 'rev-2b')
287
wt.commit('rev-3', rev_id='rev-3',
288
timestamp=1132586900, timezone=36000,
289
committer='Jane Foo <jane@foo.com>')
290
branch.tags.set_tag('v1.0rc1', 'rev-3')
291
branch.tags.set_tag('v1.0', 'rev-3')
294
297
def test_short_log_with_merges(self):
295
wt = self._prepare_tree_with_merges()
296
logfile = self.make_utf8_encoded_stringio()
297
formatter = log.ShortLogFormatter(to_file=logfile)
298
log.show_log(wt.branch, formatter)
299
self.assertEqualDiff("""\
300
2 Joe Foo\t2005-11-22 [merge]
303
1 Joe Foo\t2005-11-22
309
def test_short_log_with_merges_and_advice(self):
310
wt = self._prepare_tree_with_merges()
311
logfile = self.make_utf8_encoded_stringio()
312
formatter = log.ShortLogFormatter(to_file=logfile,
314
log.show_log(wt.branch, formatter)
315
self.assertEqualDiff("""\
316
2 Joe Foo\t2005-11-22 [merge]
319
1 Joe Foo\t2005-11-22
322
Use --include-merges or -n0 to see merged revisions.
326
def test_short_log_with_merges_and_range(self):
327
298
wt = self.make_branch_and_memory_tree('.')
329
self.addCleanup(wt.unlock)
331
wt.commit('rev-1', rev_id='rev-1',
332
timestamp=1132586655, timezone=36000,
333
committer='Joe Foo <joe@foo.com>')
334
wt.commit('rev-merged', rev_id='rev-2a',
335
timestamp=1132586700, timezone=36000,
336
committer='Joe Foo <joe@foo.com>')
337
wt.branch.set_last_revision_info(1, 'rev-1')
338
wt.set_parent_ids(['rev-1', 'rev-2a'])
339
wt.commit('rev-2b', rev_id='rev-2b',
340
timestamp=1132586800, timezone=36000,
341
committer='Joe Foo <joe@foo.com>')
342
wt.commit('rev-3a', rev_id='rev-3a',
343
timestamp=1132586800, timezone=36000,
344
committer='Joe Foo <joe@foo.com>')
345
wt.branch.set_last_revision_info(2, 'rev-2b')
346
wt.set_parent_ids(['rev-2b', 'rev-3a'])
347
wt.commit('rev-3b', rev_id='rev-3b',
348
timestamp=1132586800, timezone=36000,
349
committer='Joe Foo <joe@foo.com>')
350
logfile = self.make_utf8_encoded_stringio()
351
formatter = log.ShortLogFormatter(to_file=logfile)
352
log.show_log(wt.branch, formatter,
353
start_revision=2, end_revision=3)
354
self.assertEqualDiff("""\
355
3 Joe Foo\t2005-11-22 [merge]
302
wt.commit('rev-1', rev_id='rev-1',
303
timestamp=1132586655, timezone=36000,
304
committer='Joe Foo <joe@foo.com>')
305
wt.commit('rev-merged', rev_id='rev-2a',
306
timestamp=1132586700, timezone=36000,
307
committer='Joe Foo <joe@foo.com>')
308
wt.set_parent_ids(['rev-1', 'rev-2a'])
309
wt.branch.set_last_revision_info(1, 'rev-1')
310
wt.commit('rev-2', rev_id='rev-2b',
311
timestamp=1132586800, timezone=36000,
312
committer='Joe Foo <joe@foo.com>')
313
logfile = self.make_utf8_encoded_stringio()
314
formatter = ShortLogFormatter(to_file=logfile)
315
show_log(wt.branch, formatter)
316
self.assertEqualDiff(logfile.getvalue(), """\
358
317
2 Joe Foo\t2005-11-22 [merge]
364
def test_short_log_with_tags(self):
365
wt = self._prepare_tree_with_merges(with_tags=True)
366
logfile = self.make_utf8_encoded_stringio()
367
formatter = log.ShortLogFormatter(to_file=logfile)
368
log.show_log(wt.branch, formatter)
369
self.assertEqualDiff("""\
370
3 Jane Foo\t2005-11-22 {v1.0, v1.0rc1}
373
2 Joe Foo\t2005-11-22 {v0.2} [merge]
376
320
1 Joe Foo\t2005-11-22
382
327
def test_short_log_single_merge_revision(self):
383
328
wt = self.make_branch_and_memory_tree('.')
385
self.addCleanup(wt.unlock)
387
wt.commit('rev-1', rev_id='rev-1',
388
timestamp=1132586655, timezone=36000,
389
committer='Joe Foo <joe@foo.com>')
390
wt.commit('rev-merged', rev_id='rev-2a',
391
timestamp=1132586700, timezone=36000,
392
committer='Joe Foo <joe@foo.com>')
393
wt.set_parent_ids(['rev-1', 'rev-2a'])
394
wt.branch.set_last_revision_info(1, 'rev-1')
395
wt.commit('rev-2', rev_id='rev-2b',
396
timestamp=1132586800, timezone=36000,
397
committer='Joe Foo <joe@foo.com>')
398
logfile = self.make_utf8_encoded_stringio()
399
formatter = log.ShortLogFormatter(to_file=logfile)
400
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
402
rev = revspec.in_history(wtb)
403
log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
404
self.assertEqualDiff("""\
405
1.1.1 Joe Foo\t2005-11-22
412
class TestShortLogFormatterWithMergeRevisions(tests.TestCaseWithTransport):
414
def test_short_merge_revs_log_with_merges(self):
415
wt = self.make_branch_and_memory_tree('.')
417
self.addCleanup(wt.unlock)
419
wt.commit('rev-1', rev_id='rev-1',
420
timestamp=1132586655, timezone=36000,
421
committer='Joe Foo <joe@foo.com>')
422
wt.commit('rev-merged', rev_id='rev-2a',
423
timestamp=1132586700, timezone=36000,
424
committer='Joe Foo <joe@foo.com>')
425
wt.set_parent_ids(['rev-1', 'rev-2a'])
426
wt.branch.set_last_revision_info(1, 'rev-1')
427
wt.commit('rev-2', rev_id='rev-2b',
428
timestamp=1132586800, timezone=36000,
429
committer='Joe Foo <joe@foo.com>')
430
logfile = self.make_utf8_encoded_stringio()
431
formatter = log.ShortLogFormatter(to_file=logfile, levels=0)
432
log.show_log(wt.branch, formatter)
433
# Note that the 1.1.1 indenting is in fact correct given that
434
# the revision numbers are right justified within 5 characters
435
# for mainline revnos and 9 characters for dotted revnos.
436
self.assertEqualDiff("""\
437
2 Joe Foo\t2005-11-22 [merge]
440
1.1.1 Joe Foo\t2005-11-22
443
1 Joe Foo\t2005-11-22
449
def test_short_merge_revs_log_single_merge_revision(self):
450
wt = self.make_branch_and_memory_tree('.')
452
self.addCleanup(wt.unlock)
454
wt.commit('rev-1', rev_id='rev-1',
455
timestamp=1132586655, timezone=36000,
456
committer='Joe Foo <joe@foo.com>')
457
wt.commit('rev-merged', rev_id='rev-2a',
458
timestamp=1132586700, timezone=36000,
459
committer='Joe Foo <joe@foo.com>')
460
wt.set_parent_ids(['rev-1', 'rev-2a'])
461
wt.branch.set_last_revision_info(1, 'rev-1')
462
wt.commit('rev-2', rev_id='rev-2b',
463
timestamp=1132586800, timezone=36000,
464
committer='Joe Foo <joe@foo.com>')
465
logfile = self.make_utf8_encoded_stringio()
466
formatter = log.ShortLogFormatter(to_file=logfile, levels=0)
467
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
469
rev = revspec.in_history(wtb)
470
log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
471
self.assertEqualDiff("""\
472
1.1.1 Joe Foo\t2005-11-22
479
class TestLongLogFormatter(TestCaseWithoutPropsHandler):
332
wt.commit('rev-1', rev_id='rev-1',
333
timestamp=1132586655, timezone=36000,
334
committer='Joe Foo <joe@foo.com>')
335
wt.commit('rev-merged', rev_id='rev-2a',
336
timestamp=1132586700, timezone=36000,
337
committer='Joe Foo <joe@foo.com>')
338
wt.set_parent_ids(['rev-1', 'rev-2a'])
339
wt.branch.set_last_revision_info(1, 'rev-1')
340
wt.commit('rev-2', rev_id='rev-2b',
341
timestamp=1132586800, timezone=36000,
342
committer='Joe Foo <joe@foo.com>')
343
logfile = self.make_utf8_encoded_stringio()
344
formatter = ShortLogFormatter(to_file=logfile)
345
revspec = RevisionSpec.from_string('1.1.1')
347
rev = revspec.in_history(wtb)
348
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
349
self.assertEqualDiff(logfile.getvalue(), """\
350
1.1.1 Joe Foo\t2005-11-22
358
class TestLongLogFormatter(TestCaseWithTransport):
481
360
def test_verbose_log(self):
482
361
"""Verbose log includes changed files
486
365
wt = self.make_branch_and_tree('.')
671
546
timestamp=1132711707,
673
548
committer='Lorem Ipsum <test@example.com>',
674
authors=['John Doe <jdoe@example.com>',
675
'Jane Rey <jrey@example.com>'])
677
formatter = log.LongLogFormatter(to_file=sio)
678
log.show_log(b, formatter)
679
self.assertEqualDiff('''\
680
------------------------------------------------------------
682
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
683
committer: Lorem Ipsum <test@example.com>
684
branch nick: test_author_log
685
timestamp: Wed 2005-11-23 12:08:27 +1000
691
def test_properties_in_log(self):
692
"""Log includes the custom properties returned by the registered
695
wt = self.make_branch_and_tree('.')
697
self.build_tree(['a'])
699
b.nick = 'test_properties_in_log'
700
wt.commit(message='add a',
701
timestamp=1132711707,
703
committer='Lorem Ipsum <test@example.com>',
704
authors=['John Doe <jdoe@example.com>'])
706
formatter = log.LongLogFormatter(to_file=sio)
708
def trivial_custom_prop_handler(revision):
709
return {'test_prop':'test_value'}
711
log.properties_handler_registry.register(
712
'trivial_custom_prop_handler',
713
trivial_custom_prop_handler)
714
log.show_log(b, formatter)
716
log.properties_handler_registry.remove(
717
'trivial_custom_prop_handler')
718
self.assertEqualDiff('''\
719
------------------------------------------------------------
721
test_prop: test_value
722
author: John Doe <jdoe@example.com>
723
committer: Lorem Ipsum <test@example.com>
724
branch nick: test_properties_in_log
725
timestamp: Wed 2005-11-23 12:08:27 +1000
731
def test_properties_in_short_log(self):
732
"""Log includes the custom properties returned by the registered
735
wt = self.make_branch_and_tree('.')
737
self.build_tree(['a'])
739
b.nick = 'test_properties_in_short_log'
740
wt.commit(message='add a',
741
timestamp=1132711707,
743
committer='Lorem Ipsum <test@example.com>',
744
authors=['John Doe <jdoe@example.com>'])
746
formatter = log.ShortLogFormatter(to_file=sio)
748
def trivial_custom_prop_handler(revision):
749
return {'test_prop':'test_value'}
751
log.properties_handler_registry.register(
752
'trivial_custom_prop_handler',
753
trivial_custom_prop_handler)
754
log.show_log(b, formatter)
756
log.properties_handler_registry.remove(
757
'trivial_custom_prop_handler')
758
self.assertEqualDiff('''\
759
1 John Doe\t2005-11-23
760
test_prop: test_value
766
def test_error_in_properties_handler(self):
767
"""Log includes the custom properties returned by the registered
770
wt = self.make_branch_and_tree('.')
772
self.build_tree(['a'])
774
b.nick = 'test_author_log'
775
wt.commit(message='add a',
776
timestamp=1132711707,
778
committer='Lorem Ipsum <test@example.com>',
779
authors=['John Doe <jdoe@example.com>'],
780
revprops={'first_prop':'first_value'})
782
formatter = log.LongLogFormatter(to_file=sio)
784
def trivial_custom_prop_handler(revision):
785
raise StandardError("a test error")
787
log.properties_handler_registry.register(
788
'trivial_custom_prop_handler',
789
trivial_custom_prop_handler)
790
self.assertRaises(StandardError, log.show_log, b, formatter,)
792
log.properties_handler_registry.remove(
793
'trivial_custom_prop_handler')
795
def test_properties_handler_bad_argument(self):
796
wt = self.make_branch_and_tree('.')
798
self.build_tree(['a'])
800
b.nick = 'test_author_log'
801
wt.commit(message='add a',
802
timestamp=1132711707,
804
committer='Lorem Ipsum <test@example.com>',
805
authors=['John Doe <jdoe@example.com>'],
806
revprops={'a_prop':'test_value'})
808
formatter = log.LongLogFormatter(to_file=sio)
810
def bad_argument_prop_handler(revision):
811
return {'custom_prop_name':revision.properties['a_prop']}
813
log.properties_handler_registry.register(
814
'bad_argument_prop_handler',
815
bad_argument_prop_handler)
817
self.assertRaises(AttributeError, formatter.show_properties,
820
revision = b.repository.get_revision(b.last_revision())
821
formatter.show_properties(revision, '')
822
self.assertEqualDiff('''custom_prop_name: test_value\n''',
825
log.properties_handler_registry.remove(
826
'bad_argument_prop_handler')
829
class TestLongLogFormatterWithoutMergeRevisions(TestCaseWithoutPropsHandler):
831
def test_long_verbose_log(self):
832
"""Verbose log includes changed files
836
wt = self.make_branch_and_tree('.')
838
self.build_tree(['a'])
840
# XXX: why does a longer nick show up?
841
b.nick = 'test_verbose_log'
842
wt.commit(message='add a',
843
timestamp=1132711707,
845
committer='Lorem Ipsum <test@example.com>')
846
logfile = file('out.tmp', 'w+')
847
formatter = log.LongLogFormatter(to_file=logfile, levels=1)
848
log.show_log(b, formatter, verbose=True)
851
log_contents = logfile.read()
852
self.assertEqualDiff('''\
853
------------------------------------------------------------
855
committer: Lorem Ipsum <test@example.com>
856
branch nick: test_verbose_log
857
timestamp: Wed 2005-11-23 12:08:27 +1000
865
def test_long_verbose_contain_deltas(self):
866
wt = self.make_branch_and_tree('parent')
867
self.build_tree(['parent/f1', 'parent/f2'])
869
wt.commit('first post')
870
self.run_bzr('branch parent child')
871
os.unlink('child/f1')
872
file('child/f2', 'wb').write('hello\n')
873
self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
876
self.run_bzr('merge ../child')
877
wt.commit('merge branch 1')
879
sio = self.make_utf8_encoded_stringio()
880
lf = log.LongLogFormatter(to_file=sio, levels=1)
881
log.show_log(b, lf, verbose=True)
882
the_log = normalize_log(sio.getvalue())
883
self.assertEqualDiff("""\
884
------------------------------------------------------------
886
committer: Lorem Ipsum <test@example.com>
895
------------------------------------------------------------
897
committer: Lorem Ipsum <test@example.com>
908
def test_long_trailing_newlines(self):
909
wt = self.make_branch_and_tree('.')
910
b = make_commits_with_trailing_newlines(wt)
911
sio = self.make_utf8_encoded_stringio()
912
lf = log.LongLogFormatter(to_file=sio, levels=1)
914
self.assertEqualDiff("""\
915
------------------------------------------------------------
917
committer: Joe Foo <joe@foo.com>
919
timestamp: Mon 2005-11-21 09:32:56 -0600
921
single line with trailing newline
922
------------------------------------------------------------
924
author: Joe Bar <joe@bar.com>
925
committer: Joe Foo <joe@foo.com>
927
timestamp: Mon 2005-11-21 09:27:22 -0600
932
------------------------------------------------------------
934
committer: Joe Foo <joe@foo.com>
936
timestamp: Mon 2005-11-21 09:24:15 -0600
942
def test_long_author_in_log(self):
943
"""Log includes the author name if it's set in
944
the revision properties
946
wt = self.make_branch_and_tree('.')
948
self.build_tree(['a'])
950
b.nick = 'test_author_log'
951
wt.commit(message='add a',
952
timestamp=1132711707,
954
committer='Lorem Ipsum <test@example.com>',
955
authors=['John Doe <jdoe@example.com>'])
957
formatter = log.LongLogFormatter(to_file=sio, levels=1)
958
log.show_log(b, formatter)
959
self.assertEqualDiff('''\
960
------------------------------------------------------------
962
author: John Doe <jdoe@example.com>
963
committer: Lorem Ipsum <test@example.com>
964
branch nick: test_author_log
965
timestamp: Wed 2005-11-23 12:08:27 +1000
971
def test_long_properties_in_log(self):
972
"""Log includes the custom properties returned by the registered
975
wt = self.make_branch_and_tree('.')
977
self.build_tree(['a'])
979
b.nick = 'test_properties_in_log'
980
wt.commit(message='add a',
981
timestamp=1132711707,
983
committer='Lorem Ipsum <test@example.com>',
984
authors=['John Doe <jdoe@example.com>'])
986
formatter = log.LongLogFormatter(to_file=sio, levels=1)
988
def trivial_custom_prop_handler(revision):
989
return {'test_prop':'test_value'}
991
log.properties_handler_registry.register(
992
'trivial_custom_prop_handler',
993
trivial_custom_prop_handler)
994
log.show_log(b, formatter)
996
log.properties_handler_registry.remove(
997
'trivial_custom_prop_handler')
998
self.assertEqualDiff('''\
999
------------------------------------------------------------
1001
test_prop: test_value
1002
author: John Doe <jdoe@example.com>
1003
committer: Lorem Ipsum <test@example.com>
1004
branch nick: test_properties_in_log
1005
timestamp: Wed 2005-11-23 12:08:27 +1000
1012
class TestLineLogFormatter(tests.TestCaseWithTransport):
549
author='John Doe <jdoe@example.com>')
551
formatter = LongLogFormatter(to_file=sio)
552
show_log(b, formatter)
553
self.assertEqualDiff(sio.getvalue(), '''\
554
------------------------------------------------------------
556
author: John Doe <jdoe@example.com>
557
committer: Lorem Ipsum <test@example.com>
558
branch nick: test_author_log
559
timestamp: Wed 2005-11-23 12:08:27 +1000
566
class TestLineLogFormatter(TestCaseWithTransport):
1014
568
def test_line_log(self):
1015
569
"""Line log should show revno
1019
573
wt = self.make_branch_and_tree('.')
1021
575
self.build_tree(['a'])
1023
577
b.nick = 'test-line-log'
1024
wt.commit(message='add a',
1025
timestamp=1132711707,
578
wt.commit(message='add a',
579
timestamp=1132711707,
1027
581
committer='Line-Log-Formatter Tester <test@line.log>')
1028
582
logfile = file('out.tmp', 'w+')
1029
formatter = log.LineLogFormatter(to_file=logfile)
1030
log.show_log(b, formatter)
583
formatter = LineLogFormatter(to_file=logfile)
584
show_log(b, formatter)
1033
587
log_contents = logfile.read()
1034
self.assertEqualDiff('1: Line-Log-Formatte... 2005-11-23 add a\n',
588
self.assertEqualDiff(log_contents,
589
'1: Line-Log-Formatte... 2005-11-23 add a\n')
1037
591
def test_trailing_newlines(self):
1038
592
wt = self.make_branch_and_tree('.')
1039
593
b = make_commits_with_trailing_newlines(wt)
1040
594
sio = self.make_utf8_encoded_stringio()
1041
lf = log.LineLogFormatter(to_file=sio)
1043
self.assertEqualDiff("""\
595
lf = LineLogFormatter(to_file=sio)
597
self.assertEqualDiff(sio.getvalue(), """\
1044
598
3: Joe Foo 2005-11-21 single line with trailing newline
1045
599
2: Joe Bar 2005-11-21 multiline
1046
600
1: Joe Foo 2005-11-21 simple log message
1050
def _prepare_tree_with_merges(self, with_tags=False):
1051
wt = self.make_branch_and_memory_tree('.')
1053
self.addCleanup(wt.unlock)
1055
wt.commit('rev-1', rev_id='rev-1',
1056
timestamp=1132586655, timezone=36000,
1057
committer='Joe Foo <joe@foo.com>')
1058
wt.commit('rev-merged', rev_id='rev-2a',
1059
timestamp=1132586700, timezone=36000,
1060
committer='Joe Foo <joe@foo.com>')
1061
wt.set_parent_ids(['rev-1', 'rev-2a'])
1062
wt.branch.set_last_revision_info(1, 'rev-1')
1063
wt.commit('rev-2', rev_id='rev-2b',
1064
timestamp=1132586800, timezone=36000,
1065
committer='Joe Foo <joe@foo.com>')
1068
branch.tags.set_tag('v0.2', 'rev-2b')
1069
wt.commit('rev-3', rev_id='rev-3',
1070
timestamp=1132586900, timezone=36000,
1071
committer='Jane Foo <jane@foo.com>')
1072
branch.tags.set_tag('v1.0rc1', 'rev-3')
1073
branch.tags.set_tag('v1.0', 'rev-3')
1076
603
def test_line_log_single_merge_revision(self):
1077
wt = self._prepare_tree_with_merges()
1078
logfile = self.make_utf8_encoded_stringio()
1079
formatter = log.LineLogFormatter(to_file=logfile)
1080
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
1082
rev = revspec.in_history(wtb)
1083
log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
1084
self.assertEqualDiff("""\
1085
1.1.1: Joe Foo 2005-11-22 rev-merged
1089
def test_line_log_with_tags(self):
1090
wt = self._prepare_tree_with_merges(with_tags=True)
1091
logfile = self.make_utf8_encoded_stringio()
1092
formatter = log.LineLogFormatter(to_file=logfile)
1093
log.show_log(wt.branch, formatter)
1094
self.assertEqualDiff("""\
1095
3: Jane Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
1096
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
1097
1: Joe Foo 2005-11-22 rev-1
1101
class TestLineLogFormatterWithMergeRevisions(tests.TestCaseWithTransport):
1103
def test_line_merge_revs_log(self):
1104
"""Line log should show revno
1108
wt = self.make_branch_and_tree('.')
1110
self.build_tree(['a'])
1112
b.nick = 'test-line-log'
1113
wt.commit(message='add a',
1114
timestamp=1132711707,
1116
committer='Line-Log-Formatter Tester <test@line.log>')
1117
logfile = file('out.tmp', 'w+')
1118
formatter = log.LineLogFormatter(to_file=logfile, levels=0)
1119
log.show_log(b, formatter)
1122
log_contents = logfile.read()
1123
self.assertEqualDiff('1: Line-Log-Formatte... 2005-11-23 add a\n',
1126
def test_line_merge_revs_log_single_merge_revision(self):
1127
wt = self.make_branch_and_memory_tree('.')
1129
self.addCleanup(wt.unlock)
1131
wt.commit('rev-1', rev_id='rev-1',
1132
timestamp=1132586655, timezone=36000,
1133
committer='Joe Foo <joe@foo.com>')
1134
wt.commit('rev-merged', rev_id='rev-2a',
1135
timestamp=1132586700, timezone=36000,
1136
committer='Joe Foo <joe@foo.com>')
1137
wt.set_parent_ids(['rev-1', 'rev-2a'])
1138
wt.branch.set_last_revision_info(1, 'rev-1')
1139
wt.commit('rev-2', rev_id='rev-2b',
1140
timestamp=1132586800, timezone=36000,
1141
committer='Joe Foo <joe@foo.com>')
1142
logfile = self.make_utf8_encoded_stringio()
1143
formatter = log.LineLogFormatter(to_file=logfile, levels=0)
1144
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
1146
rev = revspec.in_history(wtb)
1147
log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
1148
self.assertEqualDiff("""\
1149
1.1.1: Joe Foo 2005-11-22 rev-merged
1153
def test_line_merge_revs_log_with_merges(self):
1154
wt = self.make_branch_and_memory_tree('.')
1156
self.addCleanup(wt.unlock)
1158
wt.commit('rev-1', rev_id='rev-1',
1159
timestamp=1132586655, timezone=36000,
1160
committer='Joe Foo <joe@foo.com>')
1161
wt.commit('rev-merged', rev_id='rev-2a',
1162
timestamp=1132586700, timezone=36000,
1163
committer='Joe Foo <joe@foo.com>')
1164
wt.set_parent_ids(['rev-1', 'rev-2a'])
1165
wt.branch.set_last_revision_info(1, 'rev-1')
1166
wt.commit('rev-2', rev_id='rev-2b',
1167
timestamp=1132586800, timezone=36000,
1168
committer='Joe Foo <joe@foo.com>')
1169
logfile = self.make_utf8_encoded_stringio()
1170
formatter = log.LineLogFormatter(to_file=logfile, levels=0)
1171
log.show_log(wt.branch, formatter)
1172
self.assertEqualDiff("""\
1173
2: Joe Foo 2005-11-22 [merge] rev-2
1174
1.1.1: Joe Foo 2005-11-22 rev-merged
1175
1: Joe Foo 2005-11-22 rev-1
1179
class TestGetViewRevisions(tests.TestCaseWithTransport):
604
wt = self.make_branch_and_memory_tree('.')
608
wt.commit('rev-1', rev_id='rev-1',
609
timestamp=1132586655, timezone=36000,
610
committer='Joe Foo <joe@foo.com>')
611
wt.commit('rev-merged', rev_id='rev-2a',
612
timestamp=1132586700, timezone=36000,
613
committer='Joe Foo <joe@foo.com>')
614
wt.set_parent_ids(['rev-1', 'rev-2a'])
615
wt.branch.set_last_revision_info(1, 'rev-1')
616
wt.commit('rev-2', rev_id='rev-2b',
617
timestamp=1132586800, timezone=36000,
618
committer='Joe Foo <joe@foo.com>')
619
logfile = self.make_utf8_encoded_stringio()
620
formatter = LineLogFormatter(to_file=logfile)
621
revspec = RevisionSpec.from_string('1.1.1')
623
rev = revspec.in_history(wtb)
624
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
625
self.assertEqualDiff(logfile.getvalue(), """\
626
1.1.1: Joe Foo 2005-11-22 rev-merged
633
class TestGetViewRevisions(TestCaseWithTransport):
1181
635
def make_tree_with_commits(self):
1182
636
"""Create a tree with well-known revision ids"""
1201
655
return mainline_revs, rev_nos, wt
1203
def make_branch_with_many_merges(self):
657
def make_tree_with_many_merges(self):
1204
658
"""Create a tree with well-known revision ids"""
1205
builder = self.make_branch_builder('tree1')
1206
builder.start_series()
1207
builder.build_snapshot('1', None, [
1208
('add', ('', 'TREE_ROOT', 'directory', '')),
1209
('add', ('f', 'f-id', 'file', '1\n'))])
1210
builder.build_snapshot('2', ['1'], [])
1211
builder.build_snapshot('3a', ['2'], [
1212
('modify', ('f-id', '1\n2\n3a\n'))])
1213
builder.build_snapshot('3b', ['2', '3a'], [
1214
('modify', ('f-id', '1\n2\n3a\n'))])
1215
builder.build_snapshot('3c', ['2', '3b'], [
1216
('modify', ('f-id', '1\n2\n3a\n'))])
1217
builder.build_snapshot('4a', ['3b'], [])
1218
builder.build_snapshot('4b', ['3c', '4a'], [])
1219
builder.finish_series()
659
wt = self.make_branch_and_tree('tree1')
660
wt.commit('commit one', rev_id='1')
661
wt.commit('commit two', rev_id='2')
662
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
663
tree3.commit('commit three a', rev_id='3a')
664
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
665
tree2.merge_from_branch(tree3.branch)
666
tree2.commit('commit three b', rev_id='3b')
667
wt.merge_from_branch(tree2.branch)
668
wt.commit('commit three c', rev_id='3c')
669
tree2.commit('four-a', rev_id='4a')
670
wt.merge_from_branch(tree2.branch)
671
wt.commit('four-b', rev_id='4b')
1233
672
mainline_revs = [None, '1', '2', '3c', '4b']
1234
673
rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
1235
674
full_rev_nos_for_reference = {
1238
'3a': '2.1.1', #first commit tree 3
1239
'3b': '2.2.1', # first commit tree 2
677
'3a': '2.2.1', #first commit tree 3
678
'3b': '2.1.1', # first commit tree 2
1240
679
'3c': '3', #merges 3b to main
1241
'4a': '2.2.2', # second commit tree 2
680
'4a': '2.1.2', # second commit tree 2
1242
681
'4b': '4', # merges 4a to main
1244
return mainline_revs, rev_nos, builder.get_branch()
683
return mainline_revs, rev_nos, wt
1246
685
def test_get_view_revisions_forward(self):
1247
686
"""Test the get_view_revisions method"""
1248
687
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1250
self.addCleanup(wt.unlock)
1251
revisions = list(log.get_view_revisions(
1252
mainline_revs, rev_nos, wt.branch, 'forward'))
688
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
1253
690
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
1255
revisions2 = list(log.get_view_revisions(
1256
mainline_revs, rev_nos, wt.branch, 'forward',
1257
include_merges=False))
692
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
693
'forward', include_merges=False))
1258
694
self.assertEqual(revisions, revisions2)
1260
696
def test_get_view_revisions_reverse(self):
1261
697
"""Test the get_view_revisions with reverse"""
1262
698
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1264
self.addCleanup(wt.unlock)
1265
revisions = list(log.get_view_revisions(
1266
mainline_revs, rev_nos, wt.branch, 'reverse'))
699
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
1267
701
self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
1269
revisions2 = list(log.get_view_revisions(
1270
mainline_revs, rev_nos, wt.branch, 'reverse',
1271
include_merges=False))
703
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
704
'reverse', include_merges=False))
1272
705
self.assertEqual(revisions, revisions2)
1274
707
def test_get_view_revisions_merge(self):
1275
708
"""Test get_view_revisions when there are merges"""
1276
709
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1278
self.addCleanup(wt.unlock)
1279
revisions = list(log.get_view_revisions(
1280
mainline_revs, rev_nos, wt.branch, 'forward'))
1281
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1282
('4b', '4', 0), ('4a', '3.1.1', 1)],
1284
revisions = list(log.get_view_revisions(
1285
mainline_revs, rev_nos, wt.branch, 'forward',
1286
include_merges=False))
1287
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
710
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
712
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
713
('4b', '4', 0), ('4a', '3.1.1', 1)],
715
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
716
'forward', include_merges=False))
717
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1291
721
def test_get_view_revisions_merge_reverse(self):
1292
722
"""Test get_view_revisions in reverse when there are merges"""
1293
723
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1295
self.addCleanup(wt.unlock)
1296
revisions = list(log.get_view_revisions(
1297
mainline_revs, rev_nos, wt.branch, 'reverse'))
724
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
1298
726
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
1299
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
1301
revisions = list(log.get_view_revisions(
1302
mainline_revs, rev_nos, wt.branch, 'reverse',
1303
include_merges=False))
727
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
729
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
730
'reverse', include_merges=False))
1304
731
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
1308
735
def test_get_view_revisions_merge2(self):
1309
736
"""Test get_view_revisions when there are merges"""
1310
mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1312
self.addCleanup(b.unlock)
1313
revisions = list(log.get_view_revisions(
1314
mainline_revs, rev_nos, b, 'forward'))
737
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
738
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
1315
740
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1316
('3b', '2.2.1', 1), ('3a', '2.1.1', 2), ('4b', '4', 0),
741
('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
1318
743
self.assertEqual(expected, revisions)
1319
revisions = list(log.get_view_revisions(
1320
mainline_revs, rev_nos, b, 'forward',
1321
include_merges=False))
744
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
745
'forward', include_merges=False))
1322
746
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1327
def test_file_id_for_range(self):
1328
mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1330
self.addCleanup(b.unlock)
1332
def rev_from_rev_id(revid, branch):
1333
revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
1334
return revspec.in_history(branch)
1336
def view_revs(start_rev, end_rev, file_id, direction):
1337
revs = log.calculate_view_revisions(
1339
start_rev, # start_revision
1340
end_rev, # end_revision
1341
direction, # direction
1342
file_id, # specific_fileid
1343
True, # generate_merge_revisions
1347
rev_3a = rev_from_rev_id('3a', b)
1348
rev_4b = rev_from_rev_id('4b', b)
1349
self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1), ('3a', '2.1.1', 2)],
1350
view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
1351
# Note: 3c still appears before 3a here because of depth-based sorting
1352
self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1), ('3a', '2.1.1', 2)],
1353
view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
1356
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
751
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
1358
753
def create_tree_with_single_merge(self):
1359
754
"""Create a branch with a moderate layout.
1409
804
tree.commit('D', rev_id='D')
1411
806
# Switch to a read lock for this tree.
1412
# We still have an addCleanup(tree.unlock) pending
807
# We still have addCleanup(unlock)
1414
809
tree.lock_read()
1417
def check_delta(self, delta, **kw):
1418
"""Check the filenames touched by a delta are as expected.
1420
Caller only have to pass in the list of files for each part, all
1421
unspecified parts are considered empty (and checked as such).
1423
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
1424
# By default we expect an empty list
1425
expected = kw.get(n, [])
1426
# strip out only the path components
1427
got = [x[0] for x in getattr(delta, n)]
1428
self.assertEqual(expected, got)
1430
812
def test_tree_with_single_merge(self):
1431
813
"""Make sure the tree layout is correct."""
1432
814
tree = self.create_tree_with_single_merge()
1433
815
rev_A_tree = tree.branch.repository.revision_tree('A')
1434
816
rev_B_tree = tree.branch.repository.revision_tree('B')
818
f1_changed = (u'f1', 'f1-id', 'file', True, False)
819
f2_changed = (u'f2', 'f2-id', 'file', True, False)
820
f3_changed = (u'f3', 'f3-id', 'file', True, False)
822
delta = rev_B_tree.changes_from(rev_A_tree)
823
self.assertEqual([f1_changed, f3_changed], delta.modified)
824
self.assertEqual([], delta.renamed)
825
self.assertEqual([], delta.added)
826
self.assertEqual([], delta.removed)
1435
828
rev_C_tree = tree.branch.repository.revision_tree('C')
829
delta = rev_C_tree.changes_from(rev_A_tree)
830
self.assertEqual([f2_changed, f3_changed], delta.modified)
831
self.assertEqual([], delta.renamed)
832
self.assertEqual([], delta.added)
833
self.assertEqual([], delta.removed)
1436
835
rev_D_tree = tree.branch.repository.revision_tree('D')
1438
self.check_delta(rev_B_tree.changes_from(rev_A_tree),
1439
modified=['f1', 'f3'])
1441
self.check_delta(rev_C_tree.changes_from(rev_A_tree),
1442
modified=['f2', 'f3'])
1444
self.check_delta(rev_D_tree.changes_from(rev_B_tree),
1445
modified=['f2', 'f3'])
1447
self.check_delta(rev_D_tree.changes_from(rev_C_tree),
1448
modified=['f1', 'f3'])
836
delta = rev_D_tree.changes_from(rev_B_tree)
837
self.assertEqual([f2_changed, f3_changed], delta.modified)
838
self.assertEqual([], delta.renamed)
839
self.assertEqual([], delta.added)
840
self.assertEqual([], delta.removed)
842
delta = rev_D_tree.changes_from(rev_C_tree)
843
self.assertEqual([f1_changed, f3_changed], delta.modified)
844
self.assertEqual([], delta.renamed)
845
self.assertEqual([], delta.added)
846
self.assertEqual([], delta.removed)
1450
848
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
1451
"""Ensure _filter_revisions_touching_file_id returns the right values.
849
"""Make sure _filter_revisions_touching_file_id returns the right values.
1453
851
Get the return value from _filter_revisions_touching_file_id and make
1454
852
sure they are correct.
1456
# The api for _filter_revisions_touching_file_id is a little crazy.
854
# The api for _get_revisions_touching_file_id is a little crazy,
1457
855
# So we do the setup here.
1458
856
mainline = tree.branch.revision_history()
1459
857
mainline.insert(0, None)
1552
928
self.assertEqual('jsmith@example.com', lf.short_author(rev))
1553
929
rev.properties['author'] = 'John Smith jsmith@example.com'
1554
930
self.assertEqual('John Smith', lf.short_author(rev))
1555
del rev.properties['author']
1556
rev.properties['authors'] = ('John Smith <jsmith@example.com>\n'
1557
'Jane Rey <jrey@example.com>')
1558
self.assertEqual('John Smith', lf.short_author(rev))
1561
class TestReverseByDepth(tests.TestCase):
1562
"""Test reverse_by_depth behavior.
1564
This is used to present revisions in forward (oldest first) order in a nice
1567
The tests use lighter revision description to ease reading.
1570
def assertReversed(self, forward, backward):
1571
# Transform the descriptions to suit the API: tests use (revno, depth),
1572
# while the API expects (revid, revno, depth)
1573
def complete_revisions(l):
1574
"""Transform the description to suit the API.
1576
Tests use (revno, depth) whil the API expects (revid, revno, depth).
1577
Since the revid is arbitrary, we just duplicate revno
1579
return [ (r, r, d) for r, d in l]
1580
forward = complete_revisions(forward)
1581
backward= complete_revisions(backward)
1582
self.assertEqual(forward, log.reverse_by_depth(backward))
1585
def test_mainline_revisions(self):
1586
self.assertReversed([( '1', 0), ('2', 0)],
1587
[('2', 0), ('1', 0)])
1589
def test_merged_revisions(self):
1590
self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1591
[('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
1592
def test_shifted_merged_revisions(self):
1593
"""Test irregular layout.
1595
Requesting revisions touching a file can produce "holes" in the depths.
1597
self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1598
[('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
1600
def test_merged_without_child_revisions(self):
1601
"""Test irregular layout.
1603
Revision ranges can produce "holes" in the depths.
1605
# When a revision of higher depth doesn't follow one of lower depth, we
1606
# assume a lower depth one is virtually there
1607
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1608
[('4', 4), ('3', 3), ('2', 2), ('1', 2),])
1609
# So we get the same order after reversing below even if the original
1610
# revisions are not in the same order.
1611
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1612
[('3', 3), ('4', 4), ('2', 2), ('1', 2),])
1615
class TestHistoryChange(tests.TestCaseWithTransport):
1617
def setup_a_tree(self):
1618
tree = self.make_branch_and_tree('tree')
1620
self.addCleanup(tree.unlock)
1621
tree.commit('1a', rev_id='1a')
1622
tree.commit('2a', rev_id='2a')
1623
tree.commit('3a', rev_id='3a')
1626
def setup_ab_tree(self):
1627
tree = self.setup_a_tree()
1628
tree.set_last_revision('1a')
1629
tree.branch.set_last_revision_info(1, '1a')
1630
tree.commit('2b', rev_id='2b')
1631
tree.commit('3b', rev_id='3b')
1634
def setup_ac_tree(self):
1635
tree = self.setup_a_tree()
1636
tree.set_last_revision(revision.NULL_REVISION)
1637
tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1638
tree.commit('1c', rev_id='1c')
1639
tree.commit('2c', rev_id='2c')
1640
tree.commit('3c', rev_id='3c')
1643
def test_all_new(self):
1644
tree = self.setup_ab_tree()
1645
old, new = log.get_history_change('1a', '3a', tree.branch.repository)
1646
self.assertEqual([], old)
1647
self.assertEqual(['2a', '3a'], new)
1649
def test_all_old(self):
1650
tree = self.setup_ab_tree()
1651
old, new = log.get_history_change('3a', '1a', tree.branch.repository)
1652
self.assertEqual([], new)
1653
self.assertEqual(['2a', '3a'], old)
1655
def test_null_old(self):
1656
tree = self.setup_ab_tree()
1657
old, new = log.get_history_change(revision.NULL_REVISION,
1658
'3a', tree.branch.repository)
1659
self.assertEqual([], old)
1660
self.assertEqual(['1a', '2a', '3a'], new)
1662
def test_null_new(self):
1663
tree = self.setup_ab_tree()
1664
old, new = log.get_history_change('3a', revision.NULL_REVISION,
1665
tree.branch.repository)
1666
self.assertEqual([], new)
1667
self.assertEqual(['1a', '2a', '3a'], old)
1669
def test_diverged(self):
1670
tree = self.setup_ab_tree()
1671
old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1672
self.assertEqual(old, ['2a', '3a'])
1673
self.assertEqual(new, ['2b', '3b'])
1675
def test_unrelated(self):
1676
tree = self.setup_ac_tree()
1677
old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1678
self.assertEqual(old, ['1a', '2a', '3a'])
1679
self.assertEqual(new, ['1c', '2c', '3c'])
1681
def test_show_branch_change(self):
1682
tree = self.setup_ab_tree()
1684
log.show_branch_change(tree.branch, s, 3, '3a')
1685
self.assertContainsRe(s.getvalue(),
1686
'[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1687
'[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1689
def test_show_branch_change_no_change(self):
1690
tree = self.setup_ab_tree()
1692
log.show_branch_change(tree.branch, s, 3, '3b')
1693
self.assertEqual(s.getvalue(),
1694
'Nothing seems to have changed\n')
1696
def test_show_branch_change_no_old(self):
1697
tree = self.setup_ab_tree()
1699
log.show_branch_change(tree.branch, s, 2, '2b')
1700
self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1701
self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1703
def test_show_branch_change_no_new(self):
1704
tree = self.setup_ab_tree()
1705
tree.branch.set_last_revision_info(2, '2b')
1707
log.show_branch_change(tree.branch, s, 3, '3b')
1708
self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1709
self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')