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
32
class TestLogMixin(object):
34
def wt_commit(self, wt, message, **kwargs):
35
"""Use some mostly fixed values for commits to simplify tests.
37
Tests can use this function to get some commit attributes. The time
38
stamp is incremented at each commit.
40
if getattr(self, 'timestamp', None) is None:
41
self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
43
self.timestamp += 1 # 1 second between each commit
44
kwargs.setdefault('timestamp', self.timestamp)
45
kwargs.setdefault('timezone', 0) # UTC
46
kwargs.setdefault('committer', 'Joe Foo <joe@foo.com>')
48
return wt.commit(message, **kwargs)
51
class TestCaseForLogFormatter(tests.TestCaseWithTransport, TestLogMixin):
54
super(TestCaseForLogFormatter, self).setUp()
55
# keep a reference to the "current" custom prop. handler registry
56
self.properties_handler_registry = log.properties_handler_registry
57
# Use a clean registry for log
58
log.properties_handler_registry = registry.Registry()
61
log.properties_handler_registry = self.properties_handler_registry
62
self.addCleanup(restore)
64
def assertFormatterResult(self, result, branch, formatter_class,
65
formatter_kwargs=None, show_log_kwargs=None):
66
logfile = self.make_utf8_encoded_stringio()
67
if formatter_kwargs is None:
69
formatter = formatter_class(to_file=logfile, **formatter_kwargs)
70
if show_log_kwargs is None:
72
log.show_log(branch, formatter, **show_log_kwargs)
73
self.assertEqualDiff(result, logfile.getvalue())
75
def make_standard_commit(self, branch_nick, **kwargs):
76
wt = self.make_branch_and_tree('.')
78
self.addCleanup(wt.unlock)
79
self.build_tree(['a'])
81
wt.branch.nick = branch_nick
82
kwargs.setdefault('committer', 'Lorem Ipsum <test@example.com>')
83
kwargs.setdefault('authors', ['John Doe <jdoe@example.com>'])
84
self.wt_commit(wt, 'add a', **kwargs)
87
def make_commits_with_trailing_newlines(self, wt):
88
"""Helper method for LogFormatter tests"""
91
self.build_tree_contents([('a', 'hello moto\n')])
92
self.wt_commit(wt, 'simple log message', rev_id='a1')
93
self.build_tree_contents([('b', 'goodbye\n')])
95
self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id='a2')
97
self.build_tree_contents([('c', 'just another manic monday\n')])
99
self.wt_commit(wt, 'single line with trailing newline\n', rev_id='a3')
102
def _prepare_tree_with_merges(self, with_tags=False):
103
wt = self.make_branch_and_memory_tree('.')
105
self.addCleanup(wt.unlock)
107
self.wt_commit(wt, 'rev-1', rev_id='rev-1')
108
self.wt_commit(wt, 'rev-merged', rev_id='rev-2a')
109
wt.set_parent_ids(['rev-1', 'rev-2a'])
110
wt.branch.set_last_revision_info(1, 'rev-1')
111
self.wt_commit(wt, 'rev-2', rev_id='rev-2b')
114
branch.tags.set_tag('v0.2', 'rev-2b')
115
self.wt_commit(wt, 'rev-3', rev_id='rev-3')
116
branch.tags.set_tag('v1.0rc1', 'rev-3')
117
branch.tags.set_tag('v1.0', 'rev-3')
121
class LogCatcher(log.LogFormatter):
122
"""Pull log messages into a list rather than displaying them.
124
To simplify testing we save logged revisions here rather than actually
125
formatting anything, so that we can precisely check the result without
126
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.
129
supports_merge_revisions = True
130
51
supports_delta = True
134
def __init__(self, *args, **kwargs):
135
kwargs.update(dict(to_file=None))
136
super(LogCatcher, self).__init__(*args, **kwargs)
54
super(LogCatcher, self).__init__(to_file=None)
139
57
def log_revision(self, revision):
140
self.revisions.append(revision)
143
class TestShowLog(tests.TestCaseWithTransport):
58
self.logs.append(revision)
61
class TestShowLog(TestCaseWithTransport):
145
63
def checkDelta(self, delta, **kw):
146
"""Check the filenames touched by a delta are as expected.
148
Caller only have to pass in the list of files for each part, all
149
unspecified parts are considered empty (and checked as such).
64
"""Check the filenames touched by a delta are as expected."""
151
65
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
152
# By default we expect an empty list
153
66
expected = kw.get(n, [])
154
67
# strip out only the path components
155
68
got = [x[0] for x in getattr(delta, n)]
156
self.assertEqual(expected, got)
158
def assertInvalidRevisonNumber(self, br, start, end):
160
self.assertRaises(errors.InvalidRevisionNumber,
161
log.show_log, br, lf,
162
start_revision=start, end_revision=end)
69
self.assertEquals(expected, got)
164
71
def test_cur_revno(self):
165
72
wt = self.make_branch_and_tree('.')
169
76
wt.commit('empty commit')
170
log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
172
# Since there is a single revision in the branch all the combinations
174
self.assertInvalidRevisonNumber(b, 2, 1)
175
self.assertInvalidRevisonNumber(b, 1, 2)
176
self.assertInvalidRevisonNumber(b, 0, 2)
177
self.assertInvalidRevisonNumber(b, 1, 0)
178
self.assertInvalidRevisonNumber(b, -1, 1)
179
self.assertInvalidRevisonNumber(b, 1, -1)
181
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
182
94
wt = self.make_branch_and_tree('.')
185
log.show_log(wt.branch, lf)
187
self.assertEqual([], lf.revisions)
189
def test_empty_commit(self):
190
wt = self.make_branch_and_tree('.')
192
102
wt.commit('empty commit')
193
103
lf = LogCatcher()
194
log.show_log(wt.branch, lf, verbose=True)
196
self.assertEqual(1, len(revs))
197
self.assertEqual('1', revs[0].revno)
198
self.assertEqual('empty commit', revs[0].rev.message)
199
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)
201
def test_simple_commit(self):
202
wt = self.make_branch_and_tree('.')
203
wt.commit('empty commit')
204
112
self.build_tree(['hello'])
206
114
wt.commit('add one file',
207
115
committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
208
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
209
126
lf = LogCatcher()
210
log.show_log(wt.branch, lf, verbose=True)
211
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))
212
133
# first one is most recent
213
log_entry = lf.revisions[0]
214
self.assertEqual('2', log_entry.revno)
215
self.assertEqual('add one file', log_entry.rev.message)
216
self.checkDelta(log_entry.delta, added=['hello'])
218
def test_commit_message_with_control_chars(self):
219
wt = self.make_branch_and_tree('.')
220
msg = u"All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
221
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)
223
145
lf = LogCatcher()
224
log.show_log(wt.branch, lf, verbose=True)
225
committed_msg = lf.revisions[0].rev.message
226
if wt.branch.repository._serializer.squashes_xml_invalid_characters:
227
self.assertNotEqual(msg, committed_msg)
228
self.assertTrue(len(committed_msg) > len(msg))
230
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))
232
def test_commit_message_without_control_chars(self):
233
wt = self.make_branch_and_tree('.')
152
# Check that log message with only XML-valid characters isn't
234
153
# escaped. As ElementTree apparently does some kind of
235
154
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
236
155
# included in the test commit message, even though they are
237
156
# valid XML 1.0 characters.
238
157
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
158
self.log("original commit message: %r", msg)
240
160
lf = LogCatcher()
241
log.show_log(wt.branch, lf, verbose=True)
242
committed_msg = lf.revisions[0].rev.message
243
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)
245
166
def test_deltas_in_merge_revisions(self):
246
167
"""Check deltas created for both mainline and merge revisions"""
168
eq = self.assertEquals
247
169
wt = self.make_branch_and_tree('parent')
248
170
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
262
184
lf = LogCatcher()
263
185
lf.supports_merge_revisions = True
264
log.show_log(b, lf, verbose=True)
267
self.assertEqual(3, len(revs))
270
self.assertEqual('2', logentry.revno)
271
self.assertEqual('merge child branch', logentry.rev.message)
272
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
275
self.assertEqual('1.1.1', logentry.revno)
276
self.assertEqual('remove file1 and modify file2', logentry.rev.message)
277
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
280
self.assertEqual('1', logentry.revno)
281
self.assertEqual('add file1 and file2', logentry.rev.message)
282
self.checkDelta(logentry.delta, added=['file1', 'file2'])
285
class TestShortLogFormatter(TestCaseForLogFormatter):
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)
234
def make_commits_with_trailing_newlines(wt):
235
"""Helper method for LogFormatter tests"""
238
open('a', 'wb').write('hello moto\n')
240
wt.commit('simple log message', rev_id='a1',
241
timestamp=1132586655.459960938, timezone=-6*3600,
242
committer='Joe Foo <joe@foo.com>')
243
open('b', 'wb').write('goodbye\n')
245
wt.commit('multiline\nlog\nmessage\n', rev_id='a2',
246
timestamp=1132586842.411175966, timezone=-6*3600,
247
committer='Joe Foo <joe@foo.com>',
248
author='Joe Bar <joe@bar.com>')
250
open('c', 'wb').write('just another manic monday\n')
252
wt.commit('single line with trailing newline\n', rev_id='a3',
253
timestamp=1132587176.835228920, timezone=-6*3600,
254
committer = 'Joe Foo <joe@foo.com>')
258
def normalize_log(log):
259
"""Replaces the variable lines of logs with fixed lines"""
260
author = 'author: Dolor Sit <test@example.com>'
261
committer = 'committer: Lorem Ipsum <test@example.com>'
262
lines = log.splitlines(True)
263
for idx,line in enumerate(lines):
264
stripped_line = line.lstrip()
265
indent = ' ' * (len(line) - len(stripped_line))
266
if stripped_line.startswith('author:'):
267
lines[idx] = indent + author + '\n'
268
elif stripped_line.startswith('committer:'):
269
lines[idx] = indent + committer + '\n'
270
elif stripped_line.startswith('timestamp:'):
271
lines[idx] = indent + 'timestamp: Just now\n'
272
return ''.join(lines)
275
class TestShortLogFormatter(TestCaseWithTransport):
287
277
def test_trailing_newlines(self):
288
278
wt = self.make_branch_and_tree('.')
289
b = self.make_commits_with_trailing_newlines(wt)
290
self.assertFormatterResult("""\
291
3 Joe Foo\t2005-11-22
279
b = make_commits_with_trailing_newlines(wt)
280
sio = self.make_utf8_encoded_stringio()
281
lf = ShortLogFormatter(to_file=sio)
283
self.assertEqualDiff(sio.getvalue(), """\
284
3 Joe Foo\t2005-11-21
292
285
single line with trailing newline
294
2 Joe Foo\t2005-11-22
287
2 Joe Bar\t2005-11-21
299
1 Joe Foo\t2005-11-22
292
1 Joe Foo\t2005-11-21
300
293
simple log message
303
b, log.ShortLogFormatter)
305
297
def test_short_log_with_merges(self):
306
wt = self._prepare_tree_with_merges()
307
self.assertFormatterResult("""\
308
2 Joe Foo\t2005-11-22 [merge]
311
1 Joe Foo\t2005-11-22
315
wt.branch, log.ShortLogFormatter)
317
def test_short_log_with_merges_and_advice(self):
318
wt = self._prepare_tree_with_merges()
319
self.assertFormatterResult("""\
320
2 Joe Foo\t2005-11-22 [merge]
323
1 Joe Foo\t2005-11-22
326
Use --include-merged or -n0 to see merged revisions.
328
wt.branch, log.ShortLogFormatter,
329
formatter_kwargs=dict(show_advice=True))
331
def test_short_log_with_merges_and_range(self):
332
wt = self._prepare_tree_with_merges()
333
self.wt_commit(wt, 'rev-3a', rev_id='rev-3a')
334
wt.branch.set_last_revision_info(2, 'rev-2b')
335
wt.set_parent_ids(['rev-2b', 'rev-3a'])
336
self.wt_commit(wt, 'rev-3b', rev_id='rev-3b')
337
self.assertFormatterResult("""\
338
3 Joe Foo\t2005-11-22 [merge]
341
2 Joe Foo\t2005-11-22 [merge]
345
wt.branch, log.ShortLogFormatter,
346
show_log_kwargs=dict(start_revision=2, end_revision=3))
348
def test_short_log_with_tags(self):
349
wt = self._prepare_tree_with_merges(with_tags=True)
350
self.assertFormatterResult("""\
351
3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
354
2 Joe Foo\t2005-11-22 {v0.2} [merge]
357
1 Joe Foo\t2005-11-22
361
wt.branch, log.ShortLogFormatter)
298
wt = self.make_branch_and_memory_tree('.')
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(), """\
317
2 Joe Foo\t2005-11-22 [merge]
320
1 Joe Foo\t2005-11-22
363
327
def test_short_log_single_merge_revision(self):
364
wt = self._prepare_tree_with_merges()
365
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
366
rev = revspec.in_history(wt.branch)
367
self.assertFormatterResult("""\
368
1.1.1 Joe Foo\t2005-11-22
372
wt.branch, log.ShortLogFormatter,
373
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
375
def test_show_ids(self):
376
wt = self.make_branch_and_tree('parent')
377
self.build_tree(['parent/f1', 'parent/f2'])
379
self.wt_commit(wt, 'first post', rev_id='a')
380
child_wt = wt.bzrdir.sprout('child').open_workingtree()
381
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
382
wt.merge_from_branch(child_wt.branch)
383
self.wt_commit(wt, 'merge branch 1', rev_id='c')
384
self.assertFormatterResult("""\
385
2 Joe Foo\t2005-11-22 [merge]
389
1.1.1 Joe Foo\t2005-11-22
393
1 Joe Foo\t2005-11-22
398
wt.branch, log.ShortLogFormatter,
399
formatter_kwargs=dict(levels=0,show_ids=True))
402
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
404
def test_short_merge_revs_log_with_merges(self):
405
wt = self._prepare_tree_with_merges()
406
# Note that the 1.1.1 indenting is in fact correct given that
407
# the revision numbers are right justified within 5 characters
408
# for mainline revnos and 9 characters for dotted revnos.
409
self.assertFormatterResult("""\
410
2 Joe Foo\t2005-11-22 [merge]
413
1.1.1 Joe Foo\t2005-11-22
416
1 Joe Foo\t2005-11-22
420
wt.branch, log.ShortLogFormatter,
421
formatter_kwargs=dict(levels=0))
423
def test_short_merge_revs_log_single_merge_revision(self):
424
wt = self._prepare_tree_with_merges()
425
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
426
rev = revspec.in_history(wt.branch)
427
self.assertFormatterResult("""\
428
1.1.1 Joe Foo\t2005-11-22
432
wt.branch, log.ShortLogFormatter,
433
formatter_kwargs=dict(levels=0),
434
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
437
class TestLongLogFormatter(TestCaseForLogFormatter):
328
wt = self.make_branch_and_memory_tree('.')
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):
439
360
def test_verbose_log(self):
440
361
"""Verbose log includes changed files
444
wt = self.make_standard_commit('test_verbose_log', authors=[])
445
self.assertFormatterResult('''\
365
wt = self.make_branch_and_tree('.')
367
self.build_tree(['a'])
369
# XXX: why does a longer nick show up?
370
b.nick = 'test_verbose_log'
371
wt.commit(message='add a',
372
timestamp=1132711707,
374
committer='Lorem Ipsum <test@example.com>')
375
logfile = file('out.tmp', 'w+')
376
formatter = LongLogFormatter(to_file=logfile)
377
show_log(b, formatter, verbose=True)
380
log_contents = logfile.read()
381
self.assertEqualDiff(log_contents, '''\
446
382
------------------------------------------------------------
448
384
committer: Lorem Ipsum <test@example.com>
449
385
branch nick: test_verbose_log
450
timestamp: Tue 2005-11-22 00:00:00 +0000
386
timestamp: Wed 2005-11-23 12:08:27 +1000
456
wt.branch, log.LongLogFormatter,
457
show_log_kwargs=dict(verbose=True))
459
393
def test_merges_are_indented_by_level(self):
460
394
wt = self.make_branch_and_tree('parent')
461
self.wt_commit(wt, 'first post')
462
child_wt = wt.bzrdir.sprout('child').open_workingtree()
463
self.wt_commit(child_wt, 'branch 1')
464
smallerchild_wt = wt.bzrdir.sprout('smallerchild').open_workingtree()
465
self.wt_commit(smallerchild_wt, 'branch 2')
466
child_wt.merge_from_branch(smallerchild_wt.branch)
467
self.wt_commit(child_wt, 'merge branch 2')
468
wt.merge_from_branch(child_wt.branch)
469
self.wt_commit(wt, 'merge branch 1')
470
self.assertFormatterResult("""\
395
wt.commit('first post')
396
self.run_bzr('branch parent child')
397
self.run_bzr(['commit', '-m', 'branch 1', '--unchanged', 'child'])
398
self.run_bzr('branch child smallerchild')
399
self.run_bzr(['commit', '-m', 'branch 2', '--unchanged',
402
self.run_bzr('merge ../smallerchild')
403
self.run_bzr(['commit', '-m', 'merge branch 2'])
404
os.chdir('../parent')
405
self.run_bzr('merge ../child')
406
wt.commit('merge branch 1')
408
sio = self.make_utf8_encoded_stringio()
409
lf = LongLogFormatter(to_file=sio)
410
show_log(b, lf, verbose=True)
411
log = normalize_log(sio.getvalue())
412
self.assertEqualDiff(log, """\
471
413
------------------------------------------------------------
473
committer: Joe Foo <joe@foo.com>
415
committer: Lorem Ipsum <test@example.com>
474
416
branch nick: parent
475
timestamp: Tue 2005-11-22 00:00:04 +0000
478
420
------------------------------------------------------------
480
committer: Joe Foo <joe@foo.com>
422
committer: Lorem Ipsum <test@example.com>
481
423
branch nick: child
482
timestamp: Tue 2005-11-22 00:00:03 +0000
485
427
------------------------------------------------------------
487
committer: Joe Foo <joe@foo.com>
429
committer: Lorem Ipsum <test@example.com>
488
430
branch nick: smallerchild
489
timestamp: Tue 2005-11-22 00:00:02 +0000
492
434
------------------------------------------------------------
494
committer: Joe Foo <joe@foo.com>
436
committer: Lorem Ipsum <test@example.com>
495
437
branch nick: child
496
timestamp: Tue 2005-11-22 00:00:01 +0000
499
441
------------------------------------------------------------
501
committer: Joe Foo <joe@foo.com>
443
committer: Lorem Ipsum <test@example.com>
502
444
branch nick: parent
503
timestamp: Tue 2005-11-22 00:00:00 +0000
507
wt.branch, log.LongLogFormatter,
508
formatter_kwargs=dict(levels=0),
509
show_log_kwargs=dict(verbose=True))
511
450
def test_verbose_merge_revisions_contain_deltas(self):
512
451
wt = self.make_branch_and_tree('parent')
513
452
self.build_tree(['parent/f1', 'parent/f2'])
514
453
wt.add(['f1','f2'])
515
self.wt_commit(wt, 'first post')
516
child_wt = wt.bzrdir.sprout('child').open_workingtree()
454
wt.commit('first post')
455
self.run_bzr('branch parent child')
517
456
os.unlink('child/f1')
518
self.build_tree_contents([('child/f2', 'hello\n')])
519
self.wt_commit(child_wt, 'removed f1 and modified f2')
520
wt.merge_from_branch(child_wt.branch)
521
self.wt_commit(wt, 'merge branch 1')
522
self.assertFormatterResult("""\
457
file('child/f2', 'wb').write('hello\n')
458
self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
461
self.run_bzr('merge ../child')
462
wt.commit('merge branch 1')
464
sio = self.make_utf8_encoded_stringio()
465
lf = LongLogFormatter(to_file=sio)
466
show_log(b, lf, verbose=True)
467
log = normalize_log(sio.getvalue())
468
self.assertEqualDiff(log, """\
523
469
------------------------------------------------------------
525
committer: Joe Foo <joe@foo.com>
471
committer: Lorem Ipsum <test@example.com>
526
472
branch nick: parent
527
timestamp: Tue 2005-11-22 00:00:02 +0000
582
529
committer: Joe Foo <joe@foo.com>
583
530
branch nick: test
584
timestamp: Tue 2005-11-22 00:00:00 +0000
531
timestamp: Mon 2005-11-21 09:24:15 -0600
586
533
simple log message
588
b, log.LongLogFormatter)
590
536
def test_author_in_log(self):
591
537
"""Log includes the author name if it's set in
592
538
the revision properties
594
wt = self.make_standard_commit('test_author_log',
595
authors=['John Doe <jdoe@example.com>',
596
'Jane Rey <jrey@example.com>'])
597
self.assertFormatterResult("""\
598
------------------------------------------------------------
600
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
601
committer: Lorem Ipsum <test@example.com>
602
branch nick: test_author_log
603
timestamp: Tue 2005-11-22 00:00:00 +0000
607
wt.branch, log.LongLogFormatter)
609
def test_properties_in_log(self):
610
"""Log includes the custom properties returned by the registered
613
wt = self.make_standard_commit('test_properties_in_log')
614
def trivial_custom_prop_handler(revision):
615
return {'test_prop':'test_value'}
617
# Cleaned up in setUp()
618
log.properties_handler_registry.register(
619
'trivial_custom_prop_handler',
620
trivial_custom_prop_handler)
621
self.assertFormatterResult("""\
622
------------------------------------------------------------
624
test_prop: test_value
625
author: John Doe <jdoe@example.com>
626
committer: Lorem Ipsum <test@example.com>
627
branch nick: test_properties_in_log
628
timestamp: Tue 2005-11-22 00:00:00 +0000
632
wt.branch, log.LongLogFormatter)
634
def test_properties_in_short_log(self):
635
"""Log includes the custom properties returned by the registered
638
wt = self.make_standard_commit('test_properties_in_short_log')
639
def trivial_custom_prop_handler(revision):
640
return {'test_prop':'test_value'}
642
log.properties_handler_registry.register(
643
'trivial_custom_prop_handler',
644
trivial_custom_prop_handler)
645
self.assertFormatterResult("""\
646
1 John Doe\t2005-11-22
647
test_prop: test_value
651
wt.branch, log.ShortLogFormatter)
653
def test_error_in_properties_handler(self):
654
"""Log includes the custom properties returned by the registered
657
wt = self.make_standard_commit('error_in_properties_handler',
658
revprops={'first_prop':'first_value'})
659
sio = self.make_utf8_encoded_stringio()
660
formatter = log.LongLogFormatter(to_file=sio)
661
def trivial_custom_prop_handler(revision):
662
raise StandardError("a test error")
664
log.properties_handler_registry.register(
665
'trivial_custom_prop_handler',
666
trivial_custom_prop_handler)
667
self.assertRaises(StandardError, log.show_log, wt.branch, formatter,)
669
def test_properties_handler_bad_argument(self):
670
wt = self.make_standard_commit('bad_argument',
671
revprops={'a_prop':'test_value'})
672
sio = self.make_utf8_encoded_stringio()
673
formatter = log.LongLogFormatter(to_file=sio)
674
def bad_argument_prop_handler(revision):
675
return {'custom_prop_name':revision.properties['a_prop']}
677
log.properties_handler_registry.register(
678
'bad_argument_prop_handler',
679
bad_argument_prop_handler)
681
self.assertRaises(AttributeError, formatter.show_properties,
684
revision = wt.branch.repository.get_revision(wt.branch.last_revision())
685
formatter.show_properties(revision, '')
686
self.assertEqualDiff('''custom_prop_name: test_value\n''',
689
def test_show_ids(self):
690
wt = self.make_branch_and_tree('parent')
691
self.build_tree(['parent/f1', 'parent/f2'])
693
self.wt_commit(wt, 'first post', rev_id='a')
694
child_wt = wt.bzrdir.sprout('child').open_workingtree()
695
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
696
wt.merge_from_branch(child_wt.branch)
697
self.wt_commit(wt, 'merge branch 1', rev_id='c')
698
self.assertFormatterResult("""\
699
------------------------------------------------------------
704
committer: Joe Foo <joe@foo.com>
706
timestamp: Tue 2005-11-22 00:00:02 +0000
709
------------------------------------------------------------
713
committer: Joe Foo <joe@foo.com>
715
timestamp: Tue 2005-11-22 00:00:01 +0000
718
------------------------------------------------------------
721
committer: Joe Foo <joe@foo.com>
723
timestamp: Tue 2005-11-22 00:00:00 +0000
727
wt.branch, log.LongLogFormatter,
728
formatter_kwargs=dict(levels=0,show_ids=True))
731
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
733
def test_long_verbose_log(self):
734
"""Verbose log includes changed files
738
wt = self.make_standard_commit('test_long_verbose_log', authors=[])
739
self.assertFormatterResult("""\
740
------------------------------------------------------------
742
committer: Lorem Ipsum <test@example.com>
743
branch nick: test_long_verbose_log
744
timestamp: Tue 2005-11-22 00:00:00 +0000
750
wt.branch, log.LongLogFormatter,
751
formatter_kwargs=dict(levels=1),
752
show_log_kwargs=dict(verbose=True))
754
def test_long_verbose_contain_deltas(self):
755
wt = self.make_branch_and_tree('parent')
756
self.build_tree(['parent/f1', 'parent/f2'])
758
self.wt_commit(wt, 'first post')
759
child_wt = wt.bzrdir.sprout('child').open_workingtree()
760
os.unlink('child/f1')
761
self.build_tree_contents([('child/f2', 'hello\n')])
762
self.wt_commit(child_wt, 'removed f1 and modified f2')
763
wt.merge_from_branch(child_wt.branch)
764
self.wt_commit(wt, 'merge branch 1')
765
self.assertFormatterResult("""\
766
------------------------------------------------------------
768
committer: Joe Foo <joe@foo.com>
770
timestamp: Tue 2005-11-22 00:00:02 +0000
777
------------------------------------------------------------
779
committer: Joe Foo <joe@foo.com>
781
timestamp: Tue 2005-11-22 00:00:00 +0000
788
wt.branch, log.LongLogFormatter,
789
formatter_kwargs=dict(levels=1),
790
show_log_kwargs=dict(verbose=True))
792
def test_long_trailing_newlines(self):
793
540
wt = self.make_branch_and_tree('.')
794
b = self.make_commits_with_trailing_newlines(wt)
795
self.assertFormatterResult("""\
796
------------------------------------------------------------
798
committer: Joe Foo <joe@foo.com>
800
timestamp: Tue 2005-11-22 00:00:02 +0000
802
single line with trailing newline
803
------------------------------------------------------------
805
committer: Joe Foo <joe@foo.com>
807
timestamp: Tue 2005-11-22 00:00:01 +0000
812
------------------------------------------------------------
814
committer: Joe Foo <joe@foo.com>
816
timestamp: Tue 2005-11-22 00:00:00 +0000
820
b, log.LongLogFormatter,
821
formatter_kwargs=dict(levels=1))
823
def test_long_author_in_log(self):
824
"""Log includes the author name if it's set in
825
the revision properties
827
wt = self.make_standard_commit('test_author_log')
828
self.assertFormatterResult("""\
542
self.build_tree(['a'])
544
b.nick = 'test_author_log'
545
wt.commit(message='add a',
546
timestamp=1132711707,
548
committer='Lorem Ipsum <test@example.com>',
549
author='John Doe <jdoe@example.com>')
551
formatter = LongLogFormatter(to_file=sio)
552
show_log(b, formatter)
553
self.assertEqualDiff(sio.getvalue(), '''\
829
554
------------------------------------------------------------
831
556
author: John Doe <jdoe@example.com>
832
557
committer: Lorem Ipsum <test@example.com>
833
558
branch nick: test_author_log
834
timestamp: Tue 2005-11-22 00:00:00 +0000
838
wt.branch, log.LongLogFormatter,
839
formatter_kwargs=dict(levels=1))
841
def test_long_properties_in_log(self):
842
"""Log includes the custom properties returned by the registered
845
wt = self.make_standard_commit('test_properties_in_log')
846
def trivial_custom_prop_handler(revision):
847
return {'test_prop':'test_value'}
849
log.properties_handler_registry.register(
850
'trivial_custom_prop_handler',
851
trivial_custom_prop_handler)
852
self.assertFormatterResult("""\
853
------------------------------------------------------------
855
test_prop: test_value
856
author: John Doe <jdoe@example.com>
857
committer: Lorem Ipsum <test@example.com>
858
branch nick: test_properties_in_log
859
timestamp: Tue 2005-11-22 00:00:00 +0000
863
wt.branch, log.LongLogFormatter,
864
formatter_kwargs=dict(levels=1))
867
class TestLineLogFormatter(TestCaseForLogFormatter):
559
timestamp: Wed 2005-11-23 12:08:27 +1000
566
class TestLineLogFormatter(TestCaseWithTransport):
869
568
def test_line_log(self):
870
569
"""Line log should show revno
874
wt = self.make_standard_commit('test-line-log',
875
committer='Line-Log-Formatter Tester <test@line.log>',
877
self.assertFormatterResult("""\
878
1: Line-Log-Formatte... 2005-11-22 add a
880
wt.branch, log.LineLogFormatter)
573
wt = self.make_branch_and_tree('.')
575
self.build_tree(['a'])
577
b.nick = 'test-line-log'
578
wt.commit(message='add a',
579
timestamp=1132711707,
581
committer='Line-Log-Formatter Tester <test@line.log>')
582
logfile = file('out.tmp', 'w+')
583
formatter = LineLogFormatter(to_file=logfile)
584
show_log(b, formatter)
587
log_contents = logfile.read()
588
self.assertEqualDiff(log_contents,
589
'1: Line-Log-Formatte... 2005-11-23 add a\n')
882
591
def test_trailing_newlines(self):
883
592
wt = self.make_branch_and_tree('.')
884
b = self.make_commits_with_trailing_newlines(wt)
885
self.assertFormatterResult("""\
886
3: Joe Foo 2005-11-22 single line with trailing newline
887
2: Joe Foo 2005-11-22 multiline
888
1: Joe Foo 2005-11-22 simple log message
890
b, log.LineLogFormatter)
593
b = make_commits_with_trailing_newlines(wt)
594
sio = self.make_utf8_encoded_stringio()
595
lf = LineLogFormatter(to_file=sio)
597
self.assertEqualDiff(sio.getvalue(), """\
598
3: Joe Foo 2005-11-21 single line with trailing newline
599
2: Joe Bar 2005-11-21 multiline
600
1: Joe Foo 2005-11-21 simple log message
892
603
def test_line_log_single_merge_revision(self):
893
wt = self._prepare_tree_with_merges()
894
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
895
rev = revspec.in_history(wt.branch)
896
self.assertFormatterResult("""\
897
1.1.1: Joe Foo 2005-11-22 rev-merged
899
wt.branch, log.LineLogFormatter,
900
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
902
def test_line_log_with_tags(self):
903
wt = self._prepare_tree_with_merges(with_tags=True)
904
self.assertFormatterResult("""\
905
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
906
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
907
1: Joe Foo 2005-11-22 rev-1
909
wt.branch, log.LineLogFormatter)
912
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
914
def test_line_merge_revs_log(self):
915
"""Line log should show revno
919
wt = self.make_standard_commit('test-line-log',
920
committer='Line-Log-Formatter Tester <test@line.log>',
922
self.assertFormatterResult("""\
923
1: Line-Log-Formatte... 2005-11-22 add a
925
wt.branch, log.LineLogFormatter)
927
def test_line_merge_revs_log_single_merge_revision(self):
928
wt = self._prepare_tree_with_merges()
929
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
930
rev = revspec.in_history(wt.branch)
931
self.assertFormatterResult("""\
932
1.1.1: Joe Foo 2005-11-22 rev-merged
934
wt.branch, log.LineLogFormatter,
935
formatter_kwargs=dict(levels=0),
936
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
938
def test_line_merge_revs_log_with_merges(self):
939
wt = self._prepare_tree_with_merges()
940
self.assertFormatterResult("""\
941
2: Joe Foo 2005-11-22 [merge] rev-2
942
1.1.1: Joe Foo 2005-11-22 rev-merged
943
1: Joe Foo 2005-11-22 rev-1
945
wt.branch, log.LineLogFormatter,
946
formatter_kwargs=dict(levels=0))
949
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
951
def test_gnu_changelog(self):
952
wt = self.make_standard_commit('nicky', authors=[])
953
self.assertFormatterResult('''\
954
2005-11-22 Lorem Ipsum <test@example.com>
959
wt.branch, log.GnuChangelogLogFormatter)
961
def test_with_authors(self):
962
wt = self.make_standard_commit('nicky',
963
authors=['Fooa Fooz <foo@example.com>',
964
'Bari Baro <bar@example.com>'])
965
self.assertFormatterResult('''\
966
2005-11-22 Fooa Fooz <foo@example.com>
971
wt.branch, log.GnuChangelogLogFormatter)
973
def test_verbose(self):
974
wt = self.make_standard_commit('nicky')
975
self.assertFormatterResult('''\
976
2005-11-22 John Doe <jdoe@example.com>
983
wt.branch, log.GnuChangelogLogFormatter,
984
show_log_kwargs=dict(verbose=True))
987
class TestShowChangedRevisions(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):
635
def make_tree_with_commits(self):
636
"""Create a tree with well-known revision ids"""
637
wt = self.make_branch_and_tree('tree1')
638
wt.commit('commit one', rev_id='1')
639
wt.commit('commit two', rev_id='2')
640
wt.commit('commit three', rev_id='3')
641
mainline_revs = [None, '1', '2', '3']
642
rev_nos = {'1': 1, '2': 2, '3': 3}
643
return mainline_revs, rev_nos, wt
645
def make_tree_with_merges(self):
646
"""Create a tree with well-known revision ids and a merge"""
647
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
648
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
649
tree2.commit('four-a', rev_id='4a')
650
wt.merge_from_branch(tree2.branch)
651
wt.commit('four-b', rev_id='4b')
652
mainline_revs.append('4b')
655
return mainline_revs, rev_nos, wt
657
def make_tree_with_many_merges(self):
658
"""Create a tree with well-known revision ids"""
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')
672
mainline_revs = [None, '1', '2', '3c', '4b']
673
rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
674
full_rev_nos_for_reference = {
677
'3a': '2.1.1', #first commit tree 3
678
'3b': '2.2.1', # first commit tree 2
679
'3c': '3', #merges 3b to main
680
'4a': '2.2.2', # second commit tree 2
681
'4b': '4', # merges 4a to main
683
return mainline_revs, rev_nos, wt
685
def test_get_view_revisions_forward(self):
686
"""Test the get_view_revisions method"""
687
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
688
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
690
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
692
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
693
'forward', include_merges=False))
694
self.assertEqual(revisions, revisions2)
696
def test_get_view_revisions_reverse(self):
697
"""Test the get_view_revisions with reverse"""
698
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
699
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
701
self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
703
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
704
'reverse', include_merges=False))
705
self.assertEqual(revisions, revisions2)
707
def test_get_view_revisions_merge(self):
708
"""Test get_view_revisions when there are merges"""
709
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
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),
721
def test_get_view_revisions_merge_reverse(self):
722
"""Test get_view_revisions in reverse when there are merges"""
723
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
724
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
726
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
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))
731
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
735
def test_get_view_revisions_merge2(self):
736
"""Test get_view_revisions when there are merges"""
737
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
738
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
740
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
741
('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
743
self.assertEqual(expected, revisions)
744
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
745
'forward', include_merges=False))
746
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
751
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
753
def create_tree_with_single_merge(self):
754
"""Create a branch with a moderate layout.
756
The revision graph looks like:
764
In this graph, A introduced files f1 and f2 and f3.
765
B modifies f1 and f3, and C modifies f2 and f3.
766
D merges the changes from B and C and resolves the conflict for f3.
768
# TODO: jam 20070218 This seems like it could really be done
769
# with make_branch_and_memory_tree() if we could just
770
# create the content of those files.
771
# TODO: jam 20070218 Another alternative is that we would really
772
# like to only create this tree 1 time for all tests that
773
# use it. Since 'log' only uses the tree in a readonly
774
# fashion, it seems a shame to regenerate an identical
775
# tree for each test.
776
tree = self.make_branch_and_tree('tree')
778
self.addCleanup(tree.unlock)
780
self.build_tree_contents([('tree/f1', 'A\n'),
784
tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
785
tree.commit('A', rev_id='A')
787
self.build_tree_contents([('tree/f2', 'A\nC\n'),
788
('tree/f3', 'A\nC\n'),
790
tree.commit('C', rev_id='C')
791
# Revert back to A to build the other history.
792
tree.set_last_revision('A')
793
tree.branch.set_last_revision_info(1, 'A')
794
self.build_tree_contents([('tree/f1', 'A\nB\n'),
796
('tree/f3', 'A\nB\n'),
798
tree.commit('B', rev_id='B')
799
tree.set_parent_ids(['B', 'C'])
800
self.build_tree_contents([('tree/f1', 'A\nB\n'),
801
('tree/f2', 'A\nC\n'),
802
('tree/f3', 'A\nB\nC\n'),
804
tree.commit('D', rev_id='D')
806
# Switch to a read lock for this tree.
807
# We still have addCleanup(unlock)
812
def test_tree_with_single_merge(self):
813
"""Make sure the tree layout is correct."""
814
tree = self.create_tree_with_single_merge()
815
rev_A_tree = tree.branch.repository.revision_tree('A')
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)
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)
835
rev_D_tree = tree.branch.repository.revision_tree('D')
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)
848
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
849
"""Make sure _filter_revisions_touching_file_id returns the right values.
851
Get the return value from _filter_revisions_touching_file_id and make
852
sure they are correct.
854
# The api for _get_revisions_touching_file_id is a little crazy,
855
# So we do the setup here.
856
mainline = tree.branch.revision_history()
857
mainline.insert(0, None)
858
revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
859
view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
861
actual_revs = log._filter_revisions_touching_file_id(
865
list(view_revs_iter))
866
self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
868
def test_file_id_f1(self):
869
tree = self.create_tree_with_single_merge()
870
# f1 should be marked as modified by revisions A and B
871
self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
873
def test_file_id_f2(self):
874
tree = self.create_tree_with_single_merge()
875
# f2 should be marked as modified by revisions A, C, and D
876
# because D merged the changes from C.
877
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
879
def test_file_id_f3(self):
880
tree = self.create_tree_with_single_merge()
881
# f3 should be marked as modified by revisions A, B, C, and D
882
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
885
class TestShowChangedRevisions(TestCaseWithTransport):
989
887
def test_show_changed_revisions_verbose(self):
990
888
tree = self.make_branch_and_tree('tree_a')
997
895
self.assertNotContainsRe(s.getvalue(), 'foo')
1000
class TestLogFormatter(tests.TestCase):
1003
super(TestLogFormatter, self).setUp()
1004
self.rev = revision.Revision('a-id')
1005
self.lf = log.LogFormatter(None)
898
class TestLogFormatter(TestCase):
1007
900
def test_short_committer(self):
1008
def assertCommitter(expected, committer):
1009
self.rev.committer = committer
1010
self.assertEqual(expected, self.lf.short_committer(self.rev))
1012
assertCommitter('John Doe', 'John Doe <jdoe@example.com>')
1013
assertCommitter('John Smith', 'John Smith <jsmith@example.com>')
1014
assertCommitter('John Smith', 'John Smith')
1015
assertCommitter('jsmith@example.com', 'jsmith@example.com')
1016
assertCommitter('jsmith@example.com', '<jsmith@example.com>')
1017
assertCommitter('John Smith', 'John Smith jsmith@example.com')
901
rev = Revision('a-id')
902
rev.committer = 'John Doe <jdoe@example.com>'
903
lf = LogFormatter(None)
904
self.assertEqual('John Doe', lf.short_committer(rev))
905
rev.committer = 'John Smith <jsmith@example.com>'
906
self.assertEqual('John Smith', lf.short_committer(rev))
907
rev.committer = 'John Smith'
908
self.assertEqual('John Smith', lf.short_committer(rev))
909
rev.committer = 'jsmith@example.com'
910
self.assertEqual('jsmith@example.com', lf.short_committer(rev))
911
rev.committer = '<jsmith@example.com>'
912
self.assertEqual('jsmith@example.com', lf.short_committer(rev))
913
rev.committer = 'John Smith jsmith@example.com'
914
self.assertEqual('John Smith', lf.short_committer(rev))
1019
916
def test_short_author(self):
1020
def assertAuthor(expected, author):
1021
self.rev.properties['author'] = author
1022
self.assertEqual(expected, self.lf.short_author(self.rev))
1024
assertAuthor('John Smith', 'John Smith <jsmith@example.com>')
1025
assertAuthor('John Smith', 'John Smith')
1026
assertAuthor('jsmith@example.com', 'jsmith@example.com')
1027
assertAuthor('jsmith@example.com', '<jsmith@example.com>')
1028
assertAuthor('John Smith', 'John Smith jsmith@example.com')
1030
def test_short_author_from_committer(self):
1031
self.rev.committer = 'John Doe <jdoe@example.com>'
1032
self.assertEqual('John Doe', self.lf.short_author(self.rev))
1034
def test_short_author_from_authors(self):
1035
self.rev.properties['authors'] = ('John Smith <jsmith@example.com>\n'
1036
'Jane Rey <jrey@example.com>')
1037
self.assertEqual('John Smith', self.lf.short_author(self.rev))
1040
class TestReverseByDepth(tests.TestCase):
1041
"""Test reverse_by_depth behavior.
1043
This is used to present revisions in forward (oldest first) order in a nice
1046
The tests use lighter revision description to ease reading.
1049
def assertReversed(self, forward, backward):
1050
# Transform the descriptions to suit the API: tests use (revno, depth),
1051
# while the API expects (revid, revno, depth)
1052
def complete_revisions(l):
1053
"""Transform the description to suit the API.
1055
Tests use (revno, depth) whil the API expects (revid, revno, depth).
1056
Since the revid is arbitrary, we just duplicate revno
1058
return [ (r, r, d) for r, d in l]
1059
forward = complete_revisions(forward)
1060
backward= complete_revisions(backward)
1061
self.assertEqual(forward, log.reverse_by_depth(backward))
1064
def test_mainline_revisions(self):
1065
self.assertReversed([( '1', 0), ('2', 0)],
1066
[('2', 0), ('1', 0)])
1068
def test_merged_revisions(self):
1069
self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1070
[('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
1071
def test_shifted_merged_revisions(self):
1072
"""Test irregular layout.
1074
Requesting revisions touching a file can produce "holes" in the depths.
1076
self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1077
[('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
1079
def test_merged_without_child_revisions(self):
1080
"""Test irregular layout.
1082
Revision ranges can produce "holes" in the depths.
1084
# When a revision of higher depth doesn't follow one of lower depth, we
1085
# assume a lower depth one is virtually there
1086
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1087
[('4', 4), ('3', 3), ('2', 2), ('1', 2),])
1088
# So we get the same order after reversing below even if the original
1089
# revisions are not in the same order.
1090
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1091
[('3', 3), ('4', 4), ('2', 2), ('1', 2),])
1094
class TestHistoryChange(tests.TestCaseWithTransport):
1096
def setup_a_tree(self):
1097
tree = self.make_branch_and_tree('tree')
1099
self.addCleanup(tree.unlock)
1100
tree.commit('1a', rev_id='1a')
1101
tree.commit('2a', rev_id='2a')
1102
tree.commit('3a', rev_id='3a')
1105
def setup_ab_tree(self):
1106
tree = self.setup_a_tree()
1107
tree.set_last_revision('1a')
1108
tree.branch.set_last_revision_info(1, '1a')
1109
tree.commit('2b', rev_id='2b')
1110
tree.commit('3b', rev_id='3b')
1113
def setup_ac_tree(self):
1114
tree = self.setup_a_tree()
1115
tree.set_last_revision(revision.NULL_REVISION)
1116
tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1117
tree.commit('1c', rev_id='1c')
1118
tree.commit('2c', rev_id='2c')
1119
tree.commit('3c', rev_id='3c')
1122
def test_all_new(self):
1123
tree = self.setup_ab_tree()
1124
old, new = log.get_history_change('1a', '3a', tree.branch.repository)
1125
self.assertEqual([], old)
1126
self.assertEqual(['2a', '3a'], new)
1128
def test_all_old(self):
1129
tree = self.setup_ab_tree()
1130
old, new = log.get_history_change('3a', '1a', tree.branch.repository)
1131
self.assertEqual([], new)
1132
self.assertEqual(['2a', '3a'], old)
1134
def test_null_old(self):
1135
tree = self.setup_ab_tree()
1136
old, new = log.get_history_change(revision.NULL_REVISION,
1137
'3a', tree.branch.repository)
1138
self.assertEqual([], old)
1139
self.assertEqual(['1a', '2a', '3a'], new)
1141
def test_null_new(self):
1142
tree = self.setup_ab_tree()
1143
old, new = log.get_history_change('3a', revision.NULL_REVISION,
1144
tree.branch.repository)
1145
self.assertEqual([], new)
1146
self.assertEqual(['1a', '2a', '3a'], old)
1148
def test_diverged(self):
1149
tree = self.setup_ab_tree()
1150
old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1151
self.assertEqual(old, ['2a', '3a'])
1152
self.assertEqual(new, ['2b', '3b'])
1154
def test_unrelated(self):
1155
tree = self.setup_ac_tree()
1156
old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1157
self.assertEqual(old, ['1a', '2a', '3a'])
1158
self.assertEqual(new, ['1c', '2c', '3c'])
1160
def test_show_branch_change(self):
1161
tree = self.setup_ab_tree()
1163
log.show_branch_change(tree.branch, s, 3, '3a')
1164
self.assertContainsRe(s.getvalue(),
1165
'[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1166
'[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1168
def test_show_branch_change_no_change(self):
1169
tree = self.setup_ab_tree()
1171
log.show_branch_change(tree.branch, s, 3, '3b')
1172
self.assertEqual(s.getvalue(),
1173
'Nothing seems to have changed\n')
1175
def test_show_branch_change_no_old(self):
1176
tree = self.setup_ab_tree()
1178
log.show_branch_change(tree.branch, s, 2, '2b')
1179
self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1180
self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1182
def test_show_branch_change_no_new(self):
1183
tree = self.setup_ab_tree()
1184
tree.branch.set_last_revision_info(2, '2b')
1186
log.show_branch_change(tree.branch, s, 3, '3b')
1187
self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1188
self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1191
class TestRevisionNotInBranch(TestCaseForLogFormatter):
1193
def setup_a_tree(self):
1194
tree = self.make_branch_and_tree('tree')
1196
self.addCleanup(tree.unlock)
1198
'committer': 'Joe Foo <joe@foo.com>',
1199
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1200
'timezone': 0, # UTC
1202
tree.commit('commit 1a', rev_id='1a', **kwargs)
1203
tree.commit('commit 2a', rev_id='2a', **kwargs)
1204
tree.commit('commit 3a', rev_id='3a', **kwargs)
1207
def setup_ab_tree(self):
1208
tree = self.setup_a_tree()
1209
tree.set_last_revision('1a')
1210
tree.branch.set_last_revision_info(1, '1a')
1212
'committer': 'Joe Foo <joe@foo.com>',
1213
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1214
'timezone': 0, # UTC
1216
tree.commit('commit 2b', rev_id='2b', **kwargs)
1217
tree.commit('commit 3b', rev_id='3b', **kwargs)
1220
def test_one_revision(self):
1221
tree = self.setup_ab_tree()
1223
rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1224
log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
1226
self.assertEqual(1, len(lf.revisions))
1227
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1228
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1230
def test_many_revisions(self):
1231
tree = self.setup_ab_tree()
1233
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1234
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1235
log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
1236
end_revision=end_rev)
1237
self.assertEqual(3, len(lf.revisions))
1238
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1239
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1240
self.assertEqual(None, lf.revisions[1].revno) # Out-of-branch
1241
self.assertEqual('2a', lf.revisions[1].rev.revision_id)
1242
self.assertEqual('1', lf.revisions[2].revno) # In-branch
1244
def test_long_format(self):
1245
tree = self.setup_ab_tree()
1246
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1247
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1248
self.assertFormatterResult("""\
1249
------------------------------------------------------------
1251
committer: Joe Foo <joe@foo.com>
1253
timestamp: Tue 2005-11-22 00:00:00 +0000
1256
------------------------------------------------------------
1258
committer: Joe Foo <joe@foo.com>
1260
timestamp: Tue 2005-11-22 00:00:00 +0000
1263
------------------------------------------------------------
1265
committer: Joe Foo <joe@foo.com>
1267
timestamp: Tue 2005-11-22 00:00:00 +0000
1271
tree.branch, log.LongLogFormatter, show_log_kwargs={
1272
'start_revision': start_rev, 'end_revision': end_rev
1275
def test_short_format(self):
1276
tree = self.setup_ab_tree()
1277
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1278
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1279
self.assertFormatterResult("""\
1288
1 Joe Foo\t2005-11-22
1292
tree.branch, log.ShortLogFormatter, show_log_kwargs={
1293
'start_revision': start_rev, 'end_revision': end_rev
1296
def test_line_format(self):
1297
tree = self.setup_ab_tree()
1298
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1299
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1300
self.assertFormatterResult("""\
1301
Joe Foo 2005-11-22 commit 3a
1302
Joe Foo 2005-11-22 commit 2a
1303
1: Joe Foo 2005-11-22 commit 1a
1305
tree.branch, log.LineLogFormatter, show_log_kwargs={
1306
'start_revision': start_rev, 'end_revision': end_rev
1310
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1313
TestCaseForLogFormatter.setUp(self)
1314
log.properties_handler_registry.register(
1315
'bugs_properties_handler',
1316
log._bugs_properties_handler)
1318
def make_commits_with_bugs(self):
1319
"""Helper method for LogFormatter tests"""
1320
tree = self.make_branch_and_tree(u'.')
1321
self.build_tree(['a', 'b'])
1323
self.wt_commit(tree, 'simple log message', rev_id='a1',
1324
revprops={'bugs': 'test://bug/id fixed'})
1326
self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id='a2',
1327
authors=['Joe Bar <joe@bar.com>'],
1328
revprops={'bugs': 'test://bug/id fixed\n'
1329
'test://bug/2 fixed'})
1333
def test_long_bugs(self):
1334
tree = self.make_commits_with_bugs()
1335
self.assertFormatterResult("""\
1336
------------------------------------------------------------
1338
fixes bugs: test://bug/id test://bug/2
1339
author: Joe Bar <joe@bar.com>
1340
committer: Joe Foo <joe@foo.com>
1342
timestamp: Tue 2005-11-22 00:00:01 +0000
1347
------------------------------------------------------------
1349
fixes bug: test://bug/id
1350
committer: Joe Foo <joe@foo.com>
1352
timestamp: Tue 2005-11-22 00:00:00 +0000
1356
tree.branch, log.LongLogFormatter)
1358
def test_short_bugs(self):
1359
tree = self.make_commits_with_bugs()
1360
self.assertFormatterResult("""\
1361
2 Joe Bar\t2005-11-22
1362
fixes bugs: test://bug/id test://bug/2
1367
1 Joe Foo\t2005-11-22
1368
fixes bug: test://bug/id
1372
tree.branch, log.ShortLogFormatter)
1374
def test_wrong_bugs_property(self):
1375
tree = self.make_branch_and_tree(u'.')
1376
self.build_tree(['foo'])
1377
self.wt_commit(tree, 'simple log message', rev_id='a1',
1378
revprops={'bugs': 'test://bug/id invalid_value'})
1379
self.assertFormatterResult("""\
1380
1 Joe Foo\t2005-11-22
1384
tree.branch, log.ShortLogFormatter)
1386
def test_bugs_handler_present(self):
1387
self.properties_handler_registry.get('bugs_properties_handler')
1390
class TestLogForAuthors(TestCaseForLogFormatter):
1393
TestCaseForLogFormatter.setUp(self)
1394
self.wt = self.make_standard_commit('nicky',
1395
authors=['John Doe <jdoe@example.com>',
1396
'Jane Rey <jrey@example.com>'])
1398
def assertFormatterResult(self, formatter, who, result):
1399
formatter_kwargs = dict()
1401
author_list_handler = log.author_list_registry.get(who)
1402
formatter_kwargs['author_list_handler'] = author_list_handler
1403
TestCaseForLogFormatter.assertFormatterResult(self, result,
1404
self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
1406
def test_line_default(self):
1407
self.assertFormatterResult(log.LineLogFormatter, None, """\
1408
1: John Doe 2005-11-22 add a
1411
def test_line_committer(self):
1412
self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
1413
1: Lorem Ipsum 2005-11-22 add a
1416
def test_line_first(self):
1417
self.assertFormatterResult(log.LineLogFormatter, 'first', """\
1418
1: John Doe 2005-11-22 add a
1421
def test_line_all(self):
1422
self.assertFormatterResult(log.LineLogFormatter, 'all', """\
1423
1: John Doe, Jane Rey 2005-11-22 add a
1427
def test_short_default(self):
1428
self.assertFormatterResult(log.ShortLogFormatter, None, """\
1429
1 John Doe\t2005-11-22
1434
def test_short_committer(self):
1435
self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
1436
1 Lorem Ipsum\t2005-11-22
1441
def test_short_first(self):
1442
self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
1443
1 John Doe\t2005-11-22
1448
def test_short_all(self):
1449
self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
1450
1 John Doe, Jane Rey\t2005-11-22
1455
def test_long_default(self):
1456
self.assertFormatterResult(log.LongLogFormatter, None, """\
1457
------------------------------------------------------------
1459
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1460
committer: Lorem Ipsum <test@example.com>
1462
timestamp: Tue 2005-11-22 00:00:00 +0000
1467
def test_long_committer(self):
1468
self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
1469
------------------------------------------------------------
1471
committer: Lorem Ipsum <test@example.com>
1473
timestamp: Tue 2005-11-22 00:00:00 +0000
1478
def test_long_first(self):
1479
self.assertFormatterResult(log.LongLogFormatter, 'first', """\
1480
------------------------------------------------------------
1482
author: John Doe <jdoe@example.com>
1483
committer: Lorem Ipsum <test@example.com>
1485
timestamp: Tue 2005-11-22 00:00:00 +0000
1490
def test_long_all(self):
1491
self.assertFormatterResult(log.LongLogFormatter, 'all', """\
1492
------------------------------------------------------------
1494
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1495
committer: Lorem Ipsum <test@example.com>
1497
timestamp: Tue 2005-11-22 00:00:00 +0000
1502
def test_gnu_changelog_default(self):
1503
self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
1504
2005-11-22 John Doe <jdoe@example.com>
1510
def test_gnu_changelog_committer(self):
1511
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
1512
2005-11-22 Lorem Ipsum <test@example.com>
1518
def test_gnu_changelog_first(self):
1519
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
1520
2005-11-22 John Doe <jdoe@example.com>
1526
def test_gnu_changelog_all(self):
1527
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
1528
2005-11-22 John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1535
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1537
def make_branch_with_alternate_ancestries(self, relpath='.'):
1538
# See test_merge_sorted_exclude_ancestry below for the difference with
1539
# bt.per_branch.test_iter_merge_sorted_revision.
1540
# TestIterMergeSortedRevisionsBushyGraph.
1541
# make_branch_with_alternate_ancestries
1542
# and test_merge_sorted_exclude_ancestry
1543
# See the FIXME in assertLogRevnos too.
1544
builder = branchbuilder.BranchBuilder(self.get_transport(relpath))
1556
builder.start_series()
1557
builder.build_snapshot('1', None, [
1558
('add', ('', 'TREE_ROOT', 'directory', '')),])
1559
builder.build_snapshot('1.1.1', ['1'], [])
1560
builder.build_snapshot('2', ['1'], [])
1561
builder.build_snapshot('1.2.1', ['1.1.1'], [])
1562
builder.build_snapshot('1.1.2', ['1.1.1', '1.2.1'], [])
1563
builder.build_snapshot('3', ['2', '1.1.2'], [])
1564
builder.finish_series()
1565
br = builder.get_branch()
1567
self.addCleanup(br.unlock)
1570
def assertLogRevnos(self, expected_revnos, b, start, end,
1571
exclude_common_ancestry, generate_merge_revisions=True):
1572
# FIXME: the layering in log makes it hard to test intermediate levels,
1573
# I wish adding filters with their parameters were easier...
1575
iter_revs = log._calc_view_revisions(
1576
b, start, end, direction='reverse',
1577
generate_merge_revisions=generate_merge_revisions,
1578
exclude_common_ancestry=exclude_common_ancestry)
1579
self.assertEqual(expected_revnos,
1580
[revid for revid, revno, depth in iter_revs])
1582
def test_merge_sorted_exclude_ancestry(self):
1583
b = self.make_branch_with_alternate_ancestries()
1584
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1585
b, '1', '3', exclude_common_ancestry=False)
1586
# '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1587
# it should be mentioned even if merge_sort order will make it appear
1589
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1590
b, '1.1.1', '3', exclude_common_ancestry=True)
1592
def test_merge_sorted_simple_revnos_exclude_ancestry(self):
1593
b = self.make_branch_with_alternate_ancestries()
1594
self.assertLogRevnos(['3', '2'],
1595
b, '1', '3', exclude_common_ancestry=True,
1596
generate_merge_revisions=False)
1597
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
1598
b, '1', '3', exclude_common_ancestry=True,
1599
generate_merge_revisions=True)
1602
class TestLogDefaults(TestCaseForLogFormatter):
1603
def test_default_log_level(self):
1605
Test to ensure that specifying 'levels=1' to make_log_request_dict
1606
doesn't get overwritten when using a LogFormatter that supports more
1610
wt = self._prepare_tree_with_merges()
1613
class CustomLogFormatter(log.LogFormatter):
1614
def __init__(self, *args, **kwargs):
1615
super(CustomLogFormatter, self).__init__(*args, **kwargs)
1617
def get_levels(self):
1618
# log formatter supports all levels:
1620
def log_revision(self, revision):
1621
self.revisions.append(revision)
1623
log_formatter = LogCatcher()
1624
# First request we don't specify number of levels, we should get a
1625
# sensible default (whatever the LogFormatter handles - which in this
1626
# case is 0/everything):
1627
request = log.make_log_request_dict(limit=10)
1628
log.Logger(b, request).show(log_formatter)
1629
# should have all three revisions:
1630
self.assertEquals(len(log_formatter.revisions), 3)
1633
log_formatter = LogCatcher()
1634
# now explicitly request mainline revisions only:
1635
request = log.make_log_request_dict(limit=10, levels=1)
1636
log.Logger(b, request).show(log_formatter)
1637
# should now only have 2 revisions:
1638
self.assertEquals(len(log_formatter.revisions), 2)
917
rev = Revision('a-id')
918
rev.committer = 'John Doe <jdoe@example.com>'
919
lf = LogFormatter(None)
920
self.assertEqual('John Doe', lf.short_author(rev))
921
rev.properties['author'] = 'John Smith <jsmith@example.com>'
922
self.assertEqual('John Smith', lf.short_author(rev))
923
rev.properties['author'] = 'John Smith'
924
self.assertEqual('John Smith', lf.short_author(rev))
925
rev.properties['author'] = 'jsmith@example.com'
926
self.assertEqual('jsmith@example.com', lf.short_author(rev))
927
rev.properties['author'] = '<jsmith@example.com>'
928
self.assertEqual('jsmith@example.com', lf.short_author(rev))
929
rev.properties['author'] = 'John Smith jsmith@example.com'
930
self.assertEqual('John Smith', lf.short_author(rev))