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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
from cStringIO import StringIO
20
from bzrlib import log, registry
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 TestCaseWithoutPropsHandler(TestCaseWithTransport):
31
class TestLogMixin(object):
33
def wt_commit(self, wt, message, **kwargs):
34
"""Use some mostly fixed values for commits to simplify tests.
36
Tests can use this function to get some commit attributes. The time
37
stamp is incremented at each commit.
39
if getattr(self, 'timestamp', None) is None:
40
self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
42
self.timestamp += 1 # 1 second between each commit
43
kwargs.setdefault('timestamp', self.timestamp)
44
kwargs.setdefault('timezone', 0) # UTC
45
kwargs.setdefault('committer', 'Joe Foo <joe@foo.com>')
47
return wt.commit(message, **kwargs)
50
class TestCaseForLogFormatter(tests.TestCaseWithTransport, TestLogMixin):
44
super(TestCaseWithoutPropsHandler, self).setUp()
53
super(TestCaseForLogFormatter, self).setUp()
45
54
# keep a reference to the "current" custom prop. handler registry
46
self.properties_handler_registry = \
47
log.properties_handler_registry
48
# clean up the registry in log
55
self.properties_handler_registry = log.properties_handler_registry
56
# Use a clean registry for log
49
57
log.properties_handler_registry = registry.Registry()
52
super(TestCaseWithoutPropsHandler, self)._cleanup()
53
# restore the custom properties handler registry
54
log.properties_handler_registry = \
55
self.properties_handler_registry
58
class LogCatcher(LogFormatter):
59
"""Pull log messages into list rather than displaying them.
61
For ease of testing we save log messages here rather than actually
62
formatting them, so that we can precisely check the result without
63
being too dependent on the exact formatting.
65
We should also test the LogFormatter.
60
log.properties_handler_registry = self.properties_handler_registry
61
self.addCleanup(restore)
63
def assertFormatterResult(self, result, branch, formatter_class,
64
formatter_kwargs=None, show_log_kwargs=None):
65
logfile = self.make_utf8_encoded_stringio()
66
if formatter_kwargs is None:
68
formatter = formatter_class(to_file=logfile, **formatter_kwargs)
69
if show_log_kwargs is None:
71
log.show_log(branch, formatter, **show_log_kwargs)
72
self.assertEqualDiff(result, logfile.getvalue())
74
def make_standard_commit(self, branch_nick, **kwargs):
75
wt = self.make_branch_and_tree('.')
77
self.addCleanup(wt.unlock)
78
self.build_tree(['a'])
80
wt.branch.nick = branch_nick
81
kwargs.setdefault('committer', 'Lorem Ipsum <test@example.com>')
82
kwargs.setdefault('authors', ['John Doe <jdoe@example.com>'])
83
self.wt_commit(wt, 'add a', **kwargs)
86
def make_commits_with_trailing_newlines(self, wt):
87
"""Helper method for LogFormatter tests"""
90
self.build_tree_contents([('a', 'hello moto\n')])
91
self.wt_commit(wt, 'simple log message', rev_id='a1')
92
self.build_tree_contents([('b', 'goodbye\n')])
94
self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id='a2')
96
self.build_tree_contents([('c', 'just another manic monday\n')])
98
self.wt_commit(wt, 'single line with trailing newline\n', rev_id='a3')
101
def _prepare_tree_with_merges(self, with_tags=False):
102
wt = self.make_branch_and_memory_tree('.')
104
self.addCleanup(wt.unlock)
106
self.wt_commit(wt, 'rev-1', rev_id='rev-1')
107
self.wt_commit(wt, 'rev-merged', rev_id='rev-2a')
108
wt.set_parent_ids(['rev-1', 'rev-2a'])
109
wt.branch.set_last_revision_info(1, 'rev-1')
110
self.wt_commit(wt, 'rev-2', rev_id='rev-2b')
113
branch.tags.set_tag('v0.2', 'rev-2b')
114
self.wt_commit(wt, 'rev-3', rev_id='rev-3')
115
branch.tags.set_tag('v1.0rc1', 'rev-3')
116
branch.tags.set_tag('v1.0', 'rev-3')
120
class LogCatcher(log.LogFormatter):
121
"""Pull log messages into a list rather than displaying them.
123
To simplify testing we save logged revisions here rather than actually
124
formatting anything, so that we can precisely check the result without
125
being dependent on the formatting.
128
supports_merge_revisions = True
68
129
supports_delta = True
71
super(LogCatcher, self).__init__(to_file=None)
133
def __init__(self, *args, **kwargs):
134
kwargs.update(dict(to_file=None))
135
super(LogCatcher, self).__init__(*args, **kwargs)
74
138
def log_revision(self, revision):
75
self.logs.append(revision)
78
class TestShowLog(TestCaseWithTransport):
139
self.revisions.append(revision)
142
class TestShowLog(tests.TestCaseWithTransport):
80
144
def checkDelta(self, delta, **kw):
81
"""Check the filenames touched by a delta are as expected."""
145
"""Check the filenames touched by a delta are as expected.
147
Caller only have to pass in the list of files for each part, all
148
unspecified parts are considered empty (and checked as such).
82
150
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
151
# By default we expect an empty list
83
152
expected = kw.get(n, [])
84
153
# strip out only the path components
85
154
got = [x[0] for x in getattr(delta, n)]
86
self.assertEquals(expected, got)
155
self.assertEqual(expected, got)
157
def assertInvalidRevisonNumber(self, br, start, end):
159
self.assertRaises(errors.InvalidRevisionNumber,
160
log.show_log, br, lf,
161
start_revision=start, end_revision=end)
88
163
def test_cur_revno(self):
89
164
wt = self.make_branch_and_tree('.')
93
168
wt.commit('empty commit')
94
show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
95
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
96
start_revision=2, end_revision=1)
97
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
98
start_revision=1, end_revision=2)
99
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
100
start_revision=0, end_revision=2)
101
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
102
start_revision=1, end_revision=0)
103
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
104
start_revision=-1, end_revision=1)
105
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
106
start_revision=1, end_revision=-1)
108
def test_simple_log(self):
109
eq = self.assertEquals
169
log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
171
# Since there is a single revision in the branch all the combinations
173
self.assertInvalidRevisonNumber(b, 2, 1)
174
self.assertInvalidRevisonNumber(b, 1, 2)
175
self.assertInvalidRevisonNumber(b, 0, 2)
176
self.assertInvalidRevisonNumber(b, 1, 0)
177
self.assertInvalidRevisonNumber(b, -1, 1)
178
self.assertInvalidRevisonNumber(b, 1, -1)
180
def test_empty_branch(self):
111
181
wt = self.make_branch_and_tree('.')
114
183
lf = LogCatcher()
184
log.show_log(wt.branch, lf)
186
self.assertEqual([], lf.revisions)
188
def test_empty_commit(self):
189
wt = self.make_branch_and_tree('.')
119
191
wt.commit('empty commit')
120
192
lf = LogCatcher()
121
show_log(b, lf, verbose=True)
123
eq(lf.logs[0].revno, '1')
124
eq(lf.logs[0].rev.message, 'empty commit')
126
self.log('log delta: %r' % d)
193
log.show_log(wt.branch, lf, verbose=True)
195
self.assertEqual(1, len(revs))
196
self.assertEqual('1', revs[0].revno)
197
self.assertEqual('empty commit', revs[0].rev.message)
198
self.checkDelta(revs[0].delta)
200
def test_simple_commit(self):
201
wt = self.make_branch_and_tree('.')
202
wt.commit('empty commit')
129
203
self.build_tree(['hello'])
131
205
wt.commit('add one file',
132
206
committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
133
207
u'<test@example.com>')
135
lf = self.make_utf8_encoded_stringio()
136
# log using regular thing
137
show_log(b, LongLogFormatter(lf))
139
for l in lf.readlines():
142
# get log as data structure
143
208
lf = LogCatcher()
144
show_log(b, lf, verbose=True)
146
self.log('log entries:')
147
for logentry in lf.logs:
148
self.log('%4s %s' % (logentry.revno, logentry.rev.message))
209
log.show_log(wt.branch, lf, verbose=True)
210
self.assertEqual(2, len(lf.revisions))
150
211
# first one is most recent
151
logentry = lf.logs[0]
152
eq(logentry.revno, '2')
153
eq(logentry.rev.message, 'add one file')
155
self.log('log 2 delta: %r' % d)
156
self.checkDelta(d, added=['hello'])
158
# commit a log message with control characters
159
msg = "All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
160
self.log("original commit message: %r", msg)
212
log_entry = lf.revisions[0]
213
self.assertEqual('2', log_entry.revno)
214
self.assertEqual('add one file', log_entry.rev.message)
215
self.checkDelta(log_entry.delta, added=['hello'])
217
def test_commit_message_with_control_chars(self):
218
wt = self.make_branch_and_tree('.')
219
msg = u"All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
220
msg = msg.replace(u'\r', u'\n')
162
222
lf = LogCatcher()
163
show_log(b, lf, verbose=True)
164
committed_msg = lf.logs[0].rev.message
165
self.log("escaped commit message: %r", committed_msg)
166
self.assert_(msg != committed_msg)
167
self.assert_(len(committed_msg) > len(msg))
223
log.show_log(wt.branch, lf, verbose=True)
224
committed_msg = lf.revisions[0].rev.message
225
if wt.branch.repository._serializer.squashes_xml_invalid_characters:
226
self.assertNotEqual(msg, committed_msg)
227
self.assertTrue(len(committed_msg) > len(msg))
229
self.assertEqual(msg, committed_msg)
169
# Check that log message with only XML-valid characters isn't
231
def test_commit_message_without_control_chars(self):
232
wt = self.make_branch_and_tree('.')
170
233
# escaped. As ElementTree apparently does some kind of
171
234
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
172
235
# included in the test commit message, even though they are
173
236
# valid XML 1.0 characters.
174
237
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
175
self.log("original commit message: %r", msg)
177
239
lf = LogCatcher()
178
show_log(b, lf, verbose=True)
179
committed_msg = lf.logs[0].rev.message
180
self.log("escaped commit message: %r", committed_msg)
181
self.assert_(msg == committed_msg)
240
log.show_log(wt.branch, lf, verbose=True)
241
committed_msg = lf.revisions[0].rev.message
242
self.assertEqual(msg, committed_msg)
183
244
def test_deltas_in_merge_revisions(self):
184
245
"""Check deltas created for both mainline and merge revisions"""
185
eq = self.assertEquals
186
246
wt = self.make_branch_and_tree('parent')
187
247
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
201
261
lf = LogCatcher()
202
262
lf.supports_merge_revisions = True
203
show_log(b, lf, verbose=True)
205
logentry = lf.logs[0]
206
eq(logentry.revno, '2')
207
eq(logentry.rev.message, 'merge child branch')
209
self.checkDelta(d, removed=['file1'], modified=['file2'])
210
logentry = lf.logs[1]
211
eq(logentry.revno, '1.1.1')
212
eq(logentry.rev.message, 'remove file1 and modify file2')
214
self.checkDelta(d, removed=['file1'], modified=['file2'])
215
logentry = lf.logs[2]
216
eq(logentry.revno, '1')
217
eq(logentry.rev.message, 'add file1 and file2')
219
self.checkDelta(d, added=['file1', 'file2'])
221
def test_merges_nonsupporting_formatter(self):
222
"""Tests that show_log will raise if the formatter doesn't
223
support merge revisions."""
224
wt = self.make_branch_and_memory_tree('.')
228
wt.commit('rev-1', rev_id='rev-1',
229
timestamp=1132586655, timezone=36000,
230
committer='Joe Foo <joe@foo.com>')
231
wt.commit('rev-merged', rev_id='rev-2a',
232
timestamp=1132586700, timezone=36000,
233
committer='Joe Foo <joe@foo.com>')
234
wt.set_parent_ids(['rev-1', 'rev-2a'])
235
wt.branch.set_last_revision_info(1, 'rev-1')
236
wt.commit('rev-2', rev_id='rev-2b',
237
timestamp=1132586800, timezone=36000,
238
committer='Joe Foo <joe@foo.com>')
239
logfile = self.make_utf8_encoded_stringio()
240
formatter = ShortLogFormatter(to_file=logfile)
243
revspec = RevisionSpec.from_string('1.1.1')
244
rev = revspec.in_history(wtb)
245
self.assertRaises(BzrCommandError, show_log, wtb, lf,
246
start_revision=rev, end_revision=rev)
251
def make_commits_with_trailing_newlines(wt):
252
"""Helper method for LogFormatter tests"""
255
open('a', 'wb').write('hello moto\n')
257
wt.commit('simple log message', rev_id='a1',
258
timestamp=1132586655.459960938, timezone=-6*3600,
259
committer='Joe Foo <joe@foo.com>')
260
open('b', 'wb').write('goodbye\n')
262
wt.commit('multiline\nlog\nmessage\n', rev_id='a2',
263
timestamp=1132586842.411175966, timezone=-6*3600,
264
committer='Joe Foo <joe@foo.com>',
265
author='Joe Bar <joe@bar.com>')
267
open('c', 'wb').write('just another manic monday\n')
269
wt.commit('single line with trailing newline\n', rev_id='a3',
270
timestamp=1132587176.835228920, timezone=-6*3600,
271
committer = 'Joe Foo <joe@foo.com>')
275
def normalize_log(log):
276
"""Replaces the variable lines of logs with fixed lines"""
277
author = 'author: Dolor Sit <test@example.com>'
278
committer = 'committer: Lorem Ipsum <test@example.com>'
279
lines = log.splitlines(True)
280
for idx,line in enumerate(lines):
281
stripped_line = line.lstrip()
282
indent = ' ' * (len(line) - len(stripped_line))
283
if stripped_line.startswith('author:'):
284
lines[idx] = indent + author + '\n'
285
elif stripped_line.startswith('committer:'):
286
lines[idx] = indent + committer + '\n'
287
elif stripped_line.startswith('timestamp:'):
288
lines[idx] = indent + 'timestamp: Just now\n'
289
return ''.join(lines)
292
class TestShortLogFormatter(TestCaseWithTransport):
263
log.show_log(b, lf, verbose=True)
266
self.assertEqual(3, len(revs))
269
self.assertEqual('2', logentry.revno)
270
self.assertEqual('merge child branch', logentry.rev.message)
271
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
274
self.assertEqual('1.1.1', logentry.revno)
275
self.assertEqual('remove file1 and modify file2', logentry.rev.message)
276
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
279
self.assertEqual('1', logentry.revno)
280
self.assertEqual('add file1 and file2', logentry.rev.message)
281
self.checkDelta(logentry.delta, added=['file1', 'file2'])
284
class TestShortLogFormatter(TestCaseForLogFormatter):
294
286
def test_trailing_newlines(self):
295
287
wt = self.make_branch_and_tree('.')
296
b = make_commits_with_trailing_newlines(wt)
297
sio = self.make_utf8_encoded_stringio()
298
lf = ShortLogFormatter(to_file=sio)
300
self.assertEqualDiff(sio.getvalue(), """\
301
3 Joe Foo\t2005-11-21
288
b = self.make_commits_with_trailing_newlines(wt)
289
self.assertFormatterResult("""\
290
3 Joe Foo\t2005-11-22
302
291
single line with trailing newline
304
2 Joe Bar\t2005-11-21
293
2 Joe Foo\t2005-11-22
309
1 Joe Foo\t2005-11-21
298
1 Joe Foo\t2005-11-22
310
299
simple log message
302
b, log.ShortLogFormatter)
314
304
def test_short_log_with_merges(self):
315
wt = self.make_branch_and_memory_tree('.')
319
wt.commit('rev-1', rev_id='rev-1',
320
timestamp=1132586655, timezone=36000,
321
committer='Joe Foo <joe@foo.com>')
322
wt.commit('rev-merged', rev_id='rev-2a',
323
timestamp=1132586700, timezone=36000,
324
committer='Joe Foo <joe@foo.com>')
325
wt.set_parent_ids(['rev-1', 'rev-2a'])
326
wt.branch.set_last_revision_info(1, 'rev-1')
327
wt.commit('rev-2', rev_id='rev-2b',
328
timestamp=1132586800, timezone=36000,
329
committer='Joe Foo <joe@foo.com>')
330
logfile = self.make_utf8_encoded_stringio()
331
formatter = ShortLogFormatter(to_file=logfile)
332
show_log(wt.branch, formatter)
333
self.assertEqualDiff(logfile.getvalue(), """\
334
2 Joe Foo\t2005-11-22 [merge]
337
1 Joe Foo\t2005-11-22
305
wt = self._prepare_tree_with_merges()
306
self.assertFormatterResult("""\
307
2 Joe Foo\t2005-11-22 [merge]
310
1 Joe Foo\t2005-11-22
314
wt.branch, log.ShortLogFormatter)
316
def test_short_log_with_merges_and_advice(self):
317
wt = self._prepare_tree_with_merges()
318
self.assertFormatterResult("""\
319
2 Joe Foo\t2005-11-22 [merge]
322
1 Joe Foo\t2005-11-22
325
Use --include-merged or -n0 to see merged revisions.
327
wt.branch, log.ShortLogFormatter,
328
formatter_kwargs=dict(show_advice=True))
330
def test_short_log_with_merges_and_range(self):
331
wt = self._prepare_tree_with_merges()
332
self.wt_commit(wt, 'rev-3a', rev_id='rev-3a')
333
wt.branch.set_last_revision_info(2, 'rev-2b')
334
wt.set_parent_ids(['rev-2b', 'rev-3a'])
335
self.wt_commit(wt, 'rev-3b', rev_id='rev-3b')
336
self.assertFormatterResult("""\
337
3 Joe Foo\t2005-11-22 [merge]
340
2 Joe Foo\t2005-11-22 [merge]
344
wt.branch, log.ShortLogFormatter,
345
show_log_kwargs=dict(start_revision=2, end_revision=3))
347
def test_short_log_with_tags(self):
348
wt = self._prepare_tree_with_merges(with_tags=True)
349
self.assertFormatterResult("""\
350
3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
353
2 Joe Foo\t2005-11-22 {v0.2} [merge]
356
1 Joe Foo\t2005-11-22
360
wt.branch, log.ShortLogFormatter)
344
362
def test_short_log_single_merge_revision(self):
345
wt = self.make_branch_and_memory_tree('.')
349
wt.commit('rev-1', rev_id='rev-1',
350
timestamp=1132586655, timezone=36000,
351
committer='Joe Foo <joe@foo.com>')
352
wt.commit('rev-merged', rev_id='rev-2a',
353
timestamp=1132586700, timezone=36000,
354
committer='Joe Foo <joe@foo.com>')
355
wt.set_parent_ids(['rev-1', 'rev-2a'])
356
wt.branch.set_last_revision_info(1, 'rev-1')
357
wt.commit('rev-2', rev_id='rev-2b',
358
timestamp=1132586800, timezone=36000,
359
committer='Joe Foo <joe@foo.com>')
360
logfile = self.make_utf8_encoded_stringio()
361
formatter = ShortLogFormatter(to_file=logfile)
362
revspec = RevisionSpec.from_string('1.1.1')
364
rev = revspec.in_history(wtb)
365
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
366
self.assertEqualDiff(logfile.getvalue(), """\
367
1.1.1 Joe Foo\t2005-11-22
375
class TestLongLogFormatter(TestCaseWithoutPropsHandler):
363
wt = self._prepare_tree_with_merges()
364
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
365
rev = revspec.in_history(wt.branch)
366
self.assertFormatterResult("""\
367
1.1.1 Joe Foo\t2005-11-22
371
wt.branch, log.ShortLogFormatter,
372
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
374
def test_show_ids(self):
375
wt = self.make_branch_and_tree('parent')
376
self.build_tree(['parent/f1', 'parent/f2'])
378
self.wt_commit(wt, 'first post', rev_id='a')
379
child_wt = wt.bzrdir.sprout('child').open_workingtree()
380
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
381
wt.merge_from_branch(child_wt.branch)
382
self.wt_commit(wt, 'merge branch 1', rev_id='c')
383
self.assertFormatterResult("""\
384
2 Joe Foo\t2005-11-22 [merge]
388
1.1.1 Joe Foo\t2005-11-22
392
1 Joe Foo\t2005-11-22
397
wt.branch, log.ShortLogFormatter,
398
formatter_kwargs=dict(levels=0,show_ids=True))
401
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
403
def test_short_merge_revs_log_with_merges(self):
404
wt = self._prepare_tree_with_merges()
405
# Note that the 1.1.1 indenting is in fact correct given that
406
# the revision numbers are right justified within 5 characters
407
# for mainline revnos and 9 characters for dotted revnos.
408
self.assertFormatterResult("""\
409
2 Joe Foo\t2005-11-22 [merge]
412
1.1.1 Joe Foo\t2005-11-22
415
1 Joe Foo\t2005-11-22
419
wt.branch, log.ShortLogFormatter,
420
formatter_kwargs=dict(levels=0))
422
def test_short_merge_revs_log_single_merge_revision(self):
423
wt = self._prepare_tree_with_merges()
424
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
425
rev = revspec.in_history(wt.branch)
426
self.assertFormatterResult("""\
427
1.1.1 Joe Foo\t2005-11-22
431
wt.branch, log.ShortLogFormatter,
432
formatter_kwargs=dict(levels=0),
433
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
436
class TestLongLogFormatter(TestCaseForLogFormatter):
377
438
def test_verbose_log(self):
378
439
"""Verbose log includes changed files
382
wt = self.make_branch_and_tree('.')
384
self.build_tree(['a'])
386
# XXX: why does a longer nick show up?
387
b.nick = 'test_verbose_log'
388
wt.commit(message='add a',
389
timestamp=1132711707,
391
committer='Lorem Ipsum <test@example.com>')
392
logfile = file('out.tmp', 'w+')
393
formatter = LongLogFormatter(to_file=logfile)
394
show_log(b, formatter, verbose=True)
397
log_contents = logfile.read()
398
self.assertEqualDiff(log_contents, '''\
443
wt = self.make_standard_commit('test_verbose_log', authors=[])
444
self.assertFormatterResult('''\
399
445
------------------------------------------------------------
401
447
committer: Lorem Ipsum <test@example.com>
402
448
branch nick: test_verbose_log
403
timestamp: Wed 2005-11-23 12:08:27 +1000
449
timestamp: Tue 2005-11-22 00:00:00 +0000
455
wt.branch, log.LongLogFormatter,
456
show_log_kwargs=dict(verbose=True))
410
458
def test_merges_are_indented_by_level(self):
411
459
wt = self.make_branch_and_tree('parent')
412
wt.commit('first post')
413
self.run_bzr('branch parent child')
414
self.run_bzr(['commit', '-m', 'branch 1', '--unchanged', 'child'])
415
self.run_bzr('branch child smallerchild')
416
self.run_bzr(['commit', '-m', 'branch 2', '--unchanged',
419
self.run_bzr('merge ../smallerchild')
420
self.run_bzr(['commit', '-m', 'merge branch 2'])
421
os.chdir('../parent')
422
self.run_bzr('merge ../child')
423
wt.commit('merge branch 1')
425
sio = self.make_utf8_encoded_stringio()
426
lf = LongLogFormatter(to_file=sio)
427
show_log(b, lf, verbose=True)
428
log = normalize_log(sio.getvalue())
429
self.assertEqualDiff(log, """\
460
self.wt_commit(wt, 'first post')
461
child_wt = wt.bzrdir.sprout('child').open_workingtree()
462
self.wt_commit(child_wt, 'branch 1')
463
smallerchild_wt = wt.bzrdir.sprout('smallerchild').open_workingtree()
464
self.wt_commit(smallerchild_wt, 'branch 2')
465
child_wt.merge_from_branch(smallerchild_wt.branch)
466
self.wt_commit(child_wt, 'merge branch 2')
467
wt.merge_from_branch(child_wt.branch)
468
self.wt_commit(wt, 'merge branch 1')
469
self.assertFormatterResult("""\
430
470
------------------------------------------------------------
432
committer: Lorem Ipsum <test@example.com>
472
committer: Joe Foo <joe@foo.com>
433
473
branch nick: parent
474
timestamp: Tue 2005-11-22 00:00:04 +0000
437
477
------------------------------------------------------------
439
committer: Lorem Ipsum <test@example.com>
479
committer: Joe Foo <joe@foo.com>
440
480
branch nick: child
481
timestamp: Tue 2005-11-22 00:00:03 +0000
444
484
------------------------------------------------------------
446
committer: Lorem Ipsum <test@example.com>
486
committer: Joe Foo <joe@foo.com>
447
487
branch nick: smallerchild
488
timestamp: Tue 2005-11-22 00:00:02 +0000
451
491
------------------------------------------------------------
453
committer: Lorem Ipsum <test@example.com>
493
committer: Joe Foo <joe@foo.com>
454
494
branch nick: child
495
timestamp: Tue 2005-11-22 00:00:01 +0000
458
498
------------------------------------------------------------
460
committer: Lorem Ipsum <test@example.com>
500
committer: Joe Foo <joe@foo.com>
461
501
branch nick: parent
502
timestamp: Tue 2005-11-22 00:00:00 +0000
506
wt.branch, log.LongLogFormatter,
507
formatter_kwargs=dict(levels=0),
508
show_log_kwargs=dict(verbose=True))
467
510
def test_verbose_merge_revisions_contain_deltas(self):
468
511
wt = self.make_branch_and_tree('parent')
469
512
self.build_tree(['parent/f1', 'parent/f2'])
470
513
wt.add(['f1','f2'])
471
wt.commit('first post')
472
self.run_bzr('branch parent child')
514
self.wt_commit(wt, 'first post')
515
child_wt = wt.bzrdir.sprout('child').open_workingtree()
473
516
os.unlink('child/f1')
474
file('child/f2', 'wb').write('hello\n')
475
self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
478
self.run_bzr('merge ../child')
479
wt.commit('merge branch 1')
481
sio = self.make_utf8_encoded_stringio()
482
lf = LongLogFormatter(to_file=sio)
483
show_log(b, lf, verbose=True)
484
log = normalize_log(sio.getvalue())
485
self.assertEqualDiff(log, """\
517
self.build_tree_contents([('child/f2', 'hello\n')])
518
self.wt_commit(child_wt, 'removed f1 and modified f2')
519
wt.merge_from_branch(child_wt.branch)
520
self.wt_commit(wt, 'merge branch 1')
521
self.assertFormatterResult("""\
486
522
------------------------------------------------------------
488
committer: Lorem Ipsum <test@example.com>
524
committer: Joe Foo <joe@foo.com>
489
525
branch nick: parent
526
timestamp: Tue 2005-11-22 00:00:02 +0000
546
581
committer: Joe Foo <joe@foo.com>
547
582
branch nick: test
548
timestamp: Mon 2005-11-21 09:24:15 -0600
583
timestamp: Tue 2005-11-22 00:00:00 +0000
550
585
simple log message
587
b, log.LongLogFormatter)
553
589
def test_author_in_log(self):
554
590
"""Log includes the author name if it's set in
555
591
the revision properties
557
wt = self.make_branch_and_tree('.')
559
self.build_tree(['a'])
561
b.nick = 'test_author_log'
562
wt.commit(message='add a',
563
timestamp=1132711707,
565
committer='Lorem Ipsum <test@example.com>',
566
author='John Doe <jdoe@example.com>')
568
formatter = LongLogFormatter(to_file=sio)
569
show_log(b, formatter)
570
self.assertEqualDiff(sio.getvalue(), '''\
593
wt = self.make_standard_commit('test_author_log',
594
authors=['John Doe <jdoe@example.com>',
595
'Jane Rey <jrey@example.com>'])
596
self.assertFormatterResult("""\
571
597
------------------------------------------------------------
573
author: John Doe <jdoe@example.com>
599
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
574
600
committer: Lorem Ipsum <test@example.com>
575
601
branch nick: test_author_log
576
timestamp: Wed 2005-11-23 12:08:27 +1000
602
timestamp: Tue 2005-11-22 00:00:00 +0000
606
wt.branch, log.LongLogFormatter)
581
608
def test_properties_in_log(self):
582
"""Log includes the custom properties returned by the registered
609
"""Log includes the custom properties returned by the registered
585
wt = self.make_branch_and_tree('.')
587
self.build_tree(['a'])
589
b.nick = 'test_properties_in_log'
590
wt.commit(message='add a',
591
timestamp=1132711707,
593
committer='Lorem Ipsum <test@example.com>',
594
author='John Doe <jdoe@example.com>')
596
formatter = LongLogFormatter(to_file=sio)
598
def trivial_custom_prop_handler(revision):
599
return {'test_prop':'test_value'}
601
log.properties_handler_registry.register(
602
'trivial_custom_prop_handler',
603
trivial_custom_prop_handler)
604
show_log(b, formatter)
606
log.properties_handler_registry.remove(
607
'trivial_custom_prop_handler')
608
self.assertEqualDiff(sio.getvalue(), '''\
612
wt = self.make_standard_commit('test_properties_in_log')
613
def trivial_custom_prop_handler(revision):
614
return {'test_prop':'test_value'}
616
# Cleaned up in setUp()
617
log.properties_handler_registry.register(
618
'trivial_custom_prop_handler',
619
trivial_custom_prop_handler)
620
self.assertFormatterResult("""\
609
621
------------------------------------------------------------
611
623
test_prop: test_value
612
624
author: John Doe <jdoe@example.com>
613
625
committer: Lorem Ipsum <test@example.com>
614
626
branch nick: test_properties_in_log
615
timestamp: Wed 2005-11-23 12:08:27 +1000
627
timestamp: Tue 2005-11-22 00:00:00 +0000
631
wt.branch, log.LongLogFormatter)
633
def test_properties_in_short_log(self):
634
"""Log includes the custom properties returned by the registered
637
wt = self.make_standard_commit('test_properties_in_short_log')
638
def trivial_custom_prop_handler(revision):
639
return {'test_prop':'test_value'}
641
log.properties_handler_registry.register(
642
'trivial_custom_prop_handler',
643
trivial_custom_prop_handler)
644
self.assertFormatterResult("""\
645
1 John Doe\t2005-11-22
646
test_prop: test_value
650
wt.branch, log.ShortLogFormatter)
620
652
def test_error_in_properties_handler(self):
621
"""Log includes the custom properties returned by the registered
653
"""Log includes the custom properties returned by the registered
624
wt = self.make_branch_and_tree('.')
626
self.build_tree(['a'])
628
b.nick = 'test_author_log'
629
wt.commit(message='add a',
630
timestamp=1132711707,
632
committer='Lorem Ipsum <test@example.com>',
633
author='John Doe <jdoe@example.com>',
634
revprops={'first_prop':'first_value'})
636
formatter = LongLogFormatter(to_file=sio)
638
def trivial_custom_prop_handler(revision):
639
raise StandardError("a test error")
641
log.properties_handler_registry.register(
642
'trivial_custom_prop_handler',
643
trivial_custom_prop_handler)
644
self.assertRaises(StandardError, show_log, b, formatter,)
646
log.properties_handler_registry.remove(
647
'trivial_custom_prop_handler')
656
wt = self.make_standard_commit('error_in_properties_handler',
657
revprops={'first_prop':'first_value'})
658
sio = self.make_utf8_encoded_stringio()
659
formatter = log.LongLogFormatter(to_file=sio)
660
def trivial_custom_prop_handler(revision):
661
raise StandardError("a test error")
663
log.properties_handler_registry.register(
664
'trivial_custom_prop_handler',
665
trivial_custom_prop_handler)
666
self.assertRaises(StandardError, log.show_log, wt.branch, formatter,)
649
668
def test_properties_handler_bad_argument(self):
669
wt = self.make_standard_commit('bad_argument',
670
revprops={'a_prop':'test_value'})
671
sio = self.make_utf8_encoded_stringio()
672
formatter = log.LongLogFormatter(to_file=sio)
673
def bad_argument_prop_handler(revision):
674
return {'custom_prop_name':revision.properties['a_prop']}
676
log.properties_handler_registry.register(
677
'bad_argument_prop_handler',
678
bad_argument_prop_handler)
680
self.assertRaises(AttributeError, formatter.show_properties,
683
revision = wt.branch.repository.get_revision(wt.branch.last_revision())
684
formatter.show_properties(revision, '')
685
self.assertEqualDiff('''custom_prop_name: test_value\n''',
688
def test_show_ids(self):
689
wt = self.make_branch_and_tree('parent')
690
self.build_tree(['parent/f1', 'parent/f2'])
692
self.wt_commit(wt, 'first post', rev_id='a')
693
child_wt = wt.bzrdir.sprout('child').open_workingtree()
694
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
695
wt.merge_from_branch(child_wt.branch)
696
self.wt_commit(wt, 'merge branch 1', rev_id='c')
697
self.assertFormatterResult("""\
698
------------------------------------------------------------
703
committer: Joe Foo <joe@foo.com>
705
timestamp: Tue 2005-11-22 00:00:02 +0000
708
------------------------------------------------------------
712
committer: Joe Foo <joe@foo.com>
714
timestamp: Tue 2005-11-22 00:00:01 +0000
717
------------------------------------------------------------
720
committer: Joe Foo <joe@foo.com>
722
timestamp: Tue 2005-11-22 00:00:00 +0000
726
wt.branch, log.LongLogFormatter,
727
formatter_kwargs=dict(levels=0,show_ids=True))
730
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
732
def test_long_verbose_log(self):
733
"""Verbose log includes changed files
737
wt = self.make_standard_commit('test_long_verbose_log', authors=[])
738
self.assertFormatterResult("""\
739
------------------------------------------------------------
741
committer: Lorem Ipsum <test@example.com>
742
branch nick: test_long_verbose_log
743
timestamp: Tue 2005-11-22 00:00:00 +0000
749
wt.branch, log.LongLogFormatter,
750
formatter_kwargs=dict(levels=1),
751
show_log_kwargs=dict(verbose=True))
753
def test_long_verbose_contain_deltas(self):
754
wt = self.make_branch_and_tree('parent')
755
self.build_tree(['parent/f1', 'parent/f2'])
757
self.wt_commit(wt, 'first post')
758
child_wt = wt.bzrdir.sprout('child').open_workingtree()
759
os.unlink('child/f1')
760
self.build_tree_contents([('child/f2', 'hello\n')])
761
self.wt_commit(child_wt, 'removed f1 and modified f2')
762
wt.merge_from_branch(child_wt.branch)
763
self.wt_commit(wt, 'merge branch 1')
764
self.assertFormatterResult("""\
765
------------------------------------------------------------
767
committer: Joe Foo <joe@foo.com>
769
timestamp: Tue 2005-11-22 00:00:02 +0000
776
------------------------------------------------------------
778
committer: Joe Foo <joe@foo.com>
780
timestamp: Tue 2005-11-22 00:00:00 +0000
787
wt.branch, log.LongLogFormatter,
788
formatter_kwargs=dict(levels=1),
789
show_log_kwargs=dict(verbose=True))
791
def test_long_trailing_newlines(self):
650
792
wt = self.make_branch_and_tree('.')
652
self.build_tree(['a'])
654
b.nick = 'test_author_log'
655
wt.commit(message='add a',
656
timestamp=1132711707,
658
committer='Lorem Ipsum <test@example.com>',
659
author='John Doe <jdoe@example.com>',
660
revprops={'a_prop':'test_value'})
662
formatter = LongLogFormatter(to_file=sio)
664
def bad_argument_prop_handler(revision):
665
return {'custom_prop_name':revision.properties['a_prop']}
667
log.properties_handler_registry.register(
668
'bad_argument_prop_handler',
669
bad_argument_prop_handler)
671
self.assertRaises(AttributeError, formatter.show_properties,
674
revision = b.repository.get_revision(b.last_revision())
675
formatter.show_properties(revision, '')
676
self.assertEqualDiff(sio.getvalue(),
677
'''custom_prop_name: test_value\n''')
679
log.properties_handler_registry.remove(
680
'bad_argument_prop_handler')
683
class TestLineLogFormatter(TestCaseWithTransport):
793
b = self.make_commits_with_trailing_newlines(wt)
794
self.assertFormatterResult("""\
795
------------------------------------------------------------
797
committer: Joe Foo <joe@foo.com>
799
timestamp: Tue 2005-11-22 00:00:02 +0000
801
single line with trailing newline
802
------------------------------------------------------------
804
committer: Joe Foo <joe@foo.com>
806
timestamp: Tue 2005-11-22 00:00:01 +0000
811
------------------------------------------------------------
813
committer: Joe Foo <joe@foo.com>
815
timestamp: Tue 2005-11-22 00:00:00 +0000
819
b, log.LongLogFormatter,
820
formatter_kwargs=dict(levels=1))
822
def test_long_author_in_log(self):
823
"""Log includes the author name if it's set in
824
the revision properties
826
wt = self.make_standard_commit('test_author_log')
827
self.assertFormatterResult("""\
828
------------------------------------------------------------
830
author: John Doe <jdoe@example.com>
831
committer: Lorem Ipsum <test@example.com>
832
branch nick: test_author_log
833
timestamp: Tue 2005-11-22 00:00:00 +0000
837
wt.branch, log.LongLogFormatter,
838
formatter_kwargs=dict(levels=1))
840
def test_long_properties_in_log(self):
841
"""Log includes the custom properties returned by the registered
844
wt = self.make_standard_commit('test_properties_in_log')
845
def trivial_custom_prop_handler(revision):
846
return {'test_prop':'test_value'}
848
log.properties_handler_registry.register(
849
'trivial_custom_prop_handler',
850
trivial_custom_prop_handler)
851
self.assertFormatterResult("""\
852
------------------------------------------------------------
854
test_prop: test_value
855
author: John Doe <jdoe@example.com>
856
committer: Lorem Ipsum <test@example.com>
857
branch nick: test_properties_in_log
858
timestamp: Tue 2005-11-22 00:00:00 +0000
862
wt.branch, log.LongLogFormatter,
863
formatter_kwargs=dict(levels=1))
866
class TestLineLogFormatter(TestCaseForLogFormatter):
685
868
def test_line_log(self):
686
869
"""Line log should show revno
690
wt = self.make_branch_and_tree('.')
692
self.build_tree(['a'])
694
b.nick = 'test-line-log'
695
wt.commit(message='add a',
696
timestamp=1132711707,
698
committer='Line-Log-Formatter Tester <test@line.log>')
699
logfile = file('out.tmp', 'w+')
700
formatter = LineLogFormatter(to_file=logfile)
701
show_log(b, formatter)
704
log_contents = logfile.read()
705
self.assertEqualDiff(log_contents,
706
'1: Line-Log-Formatte... 2005-11-23 add a\n')
873
wt = self.make_standard_commit('test-line-log',
874
committer='Line-Log-Formatter Tester <test@line.log>',
876
self.assertFormatterResult("""\
877
1: Line-Log-Formatte... 2005-11-22 add a
879
wt.branch, log.LineLogFormatter)
708
881
def test_trailing_newlines(self):
709
882
wt = self.make_branch_and_tree('.')
710
b = make_commits_with_trailing_newlines(wt)
711
sio = self.make_utf8_encoded_stringio()
712
lf = LineLogFormatter(to_file=sio)
714
self.assertEqualDiff(sio.getvalue(), """\
715
3: Joe Foo 2005-11-21 single line with trailing newline
716
2: Joe Bar 2005-11-21 multiline
717
1: Joe Foo 2005-11-21 simple log message
883
b = self.make_commits_with_trailing_newlines(wt)
884
self.assertFormatterResult("""\
885
3: Joe Foo 2005-11-22 single line with trailing newline
886
2: Joe Foo 2005-11-22 multiline
887
1: Joe Foo 2005-11-22 simple log message
889
b, log.LineLogFormatter)
720
891
def test_line_log_single_merge_revision(self):
721
wt = self.make_branch_and_memory_tree('.')
725
wt.commit('rev-1', rev_id='rev-1',
726
timestamp=1132586655, timezone=36000,
727
committer='Joe Foo <joe@foo.com>')
728
wt.commit('rev-merged', rev_id='rev-2a',
729
timestamp=1132586700, timezone=36000,
730
committer='Joe Foo <joe@foo.com>')
731
wt.set_parent_ids(['rev-1', 'rev-2a'])
732
wt.branch.set_last_revision_info(1, 'rev-1')
733
wt.commit('rev-2', rev_id='rev-2b',
734
timestamp=1132586800, timezone=36000,
735
committer='Joe Foo <joe@foo.com>')
736
logfile = self.make_utf8_encoded_stringio()
737
formatter = LineLogFormatter(to_file=logfile)
738
revspec = RevisionSpec.from_string('1.1.1')
740
rev = revspec.in_history(wtb)
741
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
742
self.assertEqualDiff(logfile.getvalue(), """\
743
1.1.1: Joe Foo 2005-11-22 rev-merged
750
class TestGetViewRevisions(TestCaseWithTransport):
752
def make_tree_with_commits(self):
753
"""Create a tree with well-known revision ids"""
754
wt = self.make_branch_and_tree('tree1')
755
wt.commit('commit one', rev_id='1')
756
wt.commit('commit two', rev_id='2')
757
wt.commit('commit three', rev_id='3')
758
mainline_revs = [None, '1', '2', '3']
759
rev_nos = {'1': 1, '2': 2, '3': 3}
760
return mainline_revs, rev_nos, wt
762
def make_tree_with_merges(self):
763
"""Create a tree with well-known revision ids and a merge"""
764
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
765
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
766
tree2.commit('four-a', rev_id='4a')
767
wt.merge_from_branch(tree2.branch)
768
wt.commit('four-b', rev_id='4b')
769
mainline_revs.append('4b')
772
return mainline_revs, rev_nos, wt
774
def make_tree_with_many_merges(self):
775
"""Create a tree with well-known revision ids"""
776
wt = self.make_branch_and_tree('tree1')
777
wt.commit('commit one', rev_id='1')
778
wt.commit('commit two', rev_id='2')
779
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
780
tree3.commit('commit three a', rev_id='3a')
781
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
782
tree2.merge_from_branch(tree3.branch)
783
tree2.commit('commit three b', rev_id='3b')
784
wt.merge_from_branch(tree2.branch)
785
wt.commit('commit three c', rev_id='3c')
786
tree2.commit('four-a', rev_id='4a')
787
wt.merge_from_branch(tree2.branch)
788
wt.commit('four-b', rev_id='4b')
789
mainline_revs = [None, '1', '2', '3c', '4b']
790
rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
791
full_rev_nos_for_reference = {
794
'3a': '2.1.1', #first commit tree 3
795
'3b': '2.2.1', # first commit tree 2
796
'3c': '3', #merges 3b to main
797
'4a': '2.2.2', # second commit tree 2
798
'4b': '4', # merges 4a to main
800
return mainline_revs, rev_nos, wt
802
def test_get_view_revisions_forward(self):
803
"""Test the get_view_revisions method"""
804
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
806
self.addCleanup(wt.unlock)
807
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
809
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
811
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
812
'forward', include_merges=False))
813
self.assertEqual(revisions, revisions2)
815
def test_get_view_revisions_reverse(self):
816
"""Test the get_view_revisions with reverse"""
817
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
819
self.addCleanup(wt.unlock)
820
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
822
self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
824
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
825
'reverse', include_merges=False))
826
self.assertEqual(revisions, revisions2)
828
def test_get_view_revisions_merge(self):
829
"""Test get_view_revisions when there are merges"""
830
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
832
self.addCleanup(wt.unlock)
833
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
835
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
836
('4b', '4', 0), ('4a', '3.1.1', 1)],
838
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
839
'forward', include_merges=False))
840
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
844
def test_get_view_revisions_merge_reverse(self):
845
"""Test get_view_revisions in reverse when there are merges"""
846
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
848
self.addCleanup(wt.unlock)
849
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
851
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
852
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
854
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
855
'reverse', include_merges=False))
856
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
860
def test_get_view_revisions_merge2(self):
861
"""Test get_view_revisions when there are merges"""
862
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
864
self.addCleanup(wt.unlock)
865
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
867
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
868
('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
870
self.assertEqual(expected, revisions)
871
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
872
'forward', include_merges=False))
873
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
878
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
880
def create_tree_with_single_merge(self):
881
"""Create a branch with a moderate layout.
883
The revision graph looks like:
891
In this graph, A introduced files f1 and f2 and f3.
892
B modifies f1 and f3, and C modifies f2 and f3.
893
D merges the changes from B and C and resolves the conflict for f3.
895
# TODO: jam 20070218 This seems like it could really be done
896
# with make_branch_and_memory_tree() if we could just
897
# create the content of those files.
898
# TODO: jam 20070218 Another alternative is that we would really
899
# like to only create this tree 1 time for all tests that
900
# use it. Since 'log' only uses the tree in a readonly
901
# fashion, it seems a shame to regenerate an identical
902
# tree for each test.
903
tree = self.make_branch_and_tree('tree')
905
self.addCleanup(tree.unlock)
907
self.build_tree_contents([('tree/f1', 'A\n'),
911
tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
912
tree.commit('A', rev_id='A')
914
self.build_tree_contents([('tree/f2', 'A\nC\n'),
915
('tree/f3', 'A\nC\n'),
917
tree.commit('C', rev_id='C')
918
# Revert back to A to build the other history.
919
tree.set_last_revision('A')
920
tree.branch.set_last_revision_info(1, 'A')
921
self.build_tree_contents([('tree/f1', 'A\nB\n'),
923
('tree/f3', 'A\nB\n'),
925
tree.commit('B', rev_id='B')
926
tree.set_parent_ids(['B', 'C'])
927
self.build_tree_contents([('tree/f1', 'A\nB\n'),
928
('tree/f2', 'A\nC\n'),
929
('tree/f3', 'A\nB\nC\n'),
931
tree.commit('D', rev_id='D')
933
# Switch to a read lock for this tree.
934
# We still have addCleanup(unlock)
939
def test_tree_with_single_merge(self):
940
"""Make sure the tree layout is correct."""
941
tree = self.create_tree_with_single_merge()
942
rev_A_tree = tree.branch.repository.revision_tree('A')
943
rev_B_tree = tree.branch.repository.revision_tree('B')
945
f1_changed = (u'f1', 'f1-id', 'file', True, False)
946
f2_changed = (u'f2', 'f2-id', 'file', True, False)
947
f3_changed = (u'f3', 'f3-id', 'file', True, False)
949
delta = rev_B_tree.changes_from(rev_A_tree)
950
self.assertEqual([f1_changed, f3_changed], delta.modified)
951
self.assertEqual([], delta.renamed)
952
self.assertEqual([], delta.added)
953
self.assertEqual([], delta.removed)
955
rev_C_tree = tree.branch.repository.revision_tree('C')
956
delta = rev_C_tree.changes_from(rev_A_tree)
957
self.assertEqual([f2_changed, f3_changed], delta.modified)
958
self.assertEqual([], delta.renamed)
959
self.assertEqual([], delta.added)
960
self.assertEqual([], delta.removed)
962
rev_D_tree = tree.branch.repository.revision_tree('D')
963
delta = rev_D_tree.changes_from(rev_B_tree)
964
self.assertEqual([f2_changed, f3_changed], delta.modified)
965
self.assertEqual([], delta.renamed)
966
self.assertEqual([], delta.added)
967
self.assertEqual([], delta.removed)
969
delta = rev_D_tree.changes_from(rev_C_tree)
970
self.assertEqual([f1_changed, f3_changed], delta.modified)
971
self.assertEqual([], delta.renamed)
972
self.assertEqual([], delta.added)
973
self.assertEqual([], delta.removed)
975
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
976
"""Make sure _filter_revisions_touching_file_id returns the right values.
978
Get the return value from _filter_revisions_touching_file_id and make
979
sure they are correct.
981
# The api for _get_revisions_touching_file_id is a little crazy,
982
# So we do the setup here.
983
mainline = tree.branch.revision_history()
984
mainline.insert(0, None)
985
revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
986
view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
988
actual_revs = log._filter_revisions_touching_file_id(
992
list(view_revs_iter))
993
self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
995
def test_file_id_f1(self):
996
tree = self.create_tree_with_single_merge()
997
# f1 should be marked as modified by revisions A and B
998
self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
1000
def test_file_id_f2(self):
1001
tree = self.create_tree_with_single_merge()
1002
# f2 should be marked as modified by revisions A, C, and D
1003
# because D merged the changes from C.
1004
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1006
def test_file_id_f3(self):
1007
tree = self.create_tree_with_single_merge()
1008
# f3 should be marked as modified by revisions A, B, C, and D
1009
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1011
def test_file_id_with_ghosts(self):
1012
# This is testing bug #209948, where having a ghost would cause
1013
# _filter_revisions_touching_file_id() to fail.
1014
tree = self.create_tree_with_single_merge()
1015
# We need to add a revision, so switch back to a write-locked tree
1018
first_parent = tree.last_revision()
1019
tree.set_parent_ids([first_parent, 'ghost-revision-id'])
1020
self.build_tree_contents([('tree/f1', 'A\nB\nXX\n')])
1021
tree.commit('commit with a ghost', rev_id='XX')
1022
self.assertAllRevisionsForFileID(tree, 'f1-id', ['XX', 'B', 'A'])
1023
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1026
class TestShowChangedRevisions(TestCaseWithTransport):
892
wt = self._prepare_tree_with_merges()
893
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
894
rev = revspec.in_history(wt.branch)
895
self.assertFormatterResult("""\
896
1.1.1: Joe Foo 2005-11-22 rev-merged
898
wt.branch, log.LineLogFormatter,
899
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
901
def test_line_log_with_tags(self):
902
wt = self._prepare_tree_with_merges(with_tags=True)
903
self.assertFormatterResult("""\
904
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
905
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
906
1: Joe Foo 2005-11-22 rev-1
908
wt.branch, log.LineLogFormatter)
911
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
913
def test_line_merge_revs_log(self):
914
"""Line log should show revno
918
wt = self.make_standard_commit('test-line-log',
919
committer='Line-Log-Formatter Tester <test@line.log>',
921
self.assertFormatterResult("""\
922
1: Line-Log-Formatte... 2005-11-22 add a
924
wt.branch, log.LineLogFormatter)
926
def test_line_merge_revs_log_single_merge_revision(self):
927
wt = self._prepare_tree_with_merges()
928
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
929
rev = revspec.in_history(wt.branch)
930
self.assertFormatterResult("""\
931
1.1.1: Joe Foo 2005-11-22 rev-merged
933
wt.branch, log.LineLogFormatter,
934
formatter_kwargs=dict(levels=0),
935
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
937
def test_line_merge_revs_log_with_merges(self):
938
wt = self._prepare_tree_with_merges()
939
self.assertFormatterResult("""\
940
2: Joe Foo 2005-11-22 [merge] rev-2
941
1.1.1: Joe Foo 2005-11-22 rev-merged
942
1: Joe Foo 2005-11-22 rev-1
944
wt.branch, log.LineLogFormatter,
945
formatter_kwargs=dict(levels=0))
948
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
950
def test_gnu_changelog(self):
951
wt = self.make_standard_commit('nicky', authors=[])
952
self.assertFormatterResult('''\
953
2005-11-22 Lorem Ipsum <test@example.com>
958
wt.branch, log.GnuChangelogLogFormatter)
960
def test_with_authors(self):
961
wt = self.make_standard_commit('nicky',
962
authors=['Fooa Fooz <foo@example.com>',
963
'Bari Baro <bar@example.com>'])
964
self.assertFormatterResult('''\
965
2005-11-22 Fooa Fooz <foo@example.com>
970
wt.branch, log.GnuChangelogLogFormatter)
972
def test_verbose(self):
973
wt = self.make_standard_commit('nicky')
974
self.assertFormatterResult('''\
975
2005-11-22 John Doe <jdoe@example.com>
982
wt.branch, log.GnuChangelogLogFormatter,
983
show_log_kwargs=dict(verbose=True))
986
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1028
988
def test_show_changed_revisions_verbose(self):
1029
989
tree = self.make_branch_and_tree('tree_a')
1036
996
self.assertNotContainsRe(s.getvalue(), 'foo')
1039
class TestLogFormatter(TestCase):
999
class TestLogFormatter(tests.TestCase):
1002
super(TestLogFormatter, self).setUp()
1003
self.rev = revision.Revision('a-id')
1004
self.lf = log.LogFormatter(None)
1041
1006
def test_short_committer(self):
1042
rev = Revision('a-id')
1043
rev.committer = 'John Doe <jdoe@example.com>'
1044
lf = LogFormatter(None)
1045
self.assertEqual('John Doe', lf.short_committer(rev))
1046
rev.committer = 'John Smith <jsmith@example.com>'
1047
self.assertEqual('John Smith', lf.short_committer(rev))
1048
rev.committer = 'John Smith'
1049
self.assertEqual('John Smith', lf.short_committer(rev))
1050
rev.committer = 'jsmith@example.com'
1051
self.assertEqual('jsmith@example.com', lf.short_committer(rev))
1052
rev.committer = '<jsmith@example.com>'
1053
self.assertEqual('jsmith@example.com', lf.short_committer(rev))
1054
rev.committer = 'John Smith jsmith@example.com'
1055
self.assertEqual('John Smith', lf.short_committer(rev))
1007
def assertCommitter(expected, committer):
1008
self.rev.committer = committer
1009
self.assertEqual(expected, self.lf.short_committer(self.rev))
1011
assertCommitter('John Doe', 'John Doe <jdoe@example.com>')
1012
assertCommitter('John Smith', 'John Smith <jsmith@example.com>')
1013
assertCommitter('John Smith', 'John Smith')
1014
assertCommitter('jsmith@example.com', 'jsmith@example.com')
1015
assertCommitter('jsmith@example.com', '<jsmith@example.com>')
1016
assertCommitter('John Smith', 'John Smith jsmith@example.com')
1057
1018
def test_short_author(self):
1058
rev = Revision('a-id')
1059
rev.committer = 'John Doe <jdoe@example.com>'
1060
lf = LogFormatter(None)
1061
self.assertEqual('John Doe', lf.short_author(rev))
1062
rev.properties['author'] = 'John Smith <jsmith@example.com>'
1063
self.assertEqual('John Smith', lf.short_author(rev))
1064
rev.properties['author'] = 'John Smith'
1065
self.assertEqual('John Smith', lf.short_author(rev))
1066
rev.properties['author'] = 'jsmith@example.com'
1067
self.assertEqual('jsmith@example.com', lf.short_author(rev))
1068
rev.properties['author'] = '<jsmith@example.com>'
1069
self.assertEqual('jsmith@example.com', lf.short_author(rev))
1070
rev.properties['author'] = 'John Smith jsmith@example.com'
1071
self.assertEqual('John Smith', lf.short_author(rev))
1019
def assertAuthor(expected, author):
1020
self.rev.properties['author'] = author
1021
self.assertEqual(expected, self.lf.short_author(self.rev))
1023
assertAuthor('John Smith', 'John Smith <jsmith@example.com>')
1024
assertAuthor('John Smith', 'John Smith')
1025
assertAuthor('jsmith@example.com', 'jsmith@example.com')
1026
assertAuthor('jsmith@example.com', '<jsmith@example.com>')
1027
assertAuthor('John Smith', 'John Smith jsmith@example.com')
1029
def test_short_author_from_committer(self):
1030
self.rev.committer = 'John Doe <jdoe@example.com>'
1031
self.assertEqual('John Doe', self.lf.short_author(self.rev))
1033
def test_short_author_from_authors(self):
1034
self.rev.properties['authors'] = ('John Smith <jsmith@example.com>\n'
1035
'Jane Rey <jrey@example.com>')
1036
self.assertEqual('John Smith', self.lf.short_author(self.rev))
1039
class TestReverseByDepth(tests.TestCase):
1040
"""Test reverse_by_depth behavior.
1042
This is used to present revisions in forward (oldest first) order in a nice
1045
The tests use lighter revision description to ease reading.
1048
def assertReversed(self, forward, backward):
1049
# Transform the descriptions to suit the API: tests use (revno, depth),
1050
# while the API expects (revid, revno, depth)
1051
def complete_revisions(l):
1052
"""Transform the description to suit the API.
1054
Tests use (revno, depth) whil the API expects (revid, revno, depth).
1055
Since the revid is arbitrary, we just duplicate revno
1057
return [ (r, r, d) for r, d in l]
1058
forward = complete_revisions(forward)
1059
backward= complete_revisions(backward)
1060
self.assertEqual(forward, log.reverse_by_depth(backward))
1063
def test_mainline_revisions(self):
1064
self.assertReversed([( '1', 0), ('2', 0)],
1065
[('2', 0), ('1', 0)])
1067
def test_merged_revisions(self):
1068
self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1069
[('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
1070
def test_shifted_merged_revisions(self):
1071
"""Test irregular layout.
1073
Requesting revisions touching a file can produce "holes" in the depths.
1075
self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1076
[('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
1078
def test_merged_without_child_revisions(self):
1079
"""Test irregular layout.
1081
Revision ranges can produce "holes" in the depths.
1083
# When a revision of higher depth doesn't follow one of lower depth, we
1084
# assume a lower depth one is virtually there
1085
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1086
[('4', 4), ('3', 3), ('2', 2), ('1', 2),])
1087
# So we get the same order after reversing below even if the original
1088
# revisions are not in the same order.
1089
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1090
[('3', 3), ('4', 4), ('2', 2), ('1', 2),])
1093
class TestHistoryChange(tests.TestCaseWithTransport):
1095
def setup_a_tree(self):
1096
tree = self.make_branch_and_tree('tree')
1098
self.addCleanup(tree.unlock)
1099
tree.commit('1a', rev_id='1a')
1100
tree.commit('2a', rev_id='2a')
1101
tree.commit('3a', rev_id='3a')
1104
def setup_ab_tree(self):
1105
tree = self.setup_a_tree()
1106
tree.set_last_revision('1a')
1107
tree.branch.set_last_revision_info(1, '1a')
1108
tree.commit('2b', rev_id='2b')
1109
tree.commit('3b', rev_id='3b')
1112
def setup_ac_tree(self):
1113
tree = self.setup_a_tree()
1114
tree.set_last_revision(revision.NULL_REVISION)
1115
tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1116
tree.commit('1c', rev_id='1c')
1117
tree.commit('2c', rev_id='2c')
1118
tree.commit('3c', rev_id='3c')
1121
def test_all_new(self):
1122
tree = self.setup_ab_tree()
1123
old, new = log.get_history_change('1a', '3a', tree.branch.repository)
1124
self.assertEqual([], old)
1125
self.assertEqual(['2a', '3a'], new)
1127
def test_all_old(self):
1128
tree = self.setup_ab_tree()
1129
old, new = log.get_history_change('3a', '1a', tree.branch.repository)
1130
self.assertEqual([], new)
1131
self.assertEqual(['2a', '3a'], old)
1133
def test_null_old(self):
1134
tree = self.setup_ab_tree()
1135
old, new = log.get_history_change(revision.NULL_REVISION,
1136
'3a', tree.branch.repository)
1137
self.assertEqual([], old)
1138
self.assertEqual(['1a', '2a', '3a'], new)
1140
def test_null_new(self):
1141
tree = self.setup_ab_tree()
1142
old, new = log.get_history_change('3a', revision.NULL_REVISION,
1143
tree.branch.repository)
1144
self.assertEqual([], new)
1145
self.assertEqual(['1a', '2a', '3a'], old)
1147
def test_diverged(self):
1148
tree = self.setup_ab_tree()
1149
old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1150
self.assertEqual(old, ['2a', '3a'])
1151
self.assertEqual(new, ['2b', '3b'])
1153
def test_unrelated(self):
1154
tree = self.setup_ac_tree()
1155
old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1156
self.assertEqual(old, ['1a', '2a', '3a'])
1157
self.assertEqual(new, ['1c', '2c', '3c'])
1159
def test_show_branch_change(self):
1160
tree = self.setup_ab_tree()
1162
log.show_branch_change(tree.branch, s, 3, '3a')
1163
self.assertContainsRe(s.getvalue(),
1164
'[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1165
'[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1167
def test_show_branch_change_no_change(self):
1168
tree = self.setup_ab_tree()
1170
log.show_branch_change(tree.branch, s, 3, '3b')
1171
self.assertEqual(s.getvalue(),
1172
'Nothing seems to have changed\n')
1174
def test_show_branch_change_no_old(self):
1175
tree = self.setup_ab_tree()
1177
log.show_branch_change(tree.branch, s, 2, '2b')
1178
self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1179
self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1181
def test_show_branch_change_no_new(self):
1182
tree = self.setup_ab_tree()
1183
tree.branch.set_last_revision_info(2, '2b')
1185
log.show_branch_change(tree.branch, s, 3, '3b')
1186
self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1187
self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1190
class TestRevisionNotInBranch(TestCaseForLogFormatter):
1192
def setup_a_tree(self):
1193
tree = self.make_branch_and_tree('tree')
1195
self.addCleanup(tree.unlock)
1197
'committer': 'Joe Foo <joe@foo.com>',
1198
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1199
'timezone': 0, # UTC
1201
tree.commit('commit 1a', rev_id='1a', **kwargs)
1202
tree.commit('commit 2a', rev_id='2a', **kwargs)
1203
tree.commit('commit 3a', rev_id='3a', **kwargs)
1206
def setup_ab_tree(self):
1207
tree = self.setup_a_tree()
1208
tree.set_last_revision('1a')
1209
tree.branch.set_last_revision_info(1, '1a')
1211
'committer': 'Joe Foo <joe@foo.com>',
1212
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1213
'timezone': 0, # UTC
1215
tree.commit('commit 2b', rev_id='2b', **kwargs)
1216
tree.commit('commit 3b', rev_id='3b', **kwargs)
1219
def test_one_revision(self):
1220
tree = self.setup_ab_tree()
1222
rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1223
log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
1225
self.assertEqual(1, len(lf.revisions))
1226
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1227
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1229
def test_many_revisions(self):
1230
tree = self.setup_ab_tree()
1232
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1233
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1234
log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
1235
end_revision=end_rev)
1236
self.assertEqual(3, len(lf.revisions))
1237
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1238
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1239
self.assertEqual(None, lf.revisions[1].revno) # Out-of-branch
1240
self.assertEqual('2a', lf.revisions[1].rev.revision_id)
1241
self.assertEqual('1', lf.revisions[2].revno) # In-branch
1243
def test_long_format(self):
1244
tree = self.setup_ab_tree()
1245
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1246
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1247
self.assertFormatterResult("""\
1248
------------------------------------------------------------
1250
committer: Joe Foo <joe@foo.com>
1252
timestamp: Tue 2005-11-22 00:00:00 +0000
1255
------------------------------------------------------------
1257
committer: Joe Foo <joe@foo.com>
1259
timestamp: Tue 2005-11-22 00:00:00 +0000
1262
------------------------------------------------------------
1264
committer: Joe Foo <joe@foo.com>
1266
timestamp: Tue 2005-11-22 00:00:00 +0000
1270
tree.branch, log.LongLogFormatter, show_log_kwargs={
1271
'start_revision': start_rev, 'end_revision': end_rev
1274
def test_short_format(self):
1275
tree = self.setup_ab_tree()
1276
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1277
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1278
self.assertFormatterResult("""\
1287
1 Joe Foo\t2005-11-22
1291
tree.branch, log.ShortLogFormatter, show_log_kwargs={
1292
'start_revision': start_rev, 'end_revision': end_rev
1295
def test_line_format(self):
1296
tree = self.setup_ab_tree()
1297
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1298
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1299
self.assertFormatterResult("""\
1300
Joe Foo 2005-11-22 commit 3a
1301
Joe Foo 2005-11-22 commit 2a
1302
1: Joe Foo 2005-11-22 commit 1a
1304
tree.branch, log.LineLogFormatter, show_log_kwargs={
1305
'start_revision': start_rev, 'end_revision': end_rev
1309
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1312
super(TestLogWithBugs, self).setUp()
1313
log.properties_handler_registry.register(
1314
'bugs_properties_handler',
1315
log._bugs_properties_handler)
1317
def make_commits_with_bugs(self):
1318
"""Helper method for LogFormatter tests"""
1319
tree = self.make_branch_and_tree(u'.')
1320
self.build_tree(['a', 'b'])
1322
self.wt_commit(tree, 'simple log message', rev_id='a1',
1323
revprops={'bugs': 'test://bug/id fixed'})
1325
self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id='a2',
1326
authors=['Joe Bar <joe@bar.com>'],
1327
revprops={'bugs': 'test://bug/id fixed\n'
1328
'test://bug/2 fixed'})
1332
def test_long_bugs(self):
1333
tree = self.make_commits_with_bugs()
1334
self.assertFormatterResult("""\
1335
------------------------------------------------------------
1337
fixes bugs: test://bug/id test://bug/2
1338
author: Joe Bar <joe@bar.com>
1339
committer: Joe Foo <joe@foo.com>
1341
timestamp: Tue 2005-11-22 00:00:01 +0000
1346
------------------------------------------------------------
1348
fixes bug: test://bug/id
1349
committer: Joe Foo <joe@foo.com>
1351
timestamp: Tue 2005-11-22 00:00:00 +0000
1355
tree.branch, log.LongLogFormatter)
1357
def test_short_bugs(self):
1358
tree = self.make_commits_with_bugs()
1359
self.assertFormatterResult("""\
1360
2 Joe Bar\t2005-11-22
1361
fixes bugs: test://bug/id test://bug/2
1366
1 Joe Foo\t2005-11-22
1367
fixes bug: test://bug/id
1371
tree.branch, log.ShortLogFormatter)
1373
def test_wrong_bugs_property(self):
1374
tree = self.make_branch_and_tree(u'.')
1375
self.build_tree(['foo'])
1376
self.wt_commit(tree, 'simple log message', rev_id='a1',
1377
revprops={'bugs': 'test://bug/id invalid_value'})
1378
self.assertFormatterResult("""\
1379
1 Joe Foo\t2005-11-22
1383
tree.branch, log.ShortLogFormatter)
1385
def test_bugs_handler_present(self):
1386
self.properties_handler_registry.get('bugs_properties_handler')
1389
class TestLogForAuthors(TestCaseForLogFormatter):
1392
super(TestLogForAuthors, self).setUp()
1393
self.wt = self.make_standard_commit('nicky',
1394
authors=['John Doe <jdoe@example.com>',
1395
'Jane Rey <jrey@example.com>'])
1397
def assertFormatterResult(self, formatter, who, result):
1398
formatter_kwargs = dict()
1400
author_list_handler = log.author_list_registry.get(who)
1401
formatter_kwargs['author_list_handler'] = author_list_handler
1402
TestCaseForLogFormatter.assertFormatterResult(self, result,
1403
self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
1405
def test_line_default(self):
1406
self.assertFormatterResult(log.LineLogFormatter, None, """\
1407
1: John Doe 2005-11-22 add a
1410
def test_line_committer(self):
1411
self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
1412
1: Lorem Ipsum 2005-11-22 add a
1415
def test_line_first(self):
1416
self.assertFormatterResult(log.LineLogFormatter, 'first', """\
1417
1: John Doe 2005-11-22 add a
1420
def test_line_all(self):
1421
self.assertFormatterResult(log.LineLogFormatter, 'all', """\
1422
1: John Doe, Jane Rey 2005-11-22 add a
1426
def test_short_default(self):
1427
self.assertFormatterResult(log.ShortLogFormatter, None, """\
1428
1 John Doe\t2005-11-22
1433
def test_short_committer(self):
1434
self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
1435
1 Lorem Ipsum\t2005-11-22
1440
def test_short_first(self):
1441
self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
1442
1 John Doe\t2005-11-22
1447
def test_short_all(self):
1448
self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
1449
1 John Doe, Jane Rey\t2005-11-22
1454
def test_long_default(self):
1455
self.assertFormatterResult(log.LongLogFormatter, None, """\
1456
------------------------------------------------------------
1458
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1459
committer: Lorem Ipsum <test@example.com>
1461
timestamp: Tue 2005-11-22 00:00:00 +0000
1466
def test_long_committer(self):
1467
self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
1468
------------------------------------------------------------
1470
committer: Lorem Ipsum <test@example.com>
1472
timestamp: Tue 2005-11-22 00:00:00 +0000
1477
def test_long_first(self):
1478
self.assertFormatterResult(log.LongLogFormatter, 'first', """\
1479
------------------------------------------------------------
1481
author: John Doe <jdoe@example.com>
1482
committer: Lorem Ipsum <test@example.com>
1484
timestamp: Tue 2005-11-22 00:00:00 +0000
1489
def test_long_all(self):
1490
self.assertFormatterResult(log.LongLogFormatter, 'all', """\
1491
------------------------------------------------------------
1493
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1494
committer: Lorem Ipsum <test@example.com>
1496
timestamp: Tue 2005-11-22 00:00:00 +0000
1501
def test_gnu_changelog_default(self):
1502
self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
1503
2005-11-22 John Doe <jdoe@example.com>
1509
def test_gnu_changelog_committer(self):
1510
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
1511
2005-11-22 Lorem Ipsum <test@example.com>
1517
def test_gnu_changelog_first(self):
1518
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
1519
2005-11-22 John Doe <jdoe@example.com>
1525
def test_gnu_changelog_all(self):
1526
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
1527
2005-11-22 John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1534
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1536
def make_branch_with_alternate_ancestries(self, relpath='.'):
1537
# See test_merge_sorted_exclude_ancestry below for the difference with
1538
# bt.per_branch.test_iter_merge_sorted_revision.
1539
# TestIterMergeSortedRevisionsBushyGraph.
1540
# make_branch_with_alternate_ancestries
1541
# and test_merge_sorted_exclude_ancestry
1542
# See the FIXME in assertLogRevnos too.
1543
builder = branchbuilder.BranchBuilder(self.get_transport(relpath))
1555
builder.start_series()
1556
builder.build_snapshot('1', None, [
1557
('add', ('', 'TREE_ROOT', 'directory', '')),])
1558
builder.build_snapshot('1.1.1', ['1'], [])
1559
builder.build_snapshot('2', ['1'], [])
1560
builder.build_snapshot('1.2.1', ['1.1.1'], [])
1561
builder.build_snapshot('1.1.2', ['1.1.1', '1.2.1'], [])
1562
builder.build_snapshot('3', ['2', '1.1.2'], [])
1563
builder.finish_series()
1564
br = builder.get_branch()
1566
self.addCleanup(br.unlock)
1569
def assertLogRevnos(self, expected_revnos, b, start, end,
1570
exclude_common_ancestry, generate_merge_revisions=True):
1571
# FIXME: the layering in log makes it hard to test intermediate levels,
1572
# I wish adding filters with their parameters was easier...
1574
iter_revs = log._calc_view_revisions(
1575
b, start, end, direction='reverse',
1576
generate_merge_revisions=generate_merge_revisions,
1577
exclude_common_ancestry=exclude_common_ancestry)
1578
self.assertEqual(expected_revnos,
1579
[revid for revid, revno, depth in iter_revs])
1581
def test_merge_sorted_exclude_ancestry(self):
1582
b = self.make_branch_with_alternate_ancestries()
1583
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1584
b, '1', '3', exclude_common_ancestry=False)
1585
# '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1586
# it should be mentioned even if merge_sort order will make it appear
1588
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1589
b, '1.1.1', '3', exclude_common_ancestry=True)
1591
def test_merge_sorted_simple_revnos_exclude_ancestry(self):
1592
b = self.make_branch_with_alternate_ancestries()
1593
self.assertLogRevnos(['3', '2'],
1594
b, '1', '3', exclude_common_ancestry=True,
1595
generate_merge_revisions=False)
1596
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
1597
b, '1', '3', exclude_common_ancestry=True,
1598
generate_merge_revisions=True)
1601
class TestLogDefaults(TestCaseForLogFormatter):
1602
def test_default_log_level(self):
1604
Test to ensure that specifying 'levels=1' to make_log_request_dict
1605
doesn't get overwritten when using a LogFormatter that supports more
1609
wt = self._prepare_tree_with_merges()
1612
class CustomLogFormatter(log.LogFormatter):
1613
def __init__(self, *args, **kwargs):
1614
super(CustomLogFormatter, self).__init__(*args, **kwargs)
1616
def get_levels(self):
1617
# log formatter supports all levels:
1619
def log_revision(self, revision):
1620
self.revisions.append(revision)
1622
log_formatter = LogCatcher()
1623
# First request we don't specify number of levels, we should get a
1624
# sensible default (whatever the LogFormatter handles - which in this
1625
# case is 0/everything):
1626
request = log.make_log_request_dict(limit=10)
1627
log.Logger(b, request).show(log_formatter)
1628
# should have all three revisions:
1629
self.assertEquals(len(log_formatter.revisions), 3)
1632
log_formatter = LogCatcher()
1633
# now explicitly request mainline revisions only:
1634
request = log.make_log_request_dict(limit=10, levels=1)
1635
log.Logger(b, request).show(log_formatter)
1636
# should now only have 2 revisions:
1637
self.assertEquals(len(log_formatter.revisions), 2)