~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

(jelmer) Use the absolute_import feature everywhere in bzrlib,
 and add a source test to make sure it's used everywhere. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
18
18
from cStringIO import StringIO
19
19
 
20
20
from bzrlib import (
 
21
    branchbuilder,
21
22
    errors,
22
23
    log,
23
24
    registry,
27
28
    )
28
29
 
29
30
 
30
 
class TestCaseWithoutPropsHandler(tests.TestCaseWithTransport):
 
31
class TestLogMixin(object):
 
32
 
 
33
    def wt_commit(self, wt, message, **kwargs):
 
34
        """Use some mostly fixed values for commits to simplify tests.
 
35
 
 
36
        Tests can use this function to get some commit attributes. The time
 
37
        stamp is incremented at each commit.
 
38
        """
 
39
        if getattr(self, 'timestamp', None) is None:
 
40
            self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
 
41
        else:
 
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>')
 
46
 
 
47
        return wt.commit(message, **kwargs)
 
48
 
 
49
 
 
50
class TestCaseForLogFormatter(tests.TestCaseWithTransport, TestLogMixin):
31
51
 
32
52
    def setUp(self):
33
 
        super(TestCaseWithoutPropsHandler, self).setUp()
 
53
        super(TestCaseForLogFormatter, self).setUp()
34
54
        # keep a reference to the "current" custom prop. handler registry
35
55
        self.properties_handler_registry = log.properties_handler_registry
36
56
        # Use a clean registry for log
40
60
            log.properties_handler_registry = self.properties_handler_registry
41
61
        self.addCleanup(restore)
42
62
 
 
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:
 
67
            formatter_kwargs = {}
 
68
        formatter = formatter_class(to_file=logfile, **formatter_kwargs)
 
69
        if show_log_kwargs is None:
 
70
            show_log_kwargs = {}
 
71
        log.show_log(branch, formatter, **show_log_kwargs)
 
72
        self.assertEqualDiff(result, logfile.getvalue())
 
73
 
 
74
    def make_standard_commit(self, branch_nick, **kwargs):
 
75
        wt = self.make_branch_and_tree('.')
 
76
        wt.lock_write()
 
77
        self.addCleanup(wt.unlock)
 
78
        self.build_tree(['a'])
 
79
        wt.add(['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)
 
84
        return wt
 
85
 
 
86
    def make_commits_with_trailing_newlines(self, wt):
 
87
        """Helper method for LogFormatter tests"""
 
88
        b = wt.branch
 
89
        b.nick = 'test'
 
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')])
 
93
        wt.add('b')
 
94
        self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id='a2')
 
95
 
 
96
        self.build_tree_contents([('c', 'just another manic monday\n')])
 
97
        wt.add('c')
 
98
        self.wt_commit(wt, 'single line with trailing newline\n', rev_id='a3')
 
99
        return b
 
100
 
 
101
    def _prepare_tree_with_merges(self, with_tags=False):
 
102
        wt = self.make_branch_and_memory_tree('.')
 
103
        wt.lock_write()
 
104
        self.addCleanup(wt.unlock)
 
105
        wt.add('')
 
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')
 
111
        if with_tags:
 
112
            branch = wt.branch
 
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')
 
117
        return wt
 
118
 
43
119
 
44
120
class LogCatcher(log.LogFormatter):
45
121
    """Pull log messages into a list rather than displaying them.
49
125
    being dependent on the formatting.
50
126
    """
51
127
 
 
128
    supports_merge_revisions = True
52
129
    supports_delta = True
 
130
    supports_diff = True
 
131
    preferred_levels = 0
53
132
 
54
 
    def __init__(self):
55
 
        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)
56
136
        self.revisions = []
57
137
 
58
138
    def log_revision(self, revision):
201
281
        self.checkDelta(logentry.delta, added=['file1', 'file2'])
202
282
 
203
283
 
204
 
def make_commits_with_trailing_newlines(wt):
205
 
    """Helper method for LogFormatter tests"""
206
 
    b = wt.branch
207
 
    b.nick='test'
208
 
    open('a', 'wb').write('hello moto\n')
209
 
    wt.add('a')
210
 
    wt.commit('simple log message', rev_id='a1',
211
 
              timestamp=1132586655.459960938, timezone=-6*3600,
212
 
              committer='Joe Foo <joe@foo.com>')
213
 
    open('b', 'wb').write('goodbye\n')
214
 
    wt.add('b')
215
 
    wt.commit('multiline\nlog\nmessage\n', rev_id='a2',
216
 
              timestamp=1132586842.411175966, timezone=-6*3600,
217
 
              committer='Joe Foo <joe@foo.com>',
218
 
              authors=['Joe Bar <joe@bar.com>'])
219
 
 
220
 
    open('c', 'wb').write('just another manic monday\n')
221
 
    wt.add('c')
222
 
    wt.commit('single line with trailing newline\n', rev_id='a3',
223
 
              timestamp=1132587176.835228920, timezone=-6*3600,
224
 
              committer = 'Joe Foo <joe@foo.com>')
225
 
    return b
226
 
 
227
 
 
228
 
def normalize_log(log):
229
 
    """Replaces the variable lines of logs with fixed lines"""
230
 
    author = 'author: Dolor Sit <test@example.com>'
231
 
    committer = 'committer: Lorem Ipsum <test@example.com>'
232
 
    lines = log.splitlines(True)
233
 
    for idx,line in enumerate(lines):
234
 
        stripped_line = line.lstrip()
235
 
        indent = ' ' * (len(line) - len(stripped_line))
236
 
        if stripped_line.startswith('author:'):
237
 
            lines[idx] = indent + author + '\n'
238
 
        elif stripped_line.startswith('committer:'):
239
 
            lines[idx] = indent + committer + '\n'
240
 
        elif stripped_line.startswith('timestamp:'):
241
 
            lines[idx] = indent + 'timestamp: Just now\n'
242
 
    return ''.join(lines)
243
 
 
244
 
 
245
 
class TestShortLogFormatter(tests.TestCaseWithTransport):
 
284
class TestShortLogFormatter(TestCaseForLogFormatter):
246
285
 
247
286
    def test_trailing_newlines(self):
248
287
        wt = self.make_branch_and_tree('.')
249
 
        b = make_commits_with_trailing_newlines(wt)
250
 
        sio = self.make_utf8_encoded_stringio()
251
 
        lf = log.ShortLogFormatter(to_file=sio)
252
 
        log.show_log(b, lf)
253
 
        self.assertEqualDiff("""\
254
 
    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
255
291
      single line with trailing newline
256
292
 
257
 
    2 Joe Bar\t2005-11-21
 
293
    2 Joe Foo\t2005-11-22
258
294
      multiline
259
295
      log
260
296
      message
261
297
 
262
 
    1 Joe Foo\t2005-11-21
 
298
    1 Joe Foo\t2005-11-22
263
299
      simple log message
264
300
 
265
301
""",
266
 
                             sio.getvalue())
267
 
 
268
 
    def _prepare_tree_with_merges(self, with_tags=False):
269
 
        wt = self.make_branch_and_memory_tree('.')
270
 
        wt.lock_write()
271
 
        self.addCleanup(wt.unlock)
272
 
        wt.add('')
273
 
        wt.commit('rev-1', rev_id='rev-1',
274
 
                  timestamp=1132586655, timezone=36000,
275
 
                  committer='Joe Foo <joe@foo.com>')
276
 
        wt.commit('rev-merged', rev_id='rev-2a',
277
 
                  timestamp=1132586700, timezone=36000,
278
 
                  committer='Joe Foo <joe@foo.com>')
279
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
280
 
        wt.branch.set_last_revision_info(1, 'rev-1')
281
 
        wt.commit('rev-2', rev_id='rev-2b',
282
 
                  timestamp=1132586800, timezone=36000,
283
 
                  committer='Joe Foo <joe@foo.com>')
284
 
        if with_tags:
285
 
            branch = wt.branch
286
 
            branch.tags.set_tag('v0.2', 'rev-2b')
287
 
            wt.commit('rev-3', rev_id='rev-3',
288
 
                      timestamp=1132586900, timezone=36000,
289
 
                      committer='Jane Foo <jane@foo.com>')
290
 
            branch.tags.set_tag('v1.0rc1', 'rev-3')
291
 
            branch.tags.set_tag('v1.0', 'rev-3')
292
 
        return wt
 
302
            b, log.ShortLogFormatter)
293
303
 
294
304
    def test_short_log_with_merges(self):
295
305
        wt = self._prepare_tree_with_merges()
296
 
        logfile = self.make_utf8_encoded_stringio()
297
 
        formatter = log.ShortLogFormatter(to_file=logfile)
298
 
        log.show_log(wt.branch, formatter)
299
 
        self.assertEqualDiff("""\
 
306
        self.assertFormatterResult("""\
300
307
    2 Joe Foo\t2005-11-22 [merge]
301
308
      rev-2
302
309
 
304
311
      rev-1
305
312
 
306
313
""",
307
 
                             logfile.getvalue())
 
314
            wt.branch, log.ShortLogFormatter)
308
315
 
309
316
    def test_short_log_with_merges_and_advice(self):
310
317
        wt = self._prepare_tree_with_merges()
311
 
        logfile = self.make_utf8_encoded_stringio()
312
 
        formatter = log.ShortLogFormatter(to_file=logfile,
313
 
            show_advice=True)
314
 
        log.show_log(wt.branch, formatter)
315
 
        self.assertEqualDiff("""\
 
318
        self.assertFormatterResult("""\
316
319
    2 Joe Foo\t2005-11-22 [merge]
317
320
      rev-2
318
321
 
319
322
    1 Joe Foo\t2005-11-22
320
323
      rev-1
321
324
 
322
 
Use --include-merges or -n0 to see merged revisions.
 
325
Use --include-merged or -n0 to see merged revisions.
323
326
""",
324
 
                             logfile.getvalue())
 
327
            wt.branch, log.ShortLogFormatter,
 
328
            formatter_kwargs=dict(show_advice=True))
325
329
 
326
330
    def test_short_log_with_merges_and_range(self):
327
 
        wt = self.make_branch_and_memory_tree('.')
328
 
        wt.lock_write()
329
 
        self.addCleanup(wt.unlock)
330
 
        wt.add('')
331
 
        wt.commit('rev-1', rev_id='rev-1',
332
 
                  timestamp=1132586655, timezone=36000,
333
 
                  committer='Joe Foo <joe@foo.com>')
334
 
        wt.commit('rev-merged', rev_id='rev-2a',
335
 
                  timestamp=1132586700, timezone=36000,
336
 
                  committer='Joe Foo <joe@foo.com>')
337
 
        wt.branch.set_last_revision_info(1, 'rev-1')
338
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
339
 
        wt.commit('rev-2b', rev_id='rev-2b',
340
 
                  timestamp=1132586800, timezone=36000,
341
 
                  committer='Joe Foo <joe@foo.com>')
342
 
        wt.commit('rev-3a', rev_id='rev-3a',
343
 
                  timestamp=1132586800, timezone=36000,
344
 
                  committer='Joe Foo <joe@foo.com>')
 
331
        wt = self._prepare_tree_with_merges()
 
332
        self.wt_commit(wt, 'rev-3a', rev_id='rev-3a')
345
333
        wt.branch.set_last_revision_info(2, 'rev-2b')
346
334
        wt.set_parent_ids(['rev-2b', 'rev-3a'])
347
 
        wt.commit('rev-3b', rev_id='rev-3b',
348
 
                  timestamp=1132586800, timezone=36000,
349
 
                  committer='Joe Foo <joe@foo.com>')
350
 
        logfile = self.make_utf8_encoded_stringio()
351
 
        formatter = log.ShortLogFormatter(to_file=logfile)
352
 
        log.show_log(wt.branch, formatter,
353
 
            start_revision=2, end_revision=3)
354
 
        self.assertEqualDiff("""\
 
335
        self.wt_commit(wt, 'rev-3b', rev_id='rev-3b')
 
336
        self.assertFormatterResult("""\
355
337
    3 Joe Foo\t2005-11-22 [merge]
356
338
      rev-3b
357
339
 
358
340
    2 Joe Foo\t2005-11-22 [merge]
359
 
      rev-2b
 
341
      rev-2
360
342
 
361
343
""",
362
 
                             logfile.getvalue())
 
344
            wt.branch, log.ShortLogFormatter,
 
345
            show_log_kwargs=dict(start_revision=2, end_revision=3))
363
346
 
364
347
    def test_short_log_with_tags(self):
365
348
        wt = self._prepare_tree_with_merges(with_tags=True)
366
 
        logfile = self.make_utf8_encoded_stringio()
367
 
        formatter = log.ShortLogFormatter(to_file=logfile)
368
 
        log.show_log(wt.branch, formatter)
369
 
        self.assertEqualDiff("""\
370
 
    3 Jane Foo\t2005-11-22 {v1.0, v1.0rc1}
 
349
        self.assertFormatterResult("""\
 
350
    3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
371
351
      rev-3
372
352
 
373
353
    2 Joe Foo\t2005-11-22 {v0.2} [merge]
377
357
      rev-1
378
358
 
379
359
""",
380
 
                             logfile.getvalue())
 
360
            wt.branch, log.ShortLogFormatter)
381
361
 
382
362
    def test_short_log_single_merge_revision(self):
383
 
        wt = self.make_branch_and_memory_tree('.')
384
 
        wt.lock_write()
385
 
        self.addCleanup(wt.unlock)
386
 
        wt.add('')
387
 
        wt.commit('rev-1', rev_id='rev-1',
388
 
                  timestamp=1132586655, timezone=36000,
389
 
                  committer='Joe Foo <joe@foo.com>')
390
 
        wt.commit('rev-merged', rev_id='rev-2a',
391
 
                  timestamp=1132586700, timezone=36000,
392
 
                  committer='Joe Foo <joe@foo.com>')
393
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
394
 
        wt.branch.set_last_revision_info(1, 'rev-1')
395
 
        wt.commit('rev-2', rev_id='rev-2b',
396
 
                  timestamp=1132586800, timezone=36000,
397
 
                  committer='Joe Foo <joe@foo.com>')
398
 
        logfile = self.make_utf8_encoded_stringio()
399
 
        formatter = log.ShortLogFormatter(to_file=logfile)
 
363
        wt = self._prepare_tree_with_merges()
400
364
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
401
 
        wtb = wt.branch
402
 
        rev = revspec.in_history(wtb)
403
 
        log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
404
 
        self.assertEqualDiff("""\
 
365
        rev = revspec.in_history(wt.branch)
 
366
        self.assertFormatterResult("""\
405
367
      1.1.1 Joe Foo\t2005-11-22
406
368
            rev-merged
407
369
 
408
370
""",
409
 
                             logfile.getvalue())
410
 
 
411
 
 
412
 
class TestShortLogFormatterWithMergeRevisions(tests.TestCaseWithTransport):
 
371
            wt.branch, log.ShortLogFormatter,
 
372
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
373
 
 
374
    def test_show_ids(self):
 
375
        wt = self.make_branch_and_tree('parent')
 
376
        self.build_tree(['parent/f1', 'parent/f2'])
 
377
        wt.add(['f1','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]
 
385
      revision-id:c
 
386
      merge branch 1
 
387
 
 
388
          1.1.1 Joe Foo\t2005-11-22
 
389
                revision-id:b
 
390
                branch 1 changes
 
391
 
 
392
    1 Joe Foo\t2005-11-22
 
393
      revision-id:a
 
394
      first post
 
395
 
 
396
""",
 
397
            wt.branch, log.ShortLogFormatter,
 
398
            formatter_kwargs=dict(levels=0,show_ids=True))
 
399
 
 
400
 
 
401
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
413
402
 
414
403
    def test_short_merge_revs_log_with_merges(self):
415
 
        wt = self.make_branch_and_memory_tree('.')
416
 
        wt.lock_write()
417
 
        self.addCleanup(wt.unlock)
418
 
        wt.add('')
419
 
        wt.commit('rev-1', rev_id='rev-1',
420
 
                  timestamp=1132586655, timezone=36000,
421
 
                  committer='Joe Foo <joe@foo.com>')
422
 
        wt.commit('rev-merged', rev_id='rev-2a',
423
 
                  timestamp=1132586700, timezone=36000,
424
 
                  committer='Joe Foo <joe@foo.com>')
425
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
426
 
        wt.branch.set_last_revision_info(1, 'rev-1')
427
 
        wt.commit('rev-2', rev_id='rev-2b',
428
 
                  timestamp=1132586800, timezone=36000,
429
 
                  committer='Joe Foo <joe@foo.com>')
430
 
        logfile = self.make_utf8_encoded_stringio()
431
 
        formatter = log.ShortLogFormatter(to_file=logfile, levels=0)
432
 
        log.show_log(wt.branch, formatter)
 
404
        wt = self._prepare_tree_with_merges()
433
405
        # Note that the 1.1.1 indenting is in fact correct given that
434
406
        # the revision numbers are right justified within 5 characters
435
407
        # for mainline revnos and 9 characters for dotted revnos.
436
 
        self.assertEqualDiff("""\
 
408
        self.assertFormatterResult("""\
437
409
    2 Joe Foo\t2005-11-22 [merge]
438
410
      rev-2
439
411
 
444
416
      rev-1
445
417
 
446
418
""",
447
 
                             logfile.getvalue())
 
419
            wt.branch, log.ShortLogFormatter,
 
420
            formatter_kwargs=dict(levels=0))
448
421
 
449
422
    def test_short_merge_revs_log_single_merge_revision(self):
450
 
        wt = self.make_branch_and_memory_tree('.')
451
 
        wt.lock_write()
452
 
        self.addCleanup(wt.unlock)
453
 
        wt.add('')
454
 
        wt.commit('rev-1', rev_id='rev-1',
455
 
                  timestamp=1132586655, timezone=36000,
456
 
                  committer='Joe Foo <joe@foo.com>')
457
 
        wt.commit('rev-merged', rev_id='rev-2a',
458
 
                  timestamp=1132586700, timezone=36000,
459
 
                  committer='Joe Foo <joe@foo.com>')
460
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
461
 
        wt.branch.set_last_revision_info(1, 'rev-1')
462
 
        wt.commit('rev-2', rev_id='rev-2b',
463
 
                  timestamp=1132586800, timezone=36000,
464
 
                  committer='Joe Foo <joe@foo.com>')
465
 
        logfile = self.make_utf8_encoded_stringio()
466
 
        formatter = log.ShortLogFormatter(to_file=logfile, levels=0)
 
423
        wt = self._prepare_tree_with_merges()
467
424
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
468
 
        wtb = wt.branch
469
 
        rev = revspec.in_history(wtb)
470
 
        log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
471
 
        self.assertEqualDiff("""\
 
425
        rev = revspec.in_history(wt.branch)
 
426
        self.assertFormatterResult("""\
472
427
      1.1.1 Joe Foo\t2005-11-22
473
428
            rev-merged
474
429
 
475
430
""",
476
 
                             logfile.getvalue())
477
 
 
478
 
 
479
 
class TestLongLogFormatter(TestCaseWithoutPropsHandler):
 
431
            wt.branch, log.ShortLogFormatter,
 
432
            formatter_kwargs=dict(levels=0),
 
433
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
434
 
 
435
 
 
436
class TestLongLogFormatter(TestCaseForLogFormatter):
480
437
 
481
438
    def test_verbose_log(self):
482
439
        """Verbose log includes changed files
483
440
 
484
441
        bug #4676
485
442
        """
486
 
        wt = self.make_branch_and_tree('.')
487
 
        b = wt.branch
488
 
        self.build_tree(['a'])
489
 
        wt.add('a')
490
 
        # XXX: why does a longer nick show up?
491
 
        b.nick = 'test_verbose_log'
492
 
        wt.commit(message='add a',
493
 
                  timestamp=1132711707,
494
 
                  timezone=36000,
495
 
                  committer='Lorem Ipsum <test@example.com>')
496
 
        logfile = file('out.tmp', 'w+')
497
 
        formatter = log.LongLogFormatter(to_file=logfile)
498
 
        log.show_log(b, formatter, verbose=True)
499
 
        logfile.flush()
500
 
        logfile.seek(0)
501
 
        log_contents = logfile.read()
502
 
        self.assertEqualDiff('''\
 
443
        wt = self.make_standard_commit('test_verbose_log', authors=[])
 
444
        self.assertFormatterResult('''\
503
445
------------------------------------------------------------
504
446
revno: 1
505
447
committer: Lorem Ipsum <test@example.com>
506
448
branch nick: test_verbose_log
507
 
timestamp: Wed 2005-11-23 12:08:27 +1000
 
449
timestamp: Tue 2005-11-22 00:00:00 +0000
508
450
message:
509
451
  add a
510
452
added:
511
453
  a
512
454
''',
513
 
                             log_contents)
 
455
            wt.branch, log.LongLogFormatter,
 
456
            show_log_kwargs=dict(verbose=True))
514
457
 
515
458
    def test_merges_are_indented_by_level(self):
516
459
        wt = self.make_branch_and_tree('parent')
517
 
        wt.commit('first post')
518
 
        self.run_bzr('branch parent child')
519
 
        self.run_bzr(['commit', '-m', 'branch 1', '--unchanged', 'child'])
520
 
        self.run_bzr('branch child smallerchild')
521
 
        self.run_bzr(['commit', '-m', 'branch 2', '--unchanged',
522
 
            'smallerchild'])
523
 
        os.chdir('child')
524
 
        self.run_bzr('merge ../smallerchild')
525
 
        self.run_bzr(['commit', '-m', 'merge branch 2'])
526
 
        os.chdir('../parent')
527
 
        self.run_bzr('merge ../child')
528
 
        wt.commit('merge branch 1')
529
 
        b = wt.branch
530
 
        sio = self.make_utf8_encoded_stringio()
531
 
        lf = log.LongLogFormatter(to_file=sio, levels=0)
532
 
        log.show_log(b, lf, verbose=True)
533
 
        the_log = normalize_log(sio.getvalue())
534
 
        self.assertEqualDiff("""\
 
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("""\
535
470
------------------------------------------------------------
536
471
revno: 2 [merge]
537
 
committer: Lorem Ipsum <test@example.com>
 
472
committer: Joe Foo <joe@foo.com>
538
473
branch nick: parent
539
 
timestamp: Just now
 
474
timestamp: Tue 2005-11-22 00:00:04 +0000
540
475
message:
541
476
  merge branch 1
542
477
    ------------------------------------------------------------
543
478
    revno: 1.1.2 [merge]
544
 
    committer: Lorem Ipsum <test@example.com>
 
479
    committer: Joe Foo <joe@foo.com>
545
480
    branch nick: child
546
 
    timestamp: Just now
 
481
    timestamp: Tue 2005-11-22 00:00:03 +0000
547
482
    message:
548
483
      merge branch 2
549
484
        ------------------------------------------------------------
550
485
        revno: 1.2.1
551
 
        committer: Lorem Ipsum <test@example.com>
 
486
        committer: Joe Foo <joe@foo.com>
552
487
        branch nick: smallerchild
553
 
        timestamp: Just now
 
488
        timestamp: Tue 2005-11-22 00:00:02 +0000
554
489
        message:
555
490
          branch 2
556
491
    ------------------------------------------------------------
557
492
    revno: 1.1.1
558
 
    committer: Lorem Ipsum <test@example.com>
 
493
    committer: Joe Foo <joe@foo.com>
559
494
    branch nick: child
560
 
    timestamp: Just now
 
495
    timestamp: Tue 2005-11-22 00:00:01 +0000
561
496
    message:
562
497
      branch 1
563
498
------------------------------------------------------------
564
499
revno: 1
565
 
committer: Lorem Ipsum <test@example.com>
 
500
committer: Joe Foo <joe@foo.com>
566
501
branch nick: parent
567
 
timestamp: Just now
 
502
timestamp: Tue 2005-11-22 00:00:00 +0000
568
503
message:
569
504
  first post
570
505
""",
571
 
                             the_log)
 
506
            wt.branch, log.LongLogFormatter,
 
507
            formatter_kwargs=dict(levels=0),
 
508
            show_log_kwargs=dict(verbose=True))
572
509
 
573
510
    def test_verbose_merge_revisions_contain_deltas(self):
574
511
        wt = self.make_branch_and_tree('parent')
575
512
        self.build_tree(['parent/f1', 'parent/f2'])
576
513
        wt.add(['f1','f2'])
577
 
        wt.commit('first post')
578
 
        self.run_bzr('branch parent child')
 
514
        self.wt_commit(wt, 'first post')
 
515
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
579
516
        os.unlink('child/f1')
580
 
        file('child/f2', 'wb').write('hello\n')
581
 
        self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
582
 
            'child'])
583
 
        os.chdir('parent')
584
 
        self.run_bzr('merge ../child')
585
 
        wt.commit('merge branch 1')
586
 
        b = wt.branch
587
 
        sio = self.make_utf8_encoded_stringio()
588
 
        lf = log.LongLogFormatter(to_file=sio, levels=0)
589
 
        log.show_log(b, lf, verbose=True)
590
 
        the_log = normalize_log(sio.getvalue())
591
 
        self.assertEqualDiff("""\
 
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("""\
592
522
------------------------------------------------------------
593
523
revno: 2 [merge]
594
 
committer: Lorem Ipsum <test@example.com>
 
524
committer: Joe Foo <joe@foo.com>
595
525
branch nick: parent
596
 
timestamp: Just now
 
526
timestamp: Tue 2005-11-22 00:00:02 +0000
597
527
message:
598
528
  merge branch 1
599
529
removed:
602
532
  f2
603
533
    ------------------------------------------------------------
604
534
    revno: 1.1.1
605
 
    committer: Lorem Ipsum <test@example.com>
 
535
    committer: Joe Foo <joe@foo.com>
606
536
    branch nick: child
607
 
    timestamp: Just now
 
537
    timestamp: Tue 2005-11-22 00:00:01 +0000
608
538
    message:
609
539
      removed f1 and modified f2
610
540
    removed:
613
543
      f2
614
544
------------------------------------------------------------
615
545
revno: 1
616
 
committer: Lorem Ipsum <test@example.com>
 
546
committer: Joe Foo <joe@foo.com>
617
547
branch nick: parent
618
 
timestamp: Just now
 
548
timestamp: Tue 2005-11-22 00:00:00 +0000
619
549
message:
620
550
  first post
621
551
added:
622
552
  f1
623
553
  f2
624
554
""",
625
 
                             the_log)
 
555
            wt.branch, log.LongLogFormatter,
 
556
            formatter_kwargs=dict(levels=0),
 
557
            show_log_kwargs=dict(verbose=True))
626
558
 
627
559
    def test_trailing_newlines(self):
628
560
        wt = self.make_branch_and_tree('.')
629
 
        b = make_commits_with_trailing_newlines(wt)
630
 
        sio = self.make_utf8_encoded_stringio()
631
 
        lf = log.LongLogFormatter(to_file=sio)
632
 
        log.show_log(b, lf)
633
 
        self.assertEqualDiff("""\
 
561
        b = self.make_commits_with_trailing_newlines(wt)
 
562
        self.assertFormatterResult("""\
634
563
------------------------------------------------------------
635
564
revno: 3
636
565
committer: Joe Foo <joe@foo.com>
637
566
branch nick: test
638
 
timestamp: Mon 2005-11-21 09:32:56 -0600
 
567
timestamp: Tue 2005-11-22 00:00:02 +0000
639
568
message:
640
569
  single line with trailing newline
641
570
------------------------------------------------------------
642
571
revno: 2
643
 
author: Joe Bar <joe@bar.com>
644
572
committer: Joe Foo <joe@foo.com>
645
573
branch nick: test
646
 
timestamp: Mon 2005-11-21 09:27:22 -0600
 
574
timestamp: Tue 2005-11-22 00:00:01 +0000
647
575
message:
648
576
  multiline
649
577
  log
652
580
revno: 1
653
581
committer: Joe Foo <joe@foo.com>
654
582
branch nick: test
655
 
timestamp: Mon 2005-11-21 09:24:15 -0600
 
583
timestamp: Tue 2005-11-22 00:00:00 +0000
656
584
message:
657
585
  simple log message
658
586
""",
659
 
                             sio.getvalue())
 
587
        b, log.LongLogFormatter)
660
588
 
661
589
    def test_author_in_log(self):
662
590
        """Log includes the author name if it's set in
663
591
        the revision properties
664
592
        """
665
 
        wt = self.make_branch_and_tree('.')
666
 
        b = wt.branch
667
 
        self.build_tree(['a'])
668
 
        wt.add('a')
669
 
        b.nick = 'test_author_log'
670
 
        wt.commit(message='add a',
671
 
                  timestamp=1132711707,
672
 
                  timezone=36000,
673
 
                  committer='Lorem Ipsum <test@example.com>',
674
 
                  authors=['John Doe <jdoe@example.com>',
675
 
                           'Jane Rey <jrey@example.com>'])
676
 
        sio = StringIO()
677
 
        formatter = log.LongLogFormatter(to_file=sio)
678
 
        log.show_log(b, formatter)
679
 
        self.assertEqualDiff('''\
 
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("""\
680
597
------------------------------------------------------------
681
598
revno: 1
682
599
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
683
600
committer: Lorem Ipsum <test@example.com>
684
601
branch nick: test_author_log
685
 
timestamp: Wed 2005-11-23 12:08:27 +1000
 
602
timestamp: Tue 2005-11-22 00:00:00 +0000
686
603
message:
687
604
  add a
688
 
''',
689
 
                             sio.getvalue())
 
605
""",
 
606
        wt.branch, log.LongLogFormatter)
690
607
 
691
608
    def test_properties_in_log(self):
692
609
        """Log includes the custom properties returned by the registered
693
610
        handlers.
694
611
        """
695
 
        wt = self.make_branch_and_tree('.')
696
 
        b = wt.branch
697
 
        self.build_tree(['a'])
698
 
        wt.add('a')
699
 
        b.nick = 'test_properties_in_log'
700
 
        wt.commit(message='add a',
701
 
                  timestamp=1132711707,
702
 
                  timezone=36000,
703
 
                  committer='Lorem Ipsum <test@example.com>',
704
 
                  authors=['John Doe <jdoe@example.com>'])
705
 
        sio = StringIO()
706
 
        formatter = log.LongLogFormatter(to_file=sio)
707
 
        try:
708
 
            def trivial_custom_prop_handler(revision):
709
 
                return {'test_prop':'test_value'}
 
612
        wt = self.make_standard_commit('test_properties_in_log')
 
613
        def trivial_custom_prop_handler(revision):
 
614
            return {'test_prop':'test_value'}
710
615
 
711
 
            log.properties_handler_registry.register(
712
 
                'trivial_custom_prop_handler',
713
 
                trivial_custom_prop_handler)
714
 
            log.show_log(b, formatter)
715
 
        finally:
716
 
            log.properties_handler_registry.remove(
717
 
                'trivial_custom_prop_handler')
718
 
            self.assertEqualDiff('''\
 
616
        # Cleaned up in setUp()
 
617
        log.properties_handler_registry.register(
 
618
            'trivial_custom_prop_handler',
 
619
            trivial_custom_prop_handler)
 
620
        self.assertFormatterResult("""\
719
621
------------------------------------------------------------
720
622
revno: 1
721
623
test_prop: test_value
722
624
author: John Doe <jdoe@example.com>
723
625
committer: Lorem Ipsum <test@example.com>
724
626
branch nick: test_properties_in_log
725
 
timestamp: Wed 2005-11-23 12:08:27 +1000
 
627
timestamp: Tue 2005-11-22 00:00:00 +0000
726
628
message:
727
629
  add a
728
 
''',
729
 
                                 sio.getvalue())
 
630
""",
 
631
            wt.branch, log.LongLogFormatter)
730
632
 
731
633
    def test_properties_in_short_log(self):
732
634
        """Log includes the custom properties returned by the registered
733
635
        handlers.
734
636
        """
735
 
        wt = self.make_branch_and_tree('.')
736
 
        b = wt.branch
737
 
        self.build_tree(['a'])
738
 
        wt.add('a')
739
 
        b.nick = 'test_properties_in_short_log'
740
 
        wt.commit(message='add a',
741
 
                  timestamp=1132711707,
742
 
                  timezone=36000,
743
 
                  committer='Lorem Ipsum <test@example.com>',
744
 
                  authors=['John Doe <jdoe@example.com>'])
745
 
        sio = StringIO()
746
 
        formatter = log.ShortLogFormatter(to_file=sio)
747
 
        try:
748
 
            def trivial_custom_prop_handler(revision):
749
 
                return {'test_prop':'test_value'}
 
637
        wt = self.make_standard_commit('test_properties_in_short_log')
 
638
        def trivial_custom_prop_handler(revision):
 
639
            return {'test_prop':'test_value'}
750
640
 
751
 
            log.properties_handler_registry.register(
752
 
                'trivial_custom_prop_handler',
753
 
                trivial_custom_prop_handler)
754
 
            log.show_log(b, formatter)
755
 
        finally:
756
 
            log.properties_handler_registry.remove(
757
 
                'trivial_custom_prop_handler')
758
 
            self.assertEqualDiff('''\
759
 
    1 John Doe\t2005-11-23
 
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
760
646
      test_prop: test_value
761
647
      add a
762
648
 
763
 
''',
764
 
                                 sio.getvalue())
 
649
""",
 
650
            wt.branch, log.ShortLogFormatter)
765
651
 
766
652
    def test_error_in_properties_handler(self):
767
653
        """Log includes the custom properties returned by the registered
768
654
        handlers.
769
655
        """
770
 
        wt = self.make_branch_and_tree('.')
771
 
        b = wt.branch
772
 
        self.build_tree(['a'])
773
 
        wt.add('a')
774
 
        b.nick = 'test_author_log'
775
 
        wt.commit(message='add a',
776
 
                  timestamp=1132711707,
777
 
                  timezone=36000,
778
 
                  committer='Lorem Ipsum <test@example.com>',
779
 
                  authors=['John Doe <jdoe@example.com>'],
780
 
                  revprops={'first_prop':'first_value'})
781
 
        sio = StringIO()
 
656
        wt = self.make_standard_commit('error_in_properties_handler',
 
657
            revprops={'first_prop':'first_value'})
 
658
        sio = self.make_utf8_encoded_stringio()
782
659
        formatter = log.LongLogFormatter(to_file=sio)
783
 
        try:
784
 
            def trivial_custom_prop_handler(revision):
785
 
                raise StandardError("a test error")
 
660
        def trivial_custom_prop_handler(revision):
 
661
            raise StandardError("a test error")
786
662
 
787
 
            log.properties_handler_registry.register(
788
 
                'trivial_custom_prop_handler',
789
 
                trivial_custom_prop_handler)
790
 
            self.assertRaises(StandardError, log.show_log, b, formatter,)
791
 
        finally:
792
 
            log.properties_handler_registry.remove(
793
 
                'trivial_custom_prop_handler')
 
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,)
794
667
 
795
668
    def test_properties_handler_bad_argument(self):
796
 
        wt = self.make_branch_and_tree('.')
797
 
        b = wt.branch
798
 
        self.build_tree(['a'])
799
 
        wt.add('a')
800
 
        b.nick = 'test_author_log'
801
 
        wt.commit(message='add a',
802
 
                  timestamp=1132711707,
803
 
                  timezone=36000,
804
 
                  committer='Lorem Ipsum <test@example.com>',
805
 
                  authors=['John Doe <jdoe@example.com>'],
806
 
                  revprops={'a_prop':'test_value'})
807
 
        sio = StringIO()
 
669
        wt = self.make_standard_commit('bad_argument',
 
670
              revprops={'a_prop':'test_value'})
 
671
        sio = self.make_utf8_encoded_stringio()
808
672
        formatter = log.LongLogFormatter(to_file=sio)
809
 
        try:
810
 
            def bad_argument_prop_handler(revision):
811
 
                return {'custom_prop_name':revision.properties['a_prop']}
812
 
 
813
 
            log.properties_handler_registry.register(
814
 
                'bad_argument_prop_handler',
815
 
                bad_argument_prop_handler)
816
 
 
817
 
            self.assertRaises(AttributeError, formatter.show_properties,
818
 
                              'a revision', '')
819
 
 
820
 
            revision = b.repository.get_revision(b.last_revision())
821
 
            formatter.show_properties(revision, '')
822
 
            self.assertEqualDiff('''custom_prop_name: test_value\n''',
823
 
                                 sio.getvalue())
824
 
        finally:
825
 
            log.properties_handler_registry.remove(
826
 
                'bad_argument_prop_handler')
827
 
 
828
 
 
829
 
class TestLongLogFormatterWithoutMergeRevisions(TestCaseWithoutPropsHandler):
 
673
        def bad_argument_prop_handler(revision):
 
674
            return {'custom_prop_name':revision.properties['a_prop']}
 
675
 
 
676
        log.properties_handler_registry.register(
 
677
            'bad_argument_prop_handler',
 
678
            bad_argument_prop_handler)
 
679
 
 
680
        self.assertRaises(AttributeError, formatter.show_properties,
 
681
                          'a revision', '')
 
682
 
 
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''',
 
686
                             sio.getvalue())
 
687
 
 
688
    def test_show_ids(self):
 
689
        wt = self.make_branch_and_tree('parent')
 
690
        self.build_tree(['parent/f1', 'parent/f2'])
 
691
        wt.add(['f1','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
------------------------------------------------------------
 
699
revno: 2 [merge]
 
700
revision-id: c
 
701
parent: a
 
702
parent: b
 
703
committer: Joe Foo <joe@foo.com>
 
704
branch nick: parent
 
705
timestamp: Tue 2005-11-22 00:00:02 +0000
 
706
message:
 
707
  merge branch 1
 
708
    ------------------------------------------------------------
 
709
    revno: 1.1.1
 
710
    revision-id: b
 
711
    parent: a
 
712
    committer: Joe Foo <joe@foo.com>
 
713
    branch nick: child
 
714
    timestamp: Tue 2005-11-22 00:00:01 +0000
 
715
    message:
 
716
      branch 1 changes
 
717
------------------------------------------------------------
 
718
revno: 1
 
719
revision-id: a
 
720
committer: Joe Foo <joe@foo.com>
 
721
branch nick: parent
 
722
timestamp: Tue 2005-11-22 00:00:00 +0000
 
723
message:
 
724
  first post
 
725
""",
 
726
            wt.branch, log.LongLogFormatter,
 
727
            formatter_kwargs=dict(levels=0,show_ids=True))
 
728
 
 
729
 
 
730
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
830
731
 
831
732
    def test_long_verbose_log(self):
832
733
        """Verbose log includes changed files
833
734
 
834
735
        bug #4676
835
736
        """
836
 
        wt = self.make_branch_and_tree('.')
837
 
        b = wt.branch
838
 
        self.build_tree(['a'])
839
 
        wt.add('a')
840
 
        # XXX: why does a longer nick show up?
841
 
        b.nick = 'test_verbose_log'
842
 
        wt.commit(message='add a',
843
 
                  timestamp=1132711707,
844
 
                  timezone=36000,
845
 
                  committer='Lorem Ipsum <test@example.com>')
846
 
        logfile = file('out.tmp', 'w+')
847
 
        formatter = log.LongLogFormatter(to_file=logfile, levels=1)
848
 
        log.show_log(b, formatter, verbose=True)
849
 
        logfile.flush()
850
 
        logfile.seek(0)
851
 
        log_contents = logfile.read()
852
 
        self.assertEqualDiff('''\
 
737
        wt = self.make_standard_commit('test_long_verbose_log', authors=[])
 
738
        self.assertFormatterResult("""\
853
739
------------------------------------------------------------
854
740
revno: 1
855
741
committer: Lorem Ipsum <test@example.com>
856
 
branch nick: test_verbose_log
857
 
timestamp: Wed 2005-11-23 12:08:27 +1000
 
742
branch nick: test_long_verbose_log
 
743
timestamp: Tue 2005-11-22 00:00:00 +0000
858
744
message:
859
745
  add a
860
746
added:
861
747
  a
862
 
''',
863
 
                             log_contents)
 
748
""",
 
749
            wt.branch, log.LongLogFormatter,
 
750
            formatter_kwargs=dict(levels=1),
 
751
            show_log_kwargs=dict(verbose=True))
864
752
 
865
753
    def test_long_verbose_contain_deltas(self):
866
754
        wt = self.make_branch_and_tree('parent')
867
755
        self.build_tree(['parent/f1', 'parent/f2'])
868
756
        wt.add(['f1','f2'])
869
 
        wt.commit('first post')
870
 
        self.run_bzr('branch parent child')
 
757
        self.wt_commit(wt, 'first post')
 
758
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
871
759
        os.unlink('child/f1')
872
 
        file('child/f2', 'wb').write('hello\n')
873
 
        self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
874
 
            'child'])
875
 
        os.chdir('parent')
876
 
        self.run_bzr('merge ../child')
877
 
        wt.commit('merge branch 1')
878
 
        b = wt.branch
879
 
        sio = self.make_utf8_encoded_stringio()
880
 
        lf = log.LongLogFormatter(to_file=sio, levels=1)
881
 
        log.show_log(b, lf, verbose=True)
882
 
        the_log = normalize_log(sio.getvalue())
883
 
        self.assertEqualDiff("""\
 
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("""\
884
765
------------------------------------------------------------
885
766
revno: 2 [merge]
886
 
committer: Lorem Ipsum <test@example.com>
 
767
committer: Joe Foo <joe@foo.com>
887
768
branch nick: parent
888
 
timestamp: Just now
 
769
timestamp: Tue 2005-11-22 00:00:02 +0000
889
770
message:
890
771
  merge branch 1
891
772
removed:
894
775
  f2
895
776
------------------------------------------------------------
896
777
revno: 1
897
 
committer: Lorem Ipsum <test@example.com>
 
778
committer: Joe Foo <joe@foo.com>
898
779
branch nick: parent
899
 
timestamp: Just now
 
780
timestamp: Tue 2005-11-22 00:00:00 +0000
900
781
message:
901
782
  first post
902
783
added:
903
784
  f1
904
785
  f2
905
786
""",
906
 
                             the_log)
 
787
            wt.branch, log.LongLogFormatter,
 
788
            formatter_kwargs=dict(levels=1),
 
789
            show_log_kwargs=dict(verbose=True))
907
790
 
908
791
    def test_long_trailing_newlines(self):
909
792
        wt = self.make_branch_and_tree('.')
910
 
        b = make_commits_with_trailing_newlines(wt)
911
 
        sio = self.make_utf8_encoded_stringio()
912
 
        lf = log.LongLogFormatter(to_file=sio, levels=1)
913
 
        log.show_log(b, lf)
914
 
        self.assertEqualDiff("""\
 
793
        b = self.make_commits_with_trailing_newlines(wt)
 
794
        self.assertFormatterResult("""\
915
795
------------------------------------------------------------
916
796
revno: 3
917
797
committer: Joe Foo <joe@foo.com>
918
798
branch nick: test
919
 
timestamp: Mon 2005-11-21 09:32:56 -0600
 
799
timestamp: Tue 2005-11-22 00:00:02 +0000
920
800
message:
921
801
  single line with trailing newline
922
802
------------------------------------------------------------
923
803
revno: 2
924
 
author: Joe Bar <joe@bar.com>
925
804
committer: Joe Foo <joe@foo.com>
926
805
branch nick: test
927
 
timestamp: Mon 2005-11-21 09:27:22 -0600
 
806
timestamp: Tue 2005-11-22 00:00:01 +0000
928
807
message:
929
808
  multiline
930
809
  log
933
812
revno: 1
934
813
committer: Joe Foo <joe@foo.com>
935
814
branch nick: test
936
 
timestamp: Mon 2005-11-21 09:24:15 -0600
 
815
timestamp: Tue 2005-11-22 00:00:00 +0000
937
816
message:
938
817
  simple log message
939
818
""",
940
 
                             sio.getvalue())
 
819
        b, log.LongLogFormatter,
 
820
        formatter_kwargs=dict(levels=1))
941
821
 
942
822
    def test_long_author_in_log(self):
943
823
        """Log includes the author name if it's set in
944
824
        the revision properties
945
825
        """
946
 
        wt = self.make_branch_and_tree('.')
947
 
        b = wt.branch
948
 
        self.build_tree(['a'])
949
 
        wt.add('a')
950
 
        b.nick = 'test_author_log'
951
 
        wt.commit(message='add a',
952
 
                  timestamp=1132711707,
953
 
                  timezone=36000,
954
 
                  committer='Lorem Ipsum <test@example.com>',
955
 
                  authors=['John Doe <jdoe@example.com>'])
956
 
        sio = StringIO()
957
 
        formatter = log.LongLogFormatter(to_file=sio, levels=1)
958
 
        log.show_log(b, formatter)
959
 
        self.assertEqualDiff('''\
 
826
        wt = self.make_standard_commit('test_author_log')
 
827
        self.assertFormatterResult("""\
960
828
------------------------------------------------------------
961
829
revno: 1
962
830
author: John Doe <jdoe@example.com>
963
831
committer: Lorem Ipsum <test@example.com>
964
832
branch nick: test_author_log
965
 
timestamp: Wed 2005-11-23 12:08:27 +1000
 
833
timestamp: Tue 2005-11-22 00:00:00 +0000
966
834
message:
967
835
  add a
968
 
''',
969
 
                             sio.getvalue())
 
836
""",
 
837
            wt.branch, log.LongLogFormatter,
 
838
            formatter_kwargs=dict(levels=1))
970
839
 
971
840
    def test_long_properties_in_log(self):
972
841
        """Log includes the custom properties returned by the registered
973
842
        handlers.
974
843
        """
975
 
        wt = self.make_branch_and_tree('.')
976
 
        b = wt.branch
977
 
        self.build_tree(['a'])
978
 
        wt.add('a')
979
 
        b.nick = 'test_properties_in_log'
980
 
        wt.commit(message='add a',
981
 
                  timestamp=1132711707,
982
 
                  timezone=36000,
983
 
                  committer='Lorem Ipsum <test@example.com>',
984
 
                  authors=['John Doe <jdoe@example.com>'])
985
 
        sio = StringIO()
986
 
        formatter = log.LongLogFormatter(to_file=sio, levels=1)
987
 
        try:
988
 
            def trivial_custom_prop_handler(revision):
989
 
                return {'test_prop':'test_value'}
 
844
        wt = self.make_standard_commit('test_properties_in_log')
 
845
        def trivial_custom_prop_handler(revision):
 
846
            return {'test_prop':'test_value'}
990
847
 
991
 
            log.properties_handler_registry.register(
992
 
                'trivial_custom_prop_handler',
993
 
                trivial_custom_prop_handler)
994
 
            log.show_log(b, formatter)
995
 
        finally:
996
 
            log.properties_handler_registry.remove(
997
 
                'trivial_custom_prop_handler')
998
 
            self.assertEqualDiff('''\
 
848
        log.properties_handler_registry.register(
 
849
            'trivial_custom_prop_handler',
 
850
            trivial_custom_prop_handler)
 
851
        self.assertFormatterResult("""\
999
852
------------------------------------------------------------
1000
853
revno: 1
1001
854
test_prop: test_value
1002
855
author: John Doe <jdoe@example.com>
1003
856
committer: Lorem Ipsum <test@example.com>
1004
857
branch nick: test_properties_in_log
1005
 
timestamp: Wed 2005-11-23 12:08:27 +1000
 
858
timestamp: Tue 2005-11-22 00:00:00 +0000
1006
859
message:
1007
860
  add a
1008
 
''',
1009
 
                                 sio.getvalue())
1010
 
 
1011
 
 
1012
 
class TestLineLogFormatter(tests.TestCaseWithTransport):
 
861
""",
 
862
            wt.branch, log.LongLogFormatter,
 
863
            formatter_kwargs=dict(levels=1))
 
864
 
 
865
 
 
866
class TestLineLogFormatter(TestCaseForLogFormatter):
1013
867
 
1014
868
    def test_line_log(self):
1015
869
        """Line log should show revno
1016
870
 
1017
871
        bug #5162
1018
872
        """
1019
 
        wt = self.make_branch_and_tree('.')
1020
 
        b = wt.branch
1021
 
        self.build_tree(['a'])
1022
 
        wt.add('a')
1023
 
        b.nick = 'test-line-log'
1024
 
        wt.commit(message='add a',
1025
 
                  timestamp=1132711707,
1026
 
                  timezone=36000,
1027
 
                  committer='Line-Log-Formatter Tester <test@line.log>')
1028
 
        logfile = file('out.tmp', 'w+')
1029
 
        formatter = log.LineLogFormatter(to_file=logfile)
1030
 
        log.show_log(b, formatter)
1031
 
        logfile.flush()
1032
 
        logfile.seek(0)
1033
 
        log_contents = logfile.read()
1034
 
        self.assertEqualDiff('1: Line-Log-Formatte... 2005-11-23 add a\n',
1035
 
                             log_contents)
 
873
        wt = self.make_standard_commit('test-line-log',
 
874
                committer='Line-Log-Formatter Tester <test@line.log>',
 
875
                authors=[])
 
876
        self.assertFormatterResult("""\
 
877
1: Line-Log-Formatte... 2005-11-22 add a
 
878
""",
 
879
            wt.branch, log.LineLogFormatter)
1036
880
 
1037
881
    def test_trailing_newlines(self):
1038
882
        wt = self.make_branch_and_tree('.')
1039
 
        b = make_commits_with_trailing_newlines(wt)
1040
 
        sio = self.make_utf8_encoded_stringio()
1041
 
        lf = log.LineLogFormatter(to_file=sio)
1042
 
        log.show_log(b, lf)
1043
 
        self.assertEqualDiff("""\
1044
 
3: Joe Foo 2005-11-21 single line with trailing newline
1045
 
2: Joe Bar 2005-11-21 multiline
1046
 
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
1047
888
""",
1048
 
                             sio.getvalue())
1049
 
 
1050
 
    def _prepare_tree_with_merges(self, with_tags=False):
1051
 
        wt = self.make_branch_and_memory_tree('.')
1052
 
        wt.lock_write()
1053
 
        self.addCleanup(wt.unlock)
1054
 
        wt.add('')
1055
 
        wt.commit('rev-1', rev_id='rev-1',
1056
 
                  timestamp=1132586655, timezone=36000,
1057
 
                  committer='Joe Foo <joe@foo.com>')
1058
 
        wt.commit('rev-merged', rev_id='rev-2a',
1059
 
                  timestamp=1132586700, timezone=36000,
1060
 
                  committer='Joe Foo <joe@foo.com>')
1061
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
1062
 
        wt.branch.set_last_revision_info(1, 'rev-1')
1063
 
        wt.commit('rev-2', rev_id='rev-2b',
1064
 
                  timestamp=1132586800, timezone=36000,
1065
 
                  committer='Joe Foo <joe@foo.com>')
1066
 
        if with_tags:
1067
 
            branch = wt.branch
1068
 
            branch.tags.set_tag('v0.2', 'rev-2b')
1069
 
            wt.commit('rev-3', rev_id='rev-3',
1070
 
                      timestamp=1132586900, timezone=36000,
1071
 
                      committer='Jane Foo <jane@foo.com>')
1072
 
            branch.tags.set_tag('v1.0rc1', 'rev-3')
1073
 
            branch.tags.set_tag('v1.0', 'rev-3')
1074
 
        return wt
 
889
            b, log.LineLogFormatter)
1075
890
 
1076
891
    def test_line_log_single_merge_revision(self):
1077
892
        wt = self._prepare_tree_with_merges()
1078
 
        logfile = self.make_utf8_encoded_stringio()
1079
 
        formatter = log.LineLogFormatter(to_file=logfile)
1080
893
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
1081
 
        wtb = wt.branch
1082
 
        rev = revspec.in_history(wtb)
1083
 
        log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
1084
 
        self.assertEqualDiff("""\
 
894
        rev = revspec.in_history(wt.branch)
 
895
        self.assertFormatterResult("""\
1085
896
1.1.1: Joe Foo 2005-11-22 rev-merged
1086
897
""",
1087
 
                             logfile.getvalue())
 
898
            wt.branch, log.LineLogFormatter,
 
899
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
1088
900
 
1089
901
    def test_line_log_with_tags(self):
1090
902
        wt = self._prepare_tree_with_merges(with_tags=True)
1091
 
        logfile = self.make_utf8_encoded_stringio()
1092
 
        formatter = log.LineLogFormatter(to_file=logfile)
1093
 
        log.show_log(wt.branch, formatter)
1094
 
        self.assertEqualDiff("""\
1095
 
3: Jane Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
 
903
        self.assertFormatterResult("""\
 
904
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
1096
905
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
1097
906
1: Joe Foo 2005-11-22 rev-1
1098
907
""",
1099
 
                             logfile.getvalue())
1100
 
 
1101
 
class TestLineLogFormatterWithMergeRevisions(tests.TestCaseWithTransport):
 
908
            wt.branch, log.LineLogFormatter)
 
909
 
 
910
 
 
911
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
1102
912
 
1103
913
    def test_line_merge_revs_log(self):
1104
914
        """Line log should show revno
1105
915
 
1106
916
        bug #5162
1107
917
        """
1108
 
        wt = self.make_branch_and_tree('.')
1109
 
        b = wt.branch
1110
 
        self.build_tree(['a'])
1111
 
        wt.add('a')
1112
 
        b.nick = 'test-line-log'
1113
 
        wt.commit(message='add a',
1114
 
                  timestamp=1132711707,
1115
 
                  timezone=36000,
1116
 
                  committer='Line-Log-Formatter Tester <test@line.log>')
1117
 
        logfile = file('out.tmp', 'w+')
1118
 
        formatter = log.LineLogFormatter(to_file=logfile, levels=0)
1119
 
        log.show_log(b, formatter)
1120
 
        logfile.flush()
1121
 
        logfile.seek(0)
1122
 
        log_contents = logfile.read()
1123
 
        self.assertEqualDiff('1: Line-Log-Formatte... 2005-11-23 add a\n',
1124
 
                             log_contents)
 
918
        wt = self.make_standard_commit('test-line-log',
 
919
                committer='Line-Log-Formatter Tester <test@line.log>',
 
920
                authors=[])
 
921
        self.assertFormatterResult("""\
 
922
1: Line-Log-Formatte... 2005-11-22 add a
 
923
""",
 
924
            wt.branch, log.LineLogFormatter)
1125
925
 
1126
926
    def test_line_merge_revs_log_single_merge_revision(self):
1127
 
        wt = self.make_branch_and_memory_tree('.')
1128
 
        wt.lock_write()
1129
 
        self.addCleanup(wt.unlock)
1130
 
        wt.add('')
1131
 
        wt.commit('rev-1', rev_id='rev-1',
1132
 
                  timestamp=1132586655, timezone=36000,
1133
 
                  committer='Joe Foo <joe@foo.com>')
1134
 
        wt.commit('rev-merged', rev_id='rev-2a',
1135
 
                  timestamp=1132586700, timezone=36000,
1136
 
                  committer='Joe Foo <joe@foo.com>')
1137
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
1138
 
        wt.branch.set_last_revision_info(1, 'rev-1')
1139
 
        wt.commit('rev-2', rev_id='rev-2b',
1140
 
                  timestamp=1132586800, timezone=36000,
1141
 
                  committer='Joe Foo <joe@foo.com>')
1142
 
        logfile = self.make_utf8_encoded_stringio()
1143
 
        formatter = log.LineLogFormatter(to_file=logfile, levels=0)
 
927
        wt = self._prepare_tree_with_merges()
1144
928
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
1145
 
        wtb = wt.branch
1146
 
        rev = revspec.in_history(wtb)
1147
 
        log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
1148
 
        self.assertEqualDiff("""\
 
929
        rev = revspec.in_history(wt.branch)
 
930
        self.assertFormatterResult("""\
1149
931
1.1.1: Joe Foo 2005-11-22 rev-merged
1150
932
""",
1151
 
                             logfile.getvalue())
 
933
            wt.branch, log.LineLogFormatter,
 
934
            formatter_kwargs=dict(levels=0),
 
935
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
1152
936
 
1153
937
    def test_line_merge_revs_log_with_merges(self):
1154
 
        wt = self.make_branch_and_memory_tree('.')
1155
 
        wt.lock_write()
1156
 
        self.addCleanup(wt.unlock)
1157
 
        wt.add('')
1158
 
        wt.commit('rev-1', rev_id='rev-1',
1159
 
                  timestamp=1132586655, timezone=36000,
1160
 
                  committer='Joe Foo <joe@foo.com>')
1161
 
        wt.commit('rev-merged', rev_id='rev-2a',
1162
 
                  timestamp=1132586700, timezone=36000,
1163
 
                  committer='Joe Foo <joe@foo.com>')
1164
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
1165
 
        wt.branch.set_last_revision_info(1, 'rev-1')
1166
 
        wt.commit('rev-2', rev_id='rev-2b',
1167
 
                  timestamp=1132586800, timezone=36000,
1168
 
                  committer='Joe Foo <joe@foo.com>')
1169
 
        logfile = self.make_utf8_encoded_stringio()
1170
 
        formatter = log.LineLogFormatter(to_file=logfile, levels=0)
1171
 
        log.show_log(wt.branch, formatter)
1172
 
        self.assertEqualDiff("""\
 
938
        wt = self._prepare_tree_with_merges()
 
939
        self.assertFormatterResult("""\
1173
940
2: Joe Foo 2005-11-22 [merge] rev-2
1174
941
  1.1.1: Joe Foo 2005-11-22 rev-merged
1175
942
1: Joe Foo 2005-11-22 rev-1
1176
943
""",
1177
 
                             logfile.getvalue())
1178
 
 
1179
 
class TestGetViewRevisions(tests.TestCaseWithTransport):
1180
 
 
1181
 
    def make_tree_with_commits(self):
1182
 
        """Create a tree with well-known revision ids"""
1183
 
        wt = self.make_branch_and_tree('tree1')
1184
 
        wt.commit('commit one', rev_id='1')
1185
 
        wt.commit('commit two', rev_id='2')
1186
 
        wt.commit('commit three', rev_id='3')
1187
 
        mainline_revs = [None, '1', '2', '3']
1188
 
        rev_nos = {'1': 1, '2': 2, '3': 3}
1189
 
        return mainline_revs, rev_nos, wt
1190
 
 
1191
 
    def make_tree_with_merges(self):
1192
 
        """Create a tree with well-known revision ids and a merge"""
1193
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1194
 
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
1195
 
        tree2.commit('four-a', rev_id='4a')
1196
 
        wt.merge_from_branch(tree2.branch)
1197
 
        wt.commit('four-b', rev_id='4b')
1198
 
        mainline_revs.append('4b')
1199
 
        rev_nos['4b'] = 4
1200
 
        # 4a: 3.1.1
1201
 
        return mainline_revs, rev_nos, wt
1202
 
 
1203
 
    def make_branch_with_many_merges(self):
1204
 
        """Create a tree with well-known revision ids"""
1205
 
        builder = self.make_branch_builder('tree1')
1206
 
        builder.start_series()
1207
 
        builder.build_snapshot('1', None, [
1208
 
            ('add', ('', 'TREE_ROOT', 'directory', '')),
1209
 
            ('add', ('f', 'f-id', 'file', '1\n'))])
1210
 
        builder.build_snapshot('2', ['1'], [])
1211
 
        builder.build_snapshot('3a', ['2'], [
1212
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
1213
 
        builder.build_snapshot('3b', ['2', '3a'], [
1214
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
1215
 
        builder.build_snapshot('3c', ['2', '3b'], [
1216
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
1217
 
        builder.build_snapshot('4a', ['3b'], [])
1218
 
        builder.build_snapshot('4b', ['3c', '4a'], [])
1219
 
        builder.finish_series()
1220
 
 
1221
 
        # 1
1222
 
        # |
1223
 
        # 2-.
1224
 
        # |\ \
1225
 
        # | | 3a
1226
 
        # | |/
1227
 
        # | 3b
1228
 
        # |/|
1229
 
        # 3c4a
1230
 
        # |/
1231
 
        # 4b
1232
 
 
1233
 
        mainline_revs = [None, '1', '2', '3c', '4b']
1234
 
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
1235
 
        full_rev_nos_for_reference = {
1236
 
            '1': '1',
1237
 
            '2': '2',
1238
 
            '3a': '2.1.1', #first commit tree 3
1239
 
            '3b': '2.2.1', # first commit tree 2
1240
 
            '3c': '3', #merges 3b to main
1241
 
            '4a': '2.2.2', # second commit tree 2
1242
 
            '4b': '4', # merges 4a to main
1243
 
            }
1244
 
        return mainline_revs, rev_nos, builder.get_branch()
1245
 
 
1246
 
    def test_get_view_revisions_forward(self):
1247
 
        """Test the get_view_revisions method"""
1248
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1249
 
        wt.lock_read()
1250
 
        self.addCleanup(wt.unlock)
1251
 
        revisions = list(log.get_view_revisions(
1252
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
1253
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
1254
 
                         revisions)
1255
 
        revisions2 = list(log.get_view_revisions(
1256
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1257
 
                include_merges=False))
1258
 
        self.assertEqual(revisions, revisions2)
1259
 
 
1260
 
    def test_get_view_revisions_reverse(self):
1261
 
        """Test the get_view_revisions with reverse"""
1262
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1263
 
        wt.lock_read()
1264
 
        self.addCleanup(wt.unlock)
1265
 
        revisions = list(log.get_view_revisions(
1266
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
1267
 
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
1268
 
                         revisions)
1269
 
        revisions2 = list(log.get_view_revisions(
1270
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1271
 
                include_merges=False))
1272
 
        self.assertEqual(revisions, revisions2)
1273
 
 
1274
 
    def test_get_view_revisions_merge(self):
1275
 
        """Test get_view_revisions when there are merges"""
1276
 
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1277
 
        wt.lock_read()
1278
 
        self.addCleanup(wt.unlock)
1279
 
        revisions = list(log.get_view_revisions(
1280
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
1281
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1282
 
                          ('4b', '4', 0), ('4a', '3.1.1', 1)],
1283
 
                         revisions)
1284
 
        revisions = list(log.get_view_revisions(
1285
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1286
 
                include_merges=False))
1287
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1288
 
                          ('4b', '4', 0)],
1289
 
                         revisions)
1290
 
 
1291
 
    def test_get_view_revisions_merge_reverse(self):
1292
 
        """Test get_view_revisions in reverse when there are merges"""
1293
 
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1294
 
        wt.lock_read()
1295
 
        self.addCleanup(wt.unlock)
1296
 
        revisions = list(log.get_view_revisions(
1297
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
1298
 
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
1299
 
                          ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
1300
 
                         revisions)
1301
 
        revisions = list(log.get_view_revisions(
1302
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1303
 
                include_merges=False))
1304
 
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
1305
 
                          ('1', '1', 0)],
1306
 
                         revisions)
1307
 
 
1308
 
    def test_get_view_revisions_merge2(self):
1309
 
        """Test get_view_revisions when there are merges"""
1310
 
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1311
 
        b.lock_read()
1312
 
        self.addCleanup(b.unlock)
1313
 
        revisions = list(log.get_view_revisions(
1314
 
                mainline_revs, rev_nos, b, 'forward'))
1315
 
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1316
 
                    ('3b', '2.2.1', 1), ('3a', '2.1.1', 2), ('4b', '4', 0),
1317
 
                    ('4a', '2.2.2', 1)]
1318
 
        self.assertEqual(expected, revisions)
1319
 
        revisions = list(log.get_view_revisions(
1320
 
                mainline_revs, rev_nos, b, 'forward',
1321
 
                include_merges=False))
1322
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1323
 
                          ('4b', '4', 0)],
1324
 
                         revisions)
1325
 
 
1326
 
 
1327
 
    def test_file_id_for_range(self):
1328
 
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1329
 
        b.lock_read()
1330
 
        self.addCleanup(b.unlock)
1331
 
 
1332
 
        def rev_from_rev_id(revid, branch):
1333
 
            revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
1334
 
            return revspec.in_history(branch)
1335
 
 
1336
 
        def view_revs(start_rev, end_rev, file_id, direction):
1337
 
            revs = log.calculate_view_revisions(
1338
 
                b,
1339
 
                start_rev, # start_revision
1340
 
                end_rev, # end_revision
1341
 
                direction, # direction
1342
 
                file_id, # specific_fileid
1343
 
                True, # generate_merge_revisions
1344
 
                )
1345
 
            return revs
1346
 
 
1347
 
        rev_3a = rev_from_rev_id('3a', b)
1348
 
        rev_4b = rev_from_rev_id('4b', b)
1349
 
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1), ('3a', '2.1.1', 2)],
1350
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
1351
 
        # Note: 3c still appears before 3a here because of depth-based sorting
1352
 
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1), ('3a', '2.1.1', 2)],
1353
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
1354
 
 
1355
 
 
1356
 
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
1357
 
 
1358
 
    def create_tree_with_single_merge(self):
1359
 
        """Create a branch with a moderate layout.
1360
 
 
1361
 
        The revision graph looks like:
1362
 
 
1363
 
           A
1364
 
           |\
1365
 
           B C
1366
 
           |/
1367
 
           D
1368
 
 
1369
 
        In this graph, A introduced files f1 and f2 and f3.
1370
 
        B modifies f1 and f3, and C modifies f2 and f3.
1371
 
        D merges the changes from B and C and resolves the conflict for f3.
1372
 
        """
1373
 
        # TODO: jam 20070218 This seems like it could really be done
1374
 
        #       with make_branch_and_memory_tree() if we could just
1375
 
        #       create the content of those files.
1376
 
        # TODO: jam 20070218 Another alternative is that we would really
1377
 
        #       like to only create this tree 1 time for all tests that
1378
 
        #       use it. Since 'log' only uses the tree in a readonly
1379
 
        #       fashion, it seems a shame to regenerate an identical
1380
 
        #       tree for each test.
1381
 
        tree = self.make_branch_and_tree('tree')
1382
 
        tree.lock_write()
1383
 
        self.addCleanup(tree.unlock)
1384
 
 
1385
 
        self.build_tree_contents([('tree/f1', 'A\n'),
1386
 
                                  ('tree/f2', 'A\n'),
1387
 
                                  ('tree/f3', 'A\n'),
1388
 
                                 ])
1389
 
        tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
1390
 
        tree.commit('A', rev_id='A')
1391
 
 
1392
 
        self.build_tree_contents([('tree/f2', 'A\nC\n'),
1393
 
                                  ('tree/f3', 'A\nC\n'),
1394
 
                                 ])
1395
 
        tree.commit('C', rev_id='C')
1396
 
        # Revert back to A to build the other history.
1397
 
        tree.set_last_revision('A')
1398
 
        tree.branch.set_last_revision_info(1, 'A')
1399
 
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
1400
 
                                  ('tree/f2', 'A\n'),
1401
 
                                  ('tree/f3', 'A\nB\n'),
1402
 
                                 ])
1403
 
        tree.commit('B', rev_id='B')
1404
 
        tree.set_parent_ids(['B', 'C'])
1405
 
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
1406
 
                                  ('tree/f2', 'A\nC\n'),
1407
 
                                  ('tree/f3', 'A\nB\nC\n'),
1408
 
                                 ])
1409
 
        tree.commit('D', rev_id='D')
1410
 
 
1411
 
        # Switch to a read lock for this tree.
1412
 
        # We still have an addCleanup(tree.unlock) pending
1413
 
        tree.unlock()
1414
 
        tree.lock_read()
1415
 
        return tree
1416
 
 
1417
 
    def check_delta(self, delta, **kw):
1418
 
        """Check the filenames touched by a delta are as expected.
1419
 
 
1420
 
        Caller only have to pass in the list of files for each part, all
1421
 
        unspecified parts are considered empty (and checked as such).
1422
 
        """
1423
 
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
1424
 
            # By default we expect an empty list
1425
 
            expected = kw.get(n, [])
1426
 
            # strip out only the path components
1427
 
            got = [x[0] for x in getattr(delta, n)]
1428
 
            self.assertEqual(expected, got)
1429
 
 
1430
 
    def test_tree_with_single_merge(self):
1431
 
        """Make sure the tree layout is correct."""
1432
 
        tree = self.create_tree_with_single_merge()
1433
 
        rev_A_tree = tree.branch.repository.revision_tree('A')
1434
 
        rev_B_tree = tree.branch.repository.revision_tree('B')
1435
 
        rev_C_tree = tree.branch.repository.revision_tree('C')
1436
 
        rev_D_tree = tree.branch.repository.revision_tree('D')
1437
 
 
1438
 
        self.check_delta(rev_B_tree.changes_from(rev_A_tree),
1439
 
                         modified=['f1', 'f3'])
1440
 
 
1441
 
        self.check_delta(rev_C_tree.changes_from(rev_A_tree),
1442
 
                         modified=['f2', 'f3'])
1443
 
 
1444
 
        self.check_delta(rev_D_tree.changes_from(rev_B_tree),
1445
 
                         modified=['f2', 'f3'])
1446
 
 
1447
 
        self.check_delta(rev_D_tree.changes_from(rev_C_tree),
1448
 
                         modified=['f1', 'f3'])
1449
 
 
1450
 
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
1451
 
        """Ensure _filter_revisions_touching_file_id returns the right values.
1452
 
 
1453
 
        Get the return value from _filter_revisions_touching_file_id and make
1454
 
        sure they are correct.
1455
 
        """
1456
 
        # The api for _filter_revisions_touching_file_id is a little crazy.
1457
 
        # So we do the setup here.
1458
 
        mainline = tree.branch.revision_history()
1459
 
        mainline.insert(0, None)
1460
 
        revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
1461
 
        view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
1462
 
                                                'reverse', True)
1463
 
        actual_revs = log._filter_revisions_touching_file_id(
1464
 
                            tree.branch,
1465
 
                            file_id,
1466
 
                            list(view_revs_iter))
1467
 
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
1468
 
 
1469
 
    def test_file_id_f1(self):
1470
 
        tree = self.create_tree_with_single_merge()
1471
 
        # f1 should be marked as modified by revisions A and B
1472
 
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
1473
 
 
1474
 
    def test_file_id_f2(self):
1475
 
        tree = self.create_tree_with_single_merge()
1476
 
        # f2 should be marked as modified by revisions A, C, and D
1477
 
        # because D merged the changes from C.
1478
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1479
 
 
1480
 
    def test_file_id_f3(self):
1481
 
        tree = self.create_tree_with_single_merge()
1482
 
        # f3 should be marked as modified by revisions A, B, C, and D
1483
 
        self.assertAllRevisionsForFileID(tree, 'f3-id', ['D', 'C', 'B', 'A'])
1484
 
 
1485
 
    def test_file_id_with_ghosts(self):
1486
 
        # This is testing bug #209948, where having a ghost would cause
1487
 
        # _filter_revisions_touching_file_id() to fail.
1488
 
        tree = self.create_tree_with_single_merge()
1489
 
        # We need to add a revision, so switch back to a write-locked tree
1490
 
        # (still a single addCleanup(tree.unlock) pending).
1491
 
        tree.unlock()
1492
 
        tree.lock_write()
1493
 
        first_parent = tree.last_revision()
1494
 
        tree.set_parent_ids([first_parent, 'ghost-revision-id'])
1495
 
        self.build_tree_contents([('tree/f1', 'A\nB\nXX\n')])
1496
 
        tree.commit('commit with a ghost', rev_id='XX')
1497
 
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['XX', 'B', 'A'])
1498
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1499
 
 
1500
 
    def test_unknown_file_id(self):
1501
 
        tree = self.create_tree_with_single_merge()
1502
 
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
1503
 
 
1504
 
    def test_empty_branch_unknown_file_id(self):
1505
 
        tree = self.make_branch_and_tree('tree')
1506
 
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
 
944
            wt.branch, log.LineLogFormatter,
 
945
            formatter_kwargs=dict(levels=0))
 
946
 
 
947
 
 
948
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
 
949
 
 
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>
 
954
 
 
955
\tadd a
 
956
 
 
957
''',
 
958
            wt.branch, log.GnuChangelogLogFormatter)
 
959
 
 
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>
 
966
 
 
967
\tadd a
 
968
 
 
969
''',
 
970
            wt.branch, log.GnuChangelogLogFormatter)
 
971
 
 
972
    def test_verbose(self):
 
973
        wt = self.make_standard_commit('nicky')
 
974
        self.assertFormatterResult('''\
 
975
2005-11-22  John Doe  <jdoe@example.com>
 
976
 
 
977
\t* a:
 
978
 
 
979
\tadd a
 
980
 
 
981
''',
 
982
            wt.branch, log.GnuChangelogLogFormatter,
 
983
            show_log_kwargs=dict(verbose=True))
1507
984
 
1508
985
 
1509
986
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1521
998
 
1522
999
class TestLogFormatter(tests.TestCase):
1523
1000
 
 
1001
    def setUp(self):
 
1002
        super(TestLogFormatter, self).setUp()
 
1003
        self.rev = revision.Revision('a-id')
 
1004
        self.lf = log.LogFormatter(None)
 
1005
 
1524
1006
    def test_short_committer(self):
1525
 
        rev = revision.Revision('a-id')
1526
 
        rev.committer = 'John Doe <jdoe@example.com>'
1527
 
        lf = log.LogFormatter(None)
1528
 
        self.assertEqual('John Doe', lf.short_committer(rev))
1529
 
        rev.committer = 'John Smith <jsmith@example.com>'
1530
 
        self.assertEqual('John Smith', lf.short_committer(rev))
1531
 
        rev.committer = 'John Smith'
1532
 
        self.assertEqual('John Smith', lf.short_committer(rev))
1533
 
        rev.committer = 'jsmith@example.com'
1534
 
        self.assertEqual('jsmith@example.com', lf.short_committer(rev))
1535
 
        rev.committer = '<jsmith@example.com>'
1536
 
        self.assertEqual('jsmith@example.com', lf.short_committer(rev))
1537
 
        rev.committer = 'John Smith jsmith@example.com'
1538
 
        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))
 
1010
 
 
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')
1539
1017
 
1540
1018
    def test_short_author(self):
1541
 
        rev = revision.Revision('a-id')
1542
 
        rev.committer = 'John Doe <jdoe@example.com>'
1543
 
        lf = log.LogFormatter(None)
1544
 
        self.assertEqual('John Doe', lf.short_author(rev))
1545
 
        rev.properties['author'] = 'John Smith <jsmith@example.com>'
1546
 
        self.assertEqual('John Smith', lf.short_author(rev))
1547
 
        rev.properties['author'] = 'John Smith'
1548
 
        self.assertEqual('John Smith', lf.short_author(rev))
1549
 
        rev.properties['author'] = 'jsmith@example.com'
1550
 
        self.assertEqual('jsmith@example.com', lf.short_author(rev))
1551
 
        rev.properties['author'] = '<jsmith@example.com>'
1552
 
        self.assertEqual('jsmith@example.com', lf.short_author(rev))
1553
 
        rev.properties['author'] = 'John Smith jsmith@example.com'
1554
 
        self.assertEqual('John Smith', lf.short_author(rev))
1555
 
        del rev.properties['author']
1556
 
        rev.properties['authors'] = ('John Smith <jsmith@example.com>\n'
1557
 
                'Jane Rey <jrey@example.com>')
1558
 
        self.assertEqual('John Smith', lf.short_author(rev))
 
1019
        def assertAuthor(expected, author):
 
1020
            self.rev.properties['author'] = author
 
1021
            self.assertEqual(expected, self.lf.short_author(self.rev))
 
1022
 
 
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')
 
1028
 
 
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))
 
1032
 
 
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))
1559
1037
 
1560
1038
 
1561
1039
class TestReverseByDepth(tests.TestCase):
1707
1185
        log.show_branch_change(tree.branch, s, 3, '3b')
1708
1186
        self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1709
1187
        self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
 
1188
 
 
1189
 
 
1190
class TestRevisionNotInBranch(TestCaseForLogFormatter):
 
1191
 
 
1192
    def setup_a_tree(self):
 
1193
        tree = self.make_branch_and_tree('tree')
 
1194
        tree.lock_write()
 
1195
        self.addCleanup(tree.unlock)
 
1196
        kwargs = {
 
1197
            'committer': 'Joe Foo <joe@foo.com>',
 
1198
            'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
 
1199
            'timezone': 0, # UTC
 
1200
        }
 
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)
 
1204
        return tree
 
1205
 
 
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')
 
1210
        kwargs = {
 
1211
            'committer': 'Joe Foo <joe@foo.com>',
 
1212
            'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
 
1213
            'timezone': 0, # UTC
 
1214
        }
 
1215
        tree.commit('commit 2b', rev_id='2b', **kwargs)
 
1216
        tree.commit('commit 3b', rev_id='3b', **kwargs)
 
1217
        return tree
 
1218
 
 
1219
    def test_one_revision(self):
 
1220
        tree = self.setup_ab_tree()
 
1221
        lf = LogCatcher()
 
1222
        rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1223
        log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
 
1224
                     end_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)
 
1228
 
 
1229
    def test_many_revisions(self):
 
1230
        tree = self.setup_ab_tree()
 
1231
        lf = LogCatcher()
 
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
 
1242
 
 
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
------------------------------------------------------------
 
1249
revision-id: 3a
 
1250
committer: Joe Foo <joe@foo.com>
 
1251
branch nick: tree
 
1252
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1253
message:
 
1254
  commit 3a
 
1255
------------------------------------------------------------
 
1256
revision-id: 2a
 
1257
committer: Joe Foo <joe@foo.com>
 
1258
branch nick: tree
 
1259
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1260
message:
 
1261
  commit 2a
 
1262
------------------------------------------------------------
 
1263
revno: 1
 
1264
committer: Joe Foo <joe@foo.com>
 
1265
branch nick: tree
 
1266
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1267
message:
 
1268
  commit 1a
 
1269
""",
 
1270
            tree.branch, log.LongLogFormatter, show_log_kwargs={
 
1271
                'start_revision': start_rev, 'end_revision': end_rev
 
1272
            })
 
1273
 
 
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("""\
 
1279
      Joe Foo\t2005-11-22
 
1280
      revision-id:3a
 
1281
      commit 3a
 
1282
 
 
1283
      Joe Foo\t2005-11-22
 
1284
      revision-id:2a
 
1285
      commit 2a
 
1286
 
 
1287
    1 Joe Foo\t2005-11-22
 
1288
      commit 1a
 
1289
 
 
1290
""",
 
1291
            tree.branch, log.ShortLogFormatter, show_log_kwargs={
 
1292
                'start_revision': start_rev, 'end_revision': end_rev
 
1293
            })
 
1294
 
 
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
 
1303
""",
 
1304
            tree.branch, log.LineLogFormatter, show_log_kwargs={
 
1305
                'start_revision': start_rev, 'end_revision': end_rev
 
1306
            })
 
1307
 
 
1308
 
 
1309
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
 
1310
 
 
1311
    def setUp(self):
 
1312
        TestCaseForLogFormatter.setUp(self)
 
1313
        log.properties_handler_registry.register(
 
1314
            'bugs_properties_handler',
 
1315
            log._bugs_properties_handler)
 
1316
 
 
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'])
 
1321
        tree.add('a')
 
1322
        self.wt_commit(tree, 'simple log message', rev_id='a1',
 
1323
                       revprops={'bugs': 'test://bug/id fixed'})
 
1324
        tree.add('b')
 
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'})
 
1329
        return tree
 
1330
 
 
1331
 
 
1332
    def test_long_bugs(self):
 
1333
        tree = self.make_commits_with_bugs()
 
1334
        self.assertFormatterResult("""\
 
1335
------------------------------------------------------------
 
1336
revno: 2
 
1337
fixes bugs: test://bug/id test://bug/2
 
1338
author: Joe Bar <joe@bar.com>
 
1339
committer: Joe Foo <joe@foo.com>
 
1340
branch nick: work
 
1341
timestamp: Tue 2005-11-22 00:00:01 +0000
 
1342
message:
 
1343
  multiline
 
1344
  log
 
1345
  message
 
1346
------------------------------------------------------------
 
1347
revno: 1
 
1348
fixes bug: test://bug/id
 
1349
committer: Joe Foo <joe@foo.com>
 
1350
branch nick: work
 
1351
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1352
message:
 
1353
  simple log message
 
1354
""",
 
1355
            tree.branch, log.LongLogFormatter)
 
1356
 
 
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
 
1362
      multiline
 
1363
      log
 
1364
      message
 
1365
 
 
1366
    1 Joe Foo\t2005-11-22
 
1367
      fixes bug: test://bug/id
 
1368
      simple log message
 
1369
 
 
1370
""",
 
1371
            tree.branch, log.ShortLogFormatter)
 
1372
 
 
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
 
1380
      simple log message
 
1381
 
 
1382
""",
 
1383
            tree.branch, log.ShortLogFormatter)
 
1384
 
 
1385
    def test_bugs_handler_present(self):
 
1386
        self.properties_handler_registry.get('bugs_properties_handler')
 
1387
 
 
1388
 
 
1389
class TestLogForAuthors(TestCaseForLogFormatter):
 
1390
 
 
1391
    def setUp(self):
 
1392
        TestCaseForLogFormatter.setUp(self)
 
1393
        self.wt = self.make_standard_commit('nicky',
 
1394
            authors=['John Doe <jdoe@example.com>',
 
1395
                     'Jane Rey <jrey@example.com>'])
 
1396
 
 
1397
    def assertFormatterResult(self, formatter, who, result):
 
1398
        formatter_kwargs = dict()
 
1399
        if who is not None:
 
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)
 
1404
 
 
1405
    def test_line_default(self):
 
1406
        self.assertFormatterResult(log.LineLogFormatter, None, """\
 
1407
1: John Doe 2005-11-22 add a
 
1408
""")
 
1409
 
 
1410
    def test_line_committer(self):
 
1411
        self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
 
1412
1: Lorem Ipsum 2005-11-22 add a
 
1413
""")
 
1414
 
 
1415
    def test_line_first(self):
 
1416
        self.assertFormatterResult(log.LineLogFormatter, 'first', """\
 
1417
1: John Doe 2005-11-22 add a
 
1418
""")
 
1419
 
 
1420
    def test_line_all(self):
 
1421
        self.assertFormatterResult(log.LineLogFormatter, 'all', """\
 
1422
1: John Doe, Jane Rey 2005-11-22 add a
 
1423
""")
 
1424
 
 
1425
 
 
1426
    def test_short_default(self):
 
1427
        self.assertFormatterResult(log.ShortLogFormatter, None, """\
 
1428
    1 John Doe\t2005-11-22
 
1429
      add a
 
1430
 
 
1431
""")
 
1432
 
 
1433
    def test_short_committer(self):
 
1434
        self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
 
1435
    1 Lorem Ipsum\t2005-11-22
 
1436
      add a
 
1437
 
 
1438
""")
 
1439
 
 
1440
    def test_short_first(self):
 
1441
        self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
 
1442
    1 John Doe\t2005-11-22
 
1443
      add a
 
1444
 
 
1445
""")
 
1446
 
 
1447
    def test_short_all(self):
 
1448
        self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
 
1449
    1 John Doe, Jane Rey\t2005-11-22
 
1450
      add a
 
1451
 
 
1452
""")
 
1453
 
 
1454
    def test_long_default(self):
 
1455
        self.assertFormatterResult(log.LongLogFormatter, None, """\
 
1456
------------------------------------------------------------
 
1457
revno: 1
 
1458
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
1459
committer: Lorem Ipsum <test@example.com>
 
1460
branch nick: nicky
 
1461
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1462
message:
 
1463
  add a
 
1464
""")
 
1465
 
 
1466
    def test_long_committer(self):
 
1467
        self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
 
1468
------------------------------------------------------------
 
1469
revno: 1
 
1470
committer: Lorem Ipsum <test@example.com>
 
1471
branch nick: nicky
 
1472
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1473
message:
 
1474
  add a
 
1475
""")
 
1476
 
 
1477
    def test_long_first(self):
 
1478
        self.assertFormatterResult(log.LongLogFormatter, 'first', """\
 
1479
------------------------------------------------------------
 
1480
revno: 1
 
1481
author: John Doe <jdoe@example.com>
 
1482
committer: Lorem Ipsum <test@example.com>
 
1483
branch nick: nicky
 
1484
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1485
message:
 
1486
  add a
 
1487
""")
 
1488
 
 
1489
    def test_long_all(self):
 
1490
        self.assertFormatterResult(log.LongLogFormatter, 'all', """\
 
1491
------------------------------------------------------------
 
1492
revno: 1
 
1493
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
1494
committer: Lorem Ipsum <test@example.com>
 
1495
branch nick: nicky
 
1496
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1497
message:
 
1498
  add a
 
1499
""")
 
1500
 
 
1501
    def test_gnu_changelog_default(self):
 
1502
        self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
 
1503
2005-11-22  John Doe  <jdoe@example.com>
 
1504
 
 
1505
\tadd a
 
1506
 
 
1507
""")
 
1508
 
 
1509
    def test_gnu_changelog_committer(self):
 
1510
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
 
1511
2005-11-22  Lorem Ipsum  <test@example.com>
 
1512
 
 
1513
\tadd a
 
1514
 
 
1515
""")
 
1516
 
 
1517
    def test_gnu_changelog_first(self):
 
1518
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
 
1519
2005-11-22  John Doe  <jdoe@example.com>
 
1520
 
 
1521
\tadd a
 
1522
 
 
1523
""")
 
1524
 
 
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>
 
1528
 
 
1529
\tadd a
 
1530
 
 
1531
""")
 
1532
 
 
1533
 
 
1534
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
 
1535
 
 
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))
 
1544
        # 1
 
1545
        # |\
 
1546
        # 2 \
 
1547
        # |  |
 
1548
        # |  1.1.1
 
1549
        # |  | \
 
1550
        # |  |  1.2.1
 
1551
        # |  | /
 
1552
        # |  1.1.2
 
1553
        # | /
 
1554
        # 3
 
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()
 
1565
        br.lock_read()
 
1566
        self.addCleanup(br.unlock)
 
1567
        return br
 
1568
 
 
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...
 
1573
        # -- vila 20100413
 
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])
 
1580
 
 
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
 
1587
        # after 1.1.1
 
1588
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
 
1589
                             b, '1.1.1', '3', exclude_common_ancestry=True)
 
1590
 
 
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)
 
1599
 
 
1600
 
 
1601
class TestLogDefaults(TestCaseForLogFormatter):
 
1602
    def test_default_log_level(self):
 
1603
        """
 
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
 
1606
        detail.
 
1607
        Fixes bug #747958.
 
1608
        """
 
1609
        wt = self._prepare_tree_with_merges()
 
1610
        b = wt.branch
 
1611
 
 
1612
        class CustomLogFormatter(log.LogFormatter):
 
1613
            def __init__(self, *args, **kwargs):
 
1614
                super(CustomLogFormatter, self).__init__(*args, **kwargs)
 
1615
                self.revisions = []
 
1616
            def get_levels(self):
 
1617
                # log formatter supports all levels:
 
1618
                return 0
 
1619
            def log_revision(self, revision):
 
1620
                self.revisions.append(revision)
 
1621
 
 
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)
 
1630
 
 
1631
        del log_formatter
 
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)
 
1638