/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_log.py

  • Committer: Jelmer Vernooij
  • Date: 2018-11-16 23:19:12 UTC
  • mfrom: (7180 work)
  • mto: This revision was merged to the branch mainline in revision 7294.
  • Revision ID: jelmer@jelmer.uk-20181116231912-e043vpq22bdkxa6q
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2013, 2016 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
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
 
from cStringIO import StringIO
19
18
 
20
 
from bzrlib import (
 
19
from .. import (
21
20
    branchbuilder,
22
21
    errors,
23
22
    log,
24
23
    registry,
25
24
    revision,
26
25
    revisionspec,
27
 
    symbol_versioning,
28
26
    tests,
 
27
    gpg,
 
28
    trace,
 
29
    )
 
30
from ..sixish import (
 
31
    BytesIO,
 
32
    StringIO,
 
33
    unichr,
29
34
    )
30
35
 
31
36
 
38
43
        stamp is incremented at each commit.
39
44
        """
40
45
        if getattr(self, 'timestamp', None) is None:
41
 
            self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
 
46
            self.timestamp = 1132617600  # Mon 2005-11-22 00:00:00 +0000
42
47
        else:
43
 
            self.timestamp += 1 # 1 second between each commit
 
48
            self.timestamp += 1  # 1 second between each commit
44
49
        kwargs.setdefault('timestamp', self.timestamp)
45
 
        kwargs.setdefault('timezone', 0) # UTC
 
50
        kwargs.setdefault('timezone', 0)  # UTC
46
51
        kwargs.setdefault('committer', 'Joe Foo <joe@foo.com>')
47
52
 
48
53
        return wt.commit(message, **kwargs)
88
93
        """Helper method for LogFormatter tests"""
89
94
        b = wt.branch
90
95
        b.nick = 'test'
91
 
        self.build_tree_contents([('a', 'hello moto\n')])
92
 
        self.wt_commit(wt, 'simple log message', rev_id='a1')
93
 
        self.build_tree_contents([('b', 'goodbye\n')])
 
96
        self.build_tree_contents([('a', b'hello moto\n')])
 
97
        self.wt_commit(wt, 'simple log message', rev_id=b'a1')
 
98
        self.build_tree_contents([('b', b'goodbye\n')])
94
99
        wt.add('b')
95
 
        self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id='a2')
 
100
        self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id=b'a2')
96
101
 
97
 
        self.build_tree_contents([('c', 'just another manic monday\n')])
 
102
        self.build_tree_contents([('c', b'just another manic monday\n')])
98
103
        wt.add('c')
99
 
        self.wt_commit(wt, 'single line with trailing newline\n', rev_id='a3')
 
104
        self.wt_commit(wt, 'single line with trailing newline\n', rev_id=b'a3')
100
105
        return b
101
106
 
102
107
    def _prepare_tree_with_merges(self, with_tags=False):
104
109
        wt.lock_write()
105
110
        self.addCleanup(wt.unlock)
106
111
        wt.add('')
107
 
        self.wt_commit(wt, 'rev-1', rev_id='rev-1')
108
 
        self.wt_commit(wt, 'rev-merged', rev_id='rev-2a')
109
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
110
 
        wt.branch.set_last_revision_info(1, 'rev-1')
111
 
        self.wt_commit(wt, 'rev-2', rev_id='rev-2b')
 
112
        self.wt_commit(wt, 'rev-1', rev_id=b'rev-1')
 
113
        self.wt_commit(wt, 'rev-merged', rev_id=b'rev-2a')
 
114
        wt.set_parent_ids([b'rev-1', b'rev-2a'])
 
115
        wt.branch.set_last_revision_info(1, b'rev-1')
 
116
        self.wt_commit(wt, 'rev-2', rev_id=b'rev-2b')
112
117
        if with_tags:
113
118
            branch = wt.branch
114
 
            branch.tags.set_tag('v0.2', 'rev-2b')
115
 
            self.wt_commit(wt, 'rev-3', rev_id='rev-3')
116
 
            branch.tags.set_tag('v1.0rc1', 'rev-3')
117
 
            branch.tags.set_tag('v1.0', 'rev-3')
 
119
            branch.tags.set_tag('v0.2', b'rev-2b')
 
120
            self.wt_commit(wt, 'rev-3', rev_id=b'rev-3')
 
121
            branch.tags.set_tag('v1.0rc1', b'rev-3')
 
122
            branch.tags.set_tag('v1.0', b'rev-3')
118
123
        return wt
119
124
 
 
125
 
120
126
class LogCatcher(log.LogFormatter):
121
127
    """Pull log messages into a list rather than displaying them.
122
128
 
173
179
        self.assertInvalidRevisonNumber(b, 2, 1)
174
180
        self.assertInvalidRevisonNumber(b, 1, 2)
175
181
        self.assertInvalidRevisonNumber(b, 0, 2)
176
 
        self.assertInvalidRevisonNumber(b, 1, 0)
177
182
        self.assertInvalidRevisonNumber(b, -1, 1)
178
183
        self.assertInvalidRevisonNumber(b, 1, -1)
 
184
        self.assertInvalidRevisonNumber(b, 1, 0)
179
185
 
180
186
    def test_empty_branch(self):
181
187
        wt = self.make_branch_and_tree('.')
216
222
 
217
223
    def test_commit_message_with_control_chars(self):
218
224
        wt = self.make_branch_and_tree('.')
219
 
        msg = u"All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
 
225
        msg = u"All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
220
226
        msg = msg.replace(u'\r', u'\n')
221
227
        wt.commit(msg)
222
228
        lf = LogCatcher()
250
256
        wt.commit(message='add file1 and file2')
251
257
        self.run_bzr('branch parent child')
252
258
        os.unlink('child/file1')
253
 
        file('child/file2', 'wb').write('hello\n')
 
259
        with open('child/file2', 'wb') as f:
 
260
            f.write(b'hello\n')
254
261
        self.run_bzr(['commit', '-m', 'remove file1 and modify file2',
255
 
            'child'])
 
262
                      'child'])
256
263
        os.chdir('parent')
257
264
        self.run_bzr('merge ../child')
258
265
        wt.commit('merge child branch')
280
287
        self.assertEqual('add file1 and file2', logentry.rev.message)
281
288
        self.checkDelta(logentry.delta, added=['file1', 'file2'])
282
289
 
 
290
    def test_bug_842695_log_restricted_to_dir(self):
 
291
        # Comments here indicate revision numbers in trunk  # VVVVV
 
292
        trunk = self.make_branch_and_tree('this')
 
293
        trunk.commit('initial trunk')                       # 1
 
294
        adder = trunk.controldir.sprout('adder').open_workingtree()
 
295
        merger = trunk.controldir.sprout('merger').open_workingtree()
 
296
        self.build_tree_contents([
 
297
            ('adder/dir/',),
 
298
            ('adder/dir/file', b'foo'),
 
299
            ])
 
300
        adder.add(['dir', 'dir/file'])
 
301
        adder.commit('added dir')                           # 1.1.1
 
302
        trunk.merge_from_branch(adder.branch)
 
303
        trunk.commit('merged adder into trunk')             # 2
 
304
        merger.merge_from_branch(trunk.branch)
 
305
        merger.commit('merged trunk into merger')           # 1.2.1
 
306
        # Commits are processed in increments of 200 revisions, so
 
307
        # make sure the two merges into trunk are in different chunks.
 
308
        for i in range(200):
 
309
            trunk.commit('intermediate commit %d' % i)      # 3-202
 
310
        trunk.merge_from_branch(merger.branch)
 
311
        trunk.commit('merged merger into trunk')            # 203
 
312
        file_id = trunk.path2id('dir')
 
313
        lf = LogCatcher()
 
314
        lf.supports_merge_revisions = True
 
315
        log.show_log(trunk.branch, lf, file_id)
 
316
        try:
 
317
            self.assertEqual(['2', '1.1.1'], [r.revno for r in lf.revisions])
 
318
        except AssertionError:
 
319
            raise tests.KnownFailure("bug #842695")
 
320
 
 
321
 
 
322
class TestFormatSignatureValidity(tests.TestCaseWithTransport):
 
323
 
 
324
    def verify_revision_signature(self, revid, gpg_strategy):
 
325
        return (gpg.SIGNATURE_VALID,
 
326
                u'UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>')
 
327
 
 
328
    def test_format_signature_validity_utf(self):
 
329
        """Check that GPG signatures containing UTF-8 names are formatted
 
330
        correctly."""
 
331
        wt = self.make_branch_and_tree('.')
 
332
        revid = wt.commit('empty commit')
 
333
        repo = wt.branch.repository
 
334
        # Monkey patch out checking if this rev is actually signed, since we
 
335
        # can't sign it without a heavier TestCase and LoopbackGPGStrategy
 
336
        # doesn't care anyways.
 
337
        self.overrideAttr(repo, 'verify_revision_signature',
 
338
                          self.verify_revision_signature)
 
339
        out = log.format_signature_validity(revid, wt.branch)
 
340
        self.assertEqual(
 
341
            u'valid signature from UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>',
 
342
            out)
 
343
 
283
344
 
284
345
class TestShortLogFormatter(TestCaseForLogFormatter):
285
346
 
286
347
    def test_trailing_newlines(self):
287
348
        wt = self.make_branch_and_tree('.')
288
349
        b = self.make_commits_with_trailing_newlines(wt)
289
 
        self.assertFormatterResult("""\
 
350
        self.assertFormatterResult(b"""\
290
351
    3 Joe Foo\t2005-11-22
291
352
      single line with trailing newline
292
353
 
299
360
      simple log message
300
361
 
301
362
""",
302
 
            b, log.ShortLogFormatter)
 
363
                                   b, log.ShortLogFormatter)
303
364
 
304
365
    def test_short_log_with_merges(self):
305
366
        wt = self._prepare_tree_with_merges()
306
 
        self.assertFormatterResult("""\
 
367
        self.assertFormatterResult(b"""\
307
368
    2 Joe Foo\t2005-11-22 [merge]
308
369
      rev-2
309
370
 
311
372
      rev-1
312
373
 
313
374
""",
314
 
            wt.branch, log.ShortLogFormatter)
 
375
                                   wt.branch, log.ShortLogFormatter)
315
376
 
316
377
    def test_short_log_with_merges_and_advice(self):
317
378
        wt = self._prepare_tree_with_merges()
318
 
        self.assertFormatterResult("""\
 
379
        self.assertFormatterResult(b"""\
319
380
    2 Joe Foo\t2005-11-22 [merge]
320
381
      rev-2
321
382
 
322
383
    1 Joe Foo\t2005-11-22
323
384
      rev-1
324
385
 
325
 
Use --include-merges or -n0 to see merged revisions.
 
386
Use --include-merged or -n0 to see merged revisions.
326
387
""",
327
 
            wt.branch, log.ShortLogFormatter,
328
 
            formatter_kwargs=dict(show_advice=True))
 
388
                                   wt.branch, log.ShortLogFormatter,
 
389
                                   formatter_kwargs=dict(show_advice=True))
329
390
 
330
391
    def test_short_log_with_merges_and_range(self):
331
392
        wt = self._prepare_tree_with_merges()
332
 
        self.wt_commit(wt, 'rev-3a', rev_id='rev-3a')
333
 
        wt.branch.set_last_revision_info(2, 'rev-2b')
334
 
        wt.set_parent_ids(['rev-2b', 'rev-3a'])
335
 
        self.wt_commit(wt, 'rev-3b', rev_id='rev-3b')
336
 
        self.assertFormatterResult("""\
 
393
        self.wt_commit(wt, 'rev-3a', rev_id=b'rev-3a')
 
394
        wt.branch.set_last_revision_info(2, b'rev-2b')
 
395
        wt.set_parent_ids([b'rev-2b', b'rev-3a'])
 
396
        self.wt_commit(wt, 'rev-3b', rev_id=b'rev-3b')
 
397
        self.assertFormatterResult(b"""\
337
398
    3 Joe Foo\t2005-11-22 [merge]
338
399
      rev-3b
339
400
 
341
402
      rev-2
342
403
 
343
404
""",
344
 
            wt.branch, log.ShortLogFormatter,
345
 
            show_log_kwargs=dict(start_revision=2, end_revision=3))
 
405
                                   wt.branch, log.ShortLogFormatter,
 
406
                                   show_log_kwargs=dict(start_revision=2, end_revision=3))
346
407
 
347
408
    def test_short_log_with_tags(self):
348
409
        wt = self._prepare_tree_with_merges(with_tags=True)
349
 
        self.assertFormatterResult("""\
 
410
        self.assertFormatterResult(b"""\
350
411
    3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
351
412
      rev-3
352
413
 
357
418
      rev-1
358
419
 
359
420
""",
360
 
            wt.branch, log.ShortLogFormatter)
 
421
                                   wt.branch, log.ShortLogFormatter)
361
422
 
362
423
    def test_short_log_single_merge_revision(self):
363
424
        wt = self._prepare_tree_with_merges()
364
425
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
365
426
        rev = revspec.in_history(wt.branch)
366
 
        self.assertFormatterResult("""\
 
427
        self.assertFormatterResult(b"""\
367
428
      1.1.1 Joe Foo\t2005-11-22
368
429
            rev-merged
369
430
 
370
431
""",
371
 
            wt.branch, log.ShortLogFormatter,
372
 
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
432
                                   wt.branch, log.ShortLogFormatter,
 
433
                                   show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
434
 
 
435
    def test_show_ids(self):
 
436
        wt = self.make_branch_and_tree('parent')
 
437
        self.build_tree(['parent/f1', 'parent/f2'])
 
438
        wt.add(['f1', 'f2'])
 
439
        self.wt_commit(wt, 'first post', rev_id=b'a')
 
440
        child_wt = wt.controldir.sprout('child').open_workingtree()
 
441
        self.wt_commit(child_wt, 'branch 1 changes', rev_id=b'b')
 
442
        wt.merge_from_branch(child_wt.branch)
 
443
        self.wt_commit(wt, 'merge branch 1', rev_id=b'c')
 
444
        self.assertFormatterResult(b"""\
 
445
    2 Joe Foo\t2005-11-22 [merge]
 
446
      revision-id:c
 
447
      merge branch 1
 
448
 
 
449
          1.1.1 Joe Foo\t2005-11-22
 
450
                revision-id:b
 
451
                branch 1 changes
 
452
 
 
453
    1 Joe Foo\t2005-11-22
 
454
      revision-id:a
 
455
      first post
 
456
 
 
457
""",
 
458
                                   wt.branch, log.ShortLogFormatter,
 
459
                                   formatter_kwargs=dict(levels=0, show_ids=True))
373
460
 
374
461
 
375
462
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
379
466
        # Note that the 1.1.1 indenting is in fact correct given that
380
467
        # the revision numbers are right justified within 5 characters
381
468
        # for mainline revnos and 9 characters for dotted revnos.
382
 
        self.assertFormatterResult("""\
 
469
        self.assertFormatterResult(b"""\
383
470
    2 Joe Foo\t2005-11-22 [merge]
384
471
      rev-2
385
472
 
390
477
      rev-1
391
478
 
392
479
""",
393
 
            wt.branch, log.ShortLogFormatter,
394
 
            formatter_kwargs=dict(levels=0))
 
480
                                   wt.branch, log.ShortLogFormatter,
 
481
                                   formatter_kwargs=dict(levels=0))
395
482
 
396
483
    def test_short_merge_revs_log_single_merge_revision(self):
397
484
        wt = self._prepare_tree_with_merges()
398
485
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
399
486
        rev = revspec.in_history(wt.branch)
400
 
        self.assertFormatterResult("""\
 
487
        self.assertFormatterResult(b"""\
401
488
      1.1.1 Joe Foo\t2005-11-22
402
489
            rev-merged
403
490
 
404
491
""",
405
 
            wt.branch, log.ShortLogFormatter,
406
 
            formatter_kwargs=dict(levels=0),
407
 
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
492
                                   wt.branch, log.ShortLogFormatter,
 
493
                                   formatter_kwargs=dict(levels=0),
 
494
                                   show_log_kwargs=dict(start_revision=rev, end_revision=rev))
408
495
 
409
496
 
410
497
class TestLongLogFormatter(TestCaseForLogFormatter):
415
502
        bug #4676
416
503
        """
417
504
        wt = self.make_standard_commit('test_verbose_log', authors=[])
418
 
        self.assertFormatterResult('''\
 
505
        self.assertFormatterResult(b'''\
419
506
------------------------------------------------------------
420
507
revno: 1
421
508
committer: Lorem Ipsum <test@example.com>
426
513
added:
427
514
  a
428
515
''',
429
 
            wt.branch, log.LongLogFormatter,
430
 
            show_log_kwargs=dict(verbose=True))
 
516
                                   wt.branch, log.LongLogFormatter,
 
517
                                   show_log_kwargs=dict(verbose=True))
431
518
 
432
519
    def test_merges_are_indented_by_level(self):
433
520
        wt = self.make_branch_and_tree('parent')
434
521
        self.wt_commit(wt, 'first post')
435
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
522
        child_wt = wt.controldir.sprout('child').open_workingtree()
436
523
        self.wt_commit(child_wt, 'branch 1')
437
 
        smallerchild_wt = wt.bzrdir.sprout('smallerchild').open_workingtree()
 
524
        smallerchild_wt = wt.controldir.sprout(
 
525
            'smallerchild').open_workingtree()
438
526
        self.wt_commit(smallerchild_wt, 'branch 2')
439
527
        child_wt.merge_from_branch(smallerchild_wt.branch)
440
528
        self.wt_commit(child_wt, 'merge branch 2')
441
529
        wt.merge_from_branch(child_wt.branch)
442
530
        self.wt_commit(wt, 'merge branch 1')
443
 
        self.assertFormatterResult("""\
 
531
        self.assertFormatterResult(b"""\
444
532
------------------------------------------------------------
445
533
revno: 2 [merge]
446
534
committer: Joe Foo <joe@foo.com>
477
565
message:
478
566
  first post
479
567
""",
480
 
            wt.branch, log.LongLogFormatter,
481
 
            formatter_kwargs=dict(levels=0),
482
 
            show_log_kwargs=dict(verbose=True))
 
568
                                   wt.branch, log.LongLogFormatter,
 
569
                                   formatter_kwargs=dict(levels=0),
 
570
                                   show_log_kwargs=dict(verbose=True))
483
571
 
484
572
    def test_verbose_merge_revisions_contain_deltas(self):
485
573
        wt = self.make_branch_and_tree('parent')
486
574
        self.build_tree(['parent/f1', 'parent/f2'])
487
 
        wt.add(['f1','f2'])
 
575
        wt.add(['f1', 'f2'])
488
576
        self.wt_commit(wt, 'first post')
489
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
577
        child_wt = wt.controldir.sprout('child').open_workingtree()
490
578
        os.unlink('child/f1')
491
 
        self.build_tree_contents([('child/f2', 'hello\n')])
 
579
        self.build_tree_contents([('child/f2', b'hello\n')])
492
580
        self.wt_commit(child_wt, 'removed f1 and modified f2')
493
581
        wt.merge_from_branch(child_wt.branch)
494
582
        self.wt_commit(wt, 'merge branch 1')
495
 
        self.assertFormatterResult("""\
 
583
        self.assertFormatterResult(b"""\
496
584
------------------------------------------------------------
497
585
revno: 2 [merge]
498
586
committer: Joe Foo <joe@foo.com>
526
614
  f1
527
615
  f2
528
616
""",
529
 
            wt.branch, log.LongLogFormatter,
530
 
            formatter_kwargs=dict(levels=0),
531
 
            show_log_kwargs=dict(verbose=True))
 
617
                                   wt.branch, log.LongLogFormatter,
 
618
                                   formatter_kwargs=dict(levels=0),
 
619
                                   show_log_kwargs=dict(verbose=True))
532
620
 
533
621
    def test_trailing_newlines(self):
534
622
        wt = self.make_branch_and_tree('.')
535
623
        b = self.make_commits_with_trailing_newlines(wt)
536
 
        self.assertFormatterResult("""\
 
624
        self.assertFormatterResult(b"""\
537
625
------------------------------------------------------------
538
626
revno: 3
539
627
committer: Joe Foo <joe@foo.com>
558
646
message:
559
647
  simple log message
560
648
""",
561
 
        b, log.LongLogFormatter)
 
649
                                   b, log.LongLogFormatter)
562
650
 
563
651
    def test_author_in_log(self):
564
652
        """Log includes the author name if it's set in
565
653
        the revision properties
566
654
        """
567
655
        wt = self.make_standard_commit('test_author_log',
568
 
            authors=['John Doe <jdoe@example.com>',
569
 
                     'Jane Rey <jrey@example.com>'])
570
 
        self.assertFormatterResult("""\
 
656
                                       authors=['John Doe <jdoe@example.com>',
 
657
                                                'Jane Rey <jrey@example.com>'])
 
658
        self.assertFormatterResult(b"""\
571
659
------------------------------------------------------------
572
660
revno: 1
573
661
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
577
665
message:
578
666
  add a
579
667
""",
580
 
        wt.branch, log.LongLogFormatter)
 
668
                                   wt.branch, log.LongLogFormatter)
581
669
 
582
670
    def test_properties_in_log(self):
583
671
        """Log includes the custom properties returned by the registered
584
672
        handlers.
585
673
        """
586
674
        wt = self.make_standard_commit('test_properties_in_log')
 
675
 
587
676
        def trivial_custom_prop_handler(revision):
588
 
            return {'test_prop':'test_value'}
 
677
            return {'test_prop': 'test_value'}
589
678
 
590
679
        # Cleaned up in setUp()
591
680
        log.properties_handler_registry.register(
592
681
            'trivial_custom_prop_handler',
593
682
            trivial_custom_prop_handler)
594
 
        self.assertFormatterResult("""\
 
683
        self.assertFormatterResult(b"""\
595
684
------------------------------------------------------------
596
685
revno: 1
597
686
test_prop: test_value
602
691
message:
603
692
  add a
604
693
""",
605
 
            wt.branch, log.LongLogFormatter)
 
694
                                   wt.branch, log.LongLogFormatter)
606
695
 
607
696
    def test_properties_in_short_log(self):
608
697
        """Log includes the custom properties returned by the registered
609
698
        handlers.
610
699
        """
611
700
        wt = self.make_standard_commit('test_properties_in_short_log')
 
701
 
612
702
        def trivial_custom_prop_handler(revision):
613
 
            return {'test_prop':'test_value'}
 
703
            return {'test_prop': 'test_value'}
614
704
 
615
705
        log.properties_handler_registry.register(
616
706
            'trivial_custom_prop_handler',
617
707
            trivial_custom_prop_handler)
618
 
        self.assertFormatterResult("""\
 
708
        self.assertFormatterResult(b"""\
619
709
    1 John Doe\t2005-11-22
620
710
      test_prop: test_value
621
711
      add a
622
712
 
623
713
""",
624
 
            wt.branch, log.ShortLogFormatter)
 
714
                                   wt.branch, log.ShortLogFormatter)
625
715
 
626
716
    def test_error_in_properties_handler(self):
627
717
        """Log includes the custom properties returned by the registered
628
718
        handlers.
629
719
        """
630
720
        wt = self.make_standard_commit('error_in_properties_handler',
631
 
            revprops={'first_prop':'first_value'})
 
721
                                       revprops={u'first_prop': 'first_value'})
632
722
        sio = self.make_utf8_encoded_stringio()
633
723
        formatter = log.LongLogFormatter(to_file=sio)
 
724
 
634
725
        def trivial_custom_prop_handler(revision):
635
 
            raise StandardError("a test error")
 
726
            raise Exception("a test error")
636
727
 
637
728
        log.properties_handler_registry.register(
638
729
            'trivial_custom_prop_handler',
639
730
            trivial_custom_prop_handler)
640
 
        self.assertRaises(StandardError, log.show_log, wt.branch, formatter,)
 
731
        self.assertRaises(Exception, log.show_log, wt.branch, formatter,)
641
732
 
642
733
    def test_properties_handler_bad_argument(self):
643
734
        wt = self.make_standard_commit('bad_argument',
644
 
              revprops={'a_prop':'test_value'})
 
735
                                       revprops={u'a_prop': 'test_value'})
645
736
        sio = self.make_utf8_encoded_stringio()
646
737
        formatter = log.LongLogFormatter(to_file=sio)
 
738
 
647
739
        def bad_argument_prop_handler(revision):
648
 
            return {'custom_prop_name':revision.properties['a_prop']}
 
740
            return {'custom_prop_name': revision.properties['a_prop']}
649
741
 
650
742
        log.properties_handler_registry.register(
651
743
            'bad_argument_prop_handler',
656
748
 
657
749
        revision = wt.branch.repository.get_revision(wt.branch.last_revision())
658
750
        formatter.show_properties(revision, '')
659
 
        self.assertEqualDiff('''custom_prop_name: test_value\n''',
 
751
        self.assertEqualDiff(b'''custom_prop_name: test_value\n''',
660
752
                             sio.getvalue())
661
753
 
 
754
    def test_show_ids(self):
 
755
        wt = self.make_branch_and_tree('parent')
 
756
        self.build_tree(['parent/f1', 'parent/f2'])
 
757
        wt.add(['f1', 'f2'])
 
758
        self.wt_commit(wt, 'first post', rev_id=b'a')
 
759
        child_wt = wt.controldir.sprout('child').open_workingtree()
 
760
        self.wt_commit(child_wt, 'branch 1 changes', rev_id=b'b')
 
761
        wt.merge_from_branch(child_wt.branch)
 
762
        self.wt_commit(wt, 'merge branch 1', rev_id=b'c')
 
763
        self.assertFormatterResult(b"""\
 
764
------------------------------------------------------------
 
765
revno: 2 [merge]
 
766
revision-id: c
 
767
parent: a
 
768
parent: b
 
769
committer: Joe Foo <joe@foo.com>
 
770
branch nick: parent
 
771
timestamp: Tue 2005-11-22 00:00:02 +0000
 
772
message:
 
773
  merge branch 1
 
774
    ------------------------------------------------------------
 
775
    revno: 1.1.1
 
776
    revision-id: b
 
777
    parent: a
 
778
    committer: Joe Foo <joe@foo.com>
 
779
    branch nick: child
 
780
    timestamp: Tue 2005-11-22 00:00:01 +0000
 
781
    message:
 
782
      branch 1 changes
 
783
------------------------------------------------------------
 
784
revno: 1
 
785
revision-id: a
 
786
committer: Joe Foo <joe@foo.com>
 
787
branch nick: parent
 
788
timestamp: Tue 2005-11-22 00:00:00 +0000
 
789
message:
 
790
  first post
 
791
""",
 
792
                                   wt.branch, log.LongLogFormatter,
 
793
                                   formatter_kwargs=dict(levels=0, show_ids=True))
 
794
 
662
795
 
663
796
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
664
797
 
668
801
        bug #4676
669
802
        """
670
803
        wt = self.make_standard_commit('test_long_verbose_log', authors=[])
671
 
        self.assertFormatterResult("""\
 
804
        self.assertFormatterResult(b"""\
672
805
------------------------------------------------------------
673
806
revno: 1
674
807
committer: Lorem Ipsum <test@example.com>
679
812
added:
680
813
  a
681
814
""",
682
 
            wt.branch, log.LongLogFormatter,
683
 
            formatter_kwargs=dict(levels=1),
684
 
            show_log_kwargs=dict(verbose=True))
 
815
                                   wt.branch, log.LongLogFormatter,
 
816
                                   formatter_kwargs=dict(levels=1),
 
817
                                   show_log_kwargs=dict(verbose=True))
685
818
 
686
819
    def test_long_verbose_contain_deltas(self):
687
820
        wt = self.make_branch_and_tree('parent')
688
821
        self.build_tree(['parent/f1', 'parent/f2'])
689
 
        wt.add(['f1','f2'])
 
822
        wt.add(['f1', 'f2'])
690
823
        self.wt_commit(wt, 'first post')
691
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
824
        child_wt = wt.controldir.sprout('child').open_workingtree()
692
825
        os.unlink('child/f1')
693
 
        self.build_tree_contents([('child/f2', 'hello\n')])
 
826
        self.build_tree_contents([('child/f2', b'hello\n')])
694
827
        self.wt_commit(child_wt, 'removed f1 and modified f2')
695
828
        wt.merge_from_branch(child_wt.branch)
696
829
        self.wt_commit(wt, 'merge branch 1')
697
 
        self.assertFormatterResult("""\
 
830
        self.assertFormatterResult(b"""\
698
831
------------------------------------------------------------
699
832
revno: 2 [merge]
700
833
committer: Joe Foo <joe@foo.com>
717
850
  f1
718
851
  f2
719
852
""",
720
 
            wt.branch, log.LongLogFormatter,
721
 
            formatter_kwargs=dict(levels=1),
722
 
            show_log_kwargs=dict(verbose=True))
 
853
                                   wt.branch, log.LongLogFormatter,
 
854
                                   formatter_kwargs=dict(levels=1),
 
855
                                   show_log_kwargs=dict(verbose=True))
723
856
 
724
857
    def test_long_trailing_newlines(self):
725
858
        wt = self.make_branch_and_tree('.')
726
859
        b = self.make_commits_with_trailing_newlines(wt)
727
 
        self.assertFormatterResult("""\
 
860
        self.assertFormatterResult(b"""\
728
861
------------------------------------------------------------
729
862
revno: 3
730
863
committer: Joe Foo <joe@foo.com>
749
882
message:
750
883
  simple log message
751
884
""",
752
 
        b, log.LongLogFormatter,
753
 
        formatter_kwargs=dict(levels=1))
 
885
                                   b, log.LongLogFormatter,
 
886
                                   formatter_kwargs=dict(levels=1))
754
887
 
755
888
    def test_long_author_in_log(self):
756
889
        """Log includes the author name if it's set in
757
890
        the revision properties
758
891
        """
759
892
        wt = self.make_standard_commit('test_author_log')
760
 
        self.assertFormatterResult("""\
 
893
        self.assertFormatterResult(b"""\
761
894
------------------------------------------------------------
762
895
revno: 1
763
896
author: John Doe <jdoe@example.com>
767
900
message:
768
901
  add a
769
902
""",
770
 
            wt.branch, log.LongLogFormatter,
771
 
            formatter_kwargs=dict(levels=1))
 
903
                                   wt.branch, log.LongLogFormatter,
 
904
                                   formatter_kwargs=dict(levels=1))
772
905
 
773
906
    def test_long_properties_in_log(self):
774
907
        """Log includes the custom properties returned by the registered
775
908
        handlers.
776
909
        """
777
910
        wt = self.make_standard_commit('test_properties_in_log')
 
911
 
778
912
        def trivial_custom_prop_handler(revision):
779
 
            return {'test_prop':'test_value'}
 
913
            return {'test_prop': 'test_value'}
780
914
 
781
915
        log.properties_handler_registry.register(
782
916
            'trivial_custom_prop_handler',
783
917
            trivial_custom_prop_handler)
784
 
        self.assertFormatterResult("""\
 
918
        self.assertFormatterResult(b"""\
785
919
------------------------------------------------------------
786
920
revno: 1
787
921
test_prop: test_value
792
926
message:
793
927
  add a
794
928
""",
795
 
            wt.branch, log.LongLogFormatter,
796
 
            formatter_kwargs=dict(levels=1))
 
929
                                   wt.branch, log.LongLogFormatter,
 
930
                                   formatter_kwargs=dict(levels=1))
797
931
 
798
932
 
799
933
class TestLineLogFormatter(TestCaseForLogFormatter):
804
938
        bug #5162
805
939
        """
806
940
        wt = self.make_standard_commit('test-line-log',
807
 
                committer='Line-Log-Formatter Tester <test@line.log>',
808
 
                authors=[])
809
 
        self.assertFormatterResult("""\
 
941
                                       committer='Line-Log-Formatter Tester <test@line.log>',
 
942
                                       authors=[])
 
943
        self.assertFormatterResult(b"""\
810
944
1: Line-Log-Formatte... 2005-11-22 add a
811
945
""",
812
 
            wt.branch, log.LineLogFormatter)
 
946
                                   wt.branch, log.LineLogFormatter)
813
947
 
814
948
    def test_trailing_newlines(self):
815
949
        wt = self.make_branch_and_tree('.')
816
950
        b = self.make_commits_with_trailing_newlines(wt)
817
 
        self.assertFormatterResult("""\
 
951
        self.assertFormatterResult(b"""\
818
952
3: Joe Foo 2005-11-22 single line with trailing newline
819
953
2: Joe Foo 2005-11-22 multiline
820
954
1: Joe Foo 2005-11-22 simple log message
821
955
""",
822
 
            b, log.LineLogFormatter)
 
956
                                   b, log.LineLogFormatter)
823
957
 
824
958
    def test_line_log_single_merge_revision(self):
825
959
        wt = self._prepare_tree_with_merges()
826
960
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
827
961
        rev = revspec.in_history(wt.branch)
828
 
        self.assertFormatterResult("""\
 
962
        self.assertFormatterResult(b"""\
829
963
1.1.1: Joe Foo 2005-11-22 rev-merged
830
964
""",
831
 
            wt.branch, log.LineLogFormatter,
832
 
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
965
                                   wt.branch, log.LineLogFormatter,
 
966
                                   show_log_kwargs=dict(start_revision=rev, end_revision=rev))
833
967
 
834
968
    def test_line_log_with_tags(self):
835
969
        wt = self._prepare_tree_with_merges(with_tags=True)
836
 
        self.assertFormatterResult("""\
 
970
        self.assertFormatterResult(b"""\
837
971
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
838
972
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
839
973
1: Joe Foo 2005-11-22 rev-1
840
974
""",
841
 
            wt.branch, log.LineLogFormatter)
 
975
                                   wt.branch, log.LineLogFormatter)
842
976
 
843
977
 
844
978
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
849
983
        bug #5162
850
984
        """
851
985
        wt = self.make_standard_commit('test-line-log',
852
 
                committer='Line-Log-Formatter Tester <test@line.log>',
853
 
                authors=[])
854
 
        self.assertFormatterResult("""\
 
986
                                       committer='Line-Log-Formatter Tester <test@line.log>',
 
987
                                       authors=[])
 
988
        self.assertFormatterResult(b"""\
855
989
1: Line-Log-Formatte... 2005-11-22 add a
856
990
""",
857
 
            wt.branch, log.LineLogFormatter)
 
991
                                   wt.branch, log.LineLogFormatter)
858
992
 
859
993
    def test_line_merge_revs_log_single_merge_revision(self):
860
994
        wt = self._prepare_tree_with_merges()
861
995
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
862
996
        rev = revspec.in_history(wt.branch)
863
 
        self.assertFormatterResult("""\
 
997
        self.assertFormatterResult(b"""\
864
998
1.1.1: Joe Foo 2005-11-22 rev-merged
865
999
""",
866
 
            wt.branch, log.LineLogFormatter,
867
 
            formatter_kwargs=dict(levels=0),
868
 
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
1000
                                   wt.branch, log.LineLogFormatter,
 
1001
                                   formatter_kwargs=dict(levels=0),
 
1002
                                   show_log_kwargs=dict(start_revision=rev, end_revision=rev))
869
1003
 
870
1004
    def test_line_merge_revs_log_with_merges(self):
871
1005
        wt = self._prepare_tree_with_merges()
872
 
        self.assertFormatterResult("""\
 
1006
        self.assertFormatterResult(b"""\
873
1007
2: Joe Foo 2005-11-22 [merge] rev-2
874
1008
  1.1.1: Joe Foo 2005-11-22 rev-merged
875
1009
1: Joe Foo 2005-11-22 rev-1
876
1010
""",
877
 
            wt.branch, log.LineLogFormatter,
878
 
            formatter_kwargs=dict(levels=0))
 
1011
                                   wt.branch, log.LineLogFormatter,
 
1012
                                   formatter_kwargs=dict(levels=0))
879
1013
 
880
1014
 
881
1015
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
882
1016
 
883
1017
    def test_gnu_changelog(self):
884
1018
        wt = self.make_standard_commit('nicky', authors=[])
885
 
        self.assertFormatterResult('''\
 
1019
        self.assertFormatterResult(b'''\
886
1020
2005-11-22  Lorem Ipsum  <test@example.com>
887
1021
 
888
1022
\tadd a
889
1023
 
890
1024
''',
891
 
            wt.branch, log.GnuChangelogLogFormatter)
 
1025
                                   wt.branch, log.GnuChangelogLogFormatter)
892
1026
 
893
1027
    def test_with_authors(self):
894
1028
        wt = self.make_standard_commit('nicky',
895
 
            authors=['Fooa Fooz <foo@example.com>',
896
 
                     'Bari Baro <bar@example.com>'])
897
 
        self.assertFormatterResult('''\
 
1029
                                       authors=['Fooa Fooz <foo@example.com>',
 
1030
                                                'Bari Baro <bar@example.com>'])
 
1031
        self.assertFormatterResult(b'''\
898
1032
2005-11-22  Fooa Fooz  <foo@example.com>
899
1033
 
900
1034
\tadd a
901
1035
 
902
1036
''',
903
 
            wt.branch, log.GnuChangelogLogFormatter)
 
1037
                                   wt.branch, log.GnuChangelogLogFormatter)
904
1038
 
905
1039
    def test_verbose(self):
906
1040
        wt = self.make_standard_commit('nicky')
907
 
        self.assertFormatterResult('''\
 
1041
        self.assertFormatterResult(b'''\
908
1042
2005-11-22  John Doe  <jdoe@example.com>
909
1043
 
910
1044
\t* a:
912
1046
\tadd a
913
1047
 
914
1048
''',
915
 
            wt.branch, log.GnuChangelogLogFormatter,
916
 
            show_log_kwargs=dict(verbose=True))
917
 
 
918
 
class TestGetViewRevisions(tests.TestCaseWithTransport, TestLogMixin):
919
 
 
920
 
    def _get_view_revisions(self, *args, **kwargs):
921
 
        return self.applyDeprecated(symbol_versioning.deprecated_in((2, 2, 0)),
922
 
                                    log.get_view_revisions, *args, **kwargs)
923
 
 
924
 
    def make_tree_with_commits(self):
925
 
        """Create a tree with well-known revision ids"""
926
 
        wt = self.make_branch_and_tree('tree1')
927
 
        self.wt_commit(wt, 'commit one', rev_id='1')
928
 
        self.wt_commit(wt, 'commit two', rev_id='2')
929
 
        self.wt_commit(wt, 'commit three', rev_id='3')
930
 
        mainline_revs = [None, '1', '2', '3']
931
 
        rev_nos = {'1': 1, '2': 2, '3': 3}
932
 
        return mainline_revs, rev_nos, wt
933
 
 
934
 
    def make_tree_with_merges(self):
935
 
        """Create a tree with well-known revision ids and a merge"""
936
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
937
 
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
938
 
        self.wt_commit(tree2, 'four-a', rev_id='4a')
939
 
        wt.merge_from_branch(tree2.branch)
940
 
        self.wt_commit(wt, 'four-b', rev_id='4b')
941
 
        mainline_revs.append('4b')
942
 
        rev_nos['4b'] = 4
943
 
        # 4a: 3.1.1
944
 
        return mainline_revs, rev_nos, wt
945
 
 
946
 
    def make_branch_with_many_merges(self):
947
 
        """Create a tree with well-known revision ids"""
948
 
        builder = self.make_branch_builder('tree1')
949
 
        builder.start_series()
950
 
        builder.build_snapshot('1', None, [
951
 
            ('add', ('', 'TREE_ROOT', 'directory', '')),
952
 
            ('add', ('f', 'f-id', 'file', '1\n'))])
953
 
        builder.build_snapshot('2', ['1'], [])
954
 
        builder.build_snapshot('3a', ['2'], [
955
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
956
 
        builder.build_snapshot('3b', ['2', '3a'], [
957
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
958
 
        builder.build_snapshot('3c', ['2', '3b'], [
959
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
960
 
        builder.build_snapshot('4a', ['3b'], [])
961
 
        builder.build_snapshot('4b', ['3c', '4a'], [])
962
 
        builder.finish_series()
963
 
 
964
 
        # 1
965
 
        # |
966
 
        # 2-.
967
 
        # |\ \
968
 
        # | | 3a
969
 
        # | |/
970
 
        # | 3b
971
 
        # |/|
972
 
        # 3c4a
973
 
        # |/
974
 
        # 4b
975
 
 
976
 
        mainline_revs = [None, '1', '2', '3c', '4b']
977
 
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
978
 
        full_rev_nos_for_reference = {
979
 
            '1': '1',
980
 
            '2': '2',
981
 
            '3a': '2.1.1', #first commit tree 3
982
 
            '3b': '2.2.1', # first commit tree 2
983
 
            '3c': '3', #merges 3b to main
984
 
            '4a': '2.2.2', # second commit tree 2
985
 
            '4b': '4', # merges 4a to main
986
 
            }
987
 
        return mainline_revs, rev_nos, builder.get_branch()
988
 
 
989
 
    def test_get_view_revisions_forward(self):
990
 
        """Test the get_view_revisions method"""
991
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
992
 
        wt.lock_read()
993
 
        self.addCleanup(wt.unlock)
994
 
        revisions = list(self._get_view_revisions(
995
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
996
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
997
 
                         revisions)
998
 
        revisions2 = list(self._get_view_revisions(
999
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1000
 
                include_merges=False))
1001
 
        self.assertEqual(revisions, revisions2)
1002
 
 
1003
 
    def test_get_view_revisions_reverse(self):
1004
 
        """Test the get_view_revisions with reverse"""
1005
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1006
 
        wt.lock_read()
1007
 
        self.addCleanup(wt.unlock)
1008
 
        revisions = list(self._get_view_revisions(
1009
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
1010
 
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
1011
 
                         revisions)
1012
 
        revisions2 = list(self._get_view_revisions(
1013
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1014
 
                include_merges=False))
1015
 
        self.assertEqual(revisions, revisions2)
1016
 
 
1017
 
    def test_get_view_revisions_merge(self):
1018
 
        """Test get_view_revisions when there are merges"""
1019
 
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1020
 
        wt.lock_read()
1021
 
        self.addCleanup(wt.unlock)
1022
 
        revisions = list(self._get_view_revisions(
1023
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
1024
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1025
 
                          ('4b', '4', 0), ('4a', '3.1.1', 1)],
1026
 
                         revisions)
1027
 
        revisions = list(self._get_view_revisions(
1028
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1029
 
                include_merges=False))
1030
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1031
 
                          ('4b', '4', 0)],
1032
 
                         revisions)
1033
 
 
1034
 
    def test_get_view_revisions_merge_reverse(self):
1035
 
        """Test get_view_revisions in reverse when there are merges"""
1036
 
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1037
 
        wt.lock_read()
1038
 
        self.addCleanup(wt.unlock)
1039
 
        revisions = list(self._get_view_revisions(
1040
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
1041
 
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
1042
 
                          ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
1043
 
                         revisions)
1044
 
        revisions = list(self._get_view_revisions(
1045
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1046
 
                include_merges=False))
1047
 
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
1048
 
                          ('1', '1', 0)],
1049
 
                         revisions)
1050
 
 
1051
 
    def test_get_view_revisions_merge2(self):
1052
 
        """Test get_view_revisions when there are merges"""
1053
 
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1054
 
        b.lock_read()
1055
 
        self.addCleanup(b.unlock)
1056
 
        revisions = list(self._get_view_revisions(
1057
 
                mainline_revs, rev_nos, b, 'forward'))
1058
 
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1059
 
                    ('3b', '2.2.1', 1), ('3a', '2.1.1', 2), ('4b', '4', 0),
1060
 
                    ('4a', '2.2.2', 1)]
1061
 
        self.assertEqual(expected, revisions)
1062
 
        revisions = list(self._get_view_revisions(
1063
 
                mainline_revs, rev_nos, b, 'forward',
1064
 
                include_merges=False))
1065
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1066
 
                          ('4b', '4', 0)],
1067
 
                         revisions)
1068
 
 
1069
 
    def test_file_id_for_range(self):
1070
 
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1071
 
        b.lock_read()
1072
 
        self.addCleanup(b.unlock)
1073
 
 
1074
 
        def rev_from_rev_id(revid, branch):
1075
 
            revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
1076
 
            return revspec.in_history(branch)
1077
 
 
1078
 
        def view_revs(start_rev, end_rev, file_id, direction):
1079
 
            revs = self.applyDeprecated(
1080
 
                symbol_versioning.deprecated_in((2, 2, 0)),
1081
 
                log.calculate_view_revisions,
1082
 
                b,
1083
 
                start_rev, # start_revision
1084
 
                end_rev, # end_revision
1085
 
                direction, # direction
1086
 
                file_id, # specific_fileid
1087
 
                True, # generate_merge_revisions
1088
 
                )
1089
 
            return revs
1090
 
 
1091
 
        rev_3a = rev_from_rev_id('3a', b)
1092
 
        rev_4b = rev_from_rev_id('4b', b)
1093
 
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1),
1094
 
                          ('3a', '2.1.1', 2)],
1095
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
1096
 
        # Note: 3c still appears before 3a here because of depth-based sorting
1097
 
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1),
1098
 
                          ('3a', '2.1.1', 2)],
1099
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
1100
 
 
1101
 
 
1102
 
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
1103
 
 
1104
 
    def get_view_revisions(self, *args):
1105
 
        return self.applyDeprecated(symbol_versioning.deprecated_in((2, 2, 0)),
1106
 
                                    log.get_view_revisions, *args)
1107
 
 
1108
 
    def create_tree_with_single_merge(self):
1109
 
        """Create a branch with a moderate layout.
1110
 
 
1111
 
        The revision graph looks like:
1112
 
 
1113
 
           A
1114
 
           |\
1115
 
           B C
1116
 
           |/
1117
 
           D
1118
 
 
1119
 
        In this graph, A introduced files f1 and f2 and f3.
1120
 
        B modifies f1 and f3, and C modifies f2 and f3.
1121
 
        D merges the changes from B and C and resolves the conflict for f3.
1122
 
        """
1123
 
        # TODO: jam 20070218 This seems like it could really be done
1124
 
        #       with make_branch_and_memory_tree() if we could just
1125
 
        #       create the content of those files.
1126
 
        # TODO: jam 20070218 Another alternative is that we would really
1127
 
        #       like to only create this tree 1 time for all tests that
1128
 
        #       use it. Since 'log' only uses the tree in a readonly
1129
 
        #       fashion, it seems a shame to regenerate an identical
1130
 
        #       tree for each test.
1131
 
        # TODO: vila 20100122 One way to address the shame above will be to
1132
 
        #       create a memory tree during test parametrization and give a
1133
 
        #       *copy* of this tree to each test. Copying a memory tree ought
1134
 
        #       to be cheap, at least cheaper than creating them with such
1135
 
        #       complex setups.
1136
 
        tree = self.make_branch_and_tree('tree')
1137
 
        tree.lock_write()
1138
 
        self.addCleanup(tree.unlock)
1139
 
 
1140
 
        self.build_tree_contents([('tree/f1', 'A\n'),
1141
 
                                  ('tree/f2', 'A\n'),
1142
 
                                  ('tree/f3', 'A\n'),
1143
 
                                 ])
1144
 
        tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
1145
 
        tree.commit('A', rev_id='A')
1146
 
 
1147
 
        self.build_tree_contents([('tree/f2', 'A\nC\n'),
1148
 
                                  ('tree/f3', 'A\nC\n'),
1149
 
                                 ])
1150
 
        tree.commit('C', rev_id='C')
1151
 
        # Revert back to A to build the other history.
1152
 
        tree.set_last_revision('A')
1153
 
        tree.branch.set_last_revision_info(1, 'A')
1154
 
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
1155
 
                                  ('tree/f2', 'A\n'),
1156
 
                                  ('tree/f3', 'A\nB\n'),
1157
 
                                 ])
1158
 
        tree.commit('B', rev_id='B')
1159
 
        tree.set_parent_ids(['B', 'C'])
1160
 
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
1161
 
                                  ('tree/f2', 'A\nC\n'),
1162
 
                                  ('tree/f3', 'A\nB\nC\n'),
1163
 
                                 ])
1164
 
        tree.commit('D', rev_id='D')
1165
 
 
1166
 
        # Switch to a read lock for this tree.
1167
 
        # We still have an addCleanup(tree.unlock) pending
1168
 
        tree.unlock()
1169
 
        tree.lock_read()
1170
 
        return tree
1171
 
 
1172
 
    def check_delta(self, delta, **kw):
1173
 
        """Check the filenames touched by a delta are as expected.
1174
 
 
1175
 
        Caller only have to pass in the list of files for each part, all
1176
 
        unspecified parts are considered empty (and checked as such).
1177
 
        """
1178
 
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
1179
 
            # By default we expect an empty list
1180
 
            expected = kw.get(n, [])
1181
 
            # strip out only the path components
1182
 
            got = [x[0] for x in getattr(delta, n)]
1183
 
            self.assertEqual(expected, got)
1184
 
 
1185
 
    def test_tree_with_single_merge(self):
1186
 
        """Make sure the tree layout is correct."""
1187
 
        tree = self.create_tree_with_single_merge()
1188
 
        rev_A_tree = tree.branch.repository.revision_tree('A')
1189
 
        rev_B_tree = tree.branch.repository.revision_tree('B')
1190
 
        rev_C_tree = tree.branch.repository.revision_tree('C')
1191
 
        rev_D_tree = tree.branch.repository.revision_tree('D')
1192
 
 
1193
 
        self.check_delta(rev_B_tree.changes_from(rev_A_tree),
1194
 
                         modified=['f1', 'f3'])
1195
 
 
1196
 
        self.check_delta(rev_C_tree.changes_from(rev_A_tree),
1197
 
                         modified=['f2', 'f3'])
1198
 
 
1199
 
        self.check_delta(rev_D_tree.changes_from(rev_B_tree),
1200
 
                         modified=['f2', 'f3'])
1201
 
 
1202
 
        self.check_delta(rev_D_tree.changes_from(rev_C_tree),
1203
 
                         modified=['f1', 'f3'])
1204
 
 
1205
 
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
1206
 
        """Ensure _filter_revisions_touching_file_id returns the right values.
1207
 
 
1208
 
        Get the return value from _filter_revisions_touching_file_id and make
1209
 
        sure they are correct.
1210
 
        """
1211
 
        # The api for _filter_revisions_touching_file_id is a little crazy.
1212
 
        # So we do the setup here.
1213
 
        mainline = tree.branch.revision_history()
1214
 
        mainline.insert(0, None)
1215
 
        revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
1216
 
        view_revs_iter = self.get_view_revisions(
1217
 
            mainline, revnos, tree.branch, 'reverse', True)
1218
 
        actual_revs = log._filter_revisions_touching_file_id(
1219
 
            tree.branch, file_id, list(view_revs_iter))
1220
 
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
1221
 
 
1222
 
    def test_file_id_f1(self):
1223
 
        tree = self.create_tree_with_single_merge()
1224
 
        # f1 should be marked as modified by revisions A and B
1225
 
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
1226
 
 
1227
 
    def test_file_id_f2(self):
1228
 
        tree = self.create_tree_with_single_merge()
1229
 
        # f2 should be marked as modified by revisions A, C, and D
1230
 
        # because D merged the changes from C.
1231
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1232
 
 
1233
 
    def test_file_id_f3(self):
1234
 
        tree = self.create_tree_with_single_merge()
1235
 
        # f3 should be marked as modified by revisions A, B, C, and D
1236
 
        self.assertAllRevisionsForFileID(tree, 'f3-id', ['D', 'C', 'B', 'A'])
1237
 
 
1238
 
    def test_file_id_with_ghosts(self):
1239
 
        # This is testing bug #209948, where having a ghost would cause
1240
 
        # _filter_revisions_touching_file_id() to fail.
1241
 
        tree = self.create_tree_with_single_merge()
1242
 
        # We need to add a revision, so switch back to a write-locked tree
1243
 
        # (still a single addCleanup(tree.unlock) pending).
1244
 
        tree.unlock()
1245
 
        tree.lock_write()
1246
 
        first_parent = tree.last_revision()
1247
 
        tree.set_parent_ids([first_parent, 'ghost-revision-id'])
1248
 
        self.build_tree_contents([('tree/f1', 'A\nB\nXX\n')])
1249
 
        tree.commit('commit with a ghost', rev_id='XX')
1250
 
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['XX', 'B', 'A'])
1251
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1252
 
 
1253
 
    def test_unknown_file_id(self):
1254
 
        tree = self.create_tree_with_single_merge()
1255
 
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
1256
 
 
1257
 
    def test_empty_branch_unknown_file_id(self):
1258
 
        tree = self.make_branch_and_tree('tree')
1259
 
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
 
1049
                                   wt.branch, log.GnuChangelogLogFormatter,
 
1050
                                   show_log_kwargs=dict(verbose=True))
1260
1051
 
1261
1052
 
1262
1053
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1265
1056
        tree = self.make_branch_and_tree('tree_a')
1266
1057
        self.build_tree(['tree_a/foo'])
1267
1058
        tree.add('foo')
1268
 
        tree.commit('bar', rev_id='bar-id')
 
1059
        tree.commit('bar', rev_id=b'bar-id')
1269
1060
        s = self.make_utf8_encoded_stringio()
1270
 
        log.show_changed_revisions(tree.branch, [], ['bar-id'], s)
1271
 
        self.assertContainsRe(s.getvalue(), 'bar')
1272
 
        self.assertNotContainsRe(s.getvalue(), 'foo')
 
1061
        log.show_changed_revisions(tree.branch, [], [b'bar-id'], s)
 
1062
        self.assertContainsRe(s.getvalue(), b'bar')
 
1063
        self.assertNotContainsRe(s.getvalue(), b'foo')
1273
1064
 
1274
1065
 
1275
1066
class TestLogFormatter(tests.TestCase):
1276
1067
 
1277
1068
    def setUp(self):
1278
1069
        super(TestLogFormatter, self).setUp()
1279
 
        self.rev = revision.Revision('a-id')
 
1070
        self.rev = revision.Revision(b'a-id')
1280
1071
        self.lf = log.LogFormatter(None)
1281
1072
 
1282
1073
    def test_short_committer(self):
1330
1121
            Tests use (revno, depth) whil the API expects (revid, revno, depth).
1331
1122
            Since the revid is arbitrary, we just duplicate revno
1332
1123
            """
1333
 
            return [ (r, r, d) for r, d in l]
 
1124
            return [(r, r, d) for r, d in l]
1334
1125
        forward = complete_revisions(forward)
1335
 
        backward= complete_revisions(backward)
 
1126
        backward = complete_revisions(backward)
1336
1127
        self.assertEqual(forward, log.reverse_by_depth(backward))
1337
1128
 
1338
 
 
1339
1129
    def test_mainline_revisions(self):
1340
 
        self.assertReversed([( '1', 0), ('2', 0)],
 
1130
        self.assertReversed([('1', 0), ('2', 0)],
1341
1131
                            [('2', 0), ('1', 0)])
1342
1132
 
1343
1133
    def test_merged_revisions(self):
1344
 
        self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1345
 
                            [('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
 
1134
        self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1), ],
 
1135
                            [('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0), ])
 
1136
 
1346
1137
    def test_shifted_merged_revisions(self):
1347
1138
        """Test irregular layout.
1348
1139
 
1349
1140
        Requesting revisions touching a file can produce "holes" in the depths.
1350
1141
        """
1351
 
        self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1352
 
                            [('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
 
1142
        self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2), ],
 
1143
                            [('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0), ])
1353
1144
 
1354
1145
    def test_merged_without_child_revisions(self):
1355
1146
        """Test irregular layout.
1359
1150
        # When a revision of higher depth doesn't follow one of lower depth, we
1360
1151
        # assume a lower depth one is virtually there
1361
1152
        self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1362
 
                            [('4', 4), ('3', 3), ('2', 2), ('1', 2),])
 
1153
                            [('4', 4), ('3', 3), ('2', 2), ('1', 2), ])
1363
1154
        # So we get the same order after reversing below even if the original
1364
1155
        # revisions are not in the same order.
1365
1156
        self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1366
 
                            [('3', 3), ('4', 4), ('2', 2), ('1', 2),])
 
1157
                            [('3', 3), ('4', 4), ('2', 2), ('1', 2), ])
1367
1158
 
1368
1159
 
1369
1160
class TestHistoryChange(tests.TestCaseWithTransport):
1372
1163
        tree = self.make_branch_and_tree('tree')
1373
1164
        tree.lock_write()
1374
1165
        self.addCleanup(tree.unlock)
1375
 
        tree.commit('1a', rev_id='1a')
1376
 
        tree.commit('2a', rev_id='2a')
1377
 
        tree.commit('3a', rev_id='3a')
 
1166
        tree.commit('1a', rev_id=b'1a')
 
1167
        tree.commit('2a', rev_id=b'2a')
 
1168
        tree.commit('3a', rev_id=b'3a')
1378
1169
        return tree
1379
1170
 
1380
1171
    def setup_ab_tree(self):
1381
1172
        tree = self.setup_a_tree()
1382
 
        tree.set_last_revision('1a')
1383
 
        tree.branch.set_last_revision_info(1, '1a')
1384
 
        tree.commit('2b', rev_id='2b')
1385
 
        tree.commit('3b', rev_id='3b')
 
1173
        tree.set_last_revision(b'1a')
 
1174
        tree.branch.set_last_revision_info(1, b'1a')
 
1175
        tree.commit('2b', rev_id=b'2b')
 
1176
        tree.commit('3b', rev_id=b'3b')
1386
1177
        return tree
1387
1178
 
1388
1179
    def setup_ac_tree(self):
1389
1180
        tree = self.setup_a_tree()
1390
1181
        tree.set_last_revision(revision.NULL_REVISION)
1391
1182
        tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1392
 
        tree.commit('1c', rev_id='1c')
1393
 
        tree.commit('2c', rev_id='2c')
1394
 
        tree.commit('3c', rev_id='3c')
 
1183
        tree.commit('1c', rev_id=b'1c')
 
1184
        tree.commit('2c', rev_id=b'2c')
 
1185
        tree.commit('3c', rev_id=b'3c')
1395
1186
        return tree
1396
1187
 
1397
1188
    def test_all_new(self):
1398
1189
        tree = self.setup_ab_tree()
1399
 
        old, new = log.get_history_change('1a', '3a', tree.branch.repository)
 
1190
        old, new = log.get_history_change(b'1a', b'3a', tree.branch.repository)
1400
1191
        self.assertEqual([], old)
1401
 
        self.assertEqual(['2a', '3a'], new)
 
1192
        self.assertEqual([b'2a', b'3a'], new)
1402
1193
 
1403
1194
    def test_all_old(self):
1404
1195
        tree = self.setup_ab_tree()
1405
 
        old, new = log.get_history_change('3a', '1a', tree.branch.repository)
 
1196
        old, new = log.get_history_change(b'3a', b'1a', tree.branch.repository)
1406
1197
        self.assertEqual([], new)
1407
 
        self.assertEqual(['2a', '3a'], old)
 
1198
        self.assertEqual([b'2a', b'3a'], old)
1408
1199
 
1409
1200
    def test_null_old(self):
1410
1201
        tree = self.setup_ab_tree()
1411
1202
        old, new = log.get_history_change(revision.NULL_REVISION,
1412
 
                                          '3a', tree.branch.repository)
 
1203
                                          b'3a', tree.branch.repository)
1413
1204
        self.assertEqual([], old)
1414
 
        self.assertEqual(['1a', '2a', '3a'], new)
 
1205
        self.assertEqual([b'1a', b'2a', b'3a'], new)
1415
1206
 
1416
1207
    def test_null_new(self):
1417
1208
        tree = self.setup_ab_tree()
1418
 
        old, new = log.get_history_change('3a', revision.NULL_REVISION,
 
1209
        old, new = log.get_history_change(b'3a', revision.NULL_REVISION,
1419
1210
                                          tree.branch.repository)
1420
1211
        self.assertEqual([], new)
1421
 
        self.assertEqual(['1a', '2a', '3a'], old)
 
1212
        self.assertEqual([b'1a', b'2a', b'3a'], old)
1422
1213
 
1423
1214
    def test_diverged(self):
1424
1215
        tree = self.setup_ab_tree()
1425
 
        old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1426
 
        self.assertEqual(old, ['2a', '3a'])
1427
 
        self.assertEqual(new, ['2b', '3b'])
 
1216
        old, new = log.get_history_change(b'3a', b'3b', tree.branch.repository)
 
1217
        self.assertEqual(old, [b'2a', b'3a'])
 
1218
        self.assertEqual(new, [b'2b', b'3b'])
1428
1219
 
1429
1220
    def test_unrelated(self):
1430
1221
        tree = self.setup_ac_tree()
1431
 
        old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1432
 
        self.assertEqual(old, ['1a', '2a', '3a'])
1433
 
        self.assertEqual(new, ['1c', '2c', '3c'])
 
1222
        old, new = log.get_history_change(b'3a', b'3c', tree.branch.repository)
 
1223
        self.assertEqual(old, [b'1a', b'2a', b'3a'])
 
1224
        self.assertEqual(new, [b'1c', b'2c', b'3c'])
1434
1225
 
1435
1226
    def test_show_branch_change(self):
1436
1227
        tree = self.setup_ab_tree()
1437
1228
        s = StringIO()
1438
 
        log.show_branch_change(tree.branch, s, 3, '3a')
 
1229
        log.show_branch_change(tree.branch, s, 3, b'3a')
1439
1230
        self.assertContainsRe(s.getvalue(),
1440
 
            '[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1441
 
            '[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
 
1231
                              '[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
 
1232
                              '[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1442
1233
 
1443
1234
    def test_show_branch_change_no_change(self):
1444
1235
        tree = self.setup_ab_tree()
1445
1236
        s = StringIO()
1446
 
        log.show_branch_change(tree.branch, s, 3, '3b')
 
1237
        log.show_branch_change(tree.branch, s, 3, b'3b')
1447
1238
        self.assertEqual(s.getvalue(),
1448
 
            'Nothing seems to have changed\n')
 
1239
                         'Nothing seems to have changed\n')
1449
1240
 
1450
1241
    def test_show_branch_change_no_old(self):
1451
1242
        tree = self.setup_ab_tree()
1452
1243
        s = StringIO()
1453
 
        log.show_branch_change(tree.branch, s, 2, '2b')
 
1244
        log.show_branch_change(tree.branch, s, 2, b'2b')
1454
1245
        self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1455
1246
        self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1456
1247
 
1457
1248
    def test_show_branch_change_no_new(self):
1458
1249
        tree = self.setup_ab_tree()
1459
 
        tree.branch.set_last_revision_info(2, '2b')
 
1250
        tree.branch.set_last_revision_info(2, b'2b')
1460
1251
        s = StringIO()
1461
 
        log.show_branch_change(tree.branch, s, 3, '3b')
 
1252
        log.show_branch_change(tree.branch, s, 3, b'3b')
1462
1253
        self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1463
1254
        self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1464
1255
 
1465
1256
 
 
1257
class TestRevisionNotInBranch(TestCaseForLogFormatter):
 
1258
 
 
1259
    def setup_a_tree(self):
 
1260
        tree = self.make_branch_and_tree('tree')
 
1261
        tree.lock_write()
 
1262
        self.addCleanup(tree.unlock)
 
1263
        kwargs = {
 
1264
            'committer': 'Joe Foo <joe@foo.com>',
 
1265
            'timestamp': 1132617600,  # Mon 2005-11-22 00:00:00 +0000
 
1266
            'timezone': 0,  # UTC
 
1267
        }
 
1268
        tree.commit('commit 1a', rev_id=b'1a', **kwargs)
 
1269
        tree.commit('commit 2a', rev_id=b'2a', **kwargs)
 
1270
        tree.commit('commit 3a', rev_id=b'3a', **kwargs)
 
1271
        return tree
 
1272
 
 
1273
    def setup_ab_tree(self):
 
1274
        tree = self.setup_a_tree()
 
1275
        tree.set_last_revision(b'1a')
 
1276
        tree.branch.set_last_revision_info(1, b'1a')
 
1277
        kwargs = {
 
1278
            'committer': 'Joe Foo <joe@foo.com>',
 
1279
            'timestamp': 1132617600,  # Mon 2005-11-22 00:00:00 +0000
 
1280
            'timezone': 0,  # UTC
 
1281
        }
 
1282
        tree.commit('commit 2b', rev_id=b'2b', **kwargs)
 
1283
        tree.commit('commit 3b', rev_id=b'3b', **kwargs)
 
1284
        return tree
 
1285
 
 
1286
    def test_one_revision(self):
 
1287
        tree = self.setup_ab_tree()
 
1288
        lf = LogCatcher()
 
1289
        rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
 
1290
        log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
 
1291
                     end_revision=rev)
 
1292
        self.assertEqual(1, len(lf.revisions))
 
1293
        self.assertEqual(None, lf.revisions[0].revno)   # Out-of-branch
 
1294
        self.assertEqual(b'3a', lf.revisions[0].rev.revision_id)
 
1295
 
 
1296
    def test_many_revisions(self):
 
1297
        tree = self.setup_ab_tree()
 
1298
        lf = LogCatcher()
 
1299
        start_rev = revisionspec.RevisionInfo(tree.branch, None, b'1a')
 
1300
        end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
 
1301
        log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
 
1302
                     end_revision=end_rev)
 
1303
        self.assertEqual(3, len(lf.revisions))
 
1304
        self.assertEqual(None, lf.revisions[0].revno)   # Out-of-branch
 
1305
        self.assertEqual(b'3a', lf.revisions[0].rev.revision_id)
 
1306
        self.assertEqual(None, lf.revisions[1].revno)   # Out-of-branch
 
1307
        self.assertEqual(b'2a', lf.revisions[1].rev.revision_id)
 
1308
        self.assertEqual('1', lf.revisions[2].revno)    # In-branch
 
1309
 
 
1310
    def test_long_format(self):
 
1311
        tree = self.setup_ab_tree()
 
1312
        start_rev = revisionspec.RevisionInfo(tree.branch, None, b'1a')
 
1313
        end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
 
1314
        self.assertFormatterResult(b"""\
 
1315
------------------------------------------------------------
 
1316
revision-id: 3a
 
1317
committer: Joe Foo <joe@foo.com>
 
1318
branch nick: tree
 
1319
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1320
message:
 
1321
  commit 3a
 
1322
------------------------------------------------------------
 
1323
revision-id: 2a
 
1324
committer: Joe Foo <joe@foo.com>
 
1325
branch nick: tree
 
1326
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1327
message:
 
1328
  commit 2a
 
1329
------------------------------------------------------------
 
1330
revno: 1
 
1331
committer: Joe Foo <joe@foo.com>
 
1332
branch nick: tree
 
1333
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1334
message:
 
1335
  commit 1a
 
1336
""",
 
1337
                                   tree.branch, log.LongLogFormatter, show_log_kwargs={
 
1338
                                       'start_revision': start_rev, 'end_revision': end_rev
 
1339
                                       })
 
1340
 
 
1341
    def test_short_format(self):
 
1342
        tree = self.setup_ab_tree()
 
1343
        start_rev = revisionspec.RevisionInfo(tree.branch, None, b'1a')
 
1344
        end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
 
1345
        self.assertFormatterResult(b"""\
 
1346
      Joe Foo\t2005-11-22
 
1347
      revision-id:3a
 
1348
      commit 3a
 
1349
 
 
1350
      Joe Foo\t2005-11-22
 
1351
      revision-id:2a
 
1352
      commit 2a
 
1353
 
 
1354
    1 Joe Foo\t2005-11-22
 
1355
      commit 1a
 
1356
 
 
1357
""",
 
1358
                                   tree.branch, log.ShortLogFormatter, show_log_kwargs={
 
1359
                                       'start_revision': start_rev, 'end_revision': end_rev
 
1360
                                       })
 
1361
 
 
1362
    def test_line_format(self):
 
1363
        tree = self.setup_ab_tree()
 
1364
        start_rev = revisionspec.RevisionInfo(tree.branch, None, b'1a')
 
1365
        end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
 
1366
        self.assertFormatterResult(b"""\
 
1367
Joe Foo 2005-11-22 commit 3a
 
1368
Joe Foo 2005-11-22 commit 2a
 
1369
1: Joe Foo 2005-11-22 commit 1a
 
1370
""",
 
1371
                                   tree.branch, log.LineLogFormatter, show_log_kwargs={
 
1372
                                       'start_revision': start_rev, 'end_revision': end_rev
 
1373
                                       })
 
1374
 
1466
1375
 
1467
1376
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1468
1377
 
1469
1378
    def setUp(self):
1470
 
        TestCaseForLogFormatter.setUp(self)
 
1379
        super(TestLogWithBugs, self).setUp()
1471
1380
        log.properties_handler_registry.register(
1472
1381
            'bugs_properties_handler',
1473
1382
            log._bugs_properties_handler)
1477
1386
        tree = self.make_branch_and_tree(u'.')
1478
1387
        self.build_tree(['a', 'b'])
1479
1388
        tree.add('a')
1480
 
        self.wt_commit(tree, 'simple log message', rev_id='a1',
1481
 
                       revprops={'bugs': 'test://bug/id fixed'})
 
1389
        self.wt_commit(tree, 'simple log message', rev_id=b'a1',
 
1390
                       revprops={u'bugs': 'test://bug/id fixed'})
1482
1391
        tree.add('b')
1483
 
        self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id='a2',
 
1392
        self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id=b'a2',
1484
1393
                       authors=['Joe Bar <joe@bar.com>'],
1485
 
                       revprops={'bugs': 'test://bug/id fixed\n'
 
1394
                       revprops={u'bugs': 'test://bug/id fixed\n'
1486
1395
                                 'test://bug/2 fixed'})
1487
1396
        return tree
1488
1397
 
1489
 
 
1490
1398
    def test_long_bugs(self):
1491
1399
        tree = self.make_commits_with_bugs()
1492
 
        self.assertFormatterResult("""\
 
1400
        self.assertFormatterResult(b"""\
1493
1401
------------------------------------------------------------
1494
1402
revno: 2
1495
 
fixes bug(s): test://bug/id test://bug/2
 
1403
fixes bugs: test://bug/id test://bug/2
1496
1404
author: Joe Bar <joe@bar.com>
1497
1405
committer: Joe Foo <joe@foo.com>
1498
1406
branch nick: work
1503
1411
  message
1504
1412
------------------------------------------------------------
1505
1413
revno: 1
1506
 
fixes bug(s): test://bug/id
 
1414
fixes bug: test://bug/id
1507
1415
committer: Joe Foo <joe@foo.com>
1508
1416
branch nick: work
1509
1417
timestamp: Tue 2005-11-22 00:00:00 +0000
1510
1418
message:
1511
1419
  simple log message
1512
1420
""",
1513
 
            tree.branch, log.LongLogFormatter)
 
1421
                                   tree.branch, log.LongLogFormatter)
1514
1422
 
1515
1423
    def test_short_bugs(self):
1516
1424
        tree = self.make_commits_with_bugs()
1517
 
        self.assertFormatterResult("""\
 
1425
        self.assertFormatterResult(b"""\
1518
1426
    2 Joe Bar\t2005-11-22
1519
 
      fixes bug(s): test://bug/id test://bug/2
 
1427
      fixes bugs: test://bug/id test://bug/2
1520
1428
      multiline
1521
1429
      log
1522
1430
      message
1523
1431
 
1524
1432
    1 Joe Foo\t2005-11-22
1525
 
      fixes bug(s): test://bug/id
 
1433
      fixes bug: test://bug/id
1526
1434
      simple log message
1527
1435
 
1528
1436
""",
1529
 
            tree.branch, log.ShortLogFormatter)
 
1437
                                   tree.branch, log.ShortLogFormatter)
1530
1438
 
1531
1439
    def test_wrong_bugs_property(self):
1532
1440
        tree = self.make_branch_and_tree(u'.')
1533
1441
        self.build_tree(['foo'])
1534
 
        self.wt_commit(tree, 'simple log message', rev_id='a1',
1535
 
                       revprops={'bugs': 'test://bug/id invalid_value'})
1536
 
        self.assertFormatterResult("""\
 
1442
        self.wt_commit(tree, 'simple log message', rev_id=b'a1',
 
1443
                       revprops={u'bugs': 'test://bug/id invalid_value'})
 
1444
        self.assertFormatterResult(b"""\
1537
1445
    1 Joe Foo\t2005-11-22
1538
1446
      simple log message
1539
1447
 
1540
1448
""",
1541
 
            tree.branch, log.ShortLogFormatter)
 
1449
                                   tree.branch, log.ShortLogFormatter)
1542
1450
 
1543
1451
    def test_bugs_handler_present(self):
1544
1452
        self.properties_handler_registry.get('bugs_properties_handler')
1545
1453
 
 
1454
 
 
1455
class TestLogForAuthors(TestCaseForLogFormatter):
 
1456
 
 
1457
    def setUp(self):
 
1458
        super(TestLogForAuthors, self).setUp()
 
1459
        self.wt = self.make_standard_commit('nicky',
 
1460
                                            authors=['John Doe <jdoe@example.com>',
 
1461
                                                     'Jane Rey <jrey@example.com>'])
 
1462
 
 
1463
    def assertFormatterResult(self, formatter, who, result):
 
1464
        formatter_kwargs = dict()
 
1465
        if who is not None:
 
1466
            author_list_handler = log.author_list_registry.get(who)
 
1467
            formatter_kwargs['author_list_handler'] = author_list_handler
 
1468
        TestCaseForLogFormatter.assertFormatterResult(self, result,
 
1469
                                                      self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
 
1470
 
 
1471
    def test_line_default(self):
 
1472
        self.assertFormatterResult(log.LineLogFormatter, None, b"""\
 
1473
1: John Doe 2005-11-22 add a
 
1474
""")
 
1475
 
 
1476
    def test_line_committer(self):
 
1477
        self.assertFormatterResult(log.LineLogFormatter, 'committer', b"""\
 
1478
1: Lorem Ipsum 2005-11-22 add a
 
1479
""")
 
1480
 
 
1481
    def test_line_first(self):
 
1482
        self.assertFormatterResult(log.LineLogFormatter, 'first', b"""\
 
1483
1: John Doe 2005-11-22 add a
 
1484
""")
 
1485
 
 
1486
    def test_line_all(self):
 
1487
        self.assertFormatterResult(log.LineLogFormatter, 'all', b"""\
 
1488
1: John Doe, Jane Rey 2005-11-22 add a
 
1489
""")
 
1490
 
 
1491
    def test_short_default(self):
 
1492
        self.assertFormatterResult(log.ShortLogFormatter, None, b"""\
 
1493
    1 John Doe\t2005-11-22
 
1494
      add a
 
1495
 
 
1496
""")
 
1497
 
 
1498
    def test_short_committer(self):
 
1499
        self.assertFormatterResult(log.ShortLogFormatter, 'committer', b"""\
 
1500
    1 Lorem Ipsum\t2005-11-22
 
1501
      add a
 
1502
 
 
1503
""")
 
1504
 
 
1505
    def test_short_first(self):
 
1506
        self.assertFormatterResult(log.ShortLogFormatter, 'first', b"""\
 
1507
    1 John Doe\t2005-11-22
 
1508
      add a
 
1509
 
 
1510
""")
 
1511
 
 
1512
    def test_short_all(self):
 
1513
        self.assertFormatterResult(log.ShortLogFormatter, 'all', b"""\
 
1514
    1 John Doe, Jane Rey\t2005-11-22
 
1515
      add a
 
1516
 
 
1517
""")
 
1518
 
 
1519
    def test_long_default(self):
 
1520
        self.assertFormatterResult(log.LongLogFormatter, None, b"""\
 
1521
------------------------------------------------------------
 
1522
revno: 1
 
1523
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
1524
committer: Lorem Ipsum <test@example.com>
 
1525
branch nick: nicky
 
1526
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1527
message:
 
1528
  add a
 
1529
""")
 
1530
 
 
1531
    def test_long_committer(self):
 
1532
        self.assertFormatterResult(log.LongLogFormatter, 'committer', b"""\
 
1533
------------------------------------------------------------
 
1534
revno: 1
 
1535
committer: Lorem Ipsum <test@example.com>
 
1536
branch nick: nicky
 
1537
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1538
message:
 
1539
  add a
 
1540
""")
 
1541
 
 
1542
    def test_long_first(self):
 
1543
        self.assertFormatterResult(log.LongLogFormatter, 'first', b"""\
 
1544
------------------------------------------------------------
 
1545
revno: 1
 
1546
author: John Doe <jdoe@example.com>
 
1547
committer: Lorem Ipsum <test@example.com>
 
1548
branch nick: nicky
 
1549
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1550
message:
 
1551
  add a
 
1552
""")
 
1553
 
 
1554
    def test_long_all(self):
 
1555
        self.assertFormatterResult(log.LongLogFormatter, 'all', b"""\
 
1556
------------------------------------------------------------
 
1557
revno: 1
 
1558
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
1559
committer: Lorem Ipsum <test@example.com>
 
1560
branch nick: nicky
 
1561
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1562
message:
 
1563
  add a
 
1564
""")
 
1565
 
 
1566
    def test_gnu_changelog_default(self):
 
1567
        self.assertFormatterResult(log.GnuChangelogLogFormatter, None, b"""\
 
1568
2005-11-22  John Doe  <jdoe@example.com>
 
1569
 
 
1570
\tadd a
 
1571
 
 
1572
""")
 
1573
 
 
1574
    def test_gnu_changelog_committer(self):
 
1575
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', b"""\
 
1576
2005-11-22  Lorem Ipsum  <test@example.com>
 
1577
 
 
1578
\tadd a
 
1579
 
 
1580
""")
 
1581
 
 
1582
    def test_gnu_changelog_first(self):
 
1583
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', b"""\
 
1584
2005-11-22  John Doe  <jdoe@example.com>
 
1585
 
 
1586
\tadd a
 
1587
 
 
1588
""")
 
1589
 
 
1590
    def test_gnu_changelog_all(self):
 
1591
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', b"""\
 
1592
2005-11-22  John Doe  <jdoe@example.com>, Jane Rey  <jrey@example.com>
 
1593
 
 
1594
\tadd a
 
1595
 
 
1596
""")
 
1597
 
 
1598
 
1546
1599
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1547
1600
 
1548
1601
    def make_branch_with_alternate_ancestries(self, relpath='.'):
1549
1602
        # See test_merge_sorted_exclude_ancestry below for the difference with
1550
1603
        # bt.per_branch.test_iter_merge_sorted_revision.
1551
 
        # TestIterMergeSortedRevisionsBushyGraph. 
 
1604
        # TestIterMergeSortedRevisionsBushyGraph.
1552
1605
        # make_branch_with_alternate_ancestries
1553
1606
        # and test_merge_sorted_exclude_ancestry
1554
1607
        # See the FIXME in assertLogRevnos too.
1565
1618
        # | /
1566
1619
        # 3
1567
1620
        builder.start_series()
1568
 
        builder.build_snapshot('1', None, [
1569
 
            ('add', ('', 'TREE_ROOT', 'directory', '')),])
1570
 
        builder.build_snapshot('1.1.1', ['1'], [])
1571
 
        builder.build_snapshot('2', ['1'], [])
1572
 
        builder.build_snapshot('1.2.1', ['1.1.1'], [])
1573
 
        builder.build_snapshot('1.1.2', ['1.1.1', '1.2.1'], [])
1574
 
        builder.build_snapshot('3', ['2', '1.1.2'], [])
 
1621
        builder.build_snapshot(None, [
 
1622
            ('add', ('', b'TREE_ROOT', 'directory', '')), ],
 
1623
            revision_id=b'1')
 
1624
        builder.build_snapshot([b'1'], [], revision_id=b'1.1.1')
 
1625
        builder.build_snapshot([b'1'], [], revision_id=b'2')
 
1626
        builder.build_snapshot([b'1.1.1'], [], revision_id=b'1.2.1')
 
1627
        builder.build_snapshot([b'1.1.1', b'1.2.1'], [], revision_id=b'1.1.2')
 
1628
        builder.build_snapshot([b'2', b'1.1.2'], [], revision_id=b'3')
1575
1629
        builder.finish_series()
1576
1630
        br = builder.get_branch()
1577
1631
        br.lock_read()
1579
1633
        return br
1580
1634
 
1581
1635
    def assertLogRevnos(self, expected_revnos, b, start, end,
1582
 
                        exclude_common_ancestry):
 
1636
                        exclude_common_ancestry, generate_merge_revisions=True):
1583
1637
        # FIXME: the layering in log makes it hard to test intermediate levels,
1584
 
        # I wish adding filters with their parameters were easier...
 
1638
        # I wish adding filters with their parameters was easier...
1585
1639
        # -- vila 20100413
1586
1640
        iter_revs = log._calc_view_revisions(
1587
1641
            b, start, end, direction='reverse',
1588
 
            generate_merge_revisions=True,
 
1642
            generate_merge_revisions=generate_merge_revisions,
1589
1643
            exclude_common_ancestry=exclude_common_ancestry)
1590
1644
        self.assertEqual(expected_revnos,
1591
1645
                         [revid for revid, revno, depth in iter_revs])
1592
1646
 
1593
1647
    def test_merge_sorted_exclude_ancestry(self):
1594
1648
        b = self.make_branch_with_alternate_ancestries()
1595
 
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1596
 
                             b, '1', '3', False)
 
1649
        self.assertLogRevnos([b'3', b'1.1.2', b'1.2.1', b'1.1.1', b'2', b'1'],
 
1650
                             b, b'1', b'3', exclude_common_ancestry=False)
1597
1651
        # '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1598
1652
        # it should be mentioned even if merge_sort order will make it appear
1599
1653
        # after 1.1.1
1600
 
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1601
 
                             b, '1.1.1', '3', True)
1602
 
 
1603
 
 
 
1654
        self.assertLogRevnos([b'3', b'1.1.2', b'1.2.1', b'2'],
 
1655
                             b, b'1.1.1', b'3', exclude_common_ancestry=True)
 
1656
 
 
1657
    def test_merge_sorted_simple_revnos_exclude_ancestry(self):
 
1658
        b = self.make_branch_with_alternate_ancestries()
 
1659
        self.assertLogRevnos([b'3', b'2'],
 
1660
                             b, b'1', b'3', exclude_common_ancestry=True,
 
1661
                             generate_merge_revisions=False)
 
1662
        self.assertLogRevnos([b'3', b'1.1.2', b'1.2.1', b'1.1.1', b'2'],
 
1663
                             b, b'1', b'3', exclude_common_ancestry=True,
 
1664
                             generate_merge_revisions=True)
 
1665
 
 
1666
 
 
1667
class TestLogDefaults(TestCaseForLogFormatter):
 
1668
    def test_default_log_level(self):
 
1669
        """
 
1670
        Test to ensure that specifying 'levels=1' to make_log_request_dict
 
1671
        doesn't get overwritten when using a LogFormatter that supports more
 
1672
        detail.
 
1673
        Fixes bug #747958.
 
1674
        """
 
1675
        wt = self._prepare_tree_with_merges()
 
1676
        b = wt.branch
 
1677
 
 
1678
        class CustomLogFormatter(log.LogFormatter):
 
1679
            def __init__(self, *args, **kwargs):
 
1680
                super(CustomLogFormatter, self).__init__(*args, **kwargs)
 
1681
                self.revisions = []
 
1682
 
 
1683
            def get_levels(self):
 
1684
                # log formatter supports all levels:
 
1685
                return 0
 
1686
 
 
1687
            def log_revision(self, revision):
 
1688
                self.revisions.append(revision)
 
1689
 
 
1690
        log_formatter = LogCatcher()
 
1691
        # First request we don't specify number of levels, we should get a
 
1692
        # sensible default (whatever the LogFormatter handles - which in this
 
1693
        # case is 0/everything):
 
1694
        request = log.make_log_request_dict(limit=10)
 
1695
        log.Logger(b, request).show(log_formatter)
 
1696
        # should have all three revisions:
 
1697
        self.assertEqual(len(log_formatter.revisions), 3)
 
1698
 
 
1699
        del log_formatter
 
1700
        log_formatter = LogCatcher()
 
1701
        # now explicitly request mainline revisions only:
 
1702
        request = log.make_log_request_dict(limit=10, levels=1)
 
1703
        log.Logger(b, request).show(log_formatter)
 
1704
        # should now only have 2 revisions:
 
1705
        self.assertEqual(len(log_formatter.revisions), 2)