/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 bzrlib/tests/test_log.py

  • Committer: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

Show diffs side-by-side

added added

removed removed

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