~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Vincent Ladeuil
  • Date: 2016-02-01 19:26:41 UTC
  • mto: This revision was merged to the branch mainline in revision 6616.
  • Revision ID: v.ladeuil+lp@free.fr-20160201192641-mzn90m51rydhw00n
Open trunk again as 2.8b1

Show diffs side-by-side

added added

removed removed

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