~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-04-09 20:23:07 UTC
  • mfrom: (4265.1.4 bbc-merge)
  • Revision ID: pqm@pqm.ubuntu.com-20090409202307-n0depb16qepoe21o
(jam) Change _fetch_uses_deltas = False for CHK repos until we can
        write a better fix.

Show diffs side-by-side

added added

removed removed

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