~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: John Arbash Meinel
  • Date: 2008-07-11 21:41:24 UTC
  • mto: This revision was merged to the branch mainline in revision 3543.
  • Revision ID: john@arbash-meinel.com-20080711214124-qi09irlj7pd5cuzg
Shortcut the case when one revision is in the ancestry of the other.

At the cost of a heads() check, when one parent supersedes, we don't have to extract
the text for the other. Changes merge time from 3m37s => 3m21s. Using a
CachingParentsProvider would drop the time down to 3m11s.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
import os
18
18
from cStringIO import StringIO
19
19
 
20
 
from bzrlib import (
21
 
    errors,
22
 
    log,
23
 
    registry,
24
 
    revision,
25
 
    revisionspec,
26
 
    tests,
27
 
    )
28
 
 
29
 
 
30
 
class TestCaseWithoutPropsHandler(tests.TestCaseWithTransport):
31
 
 
32
 
    def setUp(self):
33
 
        super(TestCaseWithoutPropsHandler, self).setUp()
34
 
        # keep a reference to the "current" custom prop. handler registry
35
 
        self.properties_handler_registry = log.properties_handler_registry
36
 
        # clean up the registry in log
37
 
        log.properties_handler_registry = registry.Registry()
38
 
 
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
43
 
 
44
 
 
45
 
class LogCatcher(log.LogFormatter):
 
20
from bzrlib import log
 
21
from bzrlib.tests import TestCase, TestCaseWithTransport
 
22
from bzrlib.log import (show_log,
 
23
                        get_view_revisions,
 
24
                        LogRevision,
 
25
                        LogFormatter,
 
26
                        LongLogFormatter,
 
27
                        ShortLogFormatter,
 
28
                        LineLogFormatter)
 
29
from bzrlib.branch import Branch
 
30
from bzrlib.errors import (
 
31
    BzrCommandError,
 
32
    InvalidRevisionNumber,
 
33
    )
 
34
from bzrlib.revision import Revision
 
35
from bzrlib.revisionspec import (
 
36
    RevisionInfo,
 
37
    RevisionSpec,
 
38
    )
 
39
 
 
40
 
 
41
class LogCatcher(LogFormatter):
46
42
    """Pull log messages into list rather than displaying them.
47
43
 
48
44
    For ease of testing we save log messages here rather than actually
62
58
        self.logs.append(revision)
63
59
 
64
60
 
65
 
class TestShowLog(tests.TestCaseWithTransport):
 
61
class TestShowLog(TestCaseWithTransport):
66
62
 
67
63
    def checkDelta(self, delta, **kw):
68
 
        """Check the filenames touched by a delta are as expected.
69
 
 
70
 
        Caller only have to pass in the list of files for each part, all
71
 
        unspecified parts are considered empty (and checked as such).
72
 
        """
 
64
        """Check the filenames touched by a delta are as expected."""
73
65
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
74
 
            # By default we expect an empty list
75
66
            expected = kw.get(n, [])
76
67
            # strip out only the path components
77
68
            got = [x[0] for x in getattr(delta, n)]
78
 
            self.assertEqual(expected, got)
79
 
 
80
 
    def assertInvalidRevisonNumber(self, br, start, end):
81
 
        lf = LogCatcher()
82
 
        self.assertRaises(errors.InvalidRevisionNumber,
83
 
                          log.show_log, br, lf,
84
 
                          start_revision=start, end_revision=end)
 
69
            self.assertEquals(expected, got)
85
70
 
86
71
    def test_cur_revno(self):
87
72
        wt = self.make_branch_and_tree('.')
89
74
 
90
75
        lf = LogCatcher()
91
76
        wt.commit('empty commit')
92
 
        log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
93
 
 
94
 
        # Since there is a single revision in the branch all the combinations
95
 
        # below should fail.
96
 
        self.assertInvalidRevisonNumber(b, 2, 1)
97
 
        self.assertInvalidRevisonNumber(b, 1, 2)
98
 
        self.assertInvalidRevisonNumber(b, 0, 2)
99
 
        self.assertInvalidRevisonNumber(b, 1, 0)
100
 
        self.assertInvalidRevisonNumber(b, -1, 1)
101
 
        self.assertInvalidRevisonNumber(b, 1, -1)
102
 
 
103
 
    def test_empty_branch(self):
 
77
        show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
 
78
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
79
                          start_revision=2, end_revision=1) 
 
80
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
81
                          start_revision=1, end_revision=2) 
 
82
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
83
                          start_revision=0, end_revision=2) 
 
84
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
85
                          start_revision=1, end_revision=0) 
 
86
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
87
                          start_revision=-1, end_revision=1) 
 
88
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
89
                          start_revision=1, end_revision=-1) 
 
90
 
 
91
    def test_simple_log(self):
 
92
        eq = self.assertEquals
 
93
        
104
94
        wt = self.make_branch_and_tree('.')
 
95
        b = wt.branch
105
96
 
106
97
        lf = LogCatcher()
107
 
        log.show_log(wt.branch, lf)
 
98
        show_log(b, lf)
108
99
        # no entries yet
109
 
        self.assertEqual([], lf.logs)
110
 
 
111
 
    def test_empty_commit(self):
112
 
        wt = self.make_branch_and_tree('.')
 
100
        eq(lf.logs, [])
113
101
 
114
102
        wt.commit('empty commit')
115
103
        lf = LogCatcher()
116
 
        log.show_log(wt.branch, lf, verbose=True)
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)
 
104
        show_log(b, lf, verbose=True)
 
105
        eq(len(lf.logs), 1)
 
106
        eq(lf.logs[0].revno, '1')
 
107
        eq(lf.logs[0].rev.message, 'empty commit')
 
108
        d = lf.logs[0].delta
 
109
        self.log('log delta: %r' % d)
 
110
        self.checkDelta(d)
121
111
 
122
 
    def test_simple_commit(self):
123
 
        wt = self.make_branch_and_tree('.')
124
 
        wt.commit('empty commit')
125
112
        self.build_tree(['hello'])
126
113
        wt.add('hello')
127
114
        wt.commit('add one file',
128
115
                  committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
129
116
                            u'<test@example.com>')
 
117
 
 
118
        lf = self.make_utf8_encoded_stringio()
 
119
        # log using regular thing
 
120
        show_log(b, LongLogFormatter(lf))
 
121
        lf.seek(0)
 
122
        for l in lf.readlines():
 
123
            self.log(l)
 
124
 
 
125
        # get log as data structure
130
126
        lf = LogCatcher()
131
 
        log.show_log(wt.branch, lf, verbose=True)
132
 
        self.assertEqual(2, len(lf.logs))
 
127
        show_log(b, lf, verbose=True)
 
128
        eq(len(lf.logs), 2)
 
129
        self.log('log entries:')
 
130
        for logentry in lf.logs:
 
131
            self.log('%4s %s' % (logentry.revno, logentry.rev.message))
 
132
        
133
133
        # first one is most recent
134
 
        log_entry = lf.logs[0]
135
 
        self.assertEqual('2', log_entry.revno)
136
 
        self.assertEqual('add one file', log_entry.rev.message)
137
 
        self.checkDelta(log_entry.delta, added=['hello'])
138
 
 
139
 
    def test_commit_message_with_control_chars(self):
140
 
        wt = self.make_branch_and_tree('.')
141
 
        msg = u"All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
142
 
        msg = msg.replace(u'\r', u'\n')
 
134
        logentry = lf.logs[0]
 
135
        eq(logentry.revno, '2')
 
136
        eq(logentry.rev.message, 'add one file')
 
137
        d = logentry.delta
 
138
        self.log('log 2 delta: %r' % d)
 
139
        self.checkDelta(d, added=['hello'])
 
140
        
 
141
        # commit a log message with control characters
 
142
        msg = "All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
 
143
        self.log("original commit message: %r", msg)
143
144
        wt.commit(msg)
144
145
        lf = LogCatcher()
145
 
        log.show_log(wt.branch, lf, verbose=True)
 
146
        show_log(b, lf, verbose=True)
146
147
        committed_msg = lf.logs[0].rev.message
147
 
        self.assertNotEqual(msg, committed_msg)
148
 
        self.assertTrue(len(committed_msg) > len(msg))
 
148
        self.log("escaped commit message: %r", committed_msg)
 
149
        self.assert_(msg != committed_msg)
 
150
        self.assert_(len(committed_msg) > len(msg))
149
151
 
150
 
    def test_commit_message_without_control_chars(self):
151
 
        wt = self.make_branch_and_tree('.')
 
152
        # Check that log message with only XML-valid characters isn't
152
153
        # escaped.  As ElementTree apparently does some kind of
153
154
        # newline conversion, neither LF (\x0A) nor CR (\x0D) are
154
155
        # included in the test commit message, even though they are
155
156
        # valid XML 1.0 characters.
156
157
        msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
 
158
        self.log("original commit message: %r", msg)
157
159
        wt.commit(msg)
158
160
        lf = LogCatcher()
159
 
        log.show_log(wt.branch, lf, verbose=True)
 
161
        show_log(b, lf, verbose=True)
160
162
        committed_msg = lf.logs[0].rev.message
161
 
        self.assertEqual(msg, committed_msg)
 
163
        self.log("escaped commit message: %r", committed_msg)
 
164
        self.assert_(msg == committed_msg)
162
165
 
163
166
    def test_deltas_in_merge_revisions(self):
164
167
        """Check deltas created for both mainline and merge revisions"""
 
168
        eq = self.assertEquals
165
169
        wt = self.make_branch_and_tree('parent')
166
170
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
167
171
        wt.add('file1')
179
183
        b = wt.branch
180
184
        lf = LogCatcher()
181
185
        lf.supports_merge_revisions = True
182
 
        log.show_log(b, lf, verbose=True)
183
 
 
184
 
        self.assertEqual(3, len(lf.logs))
185
 
 
 
186
        show_log(b, lf, verbose=True)
 
187
        eq(len(lf.logs),3)
186
188
        logentry = lf.logs[0]
187
 
        self.assertEqual('2', logentry.revno)
188
 
        self.assertEqual('merge child branch', logentry.rev.message)
189
 
        self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
190
 
 
 
189
        eq(logentry.revno, '2')
 
190
        eq(logentry.rev.message, 'merge child branch')
 
191
        d = logentry.delta
 
192
        self.checkDelta(d, removed=['file1'], modified=['file2'])
191
193
        logentry = lf.logs[1]
192
 
        self.assertEqual('1.1.1', logentry.revno)
193
 
        self.assertEqual('remove file1 and modify file2', logentry.rev.message)
194
 
        self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
195
 
 
 
194
        eq(logentry.revno, '1.1.1')
 
195
        eq(logentry.rev.message, 'remove file1 and modify file2')
 
196
        d = logentry.delta
 
197
        self.checkDelta(d, removed=['file1'], modified=['file2'])
196
198
        logentry = lf.logs[2]
197
 
        self.assertEqual('1', logentry.revno)
198
 
        self.assertEqual('add file1 and file2', logentry.rev.message)
199
 
        self.checkDelta(logentry.delta, added=['file1', 'file2'])
 
199
        eq(logentry.revno, '1')
 
200
        eq(logentry.rev.message, 'add file1 and file2')
 
201
        d = logentry.delta
 
202
        self.checkDelta(d, added=['file1', 'file2'])
200
203
 
201
204
    def test_merges_nonsupporting_formatter(self):
202
205
        """Tests that show_log will raise if the formatter doesn't
203
206
        support merge revisions."""
204
207
        wt = self.make_branch_and_memory_tree('.')
205
208
        wt.lock_write()
206
 
        self.addCleanup(wt.unlock)
207
 
        wt.add('')
208
 
        wt.commit('rev-1', rev_id='rev-1',
209
 
                  timestamp=1132586655, timezone=36000,
210
 
                  committer='Joe Foo <joe@foo.com>')
211
 
        wt.commit('rev-merged', rev_id='rev-2a',
212
 
                  timestamp=1132586700, timezone=36000,
213
 
                  committer='Joe Foo <joe@foo.com>')
214
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
215
 
        wt.branch.set_last_revision_info(1, 'rev-1')
216
 
        wt.commit('rev-2', rev_id='rev-2b',
217
 
                  timestamp=1132586800, timezone=36000,
218
 
                  committer='Joe Foo <joe@foo.com>')
219
 
        logfile = self.make_utf8_encoded_stringio()
220
 
        formatter = log.ShortLogFormatter(to_file=logfile)
221
 
        wtb = wt.branch
222
 
        lf = LogCatcher()
223
 
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
224
 
        rev = revspec.in_history(wtb)
225
 
        self.assertRaises(errors.BzrCommandError, log.show_log, wtb, lf,
226
 
                          start_revision=rev, end_revision=rev)
 
209
        try:
 
210
            wt.add('')
 
211
            wt.commit('rev-1', rev_id='rev-1',
 
212
                      timestamp=1132586655, timezone=36000,
 
213
                      committer='Joe Foo <joe@foo.com>')
 
214
            wt.commit('rev-merged', rev_id='rev-2a',
 
215
                      timestamp=1132586700, timezone=36000,
 
216
                      committer='Joe Foo <joe@foo.com>')
 
217
            wt.set_parent_ids(['rev-1', 'rev-2a'])
 
218
            wt.branch.set_last_revision_info(1, 'rev-1')
 
219
            wt.commit('rev-2', rev_id='rev-2b',
 
220
                      timestamp=1132586800, timezone=36000,
 
221
                      committer='Joe Foo <joe@foo.com>')
 
222
            logfile = self.make_utf8_encoded_stringio()
 
223
            formatter = ShortLogFormatter(to_file=logfile)
 
224
            wtb = wt.branch
 
225
            lf = LogCatcher()
 
226
            revspec = RevisionSpec.from_string('1.1.1')
 
227
            rev = revspec.in_history(wtb)
 
228
            self.assertRaises(BzrCommandError, show_log, wtb, lf,
 
229
                              start_revision=rev, end_revision=rev)
 
230
        finally:
 
231
            wt.unlock()
227
232
 
228
233
 
229
234
def make_commits_with_trailing_newlines(wt):
230
 
    """Helper method for LogFormatter tests"""
 
235
    """Helper method for LogFormatter tests"""    
231
236
    b = wt.branch
232
237
    b.nick='test'
233
238
    open('a', 'wb').write('hello moto\n')
267
272
    return ''.join(lines)
268
273
 
269
274
 
270
 
class TestShortLogFormatter(tests.TestCaseWithTransport):
 
275
class TestShortLogFormatter(TestCaseWithTransport):
271
276
 
272
277
    def test_trailing_newlines(self):
273
278
        wt = self.make_branch_and_tree('.')
274
279
        b = make_commits_with_trailing_newlines(wt)
275
280
        sio = self.make_utf8_encoded_stringio()
276
 
        lf = log.ShortLogFormatter(to_file=sio)
277
 
        log.show_log(b, lf)
278
 
        self.assertEqualDiff("""\
 
281
        lf = ShortLogFormatter(to_file=sio)
 
282
        show_log(b, lf)
 
283
        self.assertEqualDiff(sio.getvalue(), """\
279
284
    3 Joe Foo\t2005-11-21
280
285
      single line with trailing newline
281
286
 
287
292
    1 Joe Foo\t2005-11-21
288
293
      simple log message
289
294
 
290
 
""",
291
 
                             sio.getvalue())
292
 
 
293
 
    def _prepare_tree_with_merges(self, with_tags=False):
294
 
        wt = self.make_branch_and_memory_tree('.')
295
 
        wt.lock_write()
296
 
        self.addCleanup(wt.unlock)
297
 
        wt.add('')
298
 
        wt.commit('rev-1', rev_id='rev-1',
299
 
                  timestamp=1132586655, timezone=36000,
300
 
                  committer='Joe Foo <joe@foo.com>')
301
 
        wt.commit('rev-merged', rev_id='rev-2a',
302
 
                  timestamp=1132586700, timezone=36000,
303
 
                  committer='Joe Foo <joe@foo.com>')
304
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
305
 
        wt.branch.set_last_revision_info(1, 'rev-1')
306
 
        wt.commit('rev-2', rev_id='rev-2b',
307
 
                  timestamp=1132586800, timezone=36000,
308
 
                  committer='Joe Foo <joe@foo.com>')
309
 
        if with_tags:
310
 
            branch = wt.branch
311
 
            branch.tags.set_tag('v0.2', 'rev-2b')
312
 
            wt.commit('rev-3', rev_id='rev-3',
313
 
                      timestamp=1132586900, timezone=36000,
314
 
                      committer='Jane Foo <jane@foo.com>')
315
 
            branch.tags.set_tag('v1.0rc1', 'rev-3')
316
 
            branch.tags.set_tag('v1.0', 'rev-3')
317
 
        return wt
 
295
""")
318
296
 
319
297
    def test_short_log_with_merges(self):
320
 
        wt = self._prepare_tree_with_merges()
321
 
        logfile = self.make_utf8_encoded_stringio()
322
 
        formatter = log.ShortLogFormatter(to_file=logfile)
323
 
        log.show_log(wt.branch, formatter)
324
 
        self.assertEqualDiff("""\
325
 
    2 Joe Foo\t2005-11-22 [merge]
326
 
      rev-2
327
 
 
328
 
    1 Joe Foo\t2005-11-22
329
 
      rev-1
330
 
 
331
 
""",
332
 
                             logfile.getvalue())
333
 
 
334
 
    def test_short_log_with_merges_and_range(self):
335
298
        wt = self.make_branch_and_memory_tree('.')
336
299
        wt.lock_write()
337
 
        self.addCleanup(wt.unlock)
338
 
        wt.add('')
339
 
        wt.commit('rev-1', rev_id='rev-1',
340
 
                  timestamp=1132586655, timezone=36000,
341
 
                  committer='Joe Foo <joe@foo.com>')
342
 
        wt.commit('rev-merged', rev_id='rev-2a',
343
 
                  timestamp=1132586700, timezone=36000,
344
 
                  committer='Joe Foo <joe@foo.com>')
345
 
        wt.branch.set_last_revision_info(1, 'rev-1')
346
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
347
 
        wt.commit('rev-2b', rev_id='rev-2b',
348
 
                  timestamp=1132586800, timezone=36000,
349
 
                  committer='Joe Foo <joe@foo.com>')
350
 
        wt.commit('rev-3a', rev_id='rev-3a',
351
 
                  timestamp=1132586800, timezone=36000,
352
 
                  committer='Joe Foo <joe@foo.com>')
353
 
        wt.branch.set_last_revision_info(2, 'rev-2b')
354
 
        wt.set_parent_ids(['rev-2b', 'rev-3a'])
355
 
        wt.commit('rev-3b', rev_id='rev-3b',
356
 
                  timestamp=1132586800, timezone=36000,
357
 
                  committer='Joe Foo <joe@foo.com>')
358
 
        logfile = self.make_utf8_encoded_stringio()
359
 
        formatter = log.ShortLogFormatter(to_file=logfile)
360
 
        log.show_log(wt.branch, formatter,
361
 
            start_revision=2, end_revision=3)
362
 
        self.assertEqualDiff("""\
363
 
    3 Joe Foo\t2005-11-22 [merge]
364
 
      rev-3b
365
 
 
 
300
        try:
 
301
            wt.add('')
 
302
            wt.commit('rev-1', rev_id='rev-1',
 
303
                      timestamp=1132586655, timezone=36000,
 
304
                      committer='Joe Foo <joe@foo.com>')
 
305
            wt.commit('rev-merged', rev_id='rev-2a',
 
306
                      timestamp=1132586700, timezone=36000,
 
307
                      committer='Joe Foo <joe@foo.com>')
 
308
            wt.set_parent_ids(['rev-1', 'rev-2a'])
 
309
            wt.branch.set_last_revision_info(1, 'rev-1')
 
310
            wt.commit('rev-2', rev_id='rev-2b',
 
311
                      timestamp=1132586800, timezone=36000,
 
312
                      committer='Joe Foo <joe@foo.com>')
 
313
            logfile = self.make_utf8_encoded_stringio()
 
314
            formatter = ShortLogFormatter(to_file=logfile)
 
315
            show_log(wt.branch, formatter)
 
316
            self.assertEqualDiff(logfile.getvalue(), """\
366
317
    2 Joe Foo\t2005-11-22 [merge]
367
 
      rev-2b
368
 
 
369
 
""",
370
 
                             logfile.getvalue())
371
 
 
372
 
    def test_short_log_with_tags(self):
373
 
        wt = self._prepare_tree_with_merges(with_tags=True)
374
 
        logfile = self.make_utf8_encoded_stringio()
375
 
        formatter = log.ShortLogFormatter(to_file=logfile)
376
 
        log.show_log(wt.branch, formatter)
377
 
        self.assertEqualDiff("""\
378
 
    3 Jane Foo\t2005-11-22 {v1.0, v1.0rc1}
379
 
      rev-3
380
 
 
381
 
    2 Joe Foo\t2005-11-22 {v0.2} [merge]
382
318
      rev-2
383
319
 
384
320
    1 Joe Foo\t2005-11-22
385
321
      rev-1
386
322
 
387
 
""",
388
 
                             logfile.getvalue())
 
323
""")
 
324
        finally:
 
325
            wt.unlock()
389
326
 
390
327
    def test_short_log_single_merge_revision(self):
391
328
        wt = self.make_branch_and_memory_tree('.')
392
329
        wt.lock_write()
393
 
        self.addCleanup(wt.unlock)
394
 
        wt.add('')
395
 
        wt.commit('rev-1', rev_id='rev-1',
396
 
                  timestamp=1132586655, timezone=36000,
397
 
                  committer='Joe Foo <joe@foo.com>')
398
 
        wt.commit('rev-merged', rev_id='rev-2a',
399
 
                  timestamp=1132586700, timezone=36000,
400
 
                  committer='Joe Foo <joe@foo.com>')
401
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
402
 
        wt.branch.set_last_revision_info(1, 'rev-1')
403
 
        wt.commit('rev-2', rev_id='rev-2b',
404
 
                  timestamp=1132586800, timezone=36000,
405
 
                  committer='Joe Foo <joe@foo.com>')
406
 
        logfile = self.make_utf8_encoded_stringio()
407
 
        formatter = log.ShortLogFormatter(to_file=logfile)
408
 
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
409
 
        wtb = wt.branch
410
 
        rev = revspec.in_history(wtb)
411
 
        log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
412
 
        self.assertEqualDiff("""\
413
 
      1.1.1 Joe Foo\t2005-11-22
414
 
            rev-merged
415
 
 
416
 
""",
417
 
                             logfile.getvalue())
418
 
 
419
 
 
420
 
class TestShortLogFormatterWithMergeRevisions(tests.TestCaseWithTransport):
421
 
 
422
 
    def test_short_merge_revs_log_with_merges(self):
423
 
        wt = self.make_branch_and_memory_tree('.')
424
 
        wt.lock_write()
425
 
        self.addCleanup(wt.unlock)
426
 
        wt.add('')
427
 
        wt.commit('rev-1', rev_id='rev-1',
428
 
                  timestamp=1132586655, timezone=36000,
429
 
                  committer='Joe Foo <joe@foo.com>')
430
 
        wt.commit('rev-merged', rev_id='rev-2a',
431
 
                  timestamp=1132586700, timezone=36000,
432
 
                  committer='Joe Foo <joe@foo.com>')
433
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
434
 
        wt.branch.set_last_revision_info(1, 'rev-1')
435
 
        wt.commit('rev-2', rev_id='rev-2b',
436
 
                  timestamp=1132586800, timezone=36000,
437
 
                  committer='Joe Foo <joe@foo.com>')
438
 
        logfile = self.make_utf8_encoded_stringio()
439
 
        formatter = log.ShortLogFormatter(to_file=logfile, levels=0)
440
 
        log.show_log(wt.branch, formatter)
441
 
        # Note that the 1.1.1 indenting is in fact correct given that
442
 
        # the revision numbers are right justified within 5 characters
443
 
        # for mainline revnos and 9 characters for dotted revnos.
444
 
        self.assertEqualDiff("""\
445
 
    2 Joe Foo\t2005-11-22 [merge]
446
 
      rev-2
447
 
 
448
 
          1.1.1 Joe Foo\t2005-11-22
449
 
                rev-merged
450
 
 
451
 
    1 Joe Foo\t2005-11-22
452
 
      rev-1
453
 
 
454
 
""",
455
 
                             logfile.getvalue())
456
 
 
457
 
    def test_short_merge_revs_log_single_merge_revision(self):
458
 
        wt = self.make_branch_and_memory_tree('.')
459
 
        wt.lock_write()
460
 
        self.addCleanup(wt.unlock)
461
 
        wt.add('')
462
 
        wt.commit('rev-1', rev_id='rev-1',
463
 
                  timestamp=1132586655, timezone=36000,
464
 
                  committer='Joe Foo <joe@foo.com>')
465
 
        wt.commit('rev-merged', rev_id='rev-2a',
466
 
                  timestamp=1132586700, timezone=36000,
467
 
                  committer='Joe Foo <joe@foo.com>')
468
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
469
 
        wt.branch.set_last_revision_info(1, 'rev-1')
470
 
        wt.commit('rev-2', rev_id='rev-2b',
471
 
                  timestamp=1132586800, timezone=36000,
472
 
                  committer='Joe Foo <joe@foo.com>')
473
 
        logfile = self.make_utf8_encoded_stringio()
474
 
        formatter = log.ShortLogFormatter(to_file=logfile, levels=0)
475
 
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
476
 
        wtb = wt.branch
477
 
        rev = revspec.in_history(wtb)
478
 
        log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
479
 
        self.assertEqualDiff("""\
480
 
      1.1.1 Joe Foo\t2005-11-22
481
 
            rev-merged
482
 
 
483
 
""",
484
 
                             logfile.getvalue())
485
 
 
486
 
 
487
 
class TestLongLogFormatter(TestCaseWithoutPropsHandler):
 
330
        try:
 
331
            wt.add('')
 
332
            wt.commit('rev-1', rev_id='rev-1',
 
333
                      timestamp=1132586655, timezone=36000,
 
334
                      committer='Joe Foo <joe@foo.com>')
 
335
            wt.commit('rev-merged', rev_id='rev-2a',
 
336
                      timestamp=1132586700, timezone=36000,
 
337
                      committer='Joe Foo <joe@foo.com>')
 
338
            wt.set_parent_ids(['rev-1', 'rev-2a'])
 
339
            wt.branch.set_last_revision_info(1, 'rev-1')
 
340
            wt.commit('rev-2', rev_id='rev-2b',
 
341
                      timestamp=1132586800, timezone=36000,
 
342
                      committer='Joe Foo <joe@foo.com>')
 
343
            logfile = self.make_utf8_encoded_stringio()
 
344
            formatter = ShortLogFormatter(to_file=logfile)
 
345
            revspec = RevisionSpec.from_string('1.1.1')
 
346
            wtb = wt.branch
 
347
            rev = revspec.in_history(wtb)
 
348
            show_log(wtb, formatter, start_revision=rev, end_revision=rev)
 
349
            self.assertEqualDiff(logfile.getvalue(), """\
 
350
1.1.1 Joe Foo\t2005-11-22
 
351
      rev-merged
 
352
 
 
353
""")
 
354
        finally:
 
355
            wt.unlock()
 
356
 
 
357
 
 
358
class TestLongLogFormatter(TestCaseWithTransport):
488
359
 
489
360
    def test_verbose_log(self):
490
361
        """Verbose log includes changed files
497
368
        wt.add('a')
498
369
        # XXX: why does a longer nick show up?
499
370
        b.nick = 'test_verbose_log'
500
 
        wt.commit(message='add a',
501
 
                  timestamp=1132711707,
 
371
        wt.commit(message='add a', 
 
372
                  timestamp=1132711707, 
502
373
                  timezone=36000,
503
374
                  committer='Lorem Ipsum <test@example.com>')
504
375
        logfile = file('out.tmp', 'w+')
505
 
        formatter = log.LongLogFormatter(to_file=logfile)
506
 
        log.show_log(b, formatter, verbose=True)
 
376
        formatter = LongLogFormatter(to_file=logfile)
 
377
        show_log(b, formatter, verbose=True)
507
378
        logfile.flush()
508
379
        logfile.seek(0)
509
380
        log_contents = logfile.read()
510
 
        self.assertEqualDiff('''\
 
381
        self.assertEqualDiff(log_contents, '''\
511
382
------------------------------------------------------------
512
383
revno: 1
513
384
committer: Lorem Ipsum <test@example.com>
517
388
  add a
518
389
added:
519
390
  a
520
 
''',
521
 
                             log_contents)
 
391
''')
522
392
 
523
393
    def test_merges_are_indented_by_level(self):
524
394
        wt = self.make_branch_and_tree('parent')
536
406
        wt.commit('merge branch 1')
537
407
        b = wt.branch
538
408
        sio = self.make_utf8_encoded_stringio()
539
 
        lf = log.LongLogFormatter(to_file=sio)
540
 
        log.show_log(b, lf, verbose=True)
541
 
        the_log = normalize_log(sio.getvalue())
542
 
        self.assertEqualDiff("""\
 
409
        lf = LongLogFormatter(to_file=sio)
 
410
        show_log(b, lf, verbose=True)
 
411
        log = normalize_log(sio.getvalue())
 
412
        self.assertEqualDiff(log, """\
543
413
------------------------------------------------------------
544
414
revno: 2
545
415
committer: Lorem Ipsum <test@example.com>
575
445
timestamp: Just now
576
446
message:
577
447
  first post
578
 
""",
579
 
                             the_log)
 
448
""")
580
449
 
581
450
    def test_verbose_merge_revisions_contain_deltas(self):
582
451
        wt = self.make_branch_and_tree('parent')
593
462
        wt.commit('merge branch 1')
594
463
        b = wt.branch
595
464
        sio = self.make_utf8_encoded_stringio()
596
 
        lf = log.LongLogFormatter(to_file=sio)
597
 
        log.show_log(b, lf, verbose=True)
598
 
        the_log = normalize_log(sio.getvalue())
599
 
        self.assertEqualDiff("""\
 
465
        lf = LongLogFormatter(to_file=sio)
 
466
        show_log(b, lf, verbose=True)
 
467
        log = normalize_log(sio.getvalue())
 
468
        self.assertEqualDiff(log, """\
600
469
------------------------------------------------------------
601
470
revno: 2
602
471
committer: Lorem Ipsum <test@example.com>
629
498
added:
630
499
  f1
631
500
  f2
632
 
""",
633
 
                             the_log)
 
501
""")
634
502
 
635
503
    def test_trailing_newlines(self):
636
504
        wt = self.make_branch_and_tree('.')
637
505
        b = make_commits_with_trailing_newlines(wt)
638
506
        sio = self.make_utf8_encoded_stringio()
639
 
        lf = log.LongLogFormatter(to_file=sio)
640
 
        log.show_log(b, lf)
641
 
        self.assertEqualDiff("""\
 
507
        lf = LongLogFormatter(to_file=sio)
 
508
        show_log(b, lf)
 
509
        self.assertEqualDiff(sio.getvalue(), """\
642
510
------------------------------------------------------------
643
511
revno: 3
644
512
committer: Joe Foo <joe@foo.com>
663
531
timestamp: Mon 2005-11-21 09:24:15 -0600
664
532
message:
665
533
  simple log message
666
 
""",
667
 
                             sio.getvalue())
 
534
""")
668
535
 
669
536
    def test_author_in_log(self):
670
537
        """Log includes the author name if it's set in
681
548
                  committer='Lorem Ipsum <test@example.com>',
682
549
                  author='John Doe <jdoe@example.com>')
683
550
        sio = StringIO()
684
 
        formatter = log.LongLogFormatter(to_file=sio)
685
 
        log.show_log(b, formatter)
686
 
        self.assertEqualDiff('''\
687
 
------------------------------------------------------------
688
 
revno: 1
689
 
author: John Doe <jdoe@example.com>
690
 
committer: Lorem Ipsum <test@example.com>
691
 
branch nick: test_author_log
692
 
timestamp: Wed 2005-11-23 12:08:27 +1000
693
 
message:
694
 
  add a
695
 
''',
696
 
                             sio.getvalue())
697
 
 
698
 
    def test_properties_in_log(self):
699
 
        """Log includes the custom properties returned by the registered 
700
 
        handlers.
701
 
        """
702
 
        wt = self.make_branch_and_tree('.')
703
 
        b = wt.branch
704
 
        self.build_tree(['a'])
705
 
        wt.add('a')
706
 
        b.nick = 'test_properties_in_log'
707
 
        wt.commit(message='add a',
708
 
                  timestamp=1132711707,
709
 
                  timezone=36000,
710
 
                  committer='Lorem Ipsum <test@example.com>',
711
 
                  author='John Doe <jdoe@example.com>')
712
 
        sio = StringIO()
713
 
        formatter = log.LongLogFormatter(to_file=sio)
714
 
        try:
715
 
            def trivial_custom_prop_handler(revision):
716
 
                return {'test_prop':'test_value'}
717
 
 
718
 
            log.properties_handler_registry.register(
719
 
                'trivial_custom_prop_handler',
720
 
                trivial_custom_prop_handler)
721
 
            log.show_log(b, formatter)
722
 
        finally:
723
 
            log.properties_handler_registry.remove(
724
 
                'trivial_custom_prop_handler')
725
 
            self.assertEqualDiff('''\
726
 
------------------------------------------------------------
727
 
revno: 1
728
 
test_prop: test_value
729
 
author: John Doe <jdoe@example.com>
730
 
committer: Lorem Ipsum <test@example.com>
731
 
branch nick: test_properties_in_log
732
 
timestamp: Wed 2005-11-23 12:08:27 +1000
733
 
message:
734
 
  add a
735
 
''',
736
 
                                 sio.getvalue())
737
 
 
738
 
    def test_properties_in_short_log(self):
739
 
        """Log includes the custom properties returned by the registered 
740
 
        handlers.
741
 
        """
742
 
        wt = self.make_branch_and_tree('.')
743
 
        b = wt.branch
744
 
        self.build_tree(['a'])
745
 
        wt.add('a')
746
 
        b.nick = 'test_properties_in_short_log'
747
 
        wt.commit(message='add a',
748
 
                  timestamp=1132711707,
749
 
                  timezone=36000,
750
 
                  committer='Lorem Ipsum <test@example.com>',
751
 
                  author='John Doe <jdoe@example.com>')
752
 
        sio = StringIO()
753
 
        formatter = log.ShortLogFormatter(to_file=sio)
754
 
        try:
755
 
            def trivial_custom_prop_handler(revision):
756
 
                return {'test_prop':'test_value'}
757
 
 
758
 
            log.properties_handler_registry.register(
759
 
                'trivial_custom_prop_handler',
760
 
                trivial_custom_prop_handler)
761
 
            log.show_log(b, formatter)
762
 
        finally:
763
 
            log.properties_handler_registry.remove(
764
 
                'trivial_custom_prop_handler')
765
 
            self.assertEqualDiff('''\
766
 
    1 John Doe\t2005-11-23
767
 
      test_prop: test_value
768
 
      add a
769
 
 
770
 
''',
771
 
                                 sio.getvalue())
772
 
 
773
 
    def test_error_in_properties_handler(self):
774
 
        """Log includes the custom properties returned by the registered 
775
 
        handlers.
776
 
        """
777
 
        wt = self.make_branch_and_tree('.')
778
 
        b = wt.branch
779
 
        self.build_tree(['a'])
780
 
        wt.add('a')
781
 
        b.nick = 'test_author_log'
782
 
        wt.commit(message='add a',
783
 
                  timestamp=1132711707,
784
 
                  timezone=36000,
785
 
                  committer='Lorem Ipsum <test@example.com>',
786
 
                  author='John Doe <jdoe@example.com>',
787
 
                  revprops={'first_prop':'first_value'})
788
 
        sio = StringIO()
789
 
        formatter = log.LongLogFormatter(to_file=sio)
790
 
        try:
791
 
            def trivial_custom_prop_handler(revision):
792
 
                raise StandardError("a test error")
793
 
 
794
 
            log.properties_handler_registry.register(
795
 
                'trivial_custom_prop_handler',
796
 
                trivial_custom_prop_handler)
797
 
            self.assertRaises(StandardError, log.show_log, b, formatter,)
798
 
        finally:
799
 
            log.properties_handler_registry.remove(
800
 
                'trivial_custom_prop_handler')
801
 
 
802
 
    def test_properties_handler_bad_argument(self):
803
 
        wt = self.make_branch_and_tree('.')
804
 
        b = wt.branch
805
 
        self.build_tree(['a'])
806
 
        wt.add('a')
807
 
        b.nick = 'test_author_log'
808
 
        wt.commit(message='add a',
809
 
                  timestamp=1132711707,
810
 
                  timezone=36000,
811
 
                  committer='Lorem Ipsum <test@example.com>',
812
 
                  author='John Doe <jdoe@example.com>',
813
 
                  revprops={'a_prop':'test_value'})
814
 
        sio = StringIO()
815
 
        formatter = log.LongLogFormatter(to_file=sio)
816
 
        try:
817
 
            def bad_argument_prop_handler(revision):
818
 
                return {'custom_prop_name':revision.properties['a_prop']}
819
 
 
820
 
            log.properties_handler_registry.register(
821
 
                'bad_argument_prop_handler',
822
 
                bad_argument_prop_handler)
823
 
 
824
 
            self.assertRaises(AttributeError, formatter.show_properties,
825
 
                              'a revision', '')
826
 
 
827
 
            revision = b.repository.get_revision(b.last_revision())
828
 
            formatter.show_properties(revision, '')
829
 
            self.assertEqualDiff('''custom_prop_name: test_value\n''',
830
 
                                 sio.getvalue())
831
 
        finally:
832
 
            log.properties_handler_registry.remove(
833
 
                'bad_argument_prop_handler')
834
 
 
835
 
 
836
 
class TestLongLogFormatterWithoutMergeRevisions(TestCaseWithoutPropsHandler):
837
 
 
838
 
    def test_long_verbose_log(self):
839
 
        """Verbose log includes changed files
840
 
        
841
 
        bug #4676
842
 
        """
843
 
        wt = self.make_branch_and_tree('.')
844
 
        b = wt.branch
845
 
        self.build_tree(['a'])
846
 
        wt.add('a')
847
 
        # XXX: why does a longer nick show up?
848
 
        b.nick = 'test_verbose_log'
849
 
        wt.commit(message='add a',
850
 
                  timestamp=1132711707,
851
 
                  timezone=36000,
852
 
                  committer='Lorem Ipsum <test@example.com>')
853
 
        logfile = file('out.tmp', 'w+')
854
 
        formatter = log.LongLogFormatter(to_file=logfile, levels=1)
855
 
        log.show_log(b, formatter, verbose=True)
856
 
        logfile.flush()
857
 
        logfile.seek(0)
858
 
        log_contents = logfile.read()
859
 
        self.assertEqualDiff('''\
860
 
------------------------------------------------------------
861
 
revno: 1
862
 
committer: Lorem Ipsum <test@example.com>
863
 
branch nick: test_verbose_log
864
 
timestamp: Wed 2005-11-23 12:08:27 +1000
865
 
message:
866
 
  add a
867
 
added:
868
 
  a
869
 
''',
870
 
                             log_contents)
871
 
 
872
 
    def test_long_verbose_contain_deltas(self):
873
 
        wt = self.make_branch_and_tree('parent')
874
 
        self.build_tree(['parent/f1', 'parent/f2'])
875
 
        wt.add(['f1','f2'])
876
 
        wt.commit('first post')
877
 
        self.run_bzr('branch parent child')
878
 
        os.unlink('child/f1')
879
 
        file('child/f2', 'wb').write('hello\n')
880
 
        self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
881
 
            'child'])
882
 
        os.chdir('parent')
883
 
        self.run_bzr('merge ../child')
884
 
        wt.commit('merge branch 1')
885
 
        b = wt.branch
886
 
        sio = self.make_utf8_encoded_stringio()
887
 
        lf = log.LongLogFormatter(to_file=sio, levels=1)
888
 
        log.show_log(b, lf, verbose=True)
889
 
        the_log = normalize_log(sio.getvalue())
890
 
        self.assertEqualDiff("""\
891
 
------------------------------------------------------------
892
 
revno: 2
893
 
committer: Lorem Ipsum <test@example.com>
894
 
branch nick: parent
895
 
timestamp: Just now
896
 
message:
897
 
  merge branch 1
898
 
removed:
899
 
  f1
900
 
modified:
901
 
  f2
902
 
------------------------------------------------------------
903
 
revno: 1
904
 
committer: Lorem Ipsum <test@example.com>
905
 
branch nick: parent
906
 
timestamp: Just now
907
 
message:
908
 
  first post
909
 
added:
910
 
  f1
911
 
  f2
912
 
""",
913
 
                             the_log)
914
 
 
915
 
    def test_long_trailing_newlines(self):
916
 
        wt = self.make_branch_and_tree('.')
917
 
        b = make_commits_with_trailing_newlines(wt)
918
 
        sio = self.make_utf8_encoded_stringio()
919
 
        lf = log.LongLogFormatter(to_file=sio, levels=1)
920
 
        log.show_log(b, lf)
921
 
        self.assertEqualDiff("""\
922
 
------------------------------------------------------------
923
 
revno: 3
924
 
committer: Joe Foo <joe@foo.com>
925
 
branch nick: test
926
 
timestamp: Mon 2005-11-21 09:32:56 -0600
927
 
message:
928
 
  single line with trailing newline
929
 
------------------------------------------------------------
930
 
revno: 2
931
 
author: Joe Bar <joe@bar.com>
932
 
committer: Joe Foo <joe@foo.com>
933
 
branch nick: test
934
 
timestamp: Mon 2005-11-21 09:27:22 -0600
935
 
message:
936
 
  multiline
937
 
  log
938
 
  message
939
 
------------------------------------------------------------
940
 
revno: 1
941
 
committer: Joe Foo <joe@foo.com>
942
 
branch nick: test
943
 
timestamp: Mon 2005-11-21 09:24:15 -0600
944
 
message:
945
 
  simple log message
946
 
""",
947
 
                             sio.getvalue())
948
 
 
949
 
    def test_long_author_in_log(self):
950
 
        """Log includes the author name if it's set in
951
 
        the revision properties
952
 
        """
953
 
        wt = self.make_branch_and_tree('.')
954
 
        b = wt.branch
955
 
        self.build_tree(['a'])
956
 
        wt.add('a')
957
 
        b.nick = 'test_author_log'
958
 
        wt.commit(message='add a',
959
 
                  timestamp=1132711707,
960
 
                  timezone=36000,
961
 
                  committer='Lorem Ipsum <test@example.com>',
962
 
                  author='John Doe <jdoe@example.com>')
963
 
        sio = StringIO()
964
 
        formatter = log.LongLogFormatter(to_file=sio, levels=1)
965
 
        log.show_log(b, formatter)
966
 
        self.assertEqualDiff('''\
967
 
------------------------------------------------------------
968
 
revno: 1
969
 
author: John Doe <jdoe@example.com>
970
 
committer: Lorem Ipsum <test@example.com>
971
 
branch nick: test_author_log
972
 
timestamp: Wed 2005-11-23 12:08:27 +1000
973
 
message:
974
 
  add a
975
 
''',
976
 
                             sio.getvalue())
977
 
 
978
 
    def test_long_properties_in_log(self):
979
 
        """Log includes the custom properties returned by the registered 
980
 
        handlers.
981
 
        """
982
 
        wt = self.make_branch_and_tree('.')
983
 
        b = wt.branch
984
 
        self.build_tree(['a'])
985
 
        wt.add('a')
986
 
        b.nick = 'test_properties_in_log'
987
 
        wt.commit(message='add a',
988
 
                  timestamp=1132711707,
989
 
                  timezone=36000,
990
 
                  committer='Lorem Ipsum <test@example.com>',
991
 
                  author='John Doe <jdoe@example.com>')
992
 
        sio = StringIO()
993
 
        formatter = log.LongLogFormatter(to_file=sio, levels=1)
994
 
        try:
995
 
            def trivial_custom_prop_handler(revision):
996
 
                return {'test_prop':'test_value'}
997
 
 
998
 
            log.properties_handler_registry.register(
999
 
                'trivial_custom_prop_handler',
1000
 
                trivial_custom_prop_handler)
1001
 
            log.show_log(b, formatter)
1002
 
        finally:
1003
 
            log.properties_handler_registry.remove(
1004
 
                'trivial_custom_prop_handler')
1005
 
            self.assertEqualDiff('''\
1006
 
------------------------------------------------------------
1007
 
revno: 1
1008
 
test_prop: test_value
1009
 
author: John Doe <jdoe@example.com>
1010
 
committer: Lorem Ipsum <test@example.com>
1011
 
branch nick: test_properties_in_log
1012
 
timestamp: Wed 2005-11-23 12:08:27 +1000
1013
 
message:
1014
 
  add a
1015
 
''',
1016
 
                                 sio.getvalue())
1017
 
 
1018
 
 
1019
 
class TestLineLogFormatter(tests.TestCaseWithTransport):
 
551
        formatter = LongLogFormatter(to_file=sio)
 
552
        show_log(b, formatter)
 
553
        self.assertEqualDiff(sio.getvalue(), '''\
 
554
------------------------------------------------------------
 
555
revno: 1
 
556
author: John Doe <jdoe@example.com>
 
557
committer: Lorem Ipsum <test@example.com>
 
558
branch nick: test_author_log
 
559
timestamp: Wed 2005-11-23 12:08:27 +1000
 
560
message:
 
561
  add a
 
562
''')
 
563
 
 
564
 
 
565
 
 
566
class TestLineLogFormatter(TestCaseWithTransport):
1020
567
 
1021
568
    def test_line_log(self):
1022
569
        """Line log should show revno
1028
575
        self.build_tree(['a'])
1029
576
        wt.add('a')
1030
577
        b.nick = 'test-line-log'
1031
 
        wt.commit(message='add a',
1032
 
                  timestamp=1132711707,
 
578
        wt.commit(message='add a', 
 
579
                  timestamp=1132711707, 
1033
580
                  timezone=36000,
1034
581
                  committer='Line-Log-Formatter Tester <test@line.log>')
1035
582
        logfile = file('out.tmp', 'w+')
1036
 
        formatter = log.LineLogFormatter(to_file=logfile)
1037
 
        log.show_log(b, formatter)
 
583
        formatter = LineLogFormatter(to_file=logfile)
 
584
        show_log(b, formatter)
1038
585
        logfile.flush()
1039
586
        logfile.seek(0)
1040
587
        log_contents = logfile.read()
1041
 
        self.assertEqualDiff('1: Line-Log-Formatte... 2005-11-23 add a\n',
1042
 
                             log_contents)
 
588
        self.assertEqualDiff(log_contents,
 
589
            '1: Line-Log-Formatte... 2005-11-23 add a\n')
1043
590
 
1044
591
    def test_trailing_newlines(self):
1045
592
        wt = self.make_branch_and_tree('.')
1046
593
        b = make_commits_with_trailing_newlines(wt)
1047
594
        sio = self.make_utf8_encoded_stringio()
1048
 
        lf = log.LineLogFormatter(to_file=sio)
1049
 
        log.show_log(b, lf)
1050
 
        self.assertEqualDiff("""\
 
595
        lf = LineLogFormatter(to_file=sio)
 
596
        show_log(b, lf)
 
597
        self.assertEqualDiff(sio.getvalue(), """\
1051
598
3: Joe Foo 2005-11-21 single line with trailing newline
1052
599
2: Joe Bar 2005-11-21 multiline
1053
600
1: Joe Foo 2005-11-21 simple log message
1054
 
""",
1055
 
                             sio.getvalue())
1056
 
 
1057
 
    def _prepare_tree_with_merges(self, with_tags=False):
1058
 
        wt = self.make_branch_and_memory_tree('.')
1059
 
        wt.lock_write()
1060
 
        self.addCleanup(wt.unlock)
1061
 
        wt.add('')
1062
 
        wt.commit('rev-1', rev_id='rev-1',
1063
 
                  timestamp=1132586655, timezone=36000,
1064
 
                  committer='Joe Foo <joe@foo.com>')
1065
 
        wt.commit('rev-merged', rev_id='rev-2a',
1066
 
                  timestamp=1132586700, timezone=36000,
1067
 
                  committer='Joe Foo <joe@foo.com>')
1068
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
1069
 
        wt.branch.set_last_revision_info(1, 'rev-1')
1070
 
        wt.commit('rev-2', rev_id='rev-2b',
1071
 
                  timestamp=1132586800, timezone=36000,
1072
 
                  committer='Joe Foo <joe@foo.com>')
1073
 
        if with_tags:
1074
 
            branch = wt.branch
1075
 
            branch.tags.set_tag('v0.2', 'rev-2b')
1076
 
            wt.commit('rev-3', rev_id='rev-3',
1077
 
                      timestamp=1132586900, timezone=36000,
1078
 
                      committer='Jane Foo <jane@foo.com>')
1079
 
            branch.tags.set_tag('v1.0rc1', 'rev-3')
1080
 
            branch.tags.set_tag('v1.0', 'rev-3')
1081
 
        return wt
 
601
""")
1082
602
 
1083
603
    def test_line_log_single_merge_revision(self):
1084
 
        wt = self._prepare_tree_with_merges()
1085
 
        logfile = self.make_utf8_encoded_stringio()
1086
 
        formatter = log.LineLogFormatter(to_file=logfile)
1087
 
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
1088
 
        wtb = wt.branch
1089
 
        rev = revspec.in_history(wtb)
1090
 
        log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
1091
 
        self.assertEqualDiff("""\
1092
 
1.1.1: Joe Foo 2005-11-22 rev-merged
1093
 
""",
1094
 
                             logfile.getvalue())
1095
 
 
1096
 
    def test_line_log_with_tags(self):
1097
 
        wt = self._prepare_tree_with_merges(with_tags=True)
1098
 
        logfile = self.make_utf8_encoded_stringio()
1099
 
        formatter = log.LineLogFormatter(to_file=logfile)
1100
 
        log.show_log(wt.branch, formatter)
1101
 
        self.assertEqualDiff("""\
1102
 
3: Jane Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
1103
 
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
1104
 
1: Joe Foo 2005-11-22 rev-1
1105
 
""",
1106
 
                             logfile.getvalue())
1107
 
 
1108
 
class TestLineLogFormatterWithMergeRevisions(tests.TestCaseWithTransport):
1109
 
 
1110
 
    def test_line_merge_revs_log(self):
1111
 
        """Line log should show revno
1112
 
        
1113
 
        bug #5162
1114
 
        """
1115
 
        wt = self.make_branch_and_tree('.')
1116
 
        b = wt.branch
1117
 
        self.build_tree(['a'])
1118
 
        wt.add('a')
1119
 
        b.nick = 'test-line-log'
1120
 
        wt.commit(message='add a',
1121
 
                  timestamp=1132711707,
1122
 
                  timezone=36000,
1123
 
                  committer='Line-Log-Formatter Tester <test@line.log>')
1124
 
        logfile = file('out.tmp', 'w+')
1125
 
        formatter = log.LineLogFormatter(to_file=logfile, levels=0)
1126
 
        log.show_log(b, formatter)
1127
 
        logfile.flush()
1128
 
        logfile.seek(0)
1129
 
        log_contents = logfile.read()
1130
 
        self.assertEqualDiff('1: Line-Log-Formatte... 2005-11-23 add a\n',
1131
 
                             log_contents)
1132
 
 
1133
 
    def test_line_merge_revs_log_single_merge_revision(self):
1134
 
        wt = self.make_branch_and_memory_tree('.')
1135
 
        wt.lock_write()
1136
 
        self.addCleanup(wt.unlock)
1137
 
        wt.add('')
1138
 
        wt.commit('rev-1', rev_id='rev-1',
1139
 
                  timestamp=1132586655, timezone=36000,
1140
 
                  committer='Joe Foo <joe@foo.com>')
1141
 
        wt.commit('rev-merged', rev_id='rev-2a',
1142
 
                  timestamp=1132586700, timezone=36000,
1143
 
                  committer='Joe Foo <joe@foo.com>')
1144
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
1145
 
        wt.branch.set_last_revision_info(1, 'rev-1')
1146
 
        wt.commit('rev-2', rev_id='rev-2b',
1147
 
                  timestamp=1132586800, timezone=36000,
1148
 
                  committer='Joe Foo <joe@foo.com>')
1149
 
        logfile = self.make_utf8_encoded_stringio()
1150
 
        formatter = log.LineLogFormatter(to_file=logfile, levels=0)
1151
 
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
1152
 
        wtb = wt.branch
1153
 
        rev = revspec.in_history(wtb)
1154
 
        log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
1155
 
        self.assertEqualDiff("""\
1156
 
1.1.1: Joe Foo 2005-11-22 rev-merged
1157
 
""",
1158
 
                             logfile.getvalue())
1159
 
 
1160
 
    def test_line_merge_revs_log_with_merges(self):
1161
 
        wt = self.make_branch_and_memory_tree('.')
1162
 
        wt.lock_write()
1163
 
        self.addCleanup(wt.unlock)
1164
 
        wt.add('')
1165
 
        wt.commit('rev-1', rev_id='rev-1',
1166
 
                  timestamp=1132586655, timezone=36000,
1167
 
                  committer='Joe Foo <joe@foo.com>')
1168
 
        wt.commit('rev-merged', rev_id='rev-2a',
1169
 
                  timestamp=1132586700, timezone=36000,
1170
 
                  committer='Joe Foo <joe@foo.com>')
1171
 
        wt.set_parent_ids(['rev-1', 'rev-2a'])
1172
 
        wt.branch.set_last_revision_info(1, 'rev-1')
1173
 
        wt.commit('rev-2', rev_id='rev-2b',
1174
 
                  timestamp=1132586800, timezone=36000,
1175
 
                  committer='Joe Foo <joe@foo.com>')
1176
 
        logfile = self.make_utf8_encoded_stringio()
1177
 
        formatter = log.LineLogFormatter(to_file=logfile, levels=0)
1178
 
        log.show_log(wt.branch, formatter)
1179
 
        self.assertEqualDiff("""\
1180
 
2: Joe Foo 2005-11-22 [merge] rev-2
1181
 
  1.1.1: Joe Foo 2005-11-22 rev-merged
1182
 
1: Joe Foo 2005-11-22 rev-1
1183
 
""",
1184
 
                             logfile.getvalue())
1185
 
 
1186
 
class TestGetViewRevisions(tests.TestCaseWithTransport):
 
604
        wt = self.make_branch_and_memory_tree('.')
 
605
        wt.lock_write()
 
606
        try:
 
607
            wt.add('')
 
608
            wt.commit('rev-1', rev_id='rev-1',
 
609
                      timestamp=1132586655, timezone=36000,
 
610
                      committer='Joe Foo <joe@foo.com>')
 
611
            wt.commit('rev-merged', rev_id='rev-2a',
 
612
                      timestamp=1132586700, timezone=36000,
 
613
                      committer='Joe Foo <joe@foo.com>')
 
614
            wt.set_parent_ids(['rev-1', 'rev-2a'])
 
615
            wt.branch.set_last_revision_info(1, 'rev-1')
 
616
            wt.commit('rev-2', rev_id='rev-2b',
 
617
                      timestamp=1132586800, timezone=36000,
 
618
                      committer='Joe Foo <joe@foo.com>')
 
619
            logfile = self.make_utf8_encoded_stringio()
 
620
            formatter = LineLogFormatter(to_file=logfile)
 
621
            revspec = RevisionSpec.from_string('1.1.1')
 
622
            wtb = wt.branch
 
623
            rev = revspec.in_history(wtb)
 
624
            show_log(wtb, formatter, start_revision=rev, end_revision=rev)
 
625
            self.assertEqualDiff(logfile.getvalue(), """\
 
626
1.1.1: Joe Foo 2005-11-22 rev-merged
 
627
""")
 
628
        finally:
 
629
            wt.unlock()
 
630
 
 
631
 
 
632
 
 
633
class TestGetViewRevisions(TestCaseWithTransport):
1187
634
 
1188
635
    def make_tree_with_commits(self):
1189
636
        """Create a tree with well-known revision ids"""
1210
657
    def make_tree_with_many_merges(self):
1211
658
        """Create a tree with well-known revision ids"""
1212
659
        wt = self.make_branch_and_tree('tree1')
1213
 
        self.build_tree_contents([('tree1/f', '1\n')])
1214
 
        wt.add(['f'], ['f-id'])
1215
660
        wt.commit('commit one', rev_id='1')
1216
661
        wt.commit('commit two', rev_id='2')
1217
 
 
1218
662
        tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
1219
 
        self.build_tree_contents([('tree3/f', '1\n2\n3a\n')])
1220
663
        tree3.commit('commit three a', rev_id='3a')
1221
 
 
1222
664
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
1223
665
        tree2.merge_from_branch(tree3.branch)
1224
666
        tree2.commit('commit three b', rev_id='3b')
1225
 
 
1226
667
        wt.merge_from_branch(tree2.branch)
1227
668
        wt.commit('commit three c', rev_id='3c')
1228
669
        tree2.commit('four-a', rev_id='4a')
1229
 
 
1230
670
        wt.merge_from_branch(tree2.branch)
1231
671
        wt.commit('four-b', rev_id='4b')
1232
 
 
1233
672
        mainline_revs = [None, '1', '2', '3c', '4b']
1234
673
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
1235
674
        full_rev_nos_for_reference = {
1248
687
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1249
688
        wt.lock_read()
1250
689
        self.addCleanup(wt.unlock)
1251
 
        revisions = list(log.get_view_revisions(
1252
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
 
690
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
691
                                            'forward'))
1253
692
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
1254
 
                         revisions)
1255
 
        revisions2 = list(log.get_view_revisions(
1256
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1257
 
                include_merges=False))
 
693
            revisions)
 
694
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
695
                                             'forward', include_merges=False))
1258
696
        self.assertEqual(revisions, revisions2)
1259
697
 
1260
698
    def test_get_view_revisions_reverse(self):
1262
700
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1263
701
        wt.lock_read()
1264
702
        self.addCleanup(wt.unlock)
1265
 
        revisions = list(log.get_view_revisions(
1266
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
 
703
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
704
                                            'reverse'))
1267
705
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
1268
 
                         revisions)
1269
 
        revisions2 = list(log.get_view_revisions(
1270
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1271
 
                include_merges=False))
 
706
            revisions)
 
707
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
708
                                             'reverse', include_merges=False))
1272
709
        self.assertEqual(revisions, revisions2)
1273
710
 
1274
711
    def test_get_view_revisions_merge(self):
1276
713
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1277
714
        wt.lock_read()
1278
715
        self.addCleanup(wt.unlock)
1279
 
        revisions = list(log.get_view_revisions(
1280
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
1281
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1282
 
                          ('4b', '4', 0), ('4a', '3.1.1', 1)],
1283
 
                         revisions)
1284
 
        revisions = list(log.get_view_revisions(
1285
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1286
 
                include_merges=False))
1287
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1288
 
                          ('4b', '4', 0)],
1289
 
                         revisions)
 
716
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
717
                                            'forward'))
 
718
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
719
            ('4b', '4', 0), ('4a', '3.1.1', 1)],
 
720
            revisions)
 
721
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
722
                                             'forward', include_merges=False))
 
723
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
724
            ('4b', '4', 0)],
 
725
            revisions)
1290
726
 
1291
727
    def test_get_view_revisions_merge_reverse(self):
1292
728
        """Test get_view_revisions in reverse when there are merges"""
1293
729
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1294
730
        wt.lock_read()
1295
731
        self.addCleanup(wt.unlock)
1296
 
        revisions = list(log.get_view_revisions(
1297
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
 
732
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
733
                                            'reverse'))
1298
734
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
1299
 
                          ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
1300
 
                         revisions)
1301
 
        revisions = list(log.get_view_revisions(
1302
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1303
 
                include_merges=False))
 
735
            ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
 
736
            revisions)
 
737
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
738
                                             'reverse', include_merges=False))
1304
739
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
1305
 
                          ('1', '1', 0)],
1306
 
                         revisions)
 
740
            ('1', '1', 0)],
 
741
            revisions)
1307
742
 
1308
743
    def test_get_view_revisions_merge2(self):
1309
744
        """Test get_view_revisions when there are merges"""
1310
745
        mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
1311
746
        wt.lock_read()
1312
747
        self.addCleanup(wt.unlock)
1313
 
        revisions = list(log.get_view_revisions(
1314
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
 
748
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
749
                                            'forward'))
1315
750
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1316
 
                    ('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
1317
 
                    ('4a', '2.2.2', 1)]
 
751
            ('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
 
752
            ('4a', '2.2.2', 1)]
1318
753
        self.assertEqual(expected, revisions)
1319
 
        revisions = list(log.get_view_revisions(
1320
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1321
 
                include_merges=False))
 
754
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
755
                                             'forward', include_merges=False))
1322
756
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1323
 
                          ('4b', '4', 0)],
1324
 
                         revisions)
1325
 
 
1326
 
 
1327
 
    def test_file_id_for_range(self):
1328
 
        mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
1329
 
        wt.lock_read()
1330
 
        self.addCleanup(wt.unlock)
1331
 
 
1332
 
        def rev_from_rev_id(revid, branch):
1333
 
            revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
1334
 
            return revspec.in_history(branch)
1335
 
 
1336
 
        def view_revs(start_rev, end_rev, file_id, direction):
1337
 
            revs = log.calculate_view_revisions(
1338
 
                wt.branch,
1339
 
                start_rev, # start_revision
1340
 
                end_rev, # end_revision
1341
 
                direction, # direction
1342
 
                file_id, # specific_fileid
1343
 
                True, # generate_merge_revisions
1344
 
                True, # allow_single_merge_revision
1345
 
                )
1346
 
            return revs
1347
 
 
1348
 
        rev_3a = rev_from_rev_id('3a', wt.branch)
1349
 
        rev_4b = rev_from_rev_id('4b', wt.branch)
1350
 
        self.assertEqual([('3c', '3', 0), ('3a', '2.1.1', 1)],
1351
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
1352
 
        # Note: 3c still appears before 3a here because of depth-based sorting
1353
 
        self.assertEqual([('3c', '3', 0), ('3a', '2.1.1', 1)],
1354
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
1355
 
 
1356
 
 
1357
 
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
 
757
            ('4b', '4', 0)],
 
758
            revisions)
 
759
 
 
760
 
 
761
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
1358
762
 
1359
763
    def create_tree_with_single_merge(self):
1360
764
        """Create a branch with a moderate layout.
1410
814
        tree.commit('D', rev_id='D')
1411
815
 
1412
816
        # Switch to a read lock for this tree.
1413
 
        # We still have an addCleanup(tree.unlock) pending
 
817
        # We still have addCleanup(unlock)
1414
818
        tree.unlock()
1415
819
        tree.lock_read()
1416
820
        return tree
1417
821
 
1418
 
    def check_delta(self, delta, **kw):
1419
 
        """Check the filenames touched by a delta are as expected.
1420
 
 
1421
 
        Caller only have to pass in the list of files for each part, all
1422
 
        unspecified parts are considered empty (and checked as such).
1423
 
        """
1424
 
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
1425
 
            # By default we expect an empty list
1426
 
            expected = kw.get(n, [])
1427
 
            # strip out only the path components
1428
 
            got = [x[0] for x in getattr(delta, n)]
1429
 
            self.assertEqual(expected, got)
1430
 
 
1431
822
    def test_tree_with_single_merge(self):
1432
823
        """Make sure the tree layout is correct."""
1433
824
        tree = self.create_tree_with_single_merge()
1434
825
        rev_A_tree = tree.branch.repository.revision_tree('A')
1435
826
        rev_B_tree = tree.branch.repository.revision_tree('B')
 
827
 
 
828
        f1_changed = (u'f1', 'f1-id', 'file', True, False)
 
829
        f2_changed = (u'f2', 'f2-id', 'file', True, False)
 
830
        f3_changed = (u'f3', 'f3-id', 'file', True, False)
 
831
 
 
832
        delta = rev_B_tree.changes_from(rev_A_tree)
 
833
        self.assertEqual([f1_changed, f3_changed], delta.modified)
 
834
        self.assertEqual([], delta.renamed)
 
835
        self.assertEqual([], delta.added)
 
836
        self.assertEqual([], delta.removed)
 
837
 
1436
838
        rev_C_tree = tree.branch.repository.revision_tree('C')
 
839
        delta = rev_C_tree.changes_from(rev_A_tree)
 
840
        self.assertEqual([f2_changed, f3_changed], delta.modified)
 
841
        self.assertEqual([], delta.renamed)
 
842
        self.assertEqual([], delta.added)
 
843
        self.assertEqual([], delta.removed)
 
844
 
1437
845
        rev_D_tree = tree.branch.repository.revision_tree('D')
1438
 
 
1439
 
        self.check_delta(rev_B_tree.changes_from(rev_A_tree),
1440
 
                         modified=['f1', 'f3'])
1441
 
 
1442
 
        self.check_delta(rev_C_tree.changes_from(rev_A_tree),
1443
 
                         modified=['f2', 'f3'])
1444
 
 
1445
 
        self.check_delta(rev_D_tree.changes_from(rev_B_tree),
1446
 
                         modified=['f2', 'f3'])
1447
 
 
1448
 
        self.check_delta(rev_D_tree.changes_from(rev_C_tree),
1449
 
                         modified=['f1', 'f3'])
 
846
        delta = rev_D_tree.changes_from(rev_B_tree)
 
847
        self.assertEqual([f2_changed, f3_changed], delta.modified)
 
848
        self.assertEqual([], delta.renamed)
 
849
        self.assertEqual([], delta.added)
 
850
        self.assertEqual([], delta.removed)
 
851
 
 
852
        delta = rev_D_tree.changes_from(rev_C_tree)
 
853
        self.assertEqual([f1_changed, f3_changed], delta.modified)
 
854
        self.assertEqual([], delta.renamed)
 
855
        self.assertEqual([], delta.added)
 
856
        self.assertEqual([], delta.removed)
1450
857
 
1451
858
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
1452
 
        """Ensure _filter_revisions_touching_file_id returns the right values.
 
859
        """Make sure _filter_revisions_touching_file_id returns the right values.
1453
860
 
1454
861
        Get the return value from _filter_revisions_touching_file_id and make
1455
862
        sure they are correct.
1456
863
        """
1457
 
        # The api for _filter_revisions_touching_file_id is a little crazy.
 
864
        # The api for _get_revisions_touching_file_id is a little crazy,
1458
865
        # So we do the setup here.
1459
866
        mainline = tree.branch.revision_history()
1460
867
        mainline.insert(0, None)
1462
869
        view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
1463
870
                                                'reverse', True)
1464
871
        actual_revs = log._filter_revisions_touching_file_id(
1465
 
                            tree.branch,
 
872
                            tree.branch, 
1466
873
                            file_id,
 
874
                            mainline,
1467
875
                            list(view_revs_iter))
1468
876
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
1469
877
 
1481
889
    def test_file_id_f3(self):
1482
890
        tree = self.create_tree_with_single_merge()
1483
891
        # f3 should be marked as modified by revisions A, B, C, and D
1484
 
        self.assertAllRevisionsForFileID(tree, 'f3-id', ['D', 'C', 'B', 'A'])
 
892
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1485
893
 
1486
894
    def test_file_id_with_ghosts(self):
1487
895
        # This is testing bug #209948, where having a ghost would cause
1488
896
        # _filter_revisions_touching_file_id() to fail.
1489
897
        tree = self.create_tree_with_single_merge()
1490
898
        # We need to add a revision, so switch back to a write-locked tree
1491
 
        # (still a single addCleanup(tree.unlock) pending).
1492
899
        tree.unlock()
1493
900
        tree.lock_write()
1494
901
        first_parent = tree.last_revision()
1499
906
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1500
907
 
1501
908
 
1502
 
class TestShowChangedRevisions(tests.TestCaseWithTransport):
 
909
class TestShowChangedRevisions(TestCaseWithTransport):
1503
910
 
1504
911
    def test_show_changed_revisions_verbose(self):
1505
912
        tree = self.make_branch_and_tree('tree_a')
1512
919
        self.assertNotContainsRe(s.getvalue(), 'foo')
1513
920
 
1514
921
 
1515
 
class TestLogFormatter(tests.TestCase):
 
922
class TestLogFormatter(TestCase):
1516
923
 
1517
924
    def test_short_committer(self):
1518
 
        rev = revision.Revision('a-id')
 
925
        rev = Revision('a-id')
1519
926
        rev.committer = 'John Doe <jdoe@example.com>'
1520
 
        lf = log.LogFormatter(None)
 
927
        lf = LogFormatter(None)
1521
928
        self.assertEqual('John Doe', lf.short_committer(rev))
1522
929
        rev.committer = 'John Smith <jsmith@example.com>'
1523
930
        self.assertEqual('John Smith', lf.short_committer(rev))
1531
938
        self.assertEqual('John Smith', lf.short_committer(rev))
1532
939
 
1533
940
    def test_short_author(self):
1534
 
        rev = revision.Revision('a-id')
 
941
        rev = Revision('a-id')
1535
942
        rev.committer = 'John Doe <jdoe@example.com>'
1536
 
        lf = log.LogFormatter(None)
 
943
        lf = LogFormatter(None)
1537
944
        self.assertEqual('John Doe', lf.short_author(rev))
1538
945
        rev.properties['author'] = 'John Smith <jsmith@example.com>'
1539
946
        self.assertEqual('John Smith', lf.short_author(rev))
1545
952
        self.assertEqual('jsmith@example.com', lf.short_author(rev))
1546
953
        rev.properties['author'] = 'John Smith jsmith@example.com'
1547
954
        self.assertEqual('John Smith', lf.short_author(rev))
1548
 
 
1549
 
 
1550
 
class TestReverseByDepth(tests.TestCase):
1551
 
    """Test reverse_by_depth behavior.
1552
 
 
1553
 
    This is used to present revisions in forward (oldest first) order in a nice
1554
 
    layout.
1555
 
 
1556
 
    The tests use lighter revision description to ease reading.
1557
 
    """
1558
 
 
1559
 
    def assertReversed(self, forward, backward):
1560
 
        # Transform the descriptions to suit the API: tests use (revno, depth),
1561
 
        # while the API expects (revid, revno, depth)
1562
 
        def complete_revisions(l):
1563
 
            """Transform the description to suit the API.
1564
 
 
1565
 
            Tests use (revno, depth) whil the API expects (revid, revno, depth).
1566
 
            Since the revid is arbitrary, we just duplicate revno
1567
 
            """
1568
 
            return [ (r, r, d) for r, d in l]
1569
 
        forward = complete_revisions(forward)
1570
 
        backward= complete_revisions(backward)
1571
 
        self.assertEqual(forward, log.reverse_by_depth(backward))
1572
 
 
1573
 
 
1574
 
    def test_mainline_revisions(self):
1575
 
        self.assertReversed([( '1', 0), ('2', 0)],
1576
 
                            [('2', 0), ('1', 0)])
1577
 
 
1578
 
    def test_merged_revisions(self):
1579
 
        self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1580
 
                            [('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
1581
 
    def test_shifted_merged_revisions(self):
1582
 
        """Test irregular layout.
1583
 
 
1584
 
        Requesting revisions touching a file can produce "holes" in the depths.
1585
 
        """
1586
 
        self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1587
 
                            [('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
1588
 
 
1589
 
    def test_merged_without_child_revisions(self):
1590
 
        """Test irregular layout.
1591
 
 
1592
 
        Revision ranges can produce "holes" in the depths.
1593
 
        """
1594
 
        # When a revision of higher depth doesn't follow one of lower depth, we
1595
 
        # assume a lower depth one is virtually there
1596
 
        self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1597
 
                            [('4', 4), ('3', 3), ('2', 2), ('1', 2),])
1598
 
        # So we get the same order after reversing below even if the original
1599
 
        # revisions are not in the same order.
1600
 
        self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1601
 
                            [('3', 3), ('4', 4), ('2', 2), ('1', 2),])
1602
 
 
1603
 
 
1604
 
class TestHistoryChange(tests.TestCaseWithTransport):
1605
 
 
1606
 
    def setup_a_tree(self):
1607
 
        tree = self.make_branch_and_tree('tree')
1608
 
        tree.lock_write()
1609
 
        self.addCleanup(tree.unlock)
1610
 
        tree.commit('1a', rev_id='1a')
1611
 
        tree.commit('2a', rev_id='2a')
1612
 
        tree.commit('3a', rev_id='3a')
1613
 
        return tree
1614
 
 
1615
 
    def setup_ab_tree(self):
1616
 
        tree = self.setup_a_tree()
1617
 
        tree.set_last_revision('1a')
1618
 
        tree.branch.set_last_revision_info(1, '1a')
1619
 
        tree.commit('2b', rev_id='2b')
1620
 
        tree.commit('3b', rev_id='3b')
1621
 
        return tree
1622
 
 
1623
 
    def setup_ac_tree(self):
1624
 
        tree = self.setup_a_tree()
1625
 
        tree.set_last_revision(revision.NULL_REVISION)
1626
 
        tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1627
 
        tree.commit('1c', rev_id='1c')
1628
 
        tree.commit('2c', rev_id='2c')
1629
 
        tree.commit('3c', rev_id='3c')
1630
 
        return tree
1631
 
 
1632
 
    def test_all_new(self):
1633
 
        tree = self.setup_ab_tree()
1634
 
        old, new = log.get_history_change('1a', '3a', tree.branch.repository)
1635
 
        self.assertEqual([], old)
1636
 
        self.assertEqual(['2a', '3a'], new)
1637
 
 
1638
 
    def test_all_old(self):
1639
 
        tree = self.setup_ab_tree()
1640
 
        old, new = log.get_history_change('3a', '1a', tree.branch.repository)
1641
 
        self.assertEqual([], new)
1642
 
        self.assertEqual(['2a', '3a'], old)
1643
 
 
1644
 
    def test_null_old(self):
1645
 
        tree = self.setup_ab_tree()
1646
 
        old, new = log.get_history_change(revision.NULL_REVISION,
1647
 
                                          '3a', tree.branch.repository)
1648
 
        self.assertEqual([], old)
1649
 
        self.assertEqual(['1a', '2a', '3a'], new)
1650
 
 
1651
 
    def test_null_new(self):
1652
 
        tree = self.setup_ab_tree()
1653
 
        old, new = log.get_history_change('3a', revision.NULL_REVISION,
1654
 
                                          tree.branch.repository)
1655
 
        self.assertEqual([], new)
1656
 
        self.assertEqual(['1a', '2a', '3a'], old)
1657
 
 
1658
 
    def test_diverged(self):
1659
 
        tree = self.setup_ab_tree()
1660
 
        old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1661
 
        self.assertEqual(old, ['2a', '3a'])
1662
 
        self.assertEqual(new, ['2b', '3b'])
1663
 
 
1664
 
    def test_unrelated(self):
1665
 
        tree = self.setup_ac_tree()
1666
 
        old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1667
 
        self.assertEqual(old, ['1a', '2a', '3a'])
1668
 
        self.assertEqual(new, ['1c', '2c', '3c'])
1669
 
 
1670
 
    def test_show_branch_change(self):
1671
 
        tree = self.setup_ab_tree()
1672
 
        s = StringIO()
1673
 
        log.show_branch_change(tree.branch, s, 3, '3a')
1674
 
        self.assertContainsRe(s.getvalue(),
1675
 
            '[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1676
 
            '[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1677
 
 
1678
 
    def test_show_branch_change_no_change(self):
1679
 
        tree = self.setup_ab_tree()
1680
 
        s = StringIO()
1681
 
        log.show_branch_change(tree.branch, s, 3, '3b')
1682
 
        self.assertEqual(s.getvalue(),
1683
 
            'Nothing seems to have changed\n')
1684
 
 
1685
 
    def test_show_branch_change_no_old(self):
1686
 
        tree = self.setup_ab_tree()
1687
 
        s = StringIO()
1688
 
        log.show_branch_change(tree.branch, s, 2, '2b')
1689
 
        self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1690
 
        self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1691
 
 
1692
 
    def test_show_branch_change_no_new(self):
1693
 
        tree = self.setup_ab_tree()
1694
 
        tree.branch.set_last_revision_info(2, '2b')
1695
 
        s = StringIO()
1696
 
        log.show_branch_change(tree.branch, s, 3, '3b')
1697
 
        self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1698
 
        self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')