/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Robert Collins
  • Date: 2007-07-04 08:08:13 UTC
  • mfrom: (2572 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2587.
  • Revision ID: robertc@robertcollins.net-20070704080813-wzebx0r88fvwj5rq
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
# -*- coding: utf-8 -*-
3
 
# vim: encoding=utf-8
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
4
2
#
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
19
17
import os
20
18
from cStringIO import StringIO
21
19
 
22
 
from bzrlib.tests import BzrTestBase, TestCaseWithTransport
23
 
from bzrlib.log import (show_log, 
24
 
                        get_view_revisions, 
25
 
                        LogFormatter, 
26
 
                        LongLogFormatter, 
27
 
                        ShortLogFormatter, 
 
20
from bzrlib import log
 
21
from bzrlib.tests import TestCaseWithTransport
 
22
from bzrlib.log import (show_log,
 
23
                        get_view_revisions,
 
24
                        LogRevision,
 
25
                        LogFormatter,
 
26
                        LongLogFormatter,
 
27
                        ShortLogFormatter,
28
28
                        LineLogFormatter)
29
29
from bzrlib.branch import Branch
30
30
from bzrlib.errors import InvalidRevisionNumber
31
31
 
32
32
 
33
 
class _LogEntry(object):
34
 
    # should probably move into bzrlib.log?
35
 
    pass
36
 
 
37
 
 
38
33
class LogCatcher(LogFormatter):
39
34
    """Pull log messages into list rather than displaying them.
40
35
 
44
39
 
45
40
    We should also test the LogFormatter.
46
41
    """
 
42
 
 
43
    supports_delta = True
 
44
 
47
45
    def __init__(self):
48
46
        super(LogCatcher, self).__init__(to_file=None)
49
47
        self.logs = []
50
48
 
51
 
    def show(self, revno, rev, delta):
52
 
        le = _LogEntry()
53
 
        le.revno = revno
54
 
        le.rev = rev
55
 
        le.delta = delta
56
 
        self.logs.append(le)
57
 
 
58
 
 
59
 
class SimpleLogTest(TestCaseWithTransport):
 
49
    def log_revision(self, revision):
 
50
        self.logs.append(revision)
 
51
 
 
52
 
 
53
class TestShowLog(TestCaseWithTransport):
60
54
 
61
55
    def checkDelta(self, delta, **kw):
62
56
        """Check the filenames touched by a delta are as expected."""
63
57
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
64
58
            expected = kw.get(n, [])
65
 
 
66
 
            # tests are written with unix paths; fix them up for windows
67
 
            #if os.sep != '/':
68
 
            #    expected = [x.replace('/', os.sep) for x in expected]
69
 
 
70
59
            # strip out only the path components
71
60
            got = [x[0] for x in getattr(delta, n)]
72
61
            self.assertEquals(expected, got)
91
80
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
92
81
                          start_revision=1, end_revision=-1) 
93
82
 
94
 
    def test_cur_revno(self):
95
 
        wt = self.make_branch_and_tree('.')
96
 
        b = wt.branch
97
 
 
98
 
        lf = LogCatcher()
99
 
        wt.commit('empty commit')
100
 
        show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
101
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
102
 
                          start_revision=2, end_revision=1) 
103
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
104
 
                          start_revision=1, end_revision=2) 
105
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
106
 
                          start_revision=0, end_revision=2) 
107
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
108
 
                          start_revision=1, end_revision=0) 
109
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
110
 
                          start_revision=-1, end_revision=1) 
111
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
112
 
                          start_revision=1, end_revision=-1) 
113
 
 
114
83
    def test_simple_log(self):
115
84
        eq = self.assertEquals
116
85
        
126
95
        lf = LogCatcher()
127
96
        show_log(b, lf, verbose=True)
128
97
        eq(len(lf.logs), 1)
129
 
        eq(lf.logs[0].revno, 1)
 
98
        eq(lf.logs[0].revno, '1')
130
99
        eq(lf.logs[0].rev.message, 'empty commit')
131
100
        d = lf.logs[0].delta
132
101
        self.log('log delta: %r' % d)
149
118
        eq(len(lf.logs), 2)
150
119
        self.log('log entries:')
151
120
        for logentry in lf.logs:
152
 
            self.log('%4d %s' % (logentry.revno, logentry.rev.message))
 
121
            self.log('%4s %s' % (logentry.revno, logentry.rev.message))
153
122
        
154
123
        # first one is most recent
155
124
        logentry = lf.logs[0]
156
 
        eq(logentry.revno, 2)
 
125
        eq(logentry.revno, '2')
157
126
        eq(logentry.rev.message, 'add one file')
158
127
        d = logentry.delta
159
128
        self.log('log 2 delta: %r' % d)
160
 
        # self.checkDelta(d, added=['hello'])
 
129
        self.checkDelta(d, added=['hello'])
161
130
        
162
131
        # commit a log message with control characters
163
132
        msg = "All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
184
153
        self.log("escaped commit message: %r", committed_msg)
185
154
        self.assert_(msg == committed_msg)
186
155
 
 
156
    def test_deltas_in_merge_revisions(self):
 
157
        """Check deltas created for both mainline and merge revisions"""
 
158
        eq = self.assertEquals
 
159
        wt = self.make_branch_and_tree('parent')
 
160
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
 
161
        wt.add('file1')
 
162
        wt.add('file2')
 
163
        wt.commit(message='add file1 and file2')
 
164
        self.run_bzr('branch', 'parent', 'child')
 
165
        os.unlink('child/file1')
 
166
        print >> file('child/file2', 'wb'), 'hello'
 
167
        self.run_bzr('commit', '-m', 'remove file1 and modify file2', 'child')
 
168
        os.chdir('parent')
 
169
        self.run_bzr('merge', '../child')
 
170
        wt.commit('merge child branch')
 
171
        os.chdir('..')
 
172
        b = wt.branch
 
173
        lf = LogCatcher()
 
174
        lf.supports_merge_revisions = True
 
175
        show_log(b, lf, verbose=True)
 
176
        eq(len(lf.logs),3)
 
177
        logentry = lf.logs[0]
 
178
        eq(logentry.revno, '2')
 
179
        eq(logentry.rev.message, 'merge child branch')
 
180
        d = logentry.delta
 
181
        self.checkDelta(d, removed=['file1'], modified=['file2'])
 
182
        logentry = lf.logs[1]
 
183
        eq(logentry.revno, '1.1.1')
 
184
        eq(logentry.rev.message, 'remove file1 and modify file2')
 
185
        d = logentry.delta
 
186
        self.checkDelta(d, removed=['file1'], modified=['file2'])
 
187
        logentry = lf.logs[2]
 
188
        eq(logentry.revno, '1')
 
189
        eq(logentry.rev.message, 'add file1 and file2')
 
190
        d = logentry.delta
 
191
        self.checkDelta(d, added=['file1', 'file2'])
 
192
 
 
193
 
 
194
def make_commits_with_trailing_newlines(wt):
 
195
    """Helper method for LogFormatter tests"""    
 
196
    b = wt.branch
 
197
    b.nick='test'
 
198
    open('a', 'wb').write('hello moto\n')
 
199
    wt.add('a')
 
200
    wt.commit('simple log message', rev_id='a1'
 
201
            , timestamp=1132586655.459960938, timezone=-6*3600
 
202
            , committer='Joe Foo <joe@foo.com>')
 
203
    open('b', 'wb').write('goodbye\n')
 
204
    wt.add('b')
 
205
    wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
 
206
            , timestamp=1132586842.411175966, timezone=-6*3600
 
207
            , committer='Joe Foo <joe@foo.com>')
 
208
 
 
209
    open('c', 'wb').write('just another manic monday\n')
 
210
    wt.add('c')
 
211
    wt.commit('single line with trailing newline\n', rev_id='a3'
 
212
            , timestamp=1132587176.835228920, timezone=-6*3600
 
213
            , committer = 'Joe Foo <joe@foo.com>')
 
214
    return b
 
215
 
 
216
 
 
217
class TestShortLogFormatter(TestCaseWithTransport):
 
218
 
187
219
    def test_trailing_newlines(self):
188
220
        wt = self.make_branch_and_tree('.')
189
 
        b = wt.branch
190
 
        b.nick='test'
191
 
        open('a', 'wb').write('hello moto\n')
192
 
        wt.add('a')
193
 
        wt.commit('simple log message', rev_id='a1'
194
 
                , timestamp=1132586655.459960938, timezone=-6*3600
195
 
                , committer='Joe Foo <joe@foo.com>')
196
 
        open('b', 'wb').write('goodbye\n')
197
 
        wt.add('b')
198
 
        wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
199
 
                , timestamp=1132586842.411175966, timezone=-6*3600
200
 
                , committer='Joe Foo <joe@foo.com>')
201
 
 
202
 
        open('c', 'wb').write('just another manic monday\n')
203
 
        wt.add('c')
204
 
        wt.commit('single line with trailing newline\n', rev_id='a3'
205
 
                , timestamp=1132587176.835228920, timezone=-6*3600
206
 
                , committer = 'Joe Foo <joe@foo.com>')
207
 
 
 
221
        b = make_commits_with_trailing_newlines(wt)
208
222
        sio = StringIO()
209
223
        lf = ShortLogFormatter(to_file=sio)
210
224
        show_log(b, lf)
222
236
 
223
237
""")
224
238
 
225
 
        sio = StringIO()
226
 
        lf = LongLogFormatter(to_file=sio)
227
 
        show_log(b, lf)
228
 
        self.assertEquals(sio.getvalue(), """\
229
 
------------------------------------------------------------
230
 
revno: 3
231
 
committer: Joe Foo <joe@foo.com>
232
 
branch nick: test
233
 
timestamp: Mon 2005-11-21 09:32:56 -0600
234
 
message:
235
 
  single line with trailing newline
236
 
------------------------------------------------------------
237
 
revno: 2
238
 
committer: Joe Foo <joe@foo.com>
239
 
branch nick: test
240
 
timestamp: Mon 2005-11-21 09:27:22 -0600
241
 
message:
242
 
  multiline
243
 
  log
244
 
  message
245
 
------------------------------------------------------------
246
 
revno: 1
247
 
committer: Joe Foo <joe@foo.com>
248
 
branch nick: test
249
 
timestamp: Mon 2005-11-21 09:24:15 -0600
250
 
message:
251
 
  simple log message
252
 
""")
253
 
        
 
239
 
 
240
class TestLongLogFormatter(TestCaseWithTransport):
 
241
 
 
242
    def normalize_log(self,log):
 
243
        """Replaces the variable lines of logs with fixed lines"""
 
244
        committer = 'committer: Lorem Ipsum <test@example.com>'
 
245
        lines = log.splitlines(True)
 
246
        for idx,line in enumerate(lines):
 
247
            stripped_line = line.lstrip()
 
248
            indent = ' ' * (len(line) - len(stripped_line))
 
249
            if stripped_line.startswith('committer:'):
 
250
                lines[idx] = indent + committer + '\n'
 
251
            if stripped_line.startswith('timestamp:'):
 
252
                lines[idx] = indent + 'timestamp: Just now\n'
 
253
        return ''.join(lines)
 
254
 
254
255
    def test_verbose_log(self):
255
256
        """Verbose log includes changed files
256
257
        
284
285
  a
285
286
''')
286
287
 
 
288
    def test_merges_are_indented_by_level(self):
 
289
        wt = self.make_branch_and_tree('parent')
 
290
        wt.commit('first post')
 
291
        self.run_bzr('branch', 'parent', 'child')
 
292
        self.run_bzr('commit', '-m', 'branch 1', '--unchanged', 'child')
 
293
        self.run_bzr('branch', 'child', 'smallerchild')
 
294
        self.run_bzr('commit', '-m', 'branch 2', '--unchanged', 'smallerchild')
 
295
        os.chdir('child')
 
296
        self.run_bzr('merge', '../smallerchild')
 
297
        self.run_bzr('commit', '-m', 'merge branch 2')
 
298
        os.chdir('../parent')
 
299
        self.run_bzr('merge', '../child')
 
300
        wt.commit('merge branch 1')
 
301
        b = wt.branch
 
302
        sio = StringIO()
 
303
        lf = LongLogFormatter(to_file=sio)
 
304
        show_log(b, lf, verbose=True)
 
305
        log = self.normalize_log(sio.getvalue())
 
306
        self.assertEqualDiff("""\
 
307
------------------------------------------------------------
 
308
revno: 2
 
309
committer: Lorem Ipsum <test@example.com>
 
310
branch nick: parent
 
311
timestamp: Just now
 
312
message:
 
313
  merge branch 1
 
314
    ------------------------------------------------------------
 
315
    revno: 1.1.2
 
316
    committer: Lorem Ipsum <test@example.com>
 
317
    branch nick: child
 
318
    timestamp: Just now
 
319
    message:
 
320
      merge branch 2
 
321
        ------------------------------------------------------------
 
322
        revno: 1.1.1.1.1
 
323
        committer: Lorem Ipsum <test@example.com>
 
324
        branch nick: smallerchild
 
325
        timestamp: Just now
 
326
        message:
 
327
          branch 2
 
328
    ------------------------------------------------------------
 
329
    revno: 1.1.1
 
330
    committer: Lorem Ipsum <test@example.com>
 
331
    branch nick: child
 
332
    timestamp: Just now
 
333
    message:
 
334
      branch 1
 
335
------------------------------------------------------------
 
336
revno: 1
 
337
committer: Lorem Ipsum <test@example.com>
 
338
branch nick: parent
 
339
timestamp: Just now
 
340
message:
 
341
  first post
 
342
""", log)
 
343
 
 
344
    def test_verbose_merge_revisions_contain_deltas(self):
 
345
        wt = self.make_branch_and_tree('parent')
 
346
        self.build_tree(['parent/f1', 'parent/f2'])
 
347
        wt.add(['f1','f2'])
 
348
        wt.commit('first post')
 
349
        self.run_bzr('branch', 'parent', 'child')
 
350
        os.unlink('child/f1')
 
351
        print >> file('child/f2', 'wb'), 'hello'
 
352
        self.run_bzr('commit', '-m', 'removed f1 and modified f2', 'child')
 
353
        os.chdir('parent')
 
354
        self.run_bzr('merge', '../child')
 
355
        wt.commit('merge branch 1')
 
356
        b = wt.branch
 
357
        sio = StringIO()
 
358
        lf = LongLogFormatter(to_file=sio)
 
359
        show_log(b, lf, verbose=True)
 
360
        log = self.normalize_log(sio.getvalue())
 
361
        self.assertEqualDiff("""\
 
362
------------------------------------------------------------
 
363
revno: 2
 
364
committer: Lorem Ipsum <test@example.com>
 
365
branch nick: parent
 
366
timestamp: Just now
 
367
message:
 
368
  merge branch 1
 
369
removed:
 
370
  f1
 
371
modified:
 
372
  f2
 
373
    ------------------------------------------------------------
 
374
    revno: 1.1.1
 
375
    committer: Lorem Ipsum <test@example.com>
 
376
    branch nick: child
 
377
    timestamp: Just now
 
378
    message:
 
379
      removed f1 and modified f2
 
380
    removed:
 
381
      f1
 
382
    modified:
 
383
      f2
 
384
------------------------------------------------------------
 
385
revno: 1
 
386
committer: Lorem Ipsum <test@example.com>
 
387
branch nick: parent
 
388
timestamp: Just now
 
389
message:
 
390
  first post
 
391
added:
 
392
  f1
 
393
  f2
 
394
""", log)
 
395
 
 
396
    def test_trailing_newlines(self):
 
397
        wt = self.make_branch_and_tree('.')
 
398
        b = make_commits_with_trailing_newlines(wt)
 
399
        sio = StringIO()
 
400
        lf = LongLogFormatter(to_file=sio)
 
401
        show_log(b, lf)
 
402
        self.assertEqualDiff(sio.getvalue(), """\
 
403
------------------------------------------------------------
 
404
revno: 3
 
405
committer: Joe Foo <joe@foo.com>
 
406
branch nick: test
 
407
timestamp: Mon 2005-11-21 09:32:56 -0600
 
408
message:
 
409
  single line with trailing newline
 
410
------------------------------------------------------------
 
411
revno: 2
 
412
committer: Joe Foo <joe@foo.com>
 
413
branch nick: test
 
414
timestamp: Mon 2005-11-21 09:27:22 -0600
 
415
message:
 
416
  multiline
 
417
  log
 
418
  message
 
419
------------------------------------------------------------
 
420
revno: 1
 
421
committer: Joe Foo <joe@foo.com>
 
422
branch nick: test
 
423
timestamp: Mon 2005-11-21 09:24:15 -0600
 
424
message:
 
425
  simple log message
 
426
""")
 
427
 
 
428
 
 
429
class TestLineLogFormatter(TestCaseWithTransport):
 
430
 
287
431
    def test_line_log(self):
288
432
        """Line log should show revno
289
433
        
306
450
        log_contents = logfile.read()
307
451
        self.assertEqualDiff(log_contents, '1: Line-Log-Formatte... 2005-11-23 add a\n')
308
452
 
 
453
    def test_short_log_with_merges(self):
 
454
        wt = self.make_branch_and_memory_tree('.')
 
455
        wt.lock_write()
 
456
        try:
 
457
            wt.add('')
 
458
            wt.commit('rev-1', rev_id='rev-1',
 
459
                      timestamp=1132586655, timezone=36000,
 
460
                      committer='Joe Foo <joe@foo.com>')
 
461
            wt.commit('rev-merged', rev_id='rev-2a',
 
462
                      timestamp=1132586700, timezone=36000,
 
463
                      committer='Joe Foo <joe@foo.com>')
 
464
            wt.set_parent_ids(['rev-1', 'rev-2a'])
 
465
            wt.branch.set_last_revision_info(1, 'rev-1')
 
466
            wt.commit('rev-2', rev_id='rev-2b',
 
467
                      timestamp=1132586800, timezone=36000,
 
468
                      committer='Joe Foo <joe@foo.com>')
 
469
            logfile = StringIO()
 
470
            formatter = ShortLogFormatter(to_file=logfile)
 
471
            show_log(wt.branch, formatter)
 
472
            logfile.flush()
 
473
            self.assertEqualDiff("""\
 
474
    2 Joe Foo\t2005-11-22 [merge]
 
475
      rev-2
 
476
 
 
477
    1 Joe Foo\t2005-11-22
 
478
      rev-1
 
479
 
 
480
""", logfile.getvalue())
 
481
        finally:
 
482
            wt.unlock()
 
483
 
 
484
    def test_trailing_newlines(self):
 
485
        wt = self.make_branch_and_tree('.')
 
486
        b = make_commits_with_trailing_newlines(wt)
 
487
        sio = StringIO()
 
488
        lf = LineLogFormatter(to_file=sio)
 
489
        show_log(b, lf)
 
490
        self.assertEqualDiff(sio.getvalue(), """\
 
491
3: Joe Foo 2005-11-21 single line with trailing newline
 
492
2: Joe Foo 2005-11-21 multiline
 
493
1: Joe Foo 2005-11-21 simple log message
 
494
""")
 
495
 
 
496
 
 
497
class TestGetViewRevisions(TestCaseWithTransport):
 
498
 
309
499
    def make_tree_with_commits(self):
310
500
        """Create a tree with well-known revision ids"""
311
501
        wt = self.make_branch_and_tree('tree1')
325
515
        wt.commit('four-b', rev_id='4b')
326
516
        mainline_revs.append('4b')
327
517
        rev_nos['4b'] = 4
 
518
        # 4a: 3.1.1
328
519
        return mainline_revs, rev_nos, wt
329
520
 
330
521
    def make_tree_with_many_merges(self):
332
523
        wt = self.make_branch_and_tree('tree1')
333
524
        wt.commit('commit one', rev_id='1')
334
525
        wt.commit('commit two', rev_id='2')
335
 
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
336
526
        tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
337
527
        tree3.commit('commit three a', rev_id='3a')
 
528
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
338
529
        tree2.merge_from_branch(tree3.branch)
339
530
        tree2.commit('commit three b', rev_id='3b')
340
531
        wt.merge_from_branch(tree2.branch)
343
534
        wt.merge_from_branch(tree2.branch)
344
535
        wt.commit('four-b', rev_id='4b')
345
536
        mainline_revs = [None, '1', '2', '3c', '4b']
346
 
        rev_nos = {'1': 1, '2': 2, '3c': 3, '4b': 4}
 
537
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
 
538
        full_rev_nos_for_reference = {
 
539
            '1': '1',
 
540
            '2': '2',
 
541
            '3a': '2.2.1', #first commit tree 3
 
542
            '3b': '2.1.1', # first commit tree 2
 
543
            '3c': '3', #merges 3b to main
 
544
            '4a': '2.1.2', # second commit tree 2
 
545
            '4b': '4', # merges 4a to main
 
546
            }
347
547
        return mainline_revs, rev_nos, wt
348
548
 
349
549
    def test_get_view_revisions_forward(self):
351
551
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
352
552
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
353
553
                                            'forward'))
354
 
        self.assertEqual(revisions, [('1', 1, 0), ('2', 2, 0), ('3', 3, 0)])
 
554
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
 
555
            revisions)
355
556
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
356
557
                                             'forward', include_merges=False))
357
558
        self.assertEqual(revisions, revisions2)
361
562
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
362
563
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
363
564
                                            'reverse'))
364
 
        self.assertEqual(revisions, [('3', 3, 0), ('2', 2, 0), ('1', 1, 0), ])
 
565
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
 
566
            revisions)
365
567
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
366
568
                                             'reverse', include_merges=False))
367
569
        self.assertEqual(revisions, revisions2)
371
573
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
372
574
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
373
575
                                            'forward'))
374
 
        self.assertEqual(revisions, [('1', 1, 0), ('2', 2, 0), ('3', 3, 0),
375
 
                                     ('4b', 4, 0), ('4a', None, 1)])
 
576
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
577
            ('4b', '4', 0), ('4a', '3.1.1', 1)],
 
578
            revisions)
376
579
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
377
580
                                             'forward', include_merges=False))
378
 
        self.assertEqual(revisions, [('1', 1, 0), ('2', 2, 0), ('3', 3, 0),
379
 
                                     ('4b', 4, 0)])
 
581
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
582
            ('4b', '4', 0)],
 
583
            revisions)
380
584
 
381
585
    def test_get_view_revisions_merge_reverse(self):
382
586
        """Test get_view_revisions in reverse when there are merges"""
383
587
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
384
588
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
385
589
                                            'reverse'))
386
 
        self.assertEqual(revisions, [('4b', 4, 0), ('4a', None, 1), 
387
 
                                     ('3', 3, 0), ('2', 2, 0), ('1', 1, 0)])
 
590
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
 
591
            ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
 
592
            revisions)
388
593
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
389
594
                                             'reverse', include_merges=False))
390
 
        self.assertEqual(revisions, [('4b', 4, 0), ('3', 3, 0), ('2', 2, 0),
391
 
                                     ('1', 1, 0)])
 
595
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
 
596
            ('1', '1', 0)],
 
597
            revisions)
392
598
 
393
599
    def test_get_view_revisions_merge2(self):
394
600
        """Test get_view_revisions when there are merges"""
395
601
        mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
396
602
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
397
603
                                            'forward'))
398
 
        expected = [('1', 1, 0), ('2', 2, 0), ('3c', 3, 0), ('3a', None, 1),
399
 
                    ('3b', None, 1), ('4b', 4, 0), ('4a', None, 1)]
400
 
        self.assertEqual(revisions, expected)
 
604
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
 
605
            ('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
 
606
            ('4a', '2.1.2', 1)]
 
607
        self.assertEqual(expected, revisions)
401
608
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
402
609
                                             'forward', include_merges=False))
403
 
        self.assertEqual(revisions, [('1', 1, 0), ('2', 2, 0), ('3c', 3, 0),
404
 
                                     ('4b', 4, 0)])
 
610
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
 
611
            ('4b', '4', 0)],
 
612
            revisions)
 
613
 
 
614
 
 
615
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
 
616
 
 
617
    def create_tree_with_single_merge(self):
 
618
        """Create a branch with a moderate layout.
 
619
 
 
620
        The revision graph looks like:
 
621
 
 
622
           A
 
623
           |\
 
624
           B C
 
625
           |/
 
626
           D
 
627
 
 
628
        In this graph, A introduced files f1 and f2 and f3.
 
629
        B modifies f1 and f3, and C modifies f2 and f3.
 
630
        D merges the changes from B and C and resolves the conflict for f3.
 
631
        """
 
632
        # TODO: jam 20070218 This seems like it could really be done
 
633
        #       with make_branch_and_memory_tree() if we could just
 
634
        #       create the content of those files.
 
635
        # TODO: jam 20070218 Another alternative is that we would really
 
636
        #       like to only create this tree 1 time for all tests that
 
637
        #       use it. Since 'log' only uses the tree in a readonly
 
638
        #       fashion, it seems a shame to regenerate an identical
 
639
        #       tree for each test.
 
640
        tree = self.make_branch_and_tree('tree')
 
641
        tree.lock_write()
 
642
        self.addCleanup(tree.unlock)
 
643
 
 
644
        self.build_tree_contents([('tree/f1', 'A\n'),
 
645
                                  ('tree/f2', 'A\n'),
 
646
                                  ('tree/f3', 'A\n'),
 
647
                                 ])
 
648
        tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
 
649
        tree.commit('A', rev_id='A')
 
650
 
 
651
        self.build_tree_contents([('tree/f2', 'A\nC\n'),
 
652
                                  ('tree/f3', 'A\nC\n'),
 
653
                                 ])
 
654
        tree.commit('C', rev_id='C')
 
655
        # Revert back to A to build the other history.
 
656
        tree.set_last_revision('A')
 
657
        tree.branch.set_last_revision_info(1, 'A')
 
658
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
 
659
                                  ('tree/f2', 'A\n'),
 
660
                                  ('tree/f3', 'A\nB\n'),
 
661
                                 ])
 
662
        tree.commit('B', rev_id='B')
 
663
        tree.set_parent_ids(['B', 'C'])
 
664
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
 
665
                                  ('tree/f2', 'A\nC\n'),
 
666
                                  ('tree/f3', 'A\nB\nC\n'),
 
667
                                 ])
 
668
        tree.commit('D', rev_id='D')
 
669
 
 
670
        # Switch to a read lock for this tree.
 
671
        # We still have addCleanup(unlock)
 
672
        tree.unlock()
 
673
        tree.lock_read()
 
674
        return tree
 
675
 
 
676
    def test_tree_with_single_merge(self):
 
677
        """Make sure the tree layout is correct."""
 
678
        tree = self.create_tree_with_single_merge()
 
679
        rev_A_tree = tree.branch.repository.revision_tree('A')
 
680
        rev_B_tree = tree.branch.repository.revision_tree('B')
 
681
 
 
682
        f1_changed = (u'f1', 'f1-id', 'file', True, False)
 
683
        f2_changed = (u'f2', 'f2-id', 'file', True, False)
 
684
        f3_changed = (u'f3', 'f3-id', 'file', True, False)
 
685
 
 
686
        delta = rev_B_tree.changes_from(rev_A_tree)
 
687
        self.assertEqual([f1_changed, f3_changed], delta.modified)
 
688
        self.assertEqual([], delta.renamed)
 
689
        self.assertEqual([], delta.added)
 
690
        self.assertEqual([], delta.removed)
 
691
 
 
692
        rev_C_tree = tree.branch.repository.revision_tree('C')
 
693
        delta = rev_C_tree.changes_from(rev_A_tree)
 
694
        self.assertEqual([f2_changed, f3_changed], delta.modified)
 
695
        self.assertEqual([], delta.renamed)
 
696
        self.assertEqual([], delta.added)
 
697
        self.assertEqual([], delta.removed)
 
698
 
 
699
        rev_D_tree = tree.branch.repository.revision_tree('D')
 
700
        delta = rev_D_tree.changes_from(rev_B_tree)
 
701
        self.assertEqual([f2_changed, f3_changed], delta.modified)
 
702
        self.assertEqual([], delta.renamed)
 
703
        self.assertEqual([], delta.added)
 
704
        self.assertEqual([], delta.removed)
 
705
 
 
706
        delta = rev_D_tree.changes_from(rev_C_tree)
 
707
        self.assertEqual([f1_changed, f3_changed], delta.modified)
 
708
        self.assertEqual([], delta.renamed)
 
709
        self.assertEqual([], delta.added)
 
710
        self.assertEqual([], delta.removed)
 
711
 
 
712
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
 
713
        """Make sure _get_revisions_touching_file_id returns the right values.
 
714
 
 
715
        Get the return value from _get_revisions_touching_file_id and make
 
716
        sure they are correct.
 
717
        """
 
718
        # The api for _get_revisions_touching_file_id is a little crazy,
 
719
        # So we do the setup here.
 
720
        mainline = tree.branch.revision_history()
 
721
        mainline.insert(0, None)
 
722
        revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
 
723
        view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
 
724
                                                'reverse', True)
 
725
        actual_revs = log._get_revisions_touching_file_id(tree.branch, file_id,
 
726
                                                          mainline,
 
727
                                                          view_revs_iter)
 
728
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
 
729
 
 
730
    def test_file_id_f1(self):
 
731
        tree = self.create_tree_with_single_merge()
 
732
        # f1 should be marked as modified by revisions A and B
 
733
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
 
734
 
 
735
    def test_file_id_f2(self):
 
736
        tree = self.create_tree_with_single_merge()
 
737
        # f2 should be marked as modified by revisions A, C, and D
 
738
        # because D merged the changes from C.
 
739
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
 
740
 
 
741
    def test_file_id_f3(self):
 
742
        tree = self.create_tree_with_single_merge()
 
743
        # f3 should be marked as modified by revisions A, B, C, and D
 
744
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])