~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-30 16:43:12 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060730164312-b025fd3ff0cee59e
rename  gpl.txt => COPYING.txt

Show diffs side-by-side

added added

removed removed

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