/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2359.1.6 by John Arbash Meinel
Create a helper tree which has a semi-interesting history.
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1685.1.80 by Wouter van Heyst
more code cleanup
2
#
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1685.1.80 by Wouter van Heyst
more code cleanup
7
#
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1685.1.80 by Wouter van Heyst
more code cleanup
12
#
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
import os
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
18
from cStringIO import StringIO
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
19
2359.1.7 by John Arbash Meinel
Create a direct test for _get_revisions_touching_file_id
20
from bzrlib import log
2530.3.6 by Martin Pool
Remove BzrTestBase alias (little used)
21
from bzrlib.tests import TestCaseWithTransport
2490.1.2 by John Arbash Meinel
Cleanup according to PEP8 and some other small whitespace fixes
22
from bzrlib.log import (show_log,
23
                        get_view_revisions,
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
24
                        LogRevision,
2490.1.2 by John Arbash Meinel
Cleanup according to PEP8 and some other small whitespace fixes
25
                        LogFormatter,
26
                        LongLogFormatter,
27
                        ShortLogFormatter,
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
28
                        LineLogFormatter)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
29
from bzrlib.branch import Branch
974.1.54 by aaron.bentley at utoronto
Fixed the revno bug in log
30
from bzrlib.errors import InvalidRevisionNumber
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
31
1685.1.69 by Wouter van Heyst
merge bzr.dev 1740
32
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
33
class LogCatcher(LogFormatter):
34
    """Pull log messages into list rather than displaying them.
35
36
    For ease of testing we save log messages here rather than actually
37
    formatting them, so that we can precisely check the result without
38
    being too dependent on the exact formatting.
39
40
    We should also test the LogFormatter.
41
    """
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
42
2490.1.2 by John Arbash Meinel
Cleanup according to PEP8 and some other small whitespace fixes
43
    supports_delta = True
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
44
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
45
    def __init__(self):
46
        super(LogCatcher, self).__init__(to_file=None)
47
        self.logs = []
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
48
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
49
    def log_revision(self, revision):
50
        self.logs.append(revision)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
51
52
2466.11.2 by Kent Gibson
Add tests for deltas in merge revisions
53
class TestShowLog(TestCaseWithTransport):
1102 by Martin Pool
- merge test refactoring from robertc
54
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
55
    def checkDelta(self, delta, **kw):
56
        """Check the filenames touched by a delta are as expected."""
57
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
58
            expected = kw.get(n, [])
59
            # strip out only the path components
60
            got = [x[0] for x in getattr(delta, n)]
61
            self.assertEquals(expected, got)
62
974.1.54 by aaron.bentley at utoronto
Fixed the revno bug in log
63
    def test_cur_revno(self):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
64
        wt = self.make_branch_and_tree('.')
65
        b = wt.branch
1092.3.4 by Robert Collins
update symlink branch to integration
66
67
        lf = LogCatcher()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
68
        wt.commit('empty commit')
1092.3.4 by Robert Collins
update symlink branch to integration
69
        show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
70
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
71
                          start_revision=2, end_revision=1) 
72
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
73
                          start_revision=1, end_revision=2) 
74
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
75
                          start_revision=0, end_revision=2) 
76
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
77
                          start_revision=1, end_revision=0) 
78
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
79
                          start_revision=-1, end_revision=1) 
80
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
81
                          start_revision=1, end_revision=-1) 
82
1102 by Martin Pool
- merge test refactoring from robertc
83
    def test_simple_log(self):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
84
        eq = self.assertEquals
85
        
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
86
        wt = self.make_branch_and_tree('.')
87
        b = wt.branch
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
88
89
        lf = LogCatcher()
90
        show_log(b, lf)
91
        # no entries yet
92
        eq(lf.logs, [])
93
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
94
        wt.commit('empty commit')
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
95
        lf = LogCatcher()
96
        show_log(b, lf, verbose=True)
97
        eq(len(lf.logs), 1)
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
98
        eq(lf.logs[0].revno, '1')
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
99
        eq(lf.logs[0].rev.message, 'empty commit')
100
        d = lf.logs[0].delta
101
        self.log('log delta: %r' % d)
102
        self.checkDelta(d)
103
104
        self.build_tree(['hello'])
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
105
        wt.add('hello')
106
        wt.commit('add one file')
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
107
108
        lf = StringIO()
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
109
        # log using regular thing
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
110
        show_log(b, LongLogFormatter(lf))
111
        lf.seek(0)
112
        for l in lf.readlines():
113
            self.log(l)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
114
115
        # get log as data structure
116
        lf = LogCatcher()
117
        show_log(b, lf, verbose=True)
118
        eq(len(lf.logs), 2)
119
        self.log('log entries:')
120
        for logentry in lf.logs:
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
121
            self.log('%4s %s' % (logentry.revno, logentry.rev.message))
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
122
        
123
        # first one is most recent
124
        logentry = lf.logs[0]
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
125
        eq(logentry.revno, '2')
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
126
        eq(logentry.rev.message, 'add one file')
127
        d = logentry.delta
128
        self.log('log 2 delta: %r' % d)
2466.11.2 by Kent Gibson
Add tests for deltas in merge revisions
129
        self.checkDelta(d, added=['hello'])
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
130
        
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
131
        # commit a log message with control characters
132
        msg = "All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
1393.4.2 by Harald Meland
Cleanup + better test of commit-msg control character escape code.
133
        self.log("original commit message: %r", msg)
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
134
        wt.commit(msg)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
135
        lf = LogCatcher()
136
        show_log(b, lf, verbose=True)
137
        committed_msg = lf.logs[0].rev.message
138
        self.log("escaped commit message: %r", committed_msg)
139
        self.assert_(msg != committed_msg)
140
        self.assert_(len(committed_msg) > len(msg))
1393.4.2 by Harald Meland
Cleanup + better test of commit-msg control character escape code.
141
142
        # Check that log message with only XML-valid characters isn't
143
        # escaped.  As ElementTree apparently does some kind of
144
        # newline conversion, neither LF (\x0A) nor CR (\x0D) are
145
        # included in the test commit message, even though they are
146
        # valid XML 1.0 characters.
147
        msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
148
        self.log("original commit message: %r", msg)
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
149
        wt.commit(msg)
1393.4.2 by Harald Meland
Cleanup + better test of commit-msg control character escape code.
150
        lf = LogCatcher()
151
        show_log(b, lf, verbose=True)
152
        committed_msg = lf.logs[0].rev.message
153
        self.log("escaped commit message: %r", committed_msg)
154
        self.assert_(msg == committed_msg)
1185.31.22 by John Arbash Meinel
[merge] bzr.dev
155
2466.11.2 by Kent Gibson
Add tests for deltas in merge revisions
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
1185.31.21 by John Arbash Meinel
Added test for log formatting, found bug when redirecting short logs to a file instead of stdout.
219
    def test_trailing_newlines(self):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
220
        wt = self.make_branch_and_tree('.')
2466.11.2 by Kent Gibson
Add tests for deltas in merge revisions
221
        b = make_commits_with_trailing_newlines(wt)
1185.31.21 by John Arbash Meinel
Added test for log formatting, found bug when redirecting short logs to a file instead of stdout.
222
        sio = StringIO()
223
        lf = ShortLogFormatter(to_file=sio)
224
        show_log(b, lf)
225
        self.assertEquals(sio.getvalue(), """\
226
    3 Joe Foo\t2005-11-21
227
      single line with trailing newline
228
229
    2 Joe Foo\t2005-11-21
230
      multiline
231
      log
232
      message
233
234
    1 Joe Foo\t2005-11-21
235
      simple log message
236
237
""")
238
2466.11.2 by Kent Gibson
Add tests for deltas in merge revisions
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
1185.33.41 by Martin Pool
Fix regression of 'bzr log -v' - it wasn't showing changed files at all. (#4676)
255
    def test_verbose_log(self):
256
        """Verbose log includes changed files
257
        
258
        bug #4676
259
        """
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
260
        wt = self.make_branch_and_tree('.')
261
        b = wt.branch
1185.33.41 by Martin Pool
Fix regression of 'bzr log -v' - it wasn't showing changed files at all. (#4676)
262
        self.build_tree(['a'])
1185.33.45 by Martin Pool
[merge] refactoring of branch vs working tree, etc (robertc)
263
        wt.add('a')
1185.33.41 by Martin Pool
Fix regression of 'bzr log -v' - it wasn't showing changed files at all. (#4676)
264
        # XXX: why does a longer nick show up?
265
        b.nick = 'test_verbose_log'
266
        wt.commit(message='add a', 
267
                  timestamp=1132711707, 
268
                  timezone=36000,
269
                  committer='Lorem Ipsum <test@example.com>')
270
        logfile = file('out.tmp', 'w+')
271
        formatter = LongLogFormatter(to_file=logfile)
272
        show_log(b, formatter, verbose=True)
273
        logfile.flush()
274
        logfile.seek(0)
275
        log_contents = logfile.read()
276
        self.assertEqualDiff(log_contents, '''\
277
------------------------------------------------------------
278
revno: 1
279
committer: Lorem Ipsum <test@example.com>
280
branch nick: test_verbose_log
281
timestamp: Wed 2005-11-23 12:08:27 +1000
282
message:
283
  add a
284
added:
285
  a
286
''')
1185.85.4 by John Arbash Meinel
currently broken, trying to fix things up.
287
2466.11.2 by Kent Gibson
Add tests for deltas in merge revisions
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
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
431
    def test_line_log(self):
432
        """Line log should show revno
433
        
434
        bug #5162
435
        """
436
        wt = self.make_branch_and_tree('.')
437
        b = wt.branch
438
        self.build_tree(['a'])
439
        wt.add('a')
440
        b.nick = 'test-line-log'
441
        wt.commit(message='add a', 
442
                  timestamp=1132711707, 
443
                  timezone=36000,
444
                  committer='Line-Log-Formatter Tester <test@line.log>')
445
        logfile = file('out.tmp', 'w+')
446
        formatter = LineLogFormatter(to_file=logfile)
447
        show_log(b, formatter)
448
        logfile.flush()
449
        logfile.seek(0)
450
        log_contents = logfile.read()
451
        self.assertEqualDiff(log_contents, '1: Line-Log-Formatte... 2005-11-23 add a\n')
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
452
2483.2.1 by John Arbash Meinel
Add a test with a merged revision
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("""\
2483.2.2 by John Arbash Meinel
Add [merge] after the timestamp for revisions with merges.
474
    2 Joe Foo\t2005-11-22 [merge]
2483.2.1 by John Arbash Meinel
Add a test with a merged revision
475
      rev-2
476
477
    1 Joe Foo\t2005-11-22
478
      rev-1
479
480
""", logfile.getvalue())
481
        finally:
482
            wt.unlock()
483
2466.11.2 by Kent Gibson
Add tests for deltas in merge revisions
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
1756.2.22 by Aaron Bentley
Apply review comments
499
    def make_tree_with_commits(self):
500
        """Create a tree with well-known revision ids"""
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
501
        wt = self.make_branch_and_tree('tree1')
502
        wt.commit('commit one', rev_id='1')
503
        wt.commit('commit two', rev_id='2')
504
        wt.commit('commit three', rev_id='3')
505
        mainline_revs = [None, '1', '2', '3']
1756.2.22 by Aaron Bentley
Apply review comments
506
        rev_nos = {'1': 1, '2': 2, '3': 3}
507
        return mainline_revs, rev_nos, wt
508
509
    def make_tree_with_merges(self):
510
        """Create a tree with well-known revision ids and a merge"""
511
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
512
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
513
        tree2.commit('four-a', rev_id='4a')
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
514
        wt.merge_from_branch(tree2.branch)
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
515
        wt.commit('four-b', rev_id='4b')
516
        mainline_revs.append('4b')
1756.2.22 by Aaron Bentley
Apply review comments
517
        rev_nos['4b'] = 4
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
518
        # 4a: 3.1.1
1756.2.22 by Aaron Bentley
Apply review comments
519
        return mainline_revs, rev_nos, wt
520
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
521
    def make_tree_with_many_merges(self):
522
        """Create a tree with well-known revision ids"""
523
        wt = self.make_branch_and_tree('tree1')
524
        wt.commit('commit one', rev_id='1')
525
        wt.commit('commit two', rev_id='2')
526
        tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
527
        tree3.commit('commit three a', rev_id='3a')
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
528
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
529
        tree2.merge_from_branch(tree3.branch)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
530
        tree2.commit('commit three b', rev_id='3b')
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
531
        wt.merge_from_branch(tree2.branch)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
532
        wt.commit('commit three c', rev_id='3c')
533
        tree2.commit('four-a', rev_id='4a')
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
534
        wt.merge_from_branch(tree2.branch)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
535
        wt.commit('four-b', rev_id='4b')
536
        mainline_revs = [None, '1', '2', '3c', '4b']
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
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
            }
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
547
        return mainline_revs, rev_nos, wt
548
1756.2.22 by Aaron Bentley
Apply review comments
549
    def test_get_view_revisions_forward(self):
550
        """Test the get_view_revisions method"""
551
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
552
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
553
                                            'forward'))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
554
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
555
            revisions)
1756.2.22 by Aaron Bentley
Apply review comments
556
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
557
                                             'forward', include_merges=False))
558
        self.assertEqual(revisions, revisions2)
559
560
    def test_get_view_revisions_reverse(self):
561
        """Test the get_view_revisions with reverse"""
562
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
563
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
564
                                            'reverse'))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
565
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
566
            revisions)
1756.2.22 by Aaron Bentley
Apply review comments
567
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
568
                                             'reverse', include_merges=False))
569
        self.assertEqual(revisions, revisions2)
570
571
    def test_get_view_revisions_merge(self):
572
        """Test get_view_revisions when there are merges"""
573
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
574
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
575
                                            'forward'))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
576
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
577
            ('4b', '4', 0), ('4a', '3.1.1', 1)],
578
            revisions)
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
579
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
1756.2.22 by Aaron Bentley
Apply review comments
580
                                             'forward', include_merges=False))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
581
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
582
            ('4b', '4', 0)],
583
            revisions)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
584
585
    def test_get_view_revisions_merge_reverse(self):
586
        """Test get_view_revisions in reverse when there are merges"""
587
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
588
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
589
                                            'reverse'))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
590
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
591
            ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
592
            revisions)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
593
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
594
                                             'reverse', include_merges=False))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
595
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
596
            ('1', '1', 0)],
597
            revisions)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
598
599
    def test_get_view_revisions_merge2(self):
600
        """Test get_view_revisions when there are merges"""
601
        mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
602
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
603
                                            'forward'))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
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)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
608
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
609
                                             'forward', include_merges=False))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
610
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
611
            ('4b', '4', 0)],
612
            revisions)
2359.1.6 by John Arbash Meinel
Create a helper tree which has a semi-interesting history.
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
2359.1.7 by John Arbash Meinel
Create a direct test for _get_revisions_touching_file_id
712
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
713
        """Make sure _filter_revisions_touching_file_id returns the right values.
2359.1.7 by John Arbash Meinel
Create a direct test for _get_revisions_touching_file_id
714
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
715
        Get the return value from _filter_revisions_touching_file_id and make
2359.1.7 by John Arbash Meinel
Create a direct test for _get_revisions_touching_file_id
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)
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
725
        actual_revs = log._filter_revisions_touching_file_id(
726
                            tree.branch, 
727
                            file_id,
728
                            mainline,
729
                            list(view_revs_iter))
2359.1.7 by John Arbash Meinel
Create a direct test for _get_revisions_touching_file_id
730
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
731
732
    def test_file_id_f1(self):
733
        tree = self.create_tree_with_single_merge()
734
        # f1 should be marked as modified by revisions A and B
735
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
736
737
    def test_file_id_f2(self):
738
        tree = self.create_tree_with_single_merge()
739
        # f2 should be marked as modified by revisions A, C, and D
740
        # because D merged the changes from C.
741
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
742
743
    def test_file_id_f3(self):
744
        tree = self.create_tree_with_single_merge()
745
        # f3 should be marked as modified by revisions A, B, C, and D
746
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])