~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_log.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-04-01 15:14:38 UTC
  • mfrom: (4202.3.2 bzr-send-tweak)
  • Revision ID: pqm@pqm.ubuntu.com-20090401151438-hqulqoazddtacbls
(andrew) Don't use get_revision_xml when writing a bundle,
        instead get all the revisions together.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
 
 
18
"""Black-box tests for bzr log."""
 
19
 
 
20
import os, re
 
21
 
 
22
from bzrlib import osutils
 
23
from bzrlib.tests.blackbox import ExternalBase
 
24
from bzrlib.tests import KnownFailure, TestCaseInTempDir, TestCaseWithTransport
 
25
from bzrlib.tests.test_log import (
 
26
    normalize_log,
 
27
    )
 
28
from bzrlib.tests import test_log
 
29
 
 
30
 
 
31
class TestCaseWithoutPropsHandler(ExternalBase,
 
32
                                  test_log.TestCaseWithoutPropsHandler):
 
33
    pass
 
34
 
 
35
 
 
36
class TestLog(ExternalBase):
 
37
 
 
38
    def _prepare(self, path='.', format=None):
 
39
        tree = self.make_branch_and_tree(path, format=format)
 
40
        self.build_tree(
 
41
            [path + '/hello.txt', path + '/goodbye.txt', path + '/meep.txt'])
 
42
        tree.add('hello.txt')
 
43
        tree.commit(message='message1')
 
44
        tree.add('goodbye.txt')
 
45
        tree.commit(message='message2')
 
46
        tree.add('meep.txt')
 
47
        tree.commit(message='message3')
 
48
        self.full_log = self.run_bzr(["log", path])[0]
 
49
        return tree
 
50
 
 
51
    def test_log_null_end_revspec(self):
 
52
        self._prepare()
 
53
        self.assertTrue('revno: 1\n' in self.full_log)
 
54
        self.assertTrue('revno: 2\n' in self.full_log)
 
55
        self.assertTrue('revno: 3\n' in self.full_log)
 
56
        self.assertTrue('message:\n  message1\n' in self.full_log)
 
57
        self.assertTrue('message:\n  message2\n' in self.full_log)
 
58
        self.assertTrue('message:\n  message3\n' in self.full_log)
 
59
 
 
60
        log = self.run_bzr("log -r 1..")[0]
 
61
        self.assertEqualDiff(log, self.full_log)
 
62
 
 
63
    def test_log_null_begin_revspec(self):
 
64
        self._prepare()
 
65
        log = self.run_bzr("log -r ..3")[0]
 
66
        self.assertEqualDiff(self.full_log, log)
 
67
 
 
68
    def test_log_null_both_revspecs(self):
 
69
        self._prepare()
 
70
        log = self.run_bzr("log -r ..")[0]
 
71
        self.assertEqualDiff(self.full_log, log)
 
72
 
 
73
    def test_log_zero_revspec(self):
 
74
        self._prepare()
 
75
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
76
                           ['log', '-r0'])
 
77
 
 
78
    def test_log_zero_begin_revspec(self):
 
79
        self._prepare()
 
80
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
81
                           ['log', '-r0..2'])
 
82
 
 
83
    def test_log_zero_end_revspec(self):
 
84
        self._prepare()
 
85
        self.run_bzr_error('bzr: ERROR: Logging revision 0 is invalid.',
 
86
                           ['log', '-r-2..0'])
 
87
 
 
88
    def test_log_unsupported_timezone(self):
 
89
        self._prepare()
 
90
        self.run_bzr_error('bzr: ERROR: Unsupported timezone format "foo", '
 
91
                           'options are "utc", "original", "local".',
 
92
                           ['log', '--timezone', 'foo'])
 
93
 
 
94
    def test_log_negative_begin_revspec_full_log(self):
 
95
        self._prepare()
 
96
        log = self.run_bzr("log -r -3..")[0]
 
97
        self.assertEqualDiff(self.full_log, log)
 
98
 
 
99
    def test_log_negative_both_revspec_full_log(self):
 
100
        self._prepare()
 
101
        log = self.run_bzr("log -r -3..-1")[0]
 
102
        self.assertEqualDiff(self.full_log, log)
 
103
 
 
104
    def test_log_negative_both_revspec_partial(self):
 
105
        self._prepare()
 
106
        log = self.run_bzr("log -r -3..-2")[0]
 
107
        self.assertTrue('revno: 1\n' in log)
 
108
        self.assertTrue('revno: 2\n' in log)
 
109
        self.assertTrue('revno: 3\n' not in log)
 
110
 
 
111
    def test_log_negative_begin_revspec(self):
 
112
        self._prepare()
 
113
        log = self.run_bzr("log -r -2..")[0]
 
114
        self.assertTrue('revno: 1\n' not in log)
 
115
        self.assertTrue('revno: 2\n' in log)
 
116
        self.assertTrue('revno: 3\n' in log)
 
117
 
 
118
    def test_log_positive_revspecs(self):
 
119
        self._prepare()
 
120
        log = self.run_bzr("log -r 1..3")[0]
 
121
        self.assertEqualDiff(self.full_log, log)
 
122
 
 
123
    def test_log_reversed_revspecs(self):
 
124
        self._prepare()
 
125
        self.run_bzr_error(('bzr: ERROR: Start revision must be older than '
 
126
                            'the end revision.\n',),
 
127
                           ['log', '-r3..1'])
 
128
 
 
129
    def test_log_revno_n_path(self):
 
130
        self._prepare(path='branch1')
 
131
        self._prepare(path='branch2')
 
132
        log = self.run_bzr("log -r revno:2:branch1..revno:3:branch2",
 
133
                          retcode=3)[0]
 
134
        log = self.run_bzr("log -r revno:1:branch2..revno:3:branch2")[0]
 
135
        self.assertEqualDiff(self.full_log, log)
 
136
        log = self.run_bzr("log -r revno:1:branch2")[0]
 
137
        self.assertTrue('revno: 1\n' in log)
 
138
        self.assertTrue('revno: 2\n' not in log)
 
139
        self.assertTrue('branch nick: branch2\n' in log)
 
140
        self.assertTrue('branch nick: branch1\n' not in log)
 
141
 
 
142
    def test_log_nonexistent_revno(self):
 
143
        self._prepare()
 
144
        (out, err) = self.run_bzr_error(args="log -r 1234",
 
145
            error_regexes=["bzr: ERROR: Requested revision: '1234' "
 
146
                "does not exist in branch:"])
 
147
 
 
148
    def test_log_nonexistent_dotted_revno(self):
 
149
        self._prepare()
 
150
        (out, err) = self.run_bzr_error(args="log -r 123.123",
 
151
            error_regexes=["bzr: ERROR: Requested revision: '123.123' "
 
152
                "does not exist in branch:"])
 
153
 
 
154
    def test_log_change_revno(self):
 
155
        self._prepare()
 
156
        expected_log = self.run_bzr("log -r 1")[0]
 
157
        log = self.run_bzr("log -c 1")[0]
 
158
        self.assertEqualDiff(expected_log, log)
 
159
 
 
160
    def test_log_change_nonexistent_revno(self):
 
161
        self._prepare()
 
162
        (out, err) = self.run_bzr_error(args="log -c 1234",
 
163
            error_regexes=["bzr: ERROR: Requested revision: '1234' "
 
164
                "does not exist in branch:"])
 
165
 
 
166
    def test_log_change_nonexistent_dotted_revno(self):
 
167
        self._prepare()
 
168
        (out, err) = self.run_bzr_error(args="log -c 123.123",
 
169
            error_regexes=["bzr: ERROR: Requested revision: '123.123' "
 
170
                "does not exist in branch:"])
 
171
 
 
172
    def test_log_change_single_revno(self):
 
173
        self._prepare()
 
174
        self.run_bzr_error('bzr: ERROR: Option --change does not'
 
175
                           ' accept revision ranges',
 
176
                           ['log', '--change', '2..3'])
 
177
 
 
178
    def test_log_change_incompatible_with_revision(self):
 
179
        self._prepare()
 
180
        self.run_bzr_error('bzr: ERROR: --revision and --change'
 
181
                           ' are mutually exclusive',
 
182
                           ['log', '--change', '2', '--revision', '3'])
 
183
 
 
184
    def test_log_nonexistent_file(self):
 
185
        # files that don't exist in either the basis tree or working tree
 
186
        # should give an error
 
187
        wt = self.make_branch_and_tree('.')
 
188
        out, err = self.run_bzr('log does-not-exist', retcode=3)
 
189
        self.assertContainsRe(
 
190
            err, 'Path unknown at end or start of revision range: does-not-exist')
 
191
 
 
192
    def test_log_with_tags(self):
 
193
        tree = self._prepare(format='dirstate-tags')
 
194
        branch = tree.branch
 
195
        branch.tags.set_tag('tag1', branch.get_rev_id(1))
 
196
        branch.tags.set_tag('tag1.1', branch.get_rev_id(1))
 
197
        branch.tags.set_tag('tag3', branch.last_revision())
 
198
 
 
199
        log = self.run_bzr("log -r-1")[0]
 
200
        self.assertTrue('tags: tag3' in log)
 
201
 
 
202
        log = self.run_bzr("log -r1")[0]
 
203
        # I guess that we can't know the order of tags in the output
 
204
        # since dicts are unordered, need to check both possibilities
 
205
        self.assertContainsRe(log, r'tags: (tag1, tag1\.1|tag1\.1, tag1)')
 
206
 
 
207
    def test_merged_log_with_tags(self):
 
208
        branch1_tree = self._prepare(path='branch1', format='dirstate-tags')
 
209
        branch1 = branch1_tree.branch
 
210
        branch2_tree = branch1_tree.bzrdir.sprout('branch2').open_workingtree()
 
211
        branch1_tree.commit(message='foobar', allow_pointless=True)
 
212
        branch1.tags.set_tag('tag1', branch1.last_revision())
 
213
        os.chdir('branch2')
 
214
        self.run_bzr('merge ../branch1') # tags don't propagate otherwise
 
215
        branch2_tree.commit(message='merge branch 1')
 
216
        log = self.run_bzr("log -n0 -r-1")[0]
 
217
        self.assertContainsRe(log, r'    tags: tag1')
 
218
        log = self.run_bzr("log -n0 -r3.1.1")[0]
 
219
        self.assertContainsRe(log, r'tags: tag1')
 
220
 
 
221
    def test_log_limit(self):
 
222
        tree = self.make_branch_and_tree('.')
 
223
        # We want more commits than our batch size starts at
 
224
        for pos in range(10):
 
225
            tree.commit("%s" % pos)
 
226
        log = self.run_bzr("log --limit 2")[0]
 
227
        self.assertNotContainsRe(log, r'revno: 1\n')
 
228
        self.assertNotContainsRe(log, r'revno: 2\n')
 
229
        self.assertNotContainsRe(log, r'revno: 3\n')
 
230
        self.assertNotContainsRe(log, r'revno: 4\n')
 
231
        self.assertNotContainsRe(log, r'revno: 5\n')
 
232
        self.assertNotContainsRe(log, r'revno: 6\n')
 
233
        self.assertNotContainsRe(log, r'revno: 7\n')
 
234
        self.assertNotContainsRe(log, r'revno: 8\n')
 
235
        self.assertContainsRe(log, r'revno: 9\n')
 
236
        self.assertContainsRe(log, r'revno: 10\n')
 
237
 
 
238
    def test_log_limit_short(self):
 
239
        self._prepare()
 
240
        log = self.run_bzr("log -l 2")[0]
 
241
        self.assertNotContainsRe(log, r'revno: 1\n')
 
242
        self.assertContainsRe(log, r'revno: 2\n')
 
243
        self.assertContainsRe(log, r'revno: 3\n')
 
244
 
 
245
    def test_log_bad_message_re(self):
 
246
        """Bad --message argument gives a sensible message
 
247
        
 
248
        See https://bugs.launchpad.net/bzr/+bug/251352
 
249
        """
 
250
        self._prepare()
 
251
        out, err = self.run_bzr(['log', '-m', '*'], retcode=3)
 
252
        self.assertEqual("bzr: ERROR: Invalid regular expression"
 
253
            " in log message filter"
 
254
            ": '*'"
 
255
            ": nothing to repeat\n", err)
 
256
        self.assertEqual('', out)
 
257
 
 
258
 
 
259
class TestLogVerbose(TestCaseWithTransport):
 
260
 
 
261
    def setUp(self):
 
262
        super(TestLogVerbose, self).setUp()
 
263
        tree = self.make_branch_and_tree('.')
 
264
        self.build_tree(['hello.txt'])
 
265
        tree.add('hello.txt')
 
266
        tree.commit(message='message1')
 
267
 
 
268
    def assertUseShortDeltaFormat(self, cmd):
 
269
        log = self.run_bzr(cmd)[0]
 
270
        # Check that we use the short status format
 
271
        self.assertContainsRe(log, '(?m)^\s*A  hello.txt$')
 
272
        self.assertNotContainsRe(log, '(?m)^\s*added:$')
 
273
 
 
274
    def assertUseLongDeltaFormat(self, cmd):
 
275
        log = self.run_bzr(cmd)[0]
 
276
        # Check that we use the long status format
 
277
        self.assertNotContainsRe(log, '(?m)^\s*A  hello.txt$')
 
278
        self.assertContainsRe(log, '(?m)^\s*added:$')
 
279
 
 
280
    def test_log_short_verbose(self):
 
281
        self.assertUseShortDeltaFormat(['log', '--short', '-v'])
 
282
 
 
283
    def test_log_short_verbose_verbose(self):
 
284
        self.assertUseLongDeltaFormat(['log', '--short', '-vv'])
 
285
 
 
286
    def test_log_long_verbose(self):
 
287
        # Check that we use the long status format, ignoring the verbosity
 
288
        # level
 
289
        self.assertUseLongDeltaFormat(['log', '--long', '-v'])
 
290
 
 
291
    def test_log_long_verbose_verbose(self):
 
292
        # Check that we use the long status format, ignoring the verbosity
 
293
        # level
 
294
        self.assertUseLongDeltaFormat(['log', '--long', '-vv'])
 
295
 
 
296
 
 
297
class TestLogMerges(TestCaseWithoutPropsHandler):
 
298
 
 
299
    def _prepare(self):
 
300
        parent_tree = self.make_branch_and_tree('parent')
 
301
        parent_tree.commit(message='first post', allow_pointless=True)
 
302
        child_tree = parent_tree.bzrdir.sprout('child').open_workingtree()
 
303
        child_tree.commit(message='branch 1', allow_pointless=True)
 
304
        smaller_tree = \
 
305
                child_tree.bzrdir.sprout('smallerchild').open_workingtree()
 
306
        smaller_tree.commit(message='branch 2', allow_pointless=True)
 
307
        child_tree.merge_from_branch(smaller_tree.branch)
 
308
        child_tree.commit(message='merge branch 2')
 
309
        parent_tree.merge_from_branch(child_tree.branch)
 
310
        parent_tree.commit(message='merge branch 1')
 
311
        os.chdir('parent')
 
312
 
 
313
    def _prepare_short(self):
 
314
        parent_tree = self.make_branch_and_tree('parent')
 
315
        parent_tree.commit(message='first post',
 
316
            timestamp=1132586700, timezone=36000,
 
317
            committer='Joe Foo <joe@foo.com>')
 
318
        child_tree = parent_tree.bzrdir.sprout('child').open_workingtree()
 
319
        child_tree.commit(message='branch 1',
 
320
            timestamp=1132586800, timezone=36000,
 
321
            committer='Joe Foo <joe@foo.com>')
 
322
        smaller_tree = \
 
323
                child_tree.bzrdir.sprout('smallerchild').open_workingtree()
 
324
        smaller_tree.commit(message='branch 2',
 
325
            timestamp=1132586900, timezone=36000,
 
326
            committer='Joe Foo <joe@foo.com>')
 
327
        child_tree.merge_from_branch(smaller_tree.branch)
 
328
        child_tree.commit(message='merge branch 2',
 
329
            timestamp=1132587000, timezone=36000,
 
330
            committer='Joe Foo <joe@foo.com>')
 
331
        parent_tree.merge_from_branch(child_tree.branch)
 
332
        parent_tree.commit(message='merge branch 1',
 
333
            timestamp=1132587100, timezone=36000,
 
334
            committer='Joe Foo <joe@foo.com>')
 
335
        os.chdir('parent')
 
336
 
 
337
    def test_merges_are_indented_by_level(self):
 
338
        self._prepare()
 
339
        out,err = self.run_bzr('log -n0')
 
340
        self.assertEqual('', err)
 
341
        log = normalize_log(out)
 
342
        self.assertEqualDiff(log, """\
 
343
------------------------------------------------------------
 
344
revno: 2 [merge]
 
345
committer: Lorem Ipsum <test@example.com>
 
346
branch nick: parent
 
347
timestamp: Just now
 
348
message:
 
349
  merge branch 1
 
350
    ------------------------------------------------------------
 
351
    revno: 1.1.2 [merge]
 
352
    committer: Lorem Ipsum <test@example.com>
 
353
    branch nick: child
 
354
    timestamp: Just now
 
355
    message:
 
356
      merge branch 2
 
357
        ------------------------------------------------------------
 
358
        revno: 1.2.1
 
359
        committer: Lorem Ipsum <test@example.com>
 
360
        branch nick: smallerchild
 
361
        timestamp: Just now
 
362
        message:
 
363
          branch 2
 
364
    ------------------------------------------------------------
 
365
    revno: 1.1.1
 
366
    committer: Lorem Ipsum <test@example.com>
 
367
    branch nick: child
 
368
    timestamp: Just now
 
369
    message:
 
370
      branch 1
 
371
------------------------------------------------------------
 
372
revno: 1
 
373
committer: Lorem Ipsum <test@example.com>
 
374
branch nick: parent
 
375
timestamp: Just now
 
376
message:
 
377
  first post
 
378
""")
 
379
 
 
380
    def test_force_merge_revisions_off(self):
 
381
        self._prepare()
 
382
        out,err = self.run_bzr('log --long -n1')
 
383
        self.assertEqual('', err)
 
384
        log = normalize_log(out)
 
385
        self.assertEqualDiff(log, """\
 
386
------------------------------------------------------------
 
387
revno: 2 [merge]
 
388
committer: Lorem Ipsum <test@example.com>
 
389
branch nick: parent
 
390
timestamp: Just now
 
391
message:
 
392
  merge branch 1
 
393
------------------------------------------------------------
 
394
revno: 1
 
395
committer: Lorem Ipsum <test@example.com>
 
396
branch nick: parent
 
397
timestamp: Just now
 
398
message:
 
399
  first post
 
400
------------------------------------------------------------
 
401
Use --levels 0 (or -n0) to see merged revisions.
 
402
""")
 
403
 
 
404
    def test_force_merge_revisions_on(self):
 
405
        self._prepare_short()
 
406
        out,err = self.run_bzr('log --short -n0')
 
407
        self.assertEqual('', err)
 
408
        log = normalize_log(out)
 
409
        self.assertEqualDiff(log, """\
 
410
    2 Joe Foo\t2005-11-22 [merge]
 
411
      merge branch 1
 
412
 
 
413
          1.1.2 Joe Foo\t2005-11-22 [merge]
 
414
                merge branch 2
 
415
 
 
416
              1.2.1 Joe Foo\t2005-11-22
 
417
                    branch 2
 
418
 
 
419
          1.1.1 Joe Foo\t2005-11-22
 
420
                branch 1
 
421
 
 
422
    1 Joe Foo\t2005-11-22
 
423
      first post
 
424
 
 
425
""")
 
426
 
 
427
    def test_force_merge_revisions_N(self):
 
428
        self._prepare_short()
 
429
        out,err = self.run_bzr('log --short -n2')
 
430
        self.assertEqual('', err)
 
431
        log = normalize_log(out)
 
432
        self.assertEqualDiff(log, """\
 
433
    2 Joe Foo\t2005-11-22 [merge]
 
434
      merge branch 1
 
435
 
 
436
          1.1.2 Joe Foo\t2005-11-22 [merge]
 
437
                merge branch 2
 
438
 
 
439
          1.1.1 Joe Foo\t2005-11-22
 
440
                branch 1
 
441
 
 
442
    1 Joe Foo\t2005-11-22
 
443
      first post
 
444
 
 
445
""")
 
446
 
 
447
    def test_merges_single_merge_rev(self):
 
448
        self._prepare()
 
449
        out,err = self.run_bzr('log -n0 -r1.1.2')
 
450
        self.assertEqual('', err)
 
451
        log = normalize_log(out)
 
452
        self.assertEqualDiff(log, """\
 
453
------------------------------------------------------------
 
454
revno: 1.1.2 [merge]
 
455
committer: Lorem Ipsum <test@example.com>
 
456
branch nick: child
 
457
timestamp: Just now
 
458
message:
 
459
  merge branch 2
 
460
    ------------------------------------------------------------
 
461
    revno: 1.2.1
 
462
    committer: Lorem Ipsum <test@example.com>
 
463
    branch nick: smallerchild
 
464
    timestamp: Just now
 
465
    message:
 
466
      branch 2
 
467
""")
 
468
 
 
469
    def test_merges_partial_range(self):
 
470
        self._prepare()
 
471
        out, err = self.run_bzr('log -n0 -r1.1.1..1.1.2')
 
472
        self.assertEqual('', err)
 
473
        log = normalize_log(out)
 
474
        self.assertEqualDiff(log, """\
 
475
------------------------------------------------------------
 
476
revno: 1.1.2 [merge]
 
477
committer: Lorem Ipsum <test@example.com>
 
478
branch nick: child
 
479
timestamp: Just now
 
480
message:
 
481
  merge branch 2
 
482
    ------------------------------------------------------------
 
483
    revno: 1.2.1
 
484
    committer: Lorem Ipsum <test@example.com>
 
485
    branch nick: smallerchild
 
486
    timestamp: Just now
 
487
    message:
 
488
      branch 2
 
489
------------------------------------------------------------
 
490
revno: 1.1.1
 
491
committer: Lorem Ipsum <test@example.com>
 
492
branch nick: child
 
493
timestamp: Just now
 
494
message:
 
495
  branch 1
 
496
""")
 
497
 
 
498
 
 
499
def subst_dates(string):
 
500
    """Replace date strings with constant values."""
 
501
    return re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-\+]\d{4}',
 
502
                  'YYYY-MM-DD HH:MM:SS +ZZZZ', string)
 
503
 
 
504
 
 
505
class TestLogDiff(TestCaseWithoutPropsHandler):
 
506
 
 
507
    def _prepare(self):
 
508
        parent_tree = self.make_branch_and_tree('parent')
 
509
        self.build_tree(['parent/file1', 'parent/file2'])
 
510
        parent_tree.add('file1')
 
511
        parent_tree.add('file2')
 
512
        parent_tree.commit(message='first post',
 
513
            timestamp=1132586655, timezone=36000,
 
514
            committer='Lorem Ipsum <test@example.com>')
 
515
        child_tree = parent_tree.bzrdir.sprout('child').open_workingtree()
 
516
        self.build_tree_contents([('child/file2', 'hello\n')])
 
517
        child_tree.commit(message='branch 1',
 
518
            timestamp=1132586700, timezone=36000,
 
519
            committer='Lorem Ipsum <test@example.com>')
 
520
        parent_tree.merge_from_branch(child_tree.branch)
 
521
        parent_tree.commit(message='merge branch 1',
 
522
            timestamp=1132586800, timezone=36000,
 
523
            committer='Lorem Ipsum <test@example.com>')
 
524
        os.chdir('parent')
 
525
 
 
526
    def test_log_show_diff_long_with_merges(self):
 
527
        self._prepare()
 
528
        out,err = self.run_bzr('log -p -n0')
 
529
        self.assertEqual('', err)
 
530
        log = normalize_log(out)
 
531
        self.assertEqualDiff(subst_dates(log), """\
 
532
------------------------------------------------------------
 
533
revno: 2 [merge]
 
534
committer: Lorem Ipsum <test@example.com>
 
535
branch nick: parent
 
536
timestamp: Just now
 
537
message:
 
538
  merge branch 1
 
539
diff:
 
540
=== modified file 'file2'
 
541
--- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
542
+++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
543
@@ -1,1 +1,1 @@
 
544
-contents of parent/file2
 
545
+hello
 
546
    ------------------------------------------------------------
 
547
    revno: 1.1.1
 
548
    committer: Lorem Ipsum <test@example.com>
 
549
    branch nick: child
 
550
    timestamp: Just now
 
551
    message:
 
552
      branch 1
 
553
    diff:
 
554
    === modified file 'file2'
 
555
    --- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
556
    +++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
557
    @@ -1,1 +1,1 @@
 
558
    -contents of parent/file2
 
559
    +hello
 
560
------------------------------------------------------------
 
561
revno: 1
 
562
committer: Lorem Ipsum <test@example.com>
 
563
branch nick: parent
 
564
timestamp: Just now
 
565
message:
 
566
  first post
 
567
diff:
 
568
=== added file 'file1'
 
569
--- file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
570
+++ file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
571
@@ -0,0 +1,1 @@
 
572
+contents of parent/file1
 
573
 
 
574
=== added file 'file2'
 
575
--- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
576
+++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
577
@@ -0,0 +1,1 @@
 
578
+contents of parent/file2
 
579
""")
 
580
 
 
581
    def test_log_show_diff_short(self):
 
582
        self._prepare()
 
583
        out,err = self.run_bzr('log -p --short')
 
584
        self.assertEqual('', err)
 
585
        log = normalize_log(out)
 
586
        self.assertEqualDiff(subst_dates(log), """\
 
587
    2 Lorem Ipsum\t2005-11-22 [merge]
 
588
      merge branch 1
 
589
      === modified file 'file2'
 
590
      --- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
591
      +++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
592
      @@ -1,1 +1,1 @@
 
593
      -contents of parent/file2
 
594
      +hello
 
595
 
 
596
    1 Lorem Ipsum\t2005-11-22
 
597
      first post
 
598
      === added file 'file1'
 
599
      --- file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
600
      +++ file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
601
      @@ -0,0 +1,1 @@
 
602
      +contents of parent/file1
 
603
\x20\x20\x20\x20\x20\x20
 
604
      === added file 'file2'
 
605
      --- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
606
      +++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
607
      @@ -0,0 +1,1 @@
 
608
      +contents of parent/file2
 
609
 
 
610
Use --levels 0 (or -n0) to see merged revisions.
 
611
""")
 
612
 
 
613
    def test_log_show_diff_line(self):
 
614
        self._prepare()
 
615
        out,err = self.run_bzr('log -p --line')
 
616
        self.assertEqual('', err)
 
617
        log = normalize_log(out)
 
618
        # Not supported by this formatter so expect plain output
 
619
        self.assertEqualDiff(subst_dates(log), """\
 
620
2: Lorem Ipsum 2005-11-22 [merge] merge branch 1
 
621
1: Lorem Ipsum 2005-11-22 first post
 
622
""")
 
623
 
 
624
    def test_log_show_diff_file(self):
 
625
        """Only the diffs for the given file are to be shown"""
 
626
        self._prepare()
 
627
        out,err = self.run_bzr('log -p --short file2')
 
628
        self.assertEqual('', err)
 
629
        log = normalize_log(out)
 
630
        self.assertEqualDiff(subst_dates(log), """\
 
631
    2 Lorem Ipsum\t2005-11-22 [merge]
 
632
      merge branch 1
 
633
      === modified file 'file2'
 
634
      --- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
635
      +++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
636
      @@ -1,1 +1,1 @@
 
637
      -contents of parent/file2
 
638
      +hello
 
639
 
 
640
    1 Lorem Ipsum\t2005-11-22
 
641
      first post
 
642
      === added file 'file2'
 
643
      --- file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
644
      +++ file2\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
645
      @@ -0,0 +1,1 @@
 
646
      +contents of parent/file2
 
647
 
 
648
Use --levels 0 (or -n0) to see merged revisions.
 
649
""")
 
650
        out,err = self.run_bzr('log -p --short file1')
 
651
        self.assertEqual('', err)
 
652
        log = normalize_log(out)
 
653
        self.assertEqualDiff(subst_dates(log), """\
 
654
    1 Lorem Ipsum\t2005-11-22
 
655
      first post
 
656
      === added file 'file1'
 
657
      --- file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
658
      +++ file1\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
659
      @@ -0,0 +1,1 @@
 
660
      +contents of parent/file1
 
661
 
 
662
""")
 
663
 
 
664
    def test_log_show_diff_non_ascii(self):
 
665
        # Smoke test for bug #328007 UnicodeDecodeError on 'log -p'
 
666
        message = u'Message with \xb5'
 
667
        body = 'Body with \xb5\n'
 
668
        wt = self.make_branch_and_tree('.')
 
669
        self.build_tree_contents([('foo', body)])
 
670
        wt.add('foo')
 
671
        wt.commit(message=message)
 
672
        # check that command won't fail with unicode error
 
673
        # don't care about exact output because we have other tests for this
 
674
        out,err = self.run_bzr('log -p --long')
 
675
        self.assertNotEqual('', out)
 
676
        self.assertEqual('', err)
 
677
        out,err = self.run_bzr('log -p --short')
 
678
        self.assertNotEqual('', out)
 
679
        self.assertEqual('', err)
 
680
        out,err = self.run_bzr('log -p --line')
 
681
        self.assertNotEqual('', out)
 
682
        self.assertEqual('', err)
 
683
 
 
684
 
 
685
class TestLogEncodings(TestCaseInTempDir):
 
686
 
 
687
    _mu = u'\xb5'
 
688
    _message = u'Message with \xb5'
 
689
 
 
690
    # Encodings which can encode mu
 
691
    good_encodings = [
 
692
        'utf-8',
 
693
        'latin-1',
 
694
        'iso-8859-1',
 
695
        'cp437', # Common windows encoding
 
696
        'cp1251', # Russian windows encoding
 
697
        'cp1258', # Common windows encoding
 
698
    ]
 
699
    # Encodings which cannot encode mu
 
700
    bad_encodings = [
 
701
        'ascii',
 
702
        'iso-8859-2',
 
703
        'koi8_r',
 
704
    ]
 
705
 
 
706
    def setUp(self):
 
707
        TestCaseInTempDir.setUp(self)
 
708
        self.user_encoding = osutils._cached_user_encoding
 
709
 
 
710
    def tearDown(self):
 
711
        osutils._cached_user_encoding = self.user_encoding
 
712
        TestCaseInTempDir.tearDown(self)
 
713
 
 
714
    def create_branch(self):
 
715
        bzr = self.run_bzr
 
716
        bzr('init')
 
717
        open('a', 'wb').write('some stuff\n')
 
718
        bzr('add a')
 
719
        bzr(['commit', '-m', self._message])
 
720
 
 
721
    def try_encoding(self, encoding, fail=False):
 
722
        bzr = self.run_bzr
 
723
        if fail:
 
724
            self.assertRaises(UnicodeEncodeError,
 
725
                self._mu.encode, encoding)
 
726
            encoded_msg = self._message.encode(encoding, 'replace')
 
727
        else:
 
728
            encoded_msg = self._message.encode(encoding)
 
729
 
 
730
        old_encoding = osutils._cached_user_encoding
 
731
        # This test requires that 'run_bzr' uses the current
 
732
        # bzrlib, because we override user_encoding, and expect
 
733
        # it to be used
 
734
        try:
 
735
            osutils._cached_user_encoding = 'ascii'
 
736
            # We should be able to handle any encoding
 
737
            out, err = bzr('log', encoding=encoding)
 
738
            if not fail:
 
739
                # Make sure we wrote mu as we expected it to exist
 
740
                self.assertNotEqual(-1, out.find(encoded_msg))
 
741
                out_unicode = out.decode(encoding)
 
742
                self.assertNotEqual(-1, out_unicode.find(self._message))
 
743
            else:
 
744
                self.assertNotEqual(-1, out.find('Message with ?'))
 
745
        finally:
 
746
            osutils._cached_user_encoding = old_encoding
 
747
 
 
748
    def test_log_handles_encoding(self):
 
749
        self.create_branch()
 
750
 
 
751
        for encoding in self.good_encodings:
 
752
            self.try_encoding(encoding)
 
753
 
 
754
    def test_log_handles_bad_encoding(self):
 
755
        self.create_branch()
 
756
 
 
757
        for encoding in self.bad_encodings:
 
758
            self.try_encoding(encoding, fail=True)
 
759
 
 
760
    def test_stdout_encoding(self):
 
761
        bzr = self.run_bzr
 
762
        osutils._cached_user_encoding = "cp1251"
 
763
 
 
764
        bzr('init')
 
765
        self.build_tree(['a'])
 
766
        bzr('add a')
 
767
        bzr(['commit', '-m', u'\u0422\u0435\u0441\u0442'])
 
768
        stdout, stderr = self.run_bzr('log', encoding='cp866')
 
769
 
 
770
        message = stdout.splitlines()[-1]
 
771
 
 
772
        # explanation of the check:
 
773
        # u'\u0422\u0435\u0441\u0442' is word 'Test' in russian
 
774
        # in cp866  encoding this is string '\x92\xa5\xe1\xe2'
 
775
        # in cp1251 encoding this is string '\xd2\xe5\xf1\xf2'
 
776
        # This test should check that output of log command
 
777
        # encoded to sys.stdout.encoding
 
778
        test_in_cp866 = '\x92\xa5\xe1\xe2'
 
779
        test_in_cp1251 = '\xd2\xe5\xf1\xf2'
 
780
        # Make sure the log string is encoded in cp866
 
781
        self.assertEquals(test_in_cp866, message[2:])
 
782
        # Make sure the cp1251 string is not found anywhere
 
783
        self.assertEquals(-1, stdout.find(test_in_cp1251))
 
784
 
 
785
 
 
786
class TestLogFile(TestCaseWithTransport):
 
787
 
 
788
    def test_log_local_branch_file(self):
 
789
        """We should be able to log files in local treeless branches"""
 
790
        tree = self.make_branch_and_tree('tree')
 
791
        self.build_tree(['tree/file'])
 
792
        tree.add('file')
 
793
        tree.commit('revision 1')
 
794
        tree.bzrdir.destroy_workingtree()
 
795
        self.run_bzr('log tree/file')
 
796
 
 
797
    def prepare_tree(self, complex=False):
 
798
        # The complex configuration includes deletes and renames
 
799
        tree = self.make_branch_and_tree('parent')
 
800
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
 
801
        tree.add('file1')
 
802
        tree.commit('add file1')
 
803
        tree.add('file2')
 
804
        tree.commit('add file2')
 
805
        tree.add('file3')
 
806
        tree.commit('add file3')
 
807
        child_tree = tree.bzrdir.sprout('child').open_workingtree()
 
808
        self.build_tree_contents([('child/file2', 'hello')])
 
809
        child_tree.commit(message='branch 1')
 
810
        tree.merge_from_branch(child_tree.branch)
 
811
        tree.commit(message='merge child branch')
 
812
        if complex:
 
813
            tree.remove('file2')
 
814
            tree.commit('remove file2')
 
815
            tree.rename_one('file3', 'file4')
 
816
            tree.commit('file3 is now called file4')
 
817
            tree.remove('file1')
 
818
            tree.commit('remove file1')
 
819
        os.chdir('parent')
 
820
 
 
821
    def test_log_file(self):
 
822
        """The log for a particular file should only list revs for that file"""
 
823
        self.prepare_tree()
 
824
        log = self.run_bzr('log -n0 file1')[0]
 
825
        self.assertContainsRe(log, 'revno: 1\n')
 
826
        self.assertNotContainsRe(log, 'revno: 2\n')
 
827
        self.assertNotContainsRe(log, 'revno: 3\n')
 
828
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
829
        self.assertNotContainsRe(log, 'revno: 4 ')
 
830
        log = self.run_bzr('log -n0 file2')[0]
 
831
        self.assertNotContainsRe(log, 'revno: 1\n')
 
832
        self.assertContainsRe(log, 'revno: 2\n')
 
833
        self.assertNotContainsRe(log, 'revno: 3\n')
 
834
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
835
        self.assertContainsRe(log, 'revno: 4 ')
 
836
        log = self.run_bzr('log -n0 file3')[0]
 
837
        self.assertNotContainsRe(log, 'revno: 1\n')
 
838
        self.assertNotContainsRe(log, 'revno: 2\n')
 
839
        self.assertContainsRe(log, 'revno: 3\n')
 
840
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
841
        self.assertNotContainsRe(log, 'revno: 4 ')
 
842
        log = self.run_bzr('log -n0 -r3.1.1 file2')[0]
 
843
        self.assertNotContainsRe(log, 'revno: 1\n')
 
844
        self.assertNotContainsRe(log, 'revno: 2\n')
 
845
        self.assertNotContainsRe(log, 'revno: 3\n')
 
846
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
847
        self.assertNotContainsRe(log, 'revno: 4 ')
 
848
        log = self.run_bzr('log -n0 -r4 file2')[0]
 
849
        self.assertNotContainsRe(log, 'revno: 1\n')
 
850
        self.assertNotContainsRe(log, 'revno: 2\n')
 
851
        self.assertNotContainsRe(log, 'revno: 3\n')
 
852
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
853
        self.assertContainsRe(log, 'revno: 4 ')
 
854
        log = self.run_bzr('log -n0 -r3.. file2')[0]
 
855
        self.assertNotContainsRe(log, 'revno: 1\n')
 
856
        self.assertNotContainsRe(log, 'revno: 2\n')
 
857
        self.assertNotContainsRe(log, 'revno: 3\n')
 
858
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
859
        self.assertContainsRe(log, 'revno: 4 ')
 
860
        log = self.run_bzr('log -n0 -r..3 file2')[0]
 
861
        self.assertNotContainsRe(log, 'revno: 1\n')
 
862
        self.assertContainsRe(log, 'revno: 2\n')
 
863
        self.assertNotContainsRe(log, 'revno: 3\n')
 
864
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
865
        self.assertNotContainsRe(log, 'revno: 4 ')
 
866
 
 
867
    def test_log_file_historical_missing(self):
 
868
        # Check logging a deleted file gives an error if the
 
869
        # file isn't found at the end or start of the revision range
 
870
        self.prepare_tree(complex=True)
 
871
        err_msg = "Path unknown at end or start of revision range: file2"
 
872
        err = self.run_bzr('log file2', retcode=3)[1]
 
873
        self.assertContainsRe(err, err_msg)
 
874
 
 
875
    def test_log_file_historical_end(self):
 
876
        # Check logging a deleted file is ok if the file existed
 
877
        # at the end the revision range
 
878
        self.prepare_tree(complex=True)
 
879
        log, err = self.run_bzr('log -n0 -r..4 file2')
 
880
        self.assertEquals('', err)
 
881
        self.assertNotContainsRe(log, 'revno: 1\n')
 
882
        self.assertContainsRe(log, 'revno: 2\n')
 
883
        self.assertNotContainsRe(log, 'revno: 3\n')
 
884
        self.assertContainsRe(log, 'revno: 3.1.1\n')
 
885
        self.assertContainsRe(log, 'revno: 4 ')
 
886
 
 
887
    def test_log_file_historical_start(self):
 
888
        # Check logging a deleted file is ok if the file existed
 
889
        # at the start of the revision range
 
890
        self.prepare_tree(complex=True)
 
891
        log, err = self.run_bzr('log file1')
 
892
        self.assertEquals('', err)
 
893
        self.assertContainsRe(log, 'revno: 1\n')
 
894
        self.assertNotContainsRe(log, 'revno: 2\n')
 
895
        self.assertNotContainsRe(log, 'revno: 3\n')
 
896
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
897
        self.assertNotContainsRe(log, 'revno: 4 ')
 
898
 
 
899
    def test_log_file_renamed(self):
 
900
        """File matched against revision range, not current tree."""
 
901
        self.prepare_tree(complex=True)
 
902
 
 
903
        # Check logging a renamed file gives an error by default
 
904
        err_msg = "Path unknown at end or start of revision range: file3"
 
905
        err = self.run_bzr('log file3', retcode=3)[1]
 
906
        self.assertContainsRe(err, err_msg)
 
907
 
 
908
        # Check we can see a renamed file if we give the right end revision
 
909
        log, err = self.run_bzr('log -r..4 file3')
 
910
        self.assertEquals('', err)
 
911
        self.assertNotContainsRe(log, 'revno: 1\n')
 
912
        self.assertNotContainsRe(log, 'revno: 2\n')
 
913
        self.assertContainsRe(log, 'revno: 3\n')
 
914
        self.assertNotContainsRe(log, 'revno: 3.1.1\n')
 
915
        self.assertNotContainsRe(log, 'revno: 4 ')
 
916
 
 
917
    def test_line_log_file(self):
 
918
        """The line log for a file should only list relevant mainline revs"""
 
919
        # Note: this also implicitly  covers the short logging case.
 
920
        # We test using --line in preference to --short because matching
 
921
        # revnos in the output of --line is more reliable.
 
922
        self.prepare_tree()
 
923
 
 
924
        # full history of file1
 
925
        log = self.run_bzr('log --line file1')[0]
 
926
        self.assertContainsRe(log, '^1:', re.MULTILINE)
 
927
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
928
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
929
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
930
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
931
 
 
932
        # full history of file2
 
933
        log = self.run_bzr('log --line file2')[0]
 
934
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
935
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
936
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
937
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
938
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
939
 
 
940
        # full history of file3
 
941
        log = self.run_bzr('log --line file3')[0]
 
942
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
943
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
944
        self.assertContainsRe(log, '^3:', re.MULTILINE)
 
945
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
946
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
947
 
 
948
        # file in a merge revision
 
949
        log = self.run_bzr('log --line -r3.1.1 file2')[0]
 
950
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
951
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
952
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
953
        self.assertContainsRe(log, '^3.1.1:', re.MULTILINE)
 
954
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
955
 
 
956
        # file in a mainline revision
 
957
        log = self.run_bzr('log --line -r4 file2')[0]
 
958
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
959
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
960
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
961
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
962
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
963
 
 
964
        # file since a revision
 
965
        log = self.run_bzr('log --line -r3.. file2')[0]
 
966
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
967
        self.assertNotContainsRe(log, '^2:', re.MULTILINE)
 
968
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
969
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
970
        self.assertContainsRe(log, '^4:', re.MULTILINE)
 
971
 
 
972
        # file up to a revision
 
973
        log = self.run_bzr('log --line -r..3 file2')[0]
 
974
        self.assertNotContainsRe(log, '^1:', re.MULTILINE)
 
975
        self.assertContainsRe(log, '^2:', re.MULTILINE)
 
976
        self.assertNotContainsRe(log, '^3:', re.MULTILINE)
 
977
        self.assertNotContainsRe(log, '^3.1.1:', re.MULTILINE)
 
978
        self.assertNotContainsRe(log, '^4:', re.MULTILINE)
 
979
 
 
980
 
 
981
class TestLogMultiple(TestCaseWithTransport):
 
982
 
 
983
    def prepare_tree(self):
 
984
        tree = self.make_branch_and_tree('parent')
 
985
        self.build_tree([
 
986
            'parent/file1',
 
987
            'parent/file2',
 
988
            'parent/dir1/',
 
989
            'parent/dir1/file5',
 
990
            'parent/dir1/dir2/',
 
991
            'parent/dir1/dir2/file3',
 
992
            'parent/file4'])
 
993
        tree.add('file1')
 
994
        tree.commit('add file1')
 
995
        tree.add('file2')
 
996
        tree.commit('add file2')
 
997
        tree.add(['dir1', 'dir1/dir2', 'dir1/dir2/file3'])
 
998
        tree.commit('add file3')
 
999
        tree.add('file4')
 
1000
        tree.commit('add file4')
 
1001
        tree.add('dir1/file5')
 
1002
        tree.commit('add file5')
 
1003
        child_tree = tree.bzrdir.sprout('child').open_workingtree()
 
1004
        self.build_tree_contents([('child/file2', 'hello')])
 
1005
        child_tree.commit(message='branch 1')
 
1006
        tree.merge_from_branch(child_tree.branch)
 
1007
        tree.commit(message='merge child branch')
 
1008
        os.chdir('parent')
 
1009
 
 
1010
    def assertRevnos(self, paths_str, expected_revnos):
 
1011
        # confirm the revision numbers in log --line output are those expected
 
1012
        out, err = self.run_bzr('log --line -n0 %s' % (paths_str,))
 
1013
        self.assertEqual('', err)
 
1014
        revnos = [s.split(':', 1)[0].lstrip() for s in out.splitlines()]
 
1015
        self.assertEqual(expected_revnos, revnos)
 
1016
 
 
1017
    def test_log_files(self):
 
1018
        """The log for multiple file should only list revs for those files"""
 
1019
        self.prepare_tree()
 
1020
        self.assertRevnos('file1 file2 dir1/dir2/file3',
 
1021
            ['6', '5.1.1', '3', '2', '1'])
 
1022
 
 
1023
    def test_log_directory(self):
 
1024
        """The log for a directory should show all nested files."""
 
1025
        self.prepare_tree()
 
1026
        self.assertRevnos('dir1', ['5', '3'])
 
1027
 
 
1028
    def test_log_nested_directory(self):
 
1029
        """The log for a directory should show all nested files."""
 
1030
        self.prepare_tree()
 
1031
        self.assertRevnos('dir1/dir2', ['3'])
 
1032
 
 
1033
    def test_log_in_nested_directory(self):
 
1034
        """The log for a directory should show all nested files."""
 
1035
        self.prepare_tree()
 
1036
        os.chdir("dir1")
 
1037
        self.assertRevnos('.', ['5', '3'])
 
1038
 
 
1039
    def test_log_files_and_directories(self):
 
1040
        """Logging files and directories together should be fine."""
 
1041
        self.prepare_tree()
 
1042
        self.assertRevnos('file4 dir1/dir2', ['4', '3'])
 
1043
 
 
1044
    def test_log_files_and_dirs_in_nested_directory(self):
 
1045
        """The log for a directory should show all nested files."""
 
1046
        self.prepare_tree()
 
1047
        os.chdir("dir1")
 
1048
        self.assertRevnos('dir2 file5', ['5', '3'])