/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

Add bzrlib.tests.per_repository_vf.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005-2011 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
18
import os
19
19
 
20
20
import bzrlib
21
 
from bzrlib.tests import TestCaseWithTransport
 
21
from bzrlib import (
 
22
    bzrdir,
 
23
    errors,
 
24
    )
22
25
from bzrlib.branch import Branch
23
 
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
24
 
from bzrlib.workingtree import WorkingTree
25
 
from bzrlib.commit import Commit
 
26
from bzrlib.bzrdir import BzrDirMetaFormat1
 
27
from bzrlib.commit import Commit, NullCommitReporter
26
28
from bzrlib.config import BranchConfig
27
 
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
 
29
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
28
30
                           LockContention)
 
31
from bzrlib.tests import SymlinkFeature, TestCaseWithTransport
29
32
 
30
33
 
31
34
# TODO: Test commit with some added, and added-but-missing files
45
48
        return "bzrlib.ahook bzrlib.ahook"
46
49
 
47
50
 
 
51
class CapturingReporter(NullCommitReporter):
 
52
    """This reporter captures the calls made to it for evaluation later."""
 
53
 
 
54
    def __init__(self):
 
55
        # a list of the calls this received
 
56
        self.calls = []
 
57
 
 
58
    def snapshot_change(self, change, path):
 
59
        self.calls.append(('change', change, path))
 
60
 
 
61
    def deleted(self, file_id):
 
62
        self.calls.append(('deleted', file_id))
 
63
 
 
64
    def missing(self, path):
 
65
        self.calls.append(('missing', path))
 
66
 
 
67
    def renamed(self, change, old_path, new_path):
 
68
        self.calls.append(('renamed', change, old_path, new_path))
 
69
 
 
70
    def is_verbose(self):
 
71
        return True
 
72
 
 
73
 
48
74
class TestCommit(TestCaseWithTransport):
49
75
 
50
76
    def test_simple_commit(self):
66
92
        eq(rev.message, 'add hello')
67
93
 
68
94
        tree1 = b.repository.revision_tree(rh[0])
 
95
        tree1.lock_read()
69
96
        text = tree1.get_file_text(file_id)
70
 
        eq(text, 'hello world')
 
97
        tree1.unlock()
 
98
        self.assertEqual('hello world', text)
71
99
 
72
100
        tree2 = b.repository.revision_tree(rh[1])
73
 
        eq(tree2.get_file_text(file_id), 'version 2')
 
101
        tree2.lock_read()
 
102
        text = tree2.get_file_text(file_id)
 
103
        tree2.unlock()
 
104
        self.assertEqual('version 2', text)
74
105
 
75
 
    def test_delete_commit(self):
76
 
        """Test a commit with a deleted file"""
 
106
    def test_missing_commit(self):
 
107
        """Test a commit with a missing file"""
77
108
        wt = self.make_branch_and_tree('.')
78
109
        b = wt.branch
79
110
        file('hello', 'w').write('hello world')
86
117
        tree = b.repository.revision_tree('rev2')
87
118
        self.assertFalse(tree.has_id('hello-id'))
88
119
 
 
120
    def test_partial_commit_move(self):
 
121
        """Test a partial commit where a file was renamed but not committed.
 
122
 
 
123
        https://bugs.launchpad.net/bzr/+bug/83039
 
124
 
 
125
        If not handled properly, commit will try to snapshot
 
126
        dialog.py with olive/ as a parent, while
 
127
        olive/ has not been snapshotted yet.
 
128
        """
 
129
        wt = self.make_branch_and_tree('.')
 
130
        b = wt.branch
 
131
        self.build_tree(['annotate/', 'annotate/foo.py',
 
132
                         'olive/', 'olive/dialog.py'
 
133
                        ])
 
134
        wt.add(['annotate', 'olive', 'annotate/foo.py', 'olive/dialog.py'])
 
135
        wt.commit(message='add files')
 
136
        wt.rename_one("olive/dialog.py", "aaa")
 
137
        self.build_tree_contents([('annotate/foo.py', 'modified\n')])
 
138
        wt.commit('renamed hello', specific_files=["annotate"])
 
139
 
89
140
    def test_pointless_commit(self):
90
141
        """Commit refuses unless there are changes or it's forced."""
91
142
        wt = self.make_branch_and_tree('.')
99
150
                          message='fails',
100
151
                          allow_pointless=False)
101
152
        self.assertEquals(b.revno(), 1)
102
 
        
 
153
 
103
154
    def test_commit_empty(self):
104
155
        """Commiting an empty tree works."""
105
156
        wt = self.make_branch_and_tree('.')
122
173
              ['hello-id', 'buongia-id'])
123
174
        wt.commit(message='add files',
124
175
                 rev_id='test@rev-1')
125
 
        
 
176
 
126
177
        os.remove('hello')
127
178
        file('buongia', 'w').write('new text')
128
179
        wt.commit(message='update text',
139
190
        eq(b.revno(), 3)
140
191
 
141
192
        tree2 = b.repository.revision_tree('test@rev-2')
 
193
        tree2.lock_read()
 
194
        self.addCleanup(tree2.unlock)
142
195
        self.assertTrue(tree2.has_filename('hello'))
143
196
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
144
197
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
145
 
        
 
198
 
146
199
        tree3 = b.repository.revision_tree('test@rev-3')
 
200
        tree3.lock_read()
 
201
        self.addCleanup(tree3.unlock)
147
202
        self.assertFalse(tree3.has_filename('hello'))
148
203
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
149
204
 
160
215
 
161
216
        eq = self.assertEquals
162
217
        tree1 = b.repository.revision_tree('test@rev-1')
 
218
        tree1.lock_read()
 
219
        self.addCleanup(tree1.unlock)
163
220
        eq(tree1.id2path('hello-id'), 'hello')
164
221
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
165
222
        self.assertFalse(tree1.has_filename('fruity'))
168
225
        eq(ie.revision, 'test@rev-1')
169
226
 
170
227
        tree2 = b.repository.revision_tree('test@rev-2')
 
228
        tree2.lock_read()
 
229
        self.addCleanup(tree2.unlock)
171
230
        eq(tree2.id2path('hello-id'), 'fruity')
172
231
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
173
232
        self.check_inventory_shape(tree2.inventory, ['fruity'])
197
256
        wt.move(['hello'], 'a')
198
257
        r2 = 'test@rev-2'
199
258
        wt.commit('two', rev_id=r2, allow_pointless=False)
200
 
        self.check_inventory_shape(wt.read_working_inventory(),
201
 
                                   ['a', 'a/hello', 'b'])
 
259
        wt.lock_read()
 
260
        try:
 
261
            self.check_inventory_shape(wt.read_working_inventory(),
 
262
                                       ['a/', 'a/hello', 'b/'])
 
263
        finally:
 
264
            wt.unlock()
202
265
 
203
266
        wt.move(['b'], 'a')
204
267
        r3 = 'test@rev-3'
205
268
        wt.commit('three', rev_id=r3, allow_pointless=False)
206
 
        self.check_inventory_shape(wt.read_working_inventory(),
207
 
                                   ['a', 'a/hello', 'a/b'])
208
 
        self.check_inventory_shape(b.repository.get_revision_inventory(r3),
209
 
                                   ['a', 'a/hello', 'a/b'])
 
269
        wt.lock_read()
 
270
        try:
 
271
            self.check_inventory_shape(wt.read_working_inventory(),
 
272
                                       ['a/', 'a/hello', 'a/b/'])
 
273
            self.check_inventory_shape(b.repository.get_inventory(r3),
 
274
                                       ['a/', 'a/hello', 'a/b/'])
 
275
        finally:
 
276
            wt.unlock()
210
277
 
211
278
        wt.move(['a/hello'], 'a/b')
212
279
        r4 = 'test@rev-4'
213
280
        wt.commit('four', rev_id=r4, allow_pointless=False)
214
 
        self.check_inventory_shape(wt.read_working_inventory(),
215
 
                                   ['a', 'a/b/hello', 'a/b'])
 
281
        wt.lock_read()
 
282
        try:
 
283
            self.check_inventory_shape(wt.read_working_inventory(),
 
284
                                       ['a/', 'a/b/hello', 'a/b/'])
 
285
        finally:
 
286
            wt.unlock()
216
287
 
217
 
        inv = b.repository.get_revision_inventory(r4)
 
288
        inv = b.repository.get_inventory(r4)
218
289
        eq(inv['hello-id'].revision, r4)
219
290
        eq(inv['a-id'].revision, r1)
220
291
        eq(inv['b-id'].revision, r3)
221
 
        
 
292
 
222
293
    def test_removed_commit(self):
223
294
        """Commit with a removed file"""
224
295
        wt = self.make_branch_and_tree('.')
319
390
                                                      allow_pointless=True,
320
391
                                                      rev_id='B',
321
392
                                                      working_tree=wt)
322
 
            self.assertEqual(Testament.from_revision(branch.repository,
323
 
                             'B').as_short_text(),
 
393
            def sign(text):
 
394
                return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
 
395
            self.assertEqual(sign(Testament.from_revision(branch.repository,
 
396
                             'B').as_short_text()),
324
397
                             branch.repository.get_signature_text('B'))
325
398
        finally:
326
399
            bzrlib.gpg.GPGStrategy = oldstrategy
392
465
            self.assertRaises(LockContention, wt.commit, 'silly')
393
466
        finally:
394
467
            master_branch.unlock()
 
468
 
 
469
    def test_commit_bound_merge(self):
 
470
        # see bug #43959; commit of a merge in a bound branch fails to push
 
471
        # the new commit into the master
 
472
        master_branch = self.make_branch('master')
 
473
        bound_tree = self.make_branch_and_tree('bound')
 
474
        bound_tree.branch.bind(master_branch)
 
475
 
 
476
        self.build_tree_contents([('bound/content_file', 'initial contents\n')])
 
477
        bound_tree.add(['content_file'])
 
478
        bound_tree.commit(message='woo!')
 
479
 
 
480
        other_bzrdir = master_branch.bzrdir.sprout('other')
 
481
        other_tree = other_bzrdir.open_workingtree()
 
482
 
 
483
        # do a commit to the other branch changing the content file so
 
484
        # that our commit after merging will have a merged revision in the
 
485
        # content file history.
 
486
        self.build_tree_contents([('other/content_file', 'change in other\n')])
 
487
        other_tree.commit('change in other')
 
488
 
 
489
        # do a merge into the bound branch from other, and then change the
 
490
        # content file locally to force a new revision (rather than using the
 
491
        # revision from other). This forces extra processing in commit.
 
492
        bound_tree.merge_from_branch(other_tree.branch)
 
493
        self.build_tree_contents([('bound/content_file', 'change in bound\n')])
 
494
 
 
495
        # before #34959 was fixed, this failed with 'revision not present in
 
496
        # weave' when trying to implicitly push from the bound branch to the master
 
497
        bound_tree.commit(message='commit of merge in bound tree')
 
498
 
 
499
    def test_commit_reporting_after_merge(self):
 
500
        # when doing a commit of a merge, the reporter needs to still
 
501
        # be called for each item that is added/removed/deleted.
 
502
        this_tree = self.make_branch_and_tree('this')
 
503
        # we need a bunch of files and dirs, to perform one action on each.
 
504
        self.build_tree([
 
505
            'this/dirtorename/',
 
506
            'this/dirtoreparent/',
 
507
            'this/dirtoleave/',
 
508
            'this/dirtoremove/',
 
509
            'this/filetoreparent',
 
510
            'this/filetorename',
 
511
            'this/filetomodify',
 
512
            'this/filetoremove',
 
513
            'this/filetoleave']
 
514
            )
 
515
        this_tree.add([
 
516
            'dirtorename',
 
517
            'dirtoreparent',
 
518
            'dirtoleave',
 
519
            'dirtoremove',
 
520
            'filetoreparent',
 
521
            'filetorename',
 
522
            'filetomodify',
 
523
            'filetoremove',
 
524
            'filetoleave']
 
525
            )
 
526
        this_tree.commit('create_files')
 
527
        other_dir = this_tree.bzrdir.sprout('other')
 
528
        other_tree = other_dir.open_workingtree()
 
529
        other_tree.lock_write()
 
530
        # perform the needed actions on the files and dirs.
 
531
        try:
 
532
            other_tree.rename_one('dirtorename', 'renameddir')
 
533
            other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
 
534
            other_tree.rename_one('filetorename', 'renamedfile')
 
535
            other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
 
536
            other_tree.remove(['dirtoremove', 'filetoremove'])
 
537
            self.build_tree_contents([
 
538
                ('other/newdir/', ),
 
539
                ('other/filetomodify', 'new content'),
 
540
                ('other/newfile', 'new file content')])
 
541
            other_tree.add('newfile')
 
542
            other_tree.add('newdir/')
 
543
            other_tree.commit('modify all sample files and dirs.')
 
544
        finally:
 
545
            other_tree.unlock()
 
546
        this_tree.merge_from_branch(other_tree.branch)
 
547
        reporter = CapturingReporter()
 
548
        this_tree.commit('do the commit', reporter=reporter)
 
549
        expected = set([
 
550
            ('change', 'modified', 'filetomodify'),
 
551
            ('change', 'added', 'newdir'),
 
552
            ('change', 'added', 'newfile'),
 
553
            ('renamed', 'renamed', 'dirtorename', 'renameddir'),
 
554
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
 
555
            ('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
 
556
            ('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
 
557
            ('deleted', 'dirtoremove'),
 
558
            ('deleted', 'filetoremove'),
 
559
            ])
 
560
        result = set(reporter.calls)
 
561
        missing = expected - result
 
562
        new = result - expected
 
563
        self.assertEqual((set(), set()), (missing, new))
 
564
 
 
565
    def test_commit_removals_respects_filespec(self):
 
566
        """Commit respects the specified_files for removals."""
 
567
        tree = self.make_branch_and_tree('.')
 
568
        self.build_tree(['a', 'b'])
 
569
        tree.add(['a', 'b'])
 
570
        tree.commit('added a, b')
 
571
        tree.remove(['a', 'b'])
 
572
        tree.commit('removed a', specific_files='a')
 
573
        basis = tree.basis_tree()
 
574
        tree.lock_read()
 
575
        try:
 
576
            self.assertIs(None, basis.path2id('a'))
 
577
            self.assertFalse(basis.path2id('b') is None)
 
578
        finally:
 
579
            tree.unlock()
 
580
 
 
581
    def test_commit_saves_1ms_timestamp(self):
 
582
        """Passing in a timestamp is saved with 1ms resolution"""
 
583
        tree = self.make_branch_and_tree('.')
 
584
        self.build_tree(['a'])
 
585
        tree.add('a')
 
586
        tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
 
587
                    rev_id='a1')
 
588
 
 
589
        rev = tree.branch.repository.get_revision('a1')
 
590
        self.assertEqual(1153248633.419, rev.timestamp)
 
591
 
 
592
    def test_commit_has_1ms_resolution(self):
 
593
        """Allowing commit to generate the timestamp also has 1ms resolution"""
 
594
        tree = self.make_branch_and_tree('.')
 
595
        self.build_tree(['a'])
 
596
        tree.add('a')
 
597
        tree.commit('added a', rev_id='a1')
 
598
 
 
599
        rev = tree.branch.repository.get_revision('a1')
 
600
        timestamp = rev.timestamp
 
601
        timestamp_1ms = round(timestamp, 3)
 
602
        self.assertEqual(timestamp_1ms, timestamp)
 
603
 
 
604
    def assertBasisTreeKind(self, kind, tree, file_id):
 
605
        basis = tree.basis_tree()
 
606
        basis.lock_read()
 
607
        try:
 
608
            self.assertEqual(kind, basis.kind(file_id))
 
609
        finally:
 
610
            basis.unlock()
 
611
 
 
612
    def test_commit_kind_changes(self):
 
613
        self.requireFeature(SymlinkFeature)
 
614
        tree = self.make_branch_and_tree('.')
 
615
        os.symlink('target', 'name')
 
616
        tree.add('name', 'a-file-id')
 
617
        tree.commit('Added a symlink')
 
618
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
619
 
 
620
        os.unlink('name')
 
621
        self.build_tree(['name'])
 
622
        tree.commit('Changed symlink to file')
 
623
        self.assertBasisTreeKind('file', tree, 'a-file-id')
 
624
 
 
625
        os.unlink('name')
 
626
        os.symlink('target', 'name')
 
627
        tree.commit('file to symlink')
 
628
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
629
 
 
630
        os.unlink('name')
 
631
        os.mkdir('name')
 
632
        tree.commit('symlink to directory')
 
633
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
634
 
 
635
        os.rmdir('name')
 
636
        os.symlink('target', 'name')
 
637
        tree.commit('directory to symlink')
 
638
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
639
 
 
640
        # prepare for directory <-> file tests
 
641
        os.unlink('name')
 
642
        os.mkdir('name')
 
643
        tree.commit('symlink to directory')
 
644
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
645
 
 
646
        os.rmdir('name')
 
647
        self.build_tree(['name'])
 
648
        tree.commit('Changed directory to file')
 
649
        self.assertBasisTreeKind('file', tree, 'a-file-id')
 
650
 
 
651
        os.unlink('name')
 
652
        os.mkdir('name')
 
653
        tree.commit('file to directory')
 
654
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
655
 
 
656
    def test_commit_unversioned_specified(self):
 
657
        """Commit should raise if specified files isn't in basis or worktree"""
 
658
        tree = self.make_branch_and_tree('.')
 
659
        self.assertRaises(errors.PathsNotVersionedError, tree.commit,
 
660
                          'message', specific_files=['bogus'])
 
661
 
 
662
    class Callback(object):
 
663
 
 
664
        def __init__(self, message, testcase):
 
665
            self.called = False
 
666
            self.message = message
 
667
            self.testcase = testcase
 
668
 
 
669
        def __call__(self, commit_obj):
 
670
            self.called = True
 
671
            self.testcase.assertTrue(isinstance(commit_obj, Commit))
 
672
            return self.message
 
673
 
 
674
    def test_commit_callback(self):
 
675
        """Commit should invoke a callback to get the message"""
 
676
 
 
677
        tree = self.make_branch_and_tree('.')
 
678
        try:
 
679
            tree.commit()
 
680
        except Exception, e:
 
681
            self.assertTrue(isinstance(e, BzrError))
 
682
            self.assertEqual('The message or message_callback keyword'
 
683
                             ' parameter is required for commit().', str(e))
 
684
        else:
 
685
            self.fail('exception not raised')
 
686
        cb = self.Callback(u'commit 1', self)
 
687
        tree.commit(message_callback=cb)
 
688
        self.assertTrue(cb.called)
 
689
        repository = tree.branch.repository
 
690
        message = repository.get_revision(tree.last_revision()).message
 
691
        self.assertEqual('commit 1', message)
 
692
 
 
693
    def test_no_callback_pointless(self):
 
694
        """Callback should not be invoked for pointless commit"""
 
695
        tree = self.make_branch_and_tree('.')
 
696
        cb = self.Callback(u'commit 2', self)
 
697
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb,
 
698
                          allow_pointless=False)
 
699
        self.assertFalse(cb.called)
 
700
 
 
701
    def test_no_callback_netfailure(self):
 
702
        """Callback should not be invoked if connectivity fails"""
 
703
        tree = self.make_branch_and_tree('.')
 
704
        cb = self.Callback(u'commit 2', self)
 
705
        repository = tree.branch.repository
 
706
        # simulate network failure
 
707
        def raise_(self, arg, arg2, arg3=None, arg4=None):
 
708
            raise errors.NoSuchFile('foo')
 
709
        repository.add_inventory = raise_
 
710
        repository.add_inventory_by_delta = raise_
 
711
        self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
 
712
        self.assertFalse(cb.called)
 
713
 
 
714
    def test_selected_file_merge_commit(self):
 
715
        """Ensure the correct error is raised"""
 
716
        tree = self.make_branch_and_tree('foo')
 
717
        # pending merge would turn into a left parent
 
718
        tree.commit('commit 1')
 
719
        tree.add_parent_tree_id('example')
 
720
        self.build_tree(['foo/bar', 'foo/baz'])
 
721
        tree.add(['bar', 'baz'])
 
722
        err = self.assertRaises(errors.CannotCommitSelectedFileMerge,
 
723
            tree.commit, 'commit 2', specific_files=['bar', 'baz'])
 
724
        self.assertEqual(['bar', 'baz'], err.files)
 
725
        self.assertEqual('Selected-file commit of merges is not supported'
 
726
                         ' yet: files bar, baz', str(err))
 
727
 
 
728
    def test_commit_ordering(self):
 
729
        """Test of corner-case commit ordering error"""
 
730
        tree = self.make_branch_and_tree('.')
 
731
        self.build_tree(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
 
732
        tree.add(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
 
733
        tree.commit('setup')
 
734
        self.build_tree(['a/c/d/'])
 
735
        tree.add('a/c/d')
 
736
        tree.rename_one('a/z/x', 'a/c/d/x')
 
737
        tree.commit('test', specific_files=['a/z/y'])
 
738
 
 
739
    def test_commit_no_author(self):
 
740
        """The default kwarg author in MutableTree.commit should not add
 
741
        the 'author' revision property.
 
742
        """
 
743
        tree = self.make_branch_and_tree('foo')
 
744
        rev_id = tree.commit('commit 1')
 
745
        rev = tree.branch.repository.get_revision(rev_id)
 
746
        self.assertFalse('author' in rev.properties)
 
747
        self.assertFalse('authors' in rev.properties)
 
748
 
 
749
    def test_commit_author(self):
 
750
        """Passing a non-empty author kwarg to MutableTree.commit should add
 
751
        the 'author' revision property.
 
752
        """
 
753
        tree = self.make_branch_and_tree('foo')
 
754
        rev_id = self.callDeprecated(['The parameter author was '
 
755
                'deprecated in version 1.13. Use authors instead'],
 
756
                tree.commit, 'commit 1', author='John Doe <jdoe@example.com>')
 
757
        rev = tree.branch.repository.get_revision(rev_id)
 
758
        self.assertEqual('John Doe <jdoe@example.com>',
 
759
                         rev.properties['authors'])
 
760
        self.assertFalse('author' in rev.properties)
 
761
 
 
762
    def test_commit_empty_authors_list(self):
 
763
        """Passing an empty list to authors shouldn't add the property."""
 
764
        tree = self.make_branch_and_tree('foo')
 
765
        rev_id = tree.commit('commit 1', authors=[])
 
766
        rev = tree.branch.repository.get_revision(rev_id)
 
767
        self.assertFalse('author' in rev.properties)
 
768
        self.assertFalse('authors' in rev.properties)
 
769
 
 
770
    def test_multiple_authors(self):
 
771
        tree = self.make_branch_and_tree('foo')
 
772
        rev_id = tree.commit('commit 1',
 
773
                authors=['John Doe <jdoe@example.com>',
 
774
                         'Jane Rey <jrey@example.com>'])
 
775
        rev = tree.branch.repository.get_revision(rev_id)
 
776
        self.assertEqual('John Doe <jdoe@example.com>\n'
 
777
                'Jane Rey <jrey@example.com>', rev.properties['authors'])
 
778
        self.assertFalse('author' in rev.properties)
 
779
 
 
780
    def test_author_and_authors_incompatible(self):
 
781
        tree = self.make_branch_and_tree('foo')
 
782
        self.assertRaises(AssertionError, tree.commit, 'commit 1',
 
783
                authors=['John Doe <jdoe@example.com>',
 
784
                         'Jane Rey <jrey@example.com>'],
 
785
                author="Jack Me <jme@example.com>")
 
786
 
 
787
    def test_author_with_newline_rejected(self):
 
788
        tree = self.make_branch_and_tree('foo')
 
789
        self.assertRaises(AssertionError, tree.commit, 'commit 1',
 
790
                authors=['John\nDoe <jdoe@example.com>'])
 
791
 
 
792
    def test_commit_with_checkout_and_branch_sharing_repo(self):
 
793
        repo = self.make_repository('repo', shared=True)
 
794
        # make_branch_and_tree ignores shared repos
 
795
        branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
 
796
        tree2 = branch.create_checkout('repo/tree2')
 
797
        tree2.commit('message', rev_id='rev1')
 
798
        self.assertTrue(tree2.branch.repository.has_revision('rev1'))