~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

  • Committer: Andrew Bennetts
  • Date: 2010-10-08 04:25:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5472.
  • Revision ID: andrew.bennetts@canonical.com-20101008042510-sg9vdhmnggilzxsk
Fix stray TAB in source.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
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
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
21
from bzrlib import (
 
22
    bzrdir,
22
23
    errors,
23
24
    lockdir,
24
25
    osutils,
28
29
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
29
30
from bzrlib.commit import Commit, NullCommitReporter
30
31
from bzrlib.config import BranchConfig
31
 
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
 
32
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
32
33
                           LockContention)
33
 
from bzrlib.tests import TestCaseWithTransport
 
34
from bzrlib.tests import SymlinkFeature, TestCaseWithTransport
34
35
from bzrlib.workingtree import WorkingTree
35
36
 
36
37
 
70
71
    def renamed(self, change, old_path, new_path):
71
72
        self.calls.append(('renamed', change, old_path, new_path))
72
73
 
 
74
    def is_verbose(self):
 
75
        return True
 
76
 
73
77
 
74
78
class TestCommit(TestCaseWithTransport):
75
79
 
92
96
        eq(rev.message, 'add hello')
93
97
 
94
98
        tree1 = b.repository.revision_tree(rh[0])
 
99
        tree1.lock_read()
95
100
        text = tree1.get_file_text(file_id)
96
 
        eq(text, 'hello world')
 
101
        tree1.unlock()
 
102
        self.assertEqual('hello world', text)
97
103
 
98
104
        tree2 = b.repository.revision_tree(rh[1])
99
 
        eq(tree2.get_file_text(file_id), 'version 2')
 
105
        tree2.lock_read()
 
106
        text = tree2.get_file_text(file_id)
 
107
        tree2.unlock()
 
108
        self.assertEqual('version 2', text)
100
109
 
101
 
    def test_delete_commit(self):
102
 
        """Test a commit with a deleted file"""
 
110
    def test_missing_commit(self):
 
111
        """Test a commit with a missing file"""
103
112
        wt = self.make_branch_and_tree('.')
104
113
        b = wt.branch
105
114
        file('hello', 'w').write('hello world')
112
121
        tree = b.repository.revision_tree('rev2')
113
122
        self.assertFalse(tree.has_id('hello-id'))
114
123
 
 
124
    def test_partial_commit_move(self):
 
125
        """Test a partial commit where a file was renamed but not committed.
 
126
 
 
127
        https://bugs.launchpad.net/bzr/+bug/83039
 
128
 
 
129
        If not handled properly, commit will try to snapshot
 
130
        dialog.py with olive/ as a parent, while
 
131
        olive/ has not been snapshotted yet.
 
132
        """
 
133
        wt = self.make_branch_and_tree('.')
 
134
        b = wt.branch
 
135
        self.build_tree(['annotate/', 'annotate/foo.py',
 
136
                         'olive/', 'olive/dialog.py'
 
137
                        ])
 
138
        wt.add(['annotate', 'olive', 'annotate/foo.py', 'olive/dialog.py'])
 
139
        wt.commit(message='add files')
 
140
        wt.rename_one("olive/dialog.py", "aaa")
 
141
        self.build_tree_contents([('annotate/foo.py', 'modified\n')])
 
142
        wt.commit('renamed hello', specific_files=["annotate"])
 
143
 
115
144
    def test_pointless_commit(self):
116
145
        """Commit refuses unless there are changes or it's forced."""
117
146
        wt = self.make_branch_and_tree('.')
125
154
                          message='fails',
126
155
                          allow_pointless=False)
127
156
        self.assertEquals(b.revno(), 1)
128
 
        
 
157
 
129
158
    def test_commit_empty(self):
130
159
        """Commiting an empty tree works."""
131
160
        wt = self.make_branch_and_tree('.')
148
177
              ['hello-id', 'buongia-id'])
149
178
        wt.commit(message='add files',
150
179
                 rev_id='test@rev-1')
151
 
        
 
180
 
152
181
        os.remove('hello')
153
182
        file('buongia', 'w').write('new text')
154
183
        wt.commit(message='update text',
165
194
        eq(b.revno(), 3)
166
195
 
167
196
        tree2 = b.repository.revision_tree('test@rev-2')
 
197
        tree2.lock_read()
 
198
        self.addCleanup(tree2.unlock)
168
199
        self.assertTrue(tree2.has_filename('hello'))
169
200
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
170
201
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
171
 
        
 
202
 
172
203
        tree3 = b.repository.revision_tree('test@rev-3')
 
204
        tree3.lock_read()
 
205
        self.addCleanup(tree3.unlock)
173
206
        self.assertFalse(tree3.has_filename('hello'))
174
207
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
175
208
 
186
219
 
187
220
        eq = self.assertEquals
188
221
        tree1 = b.repository.revision_tree('test@rev-1')
 
222
        tree1.lock_read()
 
223
        self.addCleanup(tree1.unlock)
189
224
        eq(tree1.id2path('hello-id'), 'hello')
190
225
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
191
226
        self.assertFalse(tree1.has_filename('fruity'))
194
229
        eq(ie.revision, 'test@rev-1')
195
230
 
196
231
        tree2 = b.repository.revision_tree('test@rev-2')
 
232
        tree2.lock_read()
 
233
        self.addCleanup(tree2.unlock)
197
234
        eq(tree2.id2path('hello-id'), 'fruity')
198
235
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
199
236
        self.check_inventory_shape(tree2.inventory, ['fruity'])
226
263
        wt.lock_read()
227
264
        try:
228
265
            self.check_inventory_shape(wt.read_working_inventory(),
229
 
                                       ['a', 'a/hello', 'b'])
 
266
                                       ['a/', 'a/hello', 'b/'])
230
267
        finally:
231
268
            wt.unlock()
232
269
 
236
273
        wt.lock_read()
237
274
        try:
238
275
            self.check_inventory_shape(wt.read_working_inventory(),
239
 
                                       ['a', 'a/hello', 'a/b'])
240
 
            self.check_inventory_shape(b.repository.get_revision_inventory(r3),
241
 
                                       ['a', 'a/hello', 'a/b'])
 
276
                                       ['a/', 'a/hello', 'a/b/'])
 
277
            self.check_inventory_shape(b.repository.get_inventory(r3),
 
278
                                       ['a/', 'a/hello', 'a/b/'])
242
279
        finally:
243
280
            wt.unlock()
244
281
 
248
285
        wt.lock_read()
249
286
        try:
250
287
            self.check_inventory_shape(wt.read_working_inventory(),
251
 
                                       ['a', 'a/b/hello', 'a/b'])
 
288
                                       ['a/', 'a/b/hello', 'a/b/'])
252
289
        finally:
253
290
            wt.unlock()
254
291
 
255
 
        inv = b.repository.get_revision_inventory(r4)
 
292
        inv = b.repository.get_inventory(r4)
256
293
        eq(inv['hello-id'].revision, r4)
257
294
        eq(inv['a-id'].revision, r1)
258
295
        eq(inv['b-id'].revision, r3)
357
394
                                                      allow_pointless=True,
358
395
                                                      rev_id='B',
359
396
                                                      working_tree=wt)
360
 
            self.assertEqual(Testament.from_revision(branch.repository,
361
 
                             'B').as_short_text(),
 
397
            def sign(text):
 
398
                return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
 
399
            self.assertEqual(sign(Testament.from_revision(branch.repository,
 
400
                             'B').as_short_text()),
362
401
                             branch.repository.get_signature_text('B'))
363
402
        finally:
364
403
            bzrlib.gpg.GPGStrategy = oldstrategy
425
464
        bound = master.sprout('bound')
426
465
        wt = bound.open_workingtree()
427
466
        wt.branch.set_bound_location(os.path.realpath('master'))
428
 
 
429
 
        orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
430
467
        master_branch.lock_write()
431
468
        try:
432
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = 1
433
469
            self.assertRaises(LockContention, wt.commit, 'silly')
434
470
        finally:
435
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
436
471
            master_branch.unlock()
437
472
 
438
473
    def test_commit_bound_merge(self):
449
484
        other_bzrdir = master_branch.bzrdir.sprout('other')
450
485
        other_tree = other_bzrdir.open_workingtree()
451
486
 
452
 
        # do a commit to the the other branch changing the content file so
 
487
        # do a commit to the other branch changing the content file so
453
488
        # that our commit after merging will have a merged revision in the
454
489
        # content file history.
455
490
        self.build_tree_contents([('other/content_file', 'change in other\n')])
466
501
        bound_tree.commit(message='commit of merge in bound tree')
467
502
 
468
503
    def test_commit_reporting_after_merge(self):
469
 
        # when doing a commit of a merge, the reporter needs to still 
 
504
        # when doing a commit of a merge, the reporter needs to still
470
505
        # be called for each item that is added/removed/deleted.
471
506
        this_tree = self.make_branch_and_tree('this')
472
507
        # we need a bunch of files and dirs, to perform one action on each.
515
550
        this_tree.merge_from_branch(other_tree.branch)
516
551
        reporter = CapturingReporter()
517
552
        this_tree.commit('do the commit', reporter=reporter)
518
 
        self.assertEqual([
519
 
            ('change', 'unchanged', ''),
520
 
            ('change', 'unchanged', 'dirtoleave'),
521
 
            ('change', 'unchanged', 'filetoleave'),
 
553
        expected = set([
522
554
            ('change', 'modified', 'filetomodify'),
523
555
            ('change', 'added', 'newdir'),
524
556
            ('change', 'added', 'newfile'),
525
557
            ('renamed', 'renamed', 'dirtorename', 'renameddir'),
 
558
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
526
559
            ('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
527
560
            ('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
528
 
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
529
561
            ('deleted', 'dirtoremove'),
530
562
            ('deleted', 'filetoremove'),
531
 
            ],
532
 
            reporter.calls)
 
563
            ])
 
564
        result = set(reporter.calls)
 
565
        missing = expected - result
 
566
        new = result - expected
 
567
        self.assertEqual((set(), set()), (missing, new))
533
568
 
534
569
    def test_commit_removals_respects_filespec(self):
535
570
        """Commit respects the specified_files for removals."""
579
614
            basis.unlock()
580
615
 
581
616
    def test_commit_kind_changes(self):
582
 
        if not osutils.has_symlinks():
583
 
            raise tests.TestSkipped('Test requires symlink support')
 
617
        self.requireFeature(SymlinkFeature)
584
618
        tree = self.make_branch_and_tree('.')
585
619
        os.symlink('target', 'name')
586
620
        tree.add('name', 'a-file-id')
626
660
    def test_commit_unversioned_specified(self):
627
661
        """Commit should raise if specified files isn't in basis or worktree"""
628
662
        tree = self.make_branch_and_tree('.')
629
 
        self.assertRaises(errors.PathsNotVersionedError, tree.commit, 
 
663
        self.assertRaises(errors.PathsNotVersionedError, tree.commit,
630
664
                          'message', specific_files=['bogus'])
631
665
 
632
666
    class Callback(object):
633
 
        
 
667
 
634
668
        def __init__(self, message, testcase):
635
669
            self.called = False
636
670
            self.message = message
664
698
        """Callback should not be invoked for pointless commit"""
665
699
        tree = self.make_branch_and_tree('.')
666
700
        cb = self.Callback(u'commit 2', self)
667
 
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb, 
 
701
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb,
668
702
                          allow_pointless=False)
669
703
        self.assertFalse(cb.called)
670
704
 
674
708
        cb = self.Callback(u'commit 2', self)
675
709
        repository = tree.branch.repository
676
710
        # simulate network failure
677
 
        def raise_(self, arg, arg2):
 
711
        def raise_(self, arg, arg2, arg3=None, arg4=None):
678
712
            raise errors.NoSuchFile('foo')
679
713
        repository.add_inventory = raise_
 
714
        repository.add_inventory_by_delta = raise_
680
715
        self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
681
716
        self.assertFalse(cb.called)
 
717
 
 
718
    def test_selected_file_merge_commit(self):
 
719
        """Ensure the correct error is raised"""
 
720
        tree = self.make_branch_and_tree('foo')
 
721
        # pending merge would turn into a left parent
 
722
        tree.commit('commit 1')
 
723
        tree.add_parent_tree_id('example')
 
724
        self.build_tree(['foo/bar', 'foo/baz'])
 
725
        tree.add(['bar', 'baz'])
 
726
        err = self.assertRaises(errors.CannotCommitSelectedFileMerge,
 
727
            tree.commit, 'commit 2', specific_files=['bar', 'baz'])
 
728
        self.assertEqual(['bar', 'baz'], err.files)
 
729
        self.assertEqual('Selected-file commit of merges is not supported'
 
730
                         ' yet: files bar, baz', str(err))
 
731
 
 
732
    def test_commit_ordering(self):
 
733
        """Test of corner-case commit ordering error"""
 
734
        tree = self.make_branch_and_tree('.')
 
735
        self.build_tree(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
 
736
        tree.add(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
 
737
        tree.commit('setup')
 
738
        self.build_tree(['a/c/d/'])
 
739
        tree.add('a/c/d')
 
740
        tree.rename_one('a/z/x', 'a/c/d/x')
 
741
        tree.commit('test', specific_files=['a/z/y'])
 
742
 
 
743
    def test_commit_no_author(self):
 
744
        """The default kwarg author in MutableTree.commit should not add
 
745
        the 'author' revision property.
 
746
        """
 
747
        tree = self.make_branch_and_tree('foo')
 
748
        rev_id = tree.commit('commit 1')
 
749
        rev = tree.branch.repository.get_revision(rev_id)
 
750
        self.assertFalse('author' in rev.properties)
 
751
        self.assertFalse('authors' in rev.properties)
 
752
 
 
753
    def test_commit_author(self):
 
754
        """Passing a non-empty author kwarg to MutableTree.commit should add
 
755
        the 'author' revision property.
 
756
        """
 
757
        tree = self.make_branch_and_tree('foo')
 
758
        rev_id = self.callDeprecated(['The parameter author was '
 
759
                'deprecated in version 1.13. Use authors instead'],
 
760
                tree.commit, 'commit 1', author='John Doe <jdoe@example.com>')
 
761
        rev = tree.branch.repository.get_revision(rev_id)
 
762
        self.assertEqual('John Doe <jdoe@example.com>',
 
763
                         rev.properties['authors'])
 
764
        self.assertFalse('author' in rev.properties)
 
765
 
 
766
    def test_commit_empty_authors_list(self):
 
767
        """Passing an empty list to authors shouldn't add the property."""
 
768
        tree = self.make_branch_and_tree('foo')
 
769
        rev_id = tree.commit('commit 1', authors=[])
 
770
        rev = tree.branch.repository.get_revision(rev_id)
 
771
        self.assertFalse('author' in rev.properties)
 
772
        self.assertFalse('authors' in rev.properties)
 
773
 
 
774
    def test_multiple_authors(self):
 
775
        tree = self.make_branch_and_tree('foo')
 
776
        rev_id = tree.commit('commit 1',
 
777
                authors=['John Doe <jdoe@example.com>',
 
778
                         'Jane Rey <jrey@example.com>'])
 
779
        rev = tree.branch.repository.get_revision(rev_id)
 
780
        self.assertEqual('John Doe <jdoe@example.com>\n'
 
781
                'Jane Rey <jrey@example.com>', rev.properties['authors'])
 
782
        self.assertFalse('author' in rev.properties)
 
783
 
 
784
    def test_author_and_authors_incompatible(self):
 
785
        tree = self.make_branch_and_tree('foo')
 
786
        self.assertRaises(AssertionError, tree.commit, 'commit 1',
 
787
                authors=['John Doe <jdoe@example.com>',
 
788
                         'Jane Rey <jrey@example.com>'],
 
789
                author="Jack Me <jme@example.com>")
 
790
 
 
791
    def test_author_with_newline_rejected(self):
 
792
        tree = self.make_branch_and_tree('foo')
 
793
        self.assertRaises(AssertionError, tree.commit, 'commit 1',
 
794
                authors=['John\nDoe <jdoe@example.com>'])
 
795
 
 
796
    def test_commit_with_checkout_and_branch_sharing_repo(self):
 
797
        repo = self.make_repository('repo', shared=True)
 
798
        # make_branch_and_tree ignores shared repos
 
799
        branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
 
800
        tree2 = branch.create_checkout('repo/tree2')
 
801
        tree2.commit('message', rev_id='rev1')
 
802
        self.assertTrue(tree2.branch.repository.has_revision('rev1'))