~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

  • Committer: Martin Pool
  • Date: 2007-06-18 06:54:24 UTC
  • mto: This revision was merged to the branch mainline in revision 2551.
  • Revision ID: mbp@sourcefrog.net-20070618065424-awsn4t4tv2bi4okt
Remove duplicated commit use case documentation

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
18
import os
19
19
 
20
20
import bzrlib
21
21
from bzrlib import (
22
 
    bzrdir,
23
22
    errors,
 
23
    lockdir,
 
24
    osutils,
 
25
    tests,
24
26
    )
25
27
from bzrlib.branch import Branch
26
 
from bzrlib.bzrdir import BzrDirMetaFormat1
 
28
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
27
29
from bzrlib.commit import Commit, NullCommitReporter
28
30
from bzrlib.config import BranchConfig
29
 
from bzrlib.errors import (
30
 
    PointlessCommit,
31
 
    BzrError,
32
 
    SigningFailed,
33
 
    LockContention,
34
 
    )
35
 
from bzrlib.tests import (
36
 
    TestCaseWithTransport,
37
 
    test_foreign,
38
 
    )
39
 
from bzrlib.tests.features import (
40
 
    SymlinkFeature,
41
 
    )
42
 
from bzrlib.tests.matchers import MatchesAncestry
 
31
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
 
32
                           LockContention)
 
33
from bzrlib.tests import TestCaseWithTransport
 
34
from bzrlib.workingtree import WorkingTree
43
35
 
44
36
 
45
37
# TODO: Test commit with some added, and added-but-missing files
78
70
    def renamed(self, change, old_path, new_path):
79
71
        self.calls.append(('renamed', change, old_path, new_path))
80
72
 
81
 
    def is_verbose(self):
82
 
        return True
83
 
 
84
73
 
85
74
class TestCommit(TestCaseWithTransport):
86
75
 
103
92
        eq(rev.message, 'add hello')
104
93
 
105
94
        tree1 = b.repository.revision_tree(rh[0])
106
 
        tree1.lock_read()
107
95
        text = tree1.get_file_text(file_id)
108
 
        tree1.unlock()
109
 
        self.assertEqual('hello world', text)
 
96
        eq(text, 'hello world')
110
97
 
111
98
        tree2 = b.repository.revision_tree(rh[1])
112
 
        tree2.lock_read()
113
 
        text = tree2.get_file_text(file_id)
114
 
        tree2.unlock()
115
 
        self.assertEqual('version 2', text)
116
 
 
117
 
    def test_commit_lossy_native(self):
118
 
        """Attempt a lossy commit to a native branch."""
119
 
        wt = self.make_branch_and_tree('.')
120
 
        b = wt.branch
121
 
        file('hello', 'w').write('hello world')
122
 
        wt.add('hello')
123
 
        revid = wt.commit(message='add hello', rev_id='revid', lossy=True)
124
 
        self.assertEquals('revid', revid)
125
 
 
126
 
    def test_commit_lossy_foreign(self):
127
 
        """Attempt a lossy commit to a foreign branch."""
128
 
        test_foreign.register_dummy_foreign_for_test(self)
129
 
        wt = self.make_branch_and_tree('.',
130
 
            format=test_foreign.DummyForeignVcsDirFormat())
131
 
        b = wt.branch
132
 
        file('hello', 'w').write('hello world')
133
 
        wt.add('hello')
134
 
        revid = wt.commit(message='add hello', lossy=True,
135
 
            timestamp=1302659388, timezone=0)
136
 
        self.assertEquals('dummy-v1:1302659388.0-0-UNKNOWN', revid)
137
 
 
138
 
    def test_commit_bound_lossy_foreign(self):
139
 
        """Attempt a lossy commit to a bzr branch bound to a foreign branch."""
140
 
        test_foreign.register_dummy_foreign_for_test(self)
141
 
        foreign_branch = self.make_branch('foreign',
142
 
            format=test_foreign.DummyForeignVcsDirFormat())
143
 
        wt = foreign_branch.create_checkout("local")
144
 
        b = wt.branch
145
 
        file('local/hello', 'w').write('hello world')
146
 
        wt.add('hello')
147
 
        revid = wt.commit(message='add hello', lossy=True,
148
 
            timestamp=1302659388, timezone=0)
149
 
        self.assertEquals('dummy-v1:1302659388.0-0-0', revid)
150
 
        self.assertEquals('dummy-v1:1302659388.0-0-0',
151
 
            foreign_branch.last_revision())
152
 
        self.assertEquals('dummy-v1:1302659388.0-0-0',
153
 
            wt.branch.last_revision())
154
 
 
155
 
    def test_missing_commit(self):
156
 
        """Test a commit with a missing file"""
 
99
        eq(tree2.get_file_text(file_id), 'version 2')
 
100
 
 
101
    def test_delete_commit(self):
 
102
        """Test a commit with a deleted file"""
157
103
        wt = self.make_branch_and_tree('.')
158
104
        b = wt.branch
159
105
        file('hello', 'w').write('hello world')
166
112
        tree = b.repository.revision_tree('rev2')
167
113
        self.assertFalse(tree.has_id('hello-id'))
168
114
 
169
 
    def test_partial_commit_move(self):
170
 
        """Test a partial commit where a file was renamed but not committed.
171
 
 
172
 
        https://bugs.launchpad.net/bzr/+bug/83039
173
 
 
174
 
        If not handled properly, commit will try to snapshot
175
 
        dialog.py with olive/ as a parent, while
176
 
        olive/ has not been snapshotted yet.
177
 
        """
178
 
        wt = self.make_branch_and_tree('.')
179
 
        b = wt.branch
180
 
        self.build_tree(['annotate/', 'annotate/foo.py',
181
 
                         'olive/', 'olive/dialog.py'
182
 
                        ])
183
 
        wt.add(['annotate', 'olive', 'annotate/foo.py', 'olive/dialog.py'])
184
 
        wt.commit(message='add files')
185
 
        wt.rename_one("olive/dialog.py", "aaa")
186
 
        self.build_tree_contents([('annotate/foo.py', 'modified\n')])
187
 
        wt.commit('renamed hello', specific_files=["annotate"])
188
 
 
189
115
    def test_pointless_commit(self):
190
116
        """Commit refuses unless there are changes or it's forced."""
191
117
        wt = self.make_branch_and_tree('.')
199
125
                          message='fails',
200
126
                          allow_pointless=False)
201
127
        self.assertEquals(b.revno(), 1)
202
 
 
 
128
        
203
129
    def test_commit_empty(self):
204
130
        """Commiting an empty tree works."""
205
131
        wt = self.make_branch_and_tree('.')
222
148
              ['hello-id', 'buongia-id'])
223
149
        wt.commit(message='add files',
224
150
                 rev_id='test@rev-1')
225
 
 
 
151
        
226
152
        os.remove('hello')
227
153
        file('buongia', 'w').write('new text')
228
154
        wt.commit(message='update text',
239
165
        eq(b.revno(), 3)
240
166
 
241
167
        tree2 = b.repository.revision_tree('test@rev-2')
242
 
        tree2.lock_read()
243
 
        self.addCleanup(tree2.unlock)
244
168
        self.assertTrue(tree2.has_filename('hello'))
245
169
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
246
170
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
247
 
 
 
171
        
248
172
        tree3 = b.repository.revision_tree('test@rev-3')
249
 
        tree3.lock_read()
250
 
        self.addCleanup(tree3.unlock)
251
173
        self.assertFalse(tree3.has_filename('hello'))
252
174
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
253
175
 
264
186
 
265
187
        eq = self.assertEquals
266
188
        tree1 = b.repository.revision_tree('test@rev-1')
267
 
        tree1.lock_read()
268
 
        self.addCleanup(tree1.unlock)
269
189
        eq(tree1.id2path('hello-id'), 'hello')
270
190
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
271
191
        self.assertFalse(tree1.has_filename('fruity'))
272
 
        self.check_tree_shape(tree1, ['hello'])
273
 
        eq(tree1.get_file_revision('hello-id'), 'test@rev-1')
 
192
        self.check_inventory_shape(tree1.inventory, ['hello'])
 
193
        ie = tree1.inventory['hello-id']
 
194
        eq(ie.revision, 'test@rev-1')
274
195
 
275
196
        tree2 = b.repository.revision_tree('test@rev-2')
276
 
        tree2.lock_read()
277
 
        self.addCleanup(tree2.unlock)
278
197
        eq(tree2.id2path('hello-id'), 'fruity')
279
198
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
280
 
        self.check_tree_shape(tree2, ['fruity'])
281
 
        eq(tree2.get_file_revision('hello-id'), 'test@rev-2')
 
199
        self.check_inventory_shape(tree2.inventory, ['fruity'])
 
200
        ie = tree2.inventory['hello-id']
 
201
        eq(ie.revision, 'test@rev-2')
282
202
 
283
203
    def test_reused_rev_id(self):
284
204
        """Test that a revision id cannot be reused in a branch"""
305
225
        wt.commit('two', rev_id=r2, allow_pointless=False)
306
226
        wt.lock_read()
307
227
        try:
308
 
            self.check_tree_shape(wt, ['a/', 'a/hello', 'b/'])
 
228
            self.check_inventory_shape(wt.read_working_inventory(),
 
229
                                       ['a', 'a/hello', 'b'])
309
230
        finally:
310
231
            wt.unlock()
311
232
 
314
235
        wt.commit('three', rev_id=r3, allow_pointless=False)
315
236
        wt.lock_read()
316
237
        try:
317
 
            self.check_tree_shape(wt,
318
 
                                       ['a/', 'a/hello', 'a/b/'])
319
 
            self.check_tree_shape(b.repository.revision_tree(r3),
320
 
                                       ['a/', 'a/hello', 'a/b/'])
 
238
            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'])
321
242
        finally:
322
243
            wt.unlock()
323
244
 
326
247
        wt.commit('four', rev_id=r4, allow_pointless=False)
327
248
        wt.lock_read()
328
249
        try:
329
 
            self.check_tree_shape(wt, ['a/', 'a/b/hello', 'a/b/'])
 
250
            self.check_inventory_shape(wt.read_working_inventory(),
 
251
                                       ['a', 'a/b/hello', 'a/b'])
330
252
        finally:
331
253
            wt.unlock()
332
254
 
333
 
        inv = b.repository.get_inventory(r4)
 
255
        inv = b.repository.get_revision_inventory(r4)
334
256
        eq(inv['hello-id'].revision, r4)
335
257
        eq(inv['a-id'].revision, r1)
336
258
        eq(inv['b-id'].revision, r3)
364
286
        eq = self.assertEquals
365
287
        eq(b.revision_history(), rev_ids)
366
288
        for i in range(4):
367
 
            self.assertThat(rev_ids[:i+1],
368
 
                MatchesAncestry(b.repository, rev_ids[i]))
 
289
            anc = b.repository.get_ancestry(rev_ids[i])
 
290
            eq(anc, [None] + rev_ids[:i+1])
369
291
 
370
292
    def test_commit_new_subdir_child_selective(self):
371
293
        wt = self.make_branch_and_tree('.')
394
316
    def test_strict_commit_without_unknowns(self):
395
317
        """Try and commit with no unknown files and strict = True,
396
318
        should work."""
 
319
        from bzrlib.errors import StrictCommitFailed
397
320
        wt = self.make_branch_and_tree('.')
398
321
        b = wt.branch
399
322
        file('hello', 'w').write('hello world')
425
348
        wt = self.make_branch_and_tree('.')
426
349
        branch = wt.branch
427
350
        wt.commit("base", allow_pointless=True, rev_id='A')
428
 
        self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
 
351
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
429
352
        try:
430
353
            from bzrlib.testament import Testament
431
354
            # monkey patch gpg signing mechanism
449
372
        wt = self.make_branch_and_tree('.')
450
373
        branch = wt.branch
451
374
        wt.commit("base", allow_pointless=True, rev_id='A')
452
 
        self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
 
375
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
453
376
        try:
 
377
            from bzrlib.testament import Testament
454
378
            # monkey patch gpg signing mechanism
455
379
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
456
380
            config = MustSignConfig(branch)
462
386
                              working_tree=wt)
463
387
            branch = Branch.open(self.get_url('.'))
464
388
            self.assertEqual(branch.revision_history(), ['A'])
465
 
            self.assertFalse(branch.repository.has_revision('B'))
 
389
            self.failIf(branch.repository.has_revision('B'))
466
390
        finally:
467
391
            bzrlib.gpg.GPGStrategy = oldstrategy
468
392
 
503
427
        bound = master.sprout('bound')
504
428
        wt = bound.open_workingtree()
505
429
        wt.branch.set_bound_location(os.path.realpath('master'))
 
430
 
 
431
        orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
506
432
        master_branch.lock_write()
507
433
        try:
 
434
            lockdir._DEFAULT_TIMEOUT_SECONDS = 1
508
435
            self.assertRaises(LockContention, wt.commit, 'silly')
509
436
        finally:
 
437
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
510
438
            master_branch.unlock()
511
439
 
512
440
    def test_commit_bound_merge(self):
523
451
        other_bzrdir = master_branch.bzrdir.sprout('other')
524
452
        other_tree = other_bzrdir.open_workingtree()
525
453
 
526
 
        # do a commit to the other branch changing the content file so
 
454
        # do a commit to the the other branch changing the content file so
527
455
        # that our commit after merging will have a merged revision in the
528
456
        # content file history.
529
457
        self.build_tree_contents([('other/content_file', 'change in other\n')])
540
468
        bound_tree.commit(message='commit of merge in bound tree')
541
469
 
542
470
    def test_commit_reporting_after_merge(self):
543
 
        # when doing a commit of a merge, the reporter needs to still
 
471
        # when doing a commit of a merge, the reporter needs to still 
544
472
        # be called for each item that is added/removed/deleted.
545
473
        this_tree = self.make_branch_and_tree('this')
546
474
        # we need a bunch of files and dirs, to perform one action on each.
589
517
        this_tree.merge_from_branch(other_tree.branch)
590
518
        reporter = CapturingReporter()
591
519
        this_tree.commit('do the commit', reporter=reporter)
592
 
        expected = set([
 
520
        self.assertEqual([
 
521
            ('change', 'unchanged', ''),
 
522
            ('change', 'unchanged', 'dirtoleave'),
 
523
            ('change', 'unchanged', 'filetoleave'),
593
524
            ('change', 'modified', 'filetomodify'),
594
525
            ('change', 'added', 'newdir'),
595
526
            ('change', 'added', 'newfile'),
596
527
            ('renamed', 'renamed', 'dirtorename', 'renameddir'),
597
 
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
598
528
            ('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
599
529
            ('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
 
530
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
600
531
            ('deleted', 'dirtoremove'),
601
532
            ('deleted', 'filetoremove'),
602
 
            ])
603
 
        result = set(reporter.calls)
604
 
        missing = expected - result
605
 
        new = result - expected
606
 
        self.assertEqual((set(), set()), (missing, new))
 
533
            ],
 
534
            reporter.calls)
607
535
 
608
536
    def test_commit_removals_respects_filespec(self):
609
537
        """Commit respects the specified_files for removals."""
653
581
            basis.unlock()
654
582
 
655
583
    def test_commit_kind_changes(self):
656
 
        self.requireFeature(SymlinkFeature)
 
584
        if not osutils.has_symlinks():
 
585
            raise tests.TestSkipped('Test requires symlink support')
657
586
        tree = self.make_branch_and_tree('.')
658
587
        os.symlink('target', 'name')
659
588
        tree.add('name', 'a-file-id')
699
628
    def test_commit_unversioned_specified(self):
700
629
        """Commit should raise if specified files isn't in basis or worktree"""
701
630
        tree = self.make_branch_and_tree('.')
702
 
        self.assertRaises(errors.PathsNotVersionedError, tree.commit,
 
631
        self.assertRaises(errors.PathsNotVersionedError, tree.commit, 
703
632
                          'message', specific_files=['bogus'])
704
633
 
705
634
    class Callback(object):
706
 
 
 
635
        
707
636
        def __init__(self, message, testcase):
708
637
            self.called = False
709
638
            self.message = message
737
666
        """Callback should not be invoked for pointless commit"""
738
667
        tree = self.make_branch_and_tree('.')
739
668
        cb = self.Callback(u'commit 2', self)
740
 
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb,
 
669
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb, 
741
670
                          allow_pointless=False)
742
671
        self.assertFalse(cb.called)
743
672
 
747
676
        cb = self.Callback(u'commit 2', self)
748
677
        repository = tree.branch.repository
749
678
        # simulate network failure
750
 
        def raise_(self, arg, arg2, arg3=None, arg4=None):
 
679
        def raise_(self, arg, arg2):
751
680
            raise errors.NoSuchFile('foo')
752
681
        repository.add_inventory = raise_
753
 
        repository.add_inventory_by_delta = raise_
754
682
        self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
755
683
        self.assertFalse(cb.called)
756
684
 
767
695
        self.assertEqual(['bar', 'baz'], err.files)
768
696
        self.assertEqual('Selected-file commit of merges is not supported'
769
697
                         ' yet: files bar, baz', str(err))
770
 
 
771
 
    def test_commit_ordering(self):
772
 
        """Test of corner-case commit ordering error"""
773
 
        tree = self.make_branch_and_tree('.')
774
 
        self.build_tree(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
775
 
        tree.add(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
776
 
        tree.commit('setup')
777
 
        self.build_tree(['a/c/d/'])
778
 
        tree.add('a/c/d')
779
 
        tree.rename_one('a/z/x', 'a/c/d/x')
780
 
        tree.commit('test', specific_files=['a/z/y'])
781
 
 
782
 
    def test_commit_no_author(self):
783
 
        """The default kwarg author in MutableTree.commit should not add
784
 
        the 'author' revision property.
785
 
        """
786
 
        tree = self.make_branch_and_tree('foo')
787
 
        rev_id = tree.commit('commit 1')
788
 
        rev = tree.branch.repository.get_revision(rev_id)
789
 
        self.assertFalse('author' in rev.properties)
790
 
        self.assertFalse('authors' in rev.properties)
791
 
 
792
 
    def test_commit_author(self):
793
 
        """Passing a non-empty author kwarg to MutableTree.commit should add
794
 
        the 'author' revision property.
795
 
        """
796
 
        tree = self.make_branch_and_tree('foo')
797
 
        rev_id = self.callDeprecated(['The parameter author was '
798
 
                'deprecated in version 1.13. Use authors instead'],
799
 
                tree.commit, 'commit 1', author='John Doe <jdoe@example.com>')
800
 
        rev = tree.branch.repository.get_revision(rev_id)
801
 
        self.assertEqual('John Doe <jdoe@example.com>',
802
 
                         rev.properties['authors'])
803
 
        self.assertFalse('author' in rev.properties)
804
 
 
805
 
    def test_commit_empty_authors_list(self):
806
 
        """Passing an empty list to authors shouldn't add the property."""
807
 
        tree = self.make_branch_and_tree('foo')
808
 
        rev_id = tree.commit('commit 1', authors=[])
809
 
        rev = tree.branch.repository.get_revision(rev_id)
810
 
        self.assertFalse('author' in rev.properties)
811
 
        self.assertFalse('authors' in rev.properties)
812
 
 
813
 
    def test_multiple_authors(self):
814
 
        tree = self.make_branch_and_tree('foo')
815
 
        rev_id = tree.commit('commit 1',
816
 
                authors=['John Doe <jdoe@example.com>',
817
 
                         'Jane Rey <jrey@example.com>'])
818
 
        rev = tree.branch.repository.get_revision(rev_id)
819
 
        self.assertEqual('John Doe <jdoe@example.com>\n'
820
 
                'Jane Rey <jrey@example.com>', rev.properties['authors'])
821
 
        self.assertFalse('author' in rev.properties)
822
 
 
823
 
    def test_author_and_authors_incompatible(self):
824
 
        tree = self.make_branch_and_tree('foo')
825
 
        self.assertRaises(AssertionError, tree.commit, 'commit 1',
826
 
                authors=['John Doe <jdoe@example.com>',
827
 
                         'Jane Rey <jrey@example.com>'],
828
 
                author="Jack Me <jme@example.com>")
829
 
 
830
 
    def test_author_with_newline_rejected(self):
831
 
        tree = self.make_branch_and_tree('foo')
832
 
        self.assertRaises(AssertionError, tree.commit, 'commit 1',
833
 
                authors=['John\nDoe <jdoe@example.com>'])
834
 
 
835
 
    def test_commit_with_checkout_and_branch_sharing_repo(self):
836
 
        repo = self.make_repository('repo', shared=True)
837
 
        # make_branch_and_tree ignores shared repos
838
 
        branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
839
 
        tree2 = branch.create_checkout('repo/tree2')
840
 
        tree2.commit('message', rev_id='rev1')
841
 
        self.assertTrue(tree2.branch.repository.has_revision('rev1'))