~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

  • Committer: INADA Naoki
  • Date: 2011-05-05 09:15:34 UTC
  • mto: (5830.3.3 i18n-msgfmt)
  • mto: This revision was merged to the branch mainline in revision 5873.
  • Revision ID: songofacandy@gmail.com-20110505091534-7sv835xpofwrmpt4
Add update-pot command to Makefile and tools/bzrgettext script that
extracts help text from bzr commands.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
 
    lockdir,
24
 
    osutils,
25
 
    tests,
26
24
    )
27
25
from bzrlib.branch import Branch
28
 
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
 
26
from bzrlib.bzrdir import BzrDirMetaFormat1
29
27
from bzrlib.commit import Commit, NullCommitReporter
30
28
from bzrlib.config import BranchConfig
31
 
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
 
29
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
32
30
                           LockContention)
33
 
from bzrlib.tests import TestCaseWithTransport
34
 
from bzrlib.workingtree import WorkingTree
 
31
from bzrlib.tests import (
 
32
    SymlinkFeature,
 
33
    TestCaseWithTransport,
 
34
    test_foreign,
 
35
    )
35
36
 
36
37
 
37
38
# TODO: Test commit with some added, and added-but-missing files
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')
100
 
 
101
 
    def test_delete_commit(self):
102
 
        """Test a commit with a deleted file"""
 
105
        tree2.lock_read()
 
106
        text = tree2.get_file_text(file_id)
 
107
        tree2.unlock()
 
108
        self.assertEqual('version 2', text)
 
109
 
 
110
    def test_commit_lossy_native(self):
 
111
        """Attempt a lossy commit to a native branch."""
 
112
        wt = self.make_branch_and_tree('.')
 
113
        b = wt.branch
 
114
        file('hello', 'w').write('hello world')
 
115
        wt.add('hello')
 
116
        revid = wt.commit(message='add hello', rev_id='revid', lossy=True)
 
117
        self.assertEquals('revid', revid)
 
118
 
 
119
    def test_commit_lossy_foreign(self):
 
120
        """Attempt a lossy commit to a foreign branch."""
 
121
        test_foreign.register_dummy_foreign_for_test(self)
 
122
        wt = self.make_branch_and_tree('.',
 
123
            format=test_foreign.DummyForeignVcsDirFormat())
 
124
        b = wt.branch
 
125
        file('hello', 'w').write('hello world')
 
126
        wt.add('hello')
 
127
        revid = wt.commit(message='add hello', lossy=True,
 
128
            timestamp=1302659388, timezone=0)
 
129
        self.assertEquals('dummy-v1:1302659388.0-0-UNKNOWN', revid)
 
130
 
 
131
    def test_commit_bound_lossy_foreign(self):
 
132
        """Attempt a lossy commit to a bzr branch bound to a foreign branch."""
 
133
        test_foreign.register_dummy_foreign_for_test(self)
 
134
        foreign_branch = self.make_branch('foreign',
 
135
            format=test_foreign.DummyForeignVcsDirFormat())
 
136
        wt = foreign_branch.create_checkout("local")
 
137
        b = wt.branch
 
138
        file('local/hello', 'w').write('hello world')
 
139
        wt.add('hello')
 
140
        revid = wt.commit(message='add hello', lossy=True,
 
141
            timestamp=1302659388, timezone=0)
 
142
        self.assertEquals('dummy-v1:1302659388.0-0-0', revid)
 
143
        self.assertEquals('dummy-v1:1302659388.0-0-0',
 
144
            foreign_branch.last_revision())
 
145
        self.assertEquals('dummy-v1:1302659388.0-0-0',
 
146
            wt.branch.last_revision())
 
147
 
 
148
    def test_missing_commit(self):
 
149
        """Test a commit with a missing file"""
103
150
        wt = self.make_branch_and_tree('.')
104
151
        b = wt.branch
105
152
        file('hello', 'w').write('hello world')
112
159
        tree = b.repository.revision_tree('rev2')
113
160
        self.assertFalse(tree.has_id('hello-id'))
114
161
 
 
162
    def test_partial_commit_move(self):
 
163
        """Test a partial commit where a file was renamed but not committed.
 
164
 
 
165
        https://bugs.launchpad.net/bzr/+bug/83039
 
166
 
 
167
        If not handled properly, commit will try to snapshot
 
168
        dialog.py with olive/ as a parent, while
 
169
        olive/ has not been snapshotted yet.
 
170
        """
 
171
        wt = self.make_branch_and_tree('.')
 
172
        b = wt.branch
 
173
        self.build_tree(['annotate/', 'annotate/foo.py',
 
174
                         'olive/', 'olive/dialog.py'
 
175
                        ])
 
176
        wt.add(['annotate', 'olive', 'annotate/foo.py', 'olive/dialog.py'])
 
177
        wt.commit(message='add files')
 
178
        wt.rename_one("olive/dialog.py", "aaa")
 
179
        self.build_tree_contents([('annotate/foo.py', 'modified\n')])
 
180
        wt.commit('renamed hello', specific_files=["annotate"])
 
181
 
115
182
    def test_pointless_commit(self):
116
183
        """Commit refuses unless there are changes or it's forced."""
117
184
        wt = self.make_branch_and_tree('.')
125
192
                          message='fails',
126
193
                          allow_pointless=False)
127
194
        self.assertEquals(b.revno(), 1)
128
 
        
 
195
 
129
196
    def test_commit_empty(self):
130
197
        """Commiting an empty tree works."""
131
198
        wt = self.make_branch_and_tree('.')
148
215
              ['hello-id', 'buongia-id'])
149
216
        wt.commit(message='add files',
150
217
                 rev_id='test@rev-1')
151
 
        
 
218
 
152
219
        os.remove('hello')
153
220
        file('buongia', 'w').write('new text')
154
221
        wt.commit(message='update text',
165
232
        eq(b.revno(), 3)
166
233
 
167
234
        tree2 = b.repository.revision_tree('test@rev-2')
 
235
        tree2.lock_read()
 
236
        self.addCleanup(tree2.unlock)
168
237
        self.assertTrue(tree2.has_filename('hello'))
169
238
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
170
239
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
171
 
        
 
240
 
172
241
        tree3 = b.repository.revision_tree('test@rev-3')
 
242
        tree3.lock_read()
 
243
        self.addCleanup(tree3.unlock)
173
244
        self.assertFalse(tree3.has_filename('hello'))
174
245
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
175
246
 
186
257
 
187
258
        eq = self.assertEquals
188
259
        tree1 = b.repository.revision_tree('test@rev-1')
 
260
        tree1.lock_read()
 
261
        self.addCleanup(tree1.unlock)
189
262
        eq(tree1.id2path('hello-id'), 'hello')
190
263
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
191
264
        self.assertFalse(tree1.has_filename('fruity'))
192
 
        self.check_inventory_shape(tree1.inventory, ['hello'])
193
 
        ie = tree1.inventory['hello-id']
194
 
        eq(ie.revision, 'test@rev-1')
 
265
        self.check_tree_shape(tree1, ['hello'])
 
266
        eq(tree1.get_file_revision('hello-id'), 'test@rev-1')
195
267
 
196
268
        tree2 = b.repository.revision_tree('test@rev-2')
 
269
        tree2.lock_read()
 
270
        self.addCleanup(tree2.unlock)
197
271
        eq(tree2.id2path('hello-id'), 'fruity')
198
272
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
199
 
        self.check_inventory_shape(tree2.inventory, ['fruity'])
200
 
        ie = tree2.inventory['hello-id']
201
 
        eq(ie.revision, 'test@rev-2')
 
273
        self.check_tree_shape(tree2, ['fruity'])
 
274
        eq(tree2.get_file_revision('hello-id'), 'test@rev-2')
202
275
 
203
276
    def test_reused_rev_id(self):
204
277
        """Test that a revision id cannot be reused in a branch"""
225
298
        wt.commit('two', rev_id=r2, allow_pointless=False)
226
299
        wt.lock_read()
227
300
        try:
228
 
            self.check_inventory_shape(wt.read_working_inventory(),
229
 
                                       ['a', 'a/hello', 'b'])
 
301
            self.check_tree_shape(wt, ['a/', 'a/hello', 'b/'])
230
302
        finally:
231
303
            wt.unlock()
232
304
 
235
307
        wt.commit('three', rev_id=r3, allow_pointless=False)
236
308
        wt.lock_read()
237
309
        try:
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'])
 
310
            self.check_tree_shape(wt,
 
311
                                       ['a/', 'a/hello', 'a/b/'])
 
312
            self.check_tree_shape(b.repository.revision_tree(r3),
 
313
                                       ['a/', 'a/hello', 'a/b/'])
242
314
        finally:
243
315
            wt.unlock()
244
316
 
247
319
        wt.commit('four', rev_id=r4, allow_pointless=False)
248
320
        wt.lock_read()
249
321
        try:
250
 
            self.check_inventory_shape(wt.read_working_inventory(),
251
 
                                       ['a', 'a/b/hello', 'a/b'])
 
322
            self.check_tree_shape(wt, ['a/', 'a/b/hello', 'a/b/'])
252
323
        finally:
253
324
            wt.unlock()
254
325
 
255
 
        inv = b.repository.get_revision_inventory(r4)
 
326
        inv = b.repository.get_inventory(r4)
256
327
        eq(inv['hello-id'].revision, r4)
257
328
        eq(inv['a-id'].revision, r1)
258
329
        eq(inv['b-id'].revision, r3)
316
387
    def test_strict_commit_without_unknowns(self):
317
388
        """Try and commit with no unknown files and strict = True,
318
389
        should work."""
319
 
        from bzrlib.errors import StrictCommitFailed
320
390
        wt = self.make_branch_and_tree('.')
321
391
        b = wt.branch
322
392
        file('hello', 'w').write('hello world')
348
418
        wt = self.make_branch_and_tree('.')
349
419
        branch = wt.branch
350
420
        wt.commit("base", allow_pointless=True, rev_id='A')
351
 
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
421
        self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
352
422
        try:
353
423
            from bzrlib.testament import Testament
354
424
            # monkey patch gpg signing mechanism
357
427
                                                      allow_pointless=True,
358
428
                                                      rev_id='B',
359
429
                                                      working_tree=wt)
360
 
            self.assertEqual(Testament.from_revision(branch.repository,
361
 
                             'B').as_short_text(),
 
430
            def sign(text):
 
431
                return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
 
432
            self.assertEqual(sign(Testament.from_revision(branch.repository,
 
433
                             'B').as_short_text()),
362
434
                             branch.repository.get_signature_text('B'))
363
435
        finally:
364
436
            bzrlib.gpg.GPGStrategy = oldstrategy
370
442
        wt = self.make_branch_and_tree('.')
371
443
        branch = wt.branch
372
444
        wt.commit("base", allow_pointless=True, rev_id='A')
373
 
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
445
        self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
374
446
        try:
375
 
            from bzrlib.testament import Testament
376
447
            # monkey patch gpg signing mechanism
377
448
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
378
449
            config = MustSignConfig(branch)
384
455
                              working_tree=wt)
385
456
            branch = Branch.open(self.get_url('.'))
386
457
            self.assertEqual(branch.revision_history(), ['A'])
387
 
            self.failIf(branch.repository.has_revision('B'))
 
458
            self.assertFalse(branch.repository.has_revision('B'))
388
459
        finally:
389
460
            bzrlib.gpg.GPGStrategy = oldstrategy
390
461
 
425
496
        bound = master.sprout('bound')
426
497
        wt = bound.open_workingtree()
427
498
        wt.branch.set_bound_location(os.path.realpath('master'))
428
 
 
429
 
        orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
430
499
        master_branch.lock_write()
431
500
        try:
432
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = 1
433
501
            self.assertRaises(LockContention, wt.commit, 'silly')
434
502
        finally:
435
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
436
503
            master_branch.unlock()
437
504
 
438
505
    def test_commit_bound_merge(self):
449
516
        other_bzrdir = master_branch.bzrdir.sprout('other')
450
517
        other_tree = other_bzrdir.open_workingtree()
451
518
 
452
 
        # do a commit to the the other branch changing the content file so
 
519
        # do a commit to the other branch changing the content file so
453
520
        # that our commit after merging will have a merged revision in the
454
521
        # content file history.
455
522
        self.build_tree_contents([('other/content_file', 'change in other\n')])
466
533
        bound_tree.commit(message='commit of merge in bound tree')
467
534
 
468
535
    def test_commit_reporting_after_merge(self):
469
 
        # when doing a commit of a merge, the reporter needs to still 
 
536
        # when doing a commit of a merge, the reporter needs to still
470
537
        # be called for each item that is added/removed/deleted.
471
538
        this_tree = self.make_branch_and_tree('this')
472
539
        # we need a bunch of files and dirs, to perform one action on each.
515
582
        this_tree.merge_from_branch(other_tree.branch)
516
583
        reporter = CapturingReporter()
517
584
        this_tree.commit('do the commit', reporter=reporter)
518
 
        self.assertEqual([
519
 
            ('change', 'unchanged', ''),
520
 
            ('change', 'unchanged', 'dirtoleave'),
521
 
            ('change', 'unchanged', 'filetoleave'),
 
585
        expected = set([
522
586
            ('change', 'modified', 'filetomodify'),
523
587
            ('change', 'added', 'newdir'),
524
588
            ('change', 'added', 'newfile'),
525
589
            ('renamed', 'renamed', 'dirtorename', 'renameddir'),
 
590
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
526
591
            ('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
527
592
            ('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
528
 
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
529
593
            ('deleted', 'dirtoremove'),
530
594
            ('deleted', 'filetoremove'),
531
 
            ],
532
 
            reporter.calls)
 
595
            ])
 
596
        result = set(reporter.calls)
 
597
        missing = expected - result
 
598
        new = result - expected
 
599
        self.assertEqual((set(), set()), (missing, new))
533
600
 
534
601
    def test_commit_removals_respects_filespec(self):
535
602
        """Commit respects the specified_files for removals."""
579
646
            basis.unlock()
580
647
 
581
648
    def test_commit_kind_changes(self):
582
 
        if not osutils.has_symlinks():
583
 
            raise tests.TestSkipped('Test requires symlink support')
 
649
        self.requireFeature(SymlinkFeature)
584
650
        tree = self.make_branch_and_tree('.')
585
651
        os.symlink('target', 'name')
586
652
        tree.add('name', 'a-file-id')
626
692
    def test_commit_unversioned_specified(self):
627
693
        """Commit should raise if specified files isn't in basis or worktree"""
628
694
        tree = self.make_branch_and_tree('.')
629
 
        self.assertRaises(errors.PathsNotVersionedError, tree.commit, 
 
695
        self.assertRaises(errors.PathsNotVersionedError, tree.commit,
630
696
                          'message', specific_files=['bogus'])
631
697
 
632
698
    class Callback(object):
633
 
        
 
699
 
634
700
        def __init__(self, message, testcase):
635
701
            self.called = False
636
702
            self.message = message
664
730
        """Callback should not be invoked for pointless commit"""
665
731
        tree = self.make_branch_and_tree('.')
666
732
        cb = self.Callback(u'commit 2', self)
667
 
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb, 
 
733
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb,
668
734
                          allow_pointless=False)
669
735
        self.assertFalse(cb.called)
670
736
 
674
740
        cb = self.Callback(u'commit 2', self)
675
741
        repository = tree.branch.repository
676
742
        # simulate network failure
677
 
        def raise_(self, arg, arg2):
 
743
        def raise_(self, arg, arg2, arg3=None, arg4=None):
678
744
            raise errors.NoSuchFile('foo')
679
745
        repository.add_inventory = raise_
 
746
        repository.add_inventory_by_delta = raise_
680
747
        self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
681
748
        self.assertFalse(cb.called)
 
749
 
 
750
    def test_selected_file_merge_commit(self):
 
751
        """Ensure the correct error is raised"""
 
752
        tree = self.make_branch_and_tree('foo')
 
753
        # pending merge would turn into a left parent
 
754
        tree.commit('commit 1')
 
755
        tree.add_parent_tree_id('example')
 
756
        self.build_tree(['foo/bar', 'foo/baz'])
 
757
        tree.add(['bar', 'baz'])
 
758
        err = self.assertRaises(errors.CannotCommitSelectedFileMerge,
 
759
            tree.commit, 'commit 2', specific_files=['bar', 'baz'])
 
760
        self.assertEqual(['bar', 'baz'], err.files)
 
761
        self.assertEqual('Selected-file commit of merges is not supported'
 
762
                         ' yet: files bar, baz', str(err))
 
763
 
 
764
    def test_commit_ordering(self):
 
765
        """Test of corner-case commit ordering error"""
 
766
        tree = self.make_branch_and_tree('.')
 
767
        self.build_tree(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
 
768
        tree.add(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
 
769
        tree.commit('setup')
 
770
        self.build_tree(['a/c/d/'])
 
771
        tree.add('a/c/d')
 
772
        tree.rename_one('a/z/x', 'a/c/d/x')
 
773
        tree.commit('test', specific_files=['a/z/y'])
 
774
 
 
775
    def test_commit_no_author(self):
 
776
        """The default kwarg author in MutableTree.commit should not add
 
777
        the 'author' revision property.
 
778
        """
 
779
        tree = self.make_branch_and_tree('foo')
 
780
        rev_id = tree.commit('commit 1')
 
781
        rev = tree.branch.repository.get_revision(rev_id)
 
782
        self.assertFalse('author' in rev.properties)
 
783
        self.assertFalse('authors' in rev.properties)
 
784
 
 
785
    def test_commit_author(self):
 
786
        """Passing a non-empty author kwarg to MutableTree.commit should add
 
787
        the 'author' revision property.
 
788
        """
 
789
        tree = self.make_branch_and_tree('foo')
 
790
        rev_id = self.callDeprecated(['The parameter author was '
 
791
                'deprecated in version 1.13. Use authors instead'],
 
792
                tree.commit, 'commit 1', author='John Doe <jdoe@example.com>')
 
793
        rev = tree.branch.repository.get_revision(rev_id)
 
794
        self.assertEqual('John Doe <jdoe@example.com>',
 
795
                         rev.properties['authors'])
 
796
        self.assertFalse('author' in rev.properties)
 
797
 
 
798
    def test_commit_empty_authors_list(self):
 
799
        """Passing an empty list to authors shouldn't add the property."""
 
800
        tree = self.make_branch_and_tree('foo')
 
801
        rev_id = tree.commit('commit 1', authors=[])
 
802
        rev = tree.branch.repository.get_revision(rev_id)
 
803
        self.assertFalse('author' in rev.properties)
 
804
        self.assertFalse('authors' in rev.properties)
 
805
 
 
806
    def test_multiple_authors(self):
 
807
        tree = self.make_branch_and_tree('foo')
 
808
        rev_id = tree.commit('commit 1',
 
809
                authors=['John Doe <jdoe@example.com>',
 
810
                         'Jane Rey <jrey@example.com>'])
 
811
        rev = tree.branch.repository.get_revision(rev_id)
 
812
        self.assertEqual('John Doe <jdoe@example.com>\n'
 
813
                'Jane Rey <jrey@example.com>', rev.properties['authors'])
 
814
        self.assertFalse('author' in rev.properties)
 
815
 
 
816
    def test_author_and_authors_incompatible(self):
 
817
        tree = self.make_branch_and_tree('foo')
 
818
        self.assertRaises(AssertionError, tree.commit, 'commit 1',
 
819
                authors=['John Doe <jdoe@example.com>',
 
820
                         'Jane Rey <jrey@example.com>'],
 
821
                author="Jack Me <jme@example.com>")
 
822
 
 
823
    def test_author_with_newline_rejected(self):
 
824
        tree = self.make_branch_and_tree('foo')
 
825
        self.assertRaises(AssertionError, tree.commit, 'commit 1',
 
826
                authors=['John\nDoe <jdoe@example.com>'])
 
827
 
 
828
    def test_commit_with_checkout_and_branch_sharing_repo(self):
 
829
        repo = self.make_repository('repo', shared=True)
 
830
        # make_branch_and_tree ignores shared repos
 
831
        branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
 
832
        tree2 = branch.create_checkout('repo/tree2')
 
833
        tree2.commit('message', rev_id='rev1')
 
834
        self.assertTrue(tree2.branch.repository.has_revision('rev1'))