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
21
21
from bzrlib import (
25
25
from bzrlib.branch import Branch
26
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
26
from bzrlib.bzrdir import BzrDirMetaFormat1
27
27
from bzrlib.commit import Commit, NullCommitReporter
28
28
from bzrlib.config import BranchConfig
29
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
29
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
31
from bzrlib.tests import TestCaseWithTransport
32
from bzrlib.workingtree import WorkingTree
31
from bzrlib.tests import (
33
TestCaseWithTransport,
35
38
# TODO: Test commit with some added, and added-but-missing files
90
96
eq(rev.message, 'add hello')
92
98
tree1 = b.repository.revision_tree(rh[0])
93
100
text = tree1.get_file_text(file_id)
94
eq(text, 'hello world')
102
self.assertEqual('hello world', text)
96
104
tree2 = b.repository.revision_tree(rh[1])
97
eq(tree2.get_file_text(file_id), 'version 2')
99
def test_delete_commit(self):
100
"""Test a commit with a deleted file"""
106
text = tree2.get_file_text(file_id)
108
self.assertEqual('version 2', text)
110
def test_commit_lossy_native(self):
111
"""Attempt a lossy commit to a native branch."""
112
wt = self.make_branch_and_tree('.')
114
file('hello', 'w').write('hello world')
116
revid = wt.commit(message='add hello', rev_id='revid', lossy=True)
117
self.assertEquals('revid', revid)
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())
125
file('hello', 'w').write('hello world')
127
revid = wt.commit(message='add hello', lossy=True,
128
timestamp=1302659388, timezone=0)
129
self.assertEquals('dummy-v1:1302659388.0-0-UNKNOWN', revid)
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")
138
file('local/hello', 'w').write('hello world')
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())
148
def test_missing_commit(self):
149
"""Test a commit with a missing file"""
101
150
wt = self.make_branch_and_tree('.')
103
152
file('hello', 'w').write('hello world')
110
159
tree = b.repository.revision_tree('rev2')
111
160
self.assertFalse(tree.has_id('hello-id'))
162
def test_partial_commit_move(self):
163
"""Test a partial commit where a file was renamed but not committed.
165
https://bugs.launchpad.net/bzr/+bug/83039
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.
171
wt = self.make_branch_and_tree('.')
173
self.build_tree(['annotate/', 'annotate/foo.py',
174
'olive/', 'olive/dialog.py'
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"])
113
182
def test_pointless_commit(self):
114
183
"""Commit refuses unless there are changes or it's forced."""
115
184
wt = self.make_branch_and_tree('.')
221
298
wt.move(['hello'], 'a')
222
299
r2 = 'test@rev-2'
223
300
wt.commit('two', rev_id=r2, allow_pointless=False)
224
self.check_inventory_shape(wt.read_working_inventory(),
225
['a', 'a/hello', 'b'])
303
self.check_inventory_shape(wt.read_working_inventory(),
304
['a/', 'a/hello', 'b/'])
227
308
wt.move(['b'], 'a')
228
309
r3 = 'test@rev-3'
229
310
wt.commit('three', rev_id=r3, allow_pointless=False)
230
self.check_inventory_shape(wt.read_working_inventory(),
231
['a', 'a/hello', 'a/b'])
232
self.check_inventory_shape(b.repository.get_revision_inventory(r3),
233
['a', 'a/hello', 'a/b'])
313
self.check_inventory_shape(wt.read_working_inventory(),
314
['a/', 'a/hello', 'a/b/'])
315
self.check_inventory_shape(b.repository.get_inventory(r3),
316
['a/', 'a/hello', 'a/b/'])
235
320
wt.move(['a/hello'], 'a/b')
236
321
r4 = 'test@rev-4'
237
322
wt.commit('four', rev_id=r4, allow_pointless=False)
238
self.check_inventory_shape(wt.read_working_inventory(),
239
['a', 'a/b/hello', 'a/b'])
325
self.check_inventory_shape(wt.read_working_inventory(),
326
['a/', 'a/b/hello', 'a/b/'])
241
inv = b.repository.get_revision_inventory(r4)
330
inv = b.repository.get_inventory(r4)
242
331
eq(inv['hello-id'].revision, r4)
243
332
eq(inv['a-id'].revision, r1)
244
333
eq(inv['b-id'].revision, r3)
246
335
def test_removed_commit(self):
247
336
"""Commit with a removed file"""
248
337
wt = self.make_branch_and_tree('.')
501
586
this_tree.merge_from_branch(other_tree.branch)
502
587
reporter = CapturingReporter()
503
588
this_tree.commit('do the commit', reporter=reporter)
505
('change', 'unchanged', ''),
506
('change', 'unchanged', 'dirtoleave'),
507
('change', 'unchanged', 'filetoleave'),
508
590
('change', 'modified', 'filetomodify'),
509
591
('change', 'added', 'newdir'),
510
592
('change', 'added', 'newfile'),
511
593
('renamed', 'renamed', 'dirtorename', 'renameddir'),
594
('renamed', 'renamed', 'filetorename', 'renamedfile'),
512
595
('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
513
596
('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
514
('renamed', 'renamed', 'filetorename', 'renamedfile'),
515
597
('deleted', 'dirtoremove'),
516
598
('deleted', 'filetoremove'),
600
result = set(reporter.calls)
601
missing = expected - result
602
new = result - expected
603
self.assertEqual((set(), set()), (missing, new))
520
605
def test_commit_removals_respects_filespec(self):
521
606
"""Commit respects the specified_files for removals."""
552
641
timestamp_1ms = round(timestamp, 3)
553
642
self.assertEqual(timestamp_1ms, timestamp)
644
def assertBasisTreeKind(self, kind, tree, file_id):
645
basis = tree.basis_tree()
648
self.assertEqual(kind, basis.kind(file_id))
652
def test_commit_kind_changes(self):
653
self.requireFeature(SymlinkFeature)
654
tree = self.make_branch_and_tree('.')
655
os.symlink('target', 'name')
656
tree.add('name', 'a-file-id')
657
tree.commit('Added a symlink')
658
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
661
self.build_tree(['name'])
662
tree.commit('Changed symlink to file')
663
self.assertBasisTreeKind('file', tree, 'a-file-id')
666
os.symlink('target', 'name')
667
tree.commit('file to symlink')
668
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
672
tree.commit('symlink to directory')
673
self.assertBasisTreeKind('directory', tree, 'a-file-id')
676
os.symlink('target', 'name')
677
tree.commit('directory to symlink')
678
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
680
# prepare for directory <-> file tests
683
tree.commit('symlink to directory')
684
self.assertBasisTreeKind('directory', tree, 'a-file-id')
687
self.build_tree(['name'])
688
tree.commit('Changed directory to file')
689
self.assertBasisTreeKind('file', tree, 'a-file-id')
693
tree.commit('file to directory')
694
self.assertBasisTreeKind('directory', tree, 'a-file-id')
555
696
def test_commit_unversioned_specified(self):
556
697
"""Commit should raise if specified files isn't in basis or worktree"""
557
698
tree = self.make_branch_and_tree('.')
558
self.assertRaises(errors.PathsNotVersionedError, tree.commit,
699
self.assertRaises(errors.PathsNotVersionedError, tree.commit,
559
700
'message', specific_files=['bogus'])
561
702
class Callback(object):
563
704
def __init__(self, message, testcase):
564
705
self.called = False
565
706
self.message = message
603
744
cb = self.Callback(u'commit 2', self)
604
745
repository = tree.branch.repository
605
746
# simulate network failure
606
def raise_(self, arg, arg2):
747
def raise_(self, arg, arg2, arg3=None, arg4=None):
607
748
raise errors.NoSuchFile('foo')
608
749
repository.add_inventory = raise_
750
repository.add_inventory_by_delta = raise_
609
751
self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
610
752
self.assertFalse(cb.called)
754
def test_selected_file_merge_commit(self):
755
"""Ensure the correct error is raised"""
756
tree = self.make_branch_and_tree('foo')
757
# pending merge would turn into a left parent
758
tree.commit('commit 1')
759
tree.add_parent_tree_id('example')
760
self.build_tree(['foo/bar', 'foo/baz'])
761
tree.add(['bar', 'baz'])
762
err = self.assertRaises(errors.CannotCommitSelectedFileMerge,
763
tree.commit, 'commit 2', specific_files=['bar', 'baz'])
764
self.assertEqual(['bar', 'baz'], err.files)
765
self.assertEqual('Selected-file commit of merges is not supported'
766
' yet: files bar, baz', str(err))
768
def test_commit_ordering(self):
769
"""Test of corner-case commit ordering error"""
770
tree = self.make_branch_and_tree('.')
771
self.build_tree(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
772
tree.add(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
774
self.build_tree(['a/c/d/'])
776
tree.rename_one('a/z/x', 'a/c/d/x')
777
tree.commit('test', specific_files=['a/z/y'])
779
def test_commit_no_author(self):
780
"""The default kwarg author in MutableTree.commit should not add
781
the 'author' revision property.
783
tree = self.make_branch_and_tree('foo')
784
rev_id = tree.commit('commit 1')
785
rev = tree.branch.repository.get_revision(rev_id)
786
self.assertFalse('author' in rev.properties)
787
self.assertFalse('authors' in rev.properties)
789
def test_commit_author(self):
790
"""Passing a non-empty author kwarg to MutableTree.commit should add
791
the 'author' revision property.
793
tree = self.make_branch_and_tree('foo')
794
rev_id = self.callDeprecated(['The parameter author was '
795
'deprecated in version 1.13. Use authors instead'],
796
tree.commit, 'commit 1', author='John Doe <jdoe@example.com>')
797
rev = tree.branch.repository.get_revision(rev_id)
798
self.assertEqual('John Doe <jdoe@example.com>',
799
rev.properties['authors'])
800
self.assertFalse('author' in rev.properties)
802
def test_commit_empty_authors_list(self):
803
"""Passing an empty list to authors shouldn't add the property."""
804
tree = self.make_branch_and_tree('foo')
805
rev_id = tree.commit('commit 1', authors=[])
806
rev = tree.branch.repository.get_revision(rev_id)
807
self.assertFalse('author' in rev.properties)
808
self.assertFalse('authors' in rev.properties)
810
def test_multiple_authors(self):
811
tree = self.make_branch_and_tree('foo')
812
rev_id = tree.commit('commit 1',
813
authors=['John Doe <jdoe@example.com>',
814
'Jane Rey <jrey@example.com>'])
815
rev = tree.branch.repository.get_revision(rev_id)
816
self.assertEqual('John Doe <jdoe@example.com>\n'
817
'Jane Rey <jrey@example.com>', rev.properties['authors'])
818
self.assertFalse('author' in rev.properties)
820
def test_author_and_authors_incompatible(self):
821
tree = self.make_branch_and_tree('foo')
822
self.assertRaises(AssertionError, tree.commit, 'commit 1',
823
authors=['John Doe <jdoe@example.com>',
824
'Jane Rey <jrey@example.com>'],
825
author="Jack Me <jme@example.com>")
827
def test_author_with_newline_rejected(self):
828
tree = self.make_branch_and_tree('foo')
829
self.assertRaises(AssertionError, tree.commit, 'commit 1',
830
authors=['John\nDoe <jdoe@example.com>'])
832
def test_commit_with_checkout_and_branch_sharing_repo(self):
833
repo = self.make_repository('repo', shared=True)
834
# make_branch_and_tree ignores shared repos
835
branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
836
tree2 = branch.create_checkout('repo/tree2')
837
tree2.commit('message', rev_id='rev1')
838
self.assertTrue(tree2.branch.repository.has_revision('rev1'))