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
21
21
from bzrlib import (
28
25
from bzrlib.branch import Branch
29
26
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
30
27
from bzrlib.commit import Commit, NullCommitReporter
31
28
from bzrlib.config import BranchConfig
32
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
29
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
34
from bzrlib.tests import SymlinkFeature, TestCaseWithTransport
31
from bzrlib.tests import TestCaseWithTransport
35
32
from bzrlib.workingtree import WorkingTree
121
110
tree = b.repository.revision_tree('rev2')
122
111
self.assertFalse(tree.has_id('hello-id'))
124
def test_partial_commit_move(self):
125
"""Test a partial commit where a file was renamed but not committed.
127
https://bugs.launchpad.net/bzr/+bug/83039
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.
133
wt = self.make_branch_and_tree('.')
135
self.build_tree(['annotate/', 'annotate/foo.py',
136
'olive/', 'olive/dialog.py'
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"])
144
113
def test_pointless_commit(self):
145
114
"""Commit refuses unless there are changes or it's forced."""
146
115
wt = self.make_branch_and_tree('.')
260
221
wt.move(['hello'], 'a')
261
222
r2 = 'test@rev-2'
262
223
wt.commit('two', rev_id=r2, allow_pointless=False)
265
self.check_inventory_shape(wt.read_working_inventory(),
266
['a/', 'a/hello', 'b/'])
224
self.check_inventory_shape(wt.read_working_inventory(),
225
['a', 'a/hello', 'b'])
270
227
wt.move(['b'], 'a')
271
228
r3 = 'test@rev-3'
272
229
wt.commit('three', rev_id=r3, allow_pointless=False)
275
self.check_inventory_shape(wt.read_working_inventory(),
276
['a/', 'a/hello', 'a/b/'])
277
self.check_inventory_shape(b.repository.get_inventory(r3),
278
['a/', 'a/hello', 'a/b/'])
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'])
282
235
wt.move(['a/hello'], 'a/b')
283
236
r4 = 'test@rev-4'
284
237
wt.commit('four', rev_id=r4, allow_pointless=False)
287
self.check_inventory_shape(wt.read_working_inventory(),
288
['a/', 'a/b/hello', 'a/b/'])
238
self.check_inventory_shape(wt.read_working_inventory(),
239
['a', 'a/b/hello', 'a/b'])
292
inv = b.repository.get_inventory(r4)
241
inv = b.repository.get_revision_inventory(r4)
293
242
eq(inv['hello-id'].revision, r4)
294
243
eq(inv['a-id'].revision, r1)
295
244
eq(inv['b-id'].revision, r3)
297
246
def test_removed_commit(self):
298
247
"""Commit with a removed file"""
299
248
wt = self.make_branch_and_tree('.')
550
501
this_tree.merge_from_branch(other_tree.branch)
551
502
reporter = CapturingReporter()
552
503
this_tree.commit('do the commit', reporter=reporter)
505
('change', 'unchanged', ''),
506
('change', 'unchanged', 'dirtoleave'),
507
('change', 'unchanged', 'filetoleave'),
554
508
('change', 'modified', 'filetomodify'),
555
509
('change', 'added', 'newdir'),
556
510
('change', 'added', 'newfile'),
557
511
('renamed', 'renamed', 'dirtorename', 'renameddir'),
558
('renamed', 'renamed', 'filetorename', 'renamedfile'),
559
512
('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
560
513
('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
514
('renamed', 'renamed', 'filetorename', 'renamedfile'),
561
515
('deleted', 'dirtoremove'),
562
516
('deleted', 'filetoremove'),
564
result = set(reporter.calls)
565
missing = expected - result
566
new = result - expected
567
self.assertEqual((set(), set()), (missing, new))
569
520
def test_commit_removals_respects_filespec(self):
570
521
"""Commit respects the specified_files for removals."""
605
552
timestamp_1ms = round(timestamp, 3)
606
553
self.assertEqual(timestamp_1ms, timestamp)
608
def assertBasisTreeKind(self, kind, tree, file_id):
609
basis = tree.basis_tree()
612
self.assertEqual(kind, basis.kind(file_id))
616
def test_commit_kind_changes(self):
617
self.requireFeature(SymlinkFeature)
618
tree = self.make_branch_and_tree('.')
619
os.symlink('target', 'name')
620
tree.add('name', 'a-file-id')
621
tree.commit('Added a symlink')
622
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
625
self.build_tree(['name'])
626
tree.commit('Changed symlink to file')
627
self.assertBasisTreeKind('file', tree, 'a-file-id')
630
os.symlink('target', 'name')
631
tree.commit('file to symlink')
632
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
636
tree.commit('symlink to directory')
637
self.assertBasisTreeKind('directory', tree, 'a-file-id')
640
os.symlink('target', 'name')
641
tree.commit('directory to symlink')
642
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
644
# prepare for directory <-> file tests
647
tree.commit('symlink to directory')
648
self.assertBasisTreeKind('directory', tree, 'a-file-id')
651
self.build_tree(['name'])
652
tree.commit('Changed directory to file')
653
self.assertBasisTreeKind('file', tree, 'a-file-id')
657
tree.commit('file to directory')
658
self.assertBasisTreeKind('directory', tree, 'a-file-id')
660
555
def test_commit_unversioned_specified(self):
661
556
"""Commit should raise if specified files isn't in basis or worktree"""
662
557
tree = self.make_branch_and_tree('.')
663
self.assertRaises(errors.PathsNotVersionedError, tree.commit,
558
self.assertRaises(errors.PathsNotVersionedError, tree.commit,
664
559
'message', specific_files=['bogus'])
666
561
class Callback(object):
668
563
def __init__(self, message, testcase):
669
564
self.called = False
670
565
self.message = message
708
603
cb = self.Callback(u'commit 2', self)
709
604
repository = tree.branch.repository
710
605
# simulate network failure
711
def raise_(self, arg, arg2, arg3=None, arg4=None):
606
def raise_(self, arg, arg2):
712
607
raise errors.NoSuchFile('foo')
713
608
repository.add_inventory = raise_
714
repository.add_inventory_by_delta = raise_
715
609
self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
716
610
self.assertFalse(cb.called)
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))
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'])
738
self.build_tree(['a/c/d/'])
740
tree.rename_one('a/z/x', 'a/c/d/x')
741
tree.commit('test', specific_files=['a/z/y'])
743
def test_commit_no_author(self):
744
"""The default kwarg author in MutableTree.commit should not add
745
the 'author' revision property.
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)
753
def test_commit_author(self):
754
"""Passing a non-empty author kwarg to MutableTree.commit should add
755
the 'author' revision property.
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)
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)
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)
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>")
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>'])
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'))