387
430
bound = master.sprout('bound')
388
431
wt = bound.open_workingtree()
389
432
wt.branch.set_bound_location(os.path.realpath('master'))
434
orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
390
435
master_branch.lock_write()
437
lockdir._DEFAULT_TIMEOUT_SECONDS = 1
392
438
self.assertRaises(LockContention, wt.commit, 'silly')
440
lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
394
441
master_branch.unlock()
443
def test_commit_bound_merge(self):
444
# see bug #43959; commit of a merge in a bound branch fails to push
445
# the new commit into the master
446
master_branch = self.make_branch('master')
447
bound_tree = self.make_branch_and_tree('bound')
448
bound_tree.branch.bind(master_branch)
450
self.build_tree_contents([('bound/content_file', 'initial contents\n')])
451
bound_tree.add(['content_file'])
452
bound_tree.commit(message='woo!')
454
other_bzrdir = master_branch.bzrdir.sprout('other')
455
other_tree = other_bzrdir.open_workingtree()
457
# do a commit to the the other branch changing the content file so
458
# that our commit after merging will have a merged revision in the
459
# content file history.
460
self.build_tree_contents([('other/content_file', 'change in other\n')])
461
other_tree.commit('change in other')
463
# do a merge into the bound branch from other, and then change the
464
# content file locally to force a new revision (rather than using the
465
# revision from other). This forces extra processing in commit.
466
bound_tree.merge_from_branch(other_tree.branch)
467
self.build_tree_contents([('bound/content_file', 'change in bound\n')])
469
# before #34959 was fixed, this failed with 'revision not present in
470
# weave' when trying to implicitly push from the bound branch to the master
471
bound_tree.commit(message='commit of merge in bound tree')
473
def test_commit_reporting_after_merge(self):
474
# when doing a commit of a merge, the reporter needs to still
475
# be called for each item that is added/removed/deleted.
476
this_tree = self.make_branch_and_tree('this')
477
# we need a bunch of files and dirs, to perform one action on each.
480
'this/dirtoreparent/',
483
'this/filetoreparent',
500
this_tree.commit('create_files')
501
other_dir = this_tree.bzrdir.sprout('other')
502
other_tree = other_dir.open_workingtree()
503
other_tree.lock_write()
504
# perform the needed actions on the files and dirs.
506
other_tree.rename_one('dirtorename', 'renameddir')
507
other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
508
other_tree.rename_one('filetorename', 'renamedfile')
509
other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
510
other_tree.remove(['dirtoremove', 'filetoremove'])
511
self.build_tree_contents([
513
('other/filetomodify', 'new content'),
514
('other/newfile', 'new file content')])
515
other_tree.add('newfile')
516
other_tree.add('newdir/')
517
other_tree.commit('modify all sample files and dirs.')
520
this_tree.merge_from_branch(other_tree.branch)
521
reporter = CapturingReporter()
522
this_tree.commit('do the commit', reporter=reporter)
524
('change', 'unchanged', ''),
525
('change', 'unchanged', 'dirtoleave'),
526
('change', 'unchanged', 'filetoleave'),
527
('change', 'modified', 'filetomodify'),
528
('change', 'added', 'newdir'),
529
('change', 'added', 'newfile'),
530
('renamed', 'renamed', 'dirtorename', 'renameddir'),
531
('renamed', 'renamed', 'filetorename', 'renamedfile'),
532
('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
533
('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
534
('deleted', 'dirtoremove'),
535
('deleted', 'filetoremove'),
539
def test_commit_removals_respects_filespec(self):
540
"""Commit respects the specified_files for removals."""
541
tree = self.make_branch_and_tree('.')
542
self.build_tree(['a', 'b'])
544
tree.commit('added a, b')
545
tree.remove(['a', 'b'])
546
tree.commit('removed a', specific_files='a')
547
basis = tree.basis_tree()
550
self.assertIs(None, basis.path2id('a'))
551
self.assertFalse(basis.path2id('b') is None)
555
def test_commit_saves_1ms_timestamp(self):
556
"""Passing in a timestamp is saved with 1ms resolution"""
557
tree = self.make_branch_and_tree('.')
558
self.build_tree(['a'])
560
tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
563
rev = tree.branch.repository.get_revision('a1')
564
self.assertEqual(1153248633.419, rev.timestamp)
566
def test_commit_has_1ms_resolution(self):
567
"""Allowing commit to generate the timestamp also has 1ms resolution"""
568
tree = self.make_branch_and_tree('.')
569
self.build_tree(['a'])
571
tree.commit('added a', rev_id='a1')
573
rev = tree.branch.repository.get_revision('a1')
574
timestamp = rev.timestamp
575
timestamp_1ms = round(timestamp, 3)
576
self.assertEqual(timestamp_1ms, timestamp)
578
def assertBasisTreeKind(self, kind, tree, file_id):
579
basis = tree.basis_tree()
582
self.assertEqual(kind, basis.kind(file_id))
586
def test_commit_kind_changes(self):
587
self.requireFeature(SymlinkFeature)
588
tree = self.make_branch_and_tree('.')
589
os.symlink('target', 'name')
590
tree.add('name', 'a-file-id')
591
tree.commit('Added a symlink')
592
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
595
self.build_tree(['name'])
596
tree.commit('Changed symlink to file')
597
self.assertBasisTreeKind('file', tree, 'a-file-id')
600
os.symlink('target', 'name')
601
tree.commit('file to symlink')
602
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
606
tree.commit('symlink to directory')
607
self.assertBasisTreeKind('directory', tree, 'a-file-id')
610
os.symlink('target', 'name')
611
tree.commit('directory to symlink')
612
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
614
# prepare for directory <-> file tests
617
tree.commit('symlink to directory')
618
self.assertBasisTreeKind('directory', tree, 'a-file-id')
621
self.build_tree(['name'])
622
tree.commit('Changed directory to file')
623
self.assertBasisTreeKind('file', tree, 'a-file-id')
627
tree.commit('file to directory')
628
self.assertBasisTreeKind('directory', tree, 'a-file-id')
630
def test_commit_unversioned_specified(self):
631
"""Commit should raise if specified files isn't in basis or worktree"""
632
tree = self.make_branch_and_tree('.')
633
self.assertRaises(errors.PathsNotVersionedError, tree.commit,
634
'message', specific_files=['bogus'])
636
class Callback(object):
638
def __init__(self, message, testcase):
640
self.message = message
641
self.testcase = testcase
643
def __call__(self, commit_obj):
645
self.testcase.assertTrue(isinstance(commit_obj, Commit))
648
def test_commit_callback(self):
649
"""Commit should invoke a callback to get the message"""
651
tree = self.make_branch_and_tree('.')
655
self.assertTrue(isinstance(e, BzrError))
656
self.assertEqual('The message or message_callback keyword'
657
' parameter is required for commit().', str(e))
659
self.fail('exception not raised')
660
cb = self.Callback(u'commit 1', self)
661
tree.commit(message_callback=cb)
662
self.assertTrue(cb.called)
663
repository = tree.branch.repository
664
message = repository.get_revision(tree.last_revision()).message
665
self.assertEqual('commit 1', message)
667
def test_no_callback_pointless(self):
668
"""Callback should not be invoked for pointless commit"""
669
tree = self.make_branch_and_tree('.')
670
cb = self.Callback(u'commit 2', self)
671
self.assertRaises(PointlessCommit, tree.commit, message_callback=cb,
672
allow_pointless=False)
673
self.assertFalse(cb.called)
675
def test_no_callback_netfailure(self):
676
"""Callback should not be invoked if connectivity fails"""
677
tree = self.make_branch_and_tree('.')
678
cb = self.Callback(u'commit 2', self)
679
repository = tree.branch.repository
680
# simulate network failure
681
def raise_(self, arg, arg2):
682
raise errors.NoSuchFile('foo')
683
repository.add_inventory = raise_
684
self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
685
self.assertFalse(cb.called)
687
def test_selected_file_merge_commit(self):
688
"""Ensure the correct error is raised"""
689
tree = self.make_branch_and_tree('foo')
690
# pending merge would turn into a left parent
691
tree.commit('commit 1')
692
tree.add_parent_tree_id('example')
693
self.build_tree(['foo/bar', 'foo/baz'])
694
tree.add(['bar', 'baz'])
695
err = self.assertRaises(errors.CannotCommitSelectedFileMerge,
696
tree.commit, 'commit 2', specific_files=['bar', 'baz'])
697
self.assertEqual(['bar', 'baz'], err.files)
698
self.assertEqual('Selected-file commit of merges is not supported'
699
' yet: files bar, baz', str(err))
701
def test_commit_ordering(self):
702
"""Test of corner-case commit ordering error"""
703
tree = self.make_branch_and_tree('.')
704
self.build_tree(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
705
tree.add(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
707
self.build_tree(['a/c/d/'])
709
tree.rename_one('a/z/x', 'a/c/d/x')
710
tree.commit('test', specific_files=['a/z/y'])
712
def test_commit_no_author(self):
713
"""The default kwarg author in MutableTree.commit should not add
714
the 'author' revision property.
716
tree = self.make_branch_and_tree('foo')
717
rev_id = tree.commit('commit 1')
718
rev = tree.branch.repository.get_revision(rev_id)
719
self.assertFalse('author' in rev.properties)
721
def test_commit_author(self):
722
"""Passing a non-empty author kwarg to MutableTree.commit should add
723
the 'author' revision property.
725
tree = self.make_branch_and_tree('foo')
726
rev_id = tree.commit('commit 1', author='John Doe <jdoe@example.com>')
727
rev = tree.branch.repository.get_revision(rev_id)
728
self.assertEqual('John Doe <jdoe@example.com>',
729
rev.properties['author'])