387
413
bound = master.sprout('bound')
388
414
wt = bound.open_workingtree()
389
415
wt.branch.set_bound_location(os.path.realpath('master'))
417
orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
390
418
master_branch.lock_write()
420
lockdir._DEFAULT_TIMEOUT_SECONDS = 1
392
421
self.assertRaises(LockContention, wt.commit, 'silly')
423
lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
394
424
master_branch.unlock()
426
def test_commit_bound_merge(self):
427
# see bug #43959; commit of a merge in a bound branch fails to push
428
# the new commit into the master
429
master_branch = self.make_branch('master')
430
bound_tree = self.make_branch_and_tree('bound')
431
bound_tree.branch.bind(master_branch)
433
self.build_tree_contents([('bound/content_file', 'initial contents\n')])
434
bound_tree.add(['content_file'])
435
bound_tree.commit(message='woo!')
437
other_bzrdir = master_branch.bzrdir.sprout('other')
438
other_tree = other_bzrdir.open_workingtree()
440
# do a commit to the the other branch changing the content file so
441
# that our commit after merging will have a merged revision in the
442
# content file history.
443
self.build_tree_contents([('other/content_file', 'change in other\n')])
444
other_tree.commit('change in other')
446
# do a merge into the bound branch from other, and then change the
447
# content file locally to force a new revision (rather than using the
448
# revision from other). This forces extra processing in commit.
449
bound_tree.merge_from_branch(other_tree.branch)
450
self.build_tree_contents([('bound/content_file', 'change in bound\n')])
452
# before #34959 was fixed, this failed with 'revision not present in
453
# weave' when trying to implicitly push from the bound branch to the master
454
bound_tree.commit(message='commit of merge in bound tree')
456
def test_commit_reporting_after_merge(self):
457
# when doing a commit of a merge, the reporter needs to still
458
# be called for each item that is added/removed/deleted.
459
this_tree = self.make_branch_and_tree('this')
460
# we need a bunch of files and dirs, to perform one action on each.
463
'this/dirtoreparent/',
466
'this/filetoreparent',
483
this_tree.commit('create_files')
484
other_dir = this_tree.bzrdir.sprout('other')
485
other_tree = other_dir.open_workingtree()
486
other_tree.lock_write()
487
# perform the needed actions on the files and dirs.
489
other_tree.rename_one('dirtorename', 'renameddir')
490
other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
491
other_tree.rename_one('filetorename', 'renamedfile')
492
other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
493
other_tree.remove(['dirtoremove', 'filetoremove'])
494
self.build_tree_contents([
496
('other/filetomodify', 'new content'),
497
('other/newfile', 'new file content')])
498
other_tree.add('newfile')
499
other_tree.add('newdir/')
500
other_tree.commit('modify all sample files and dirs.')
503
this_tree.merge_from_branch(other_tree.branch)
504
reporter = CapturingReporter()
505
this_tree.commit('do the commit', reporter=reporter)
507
('change', 'unchanged', ''),
508
('change', 'unchanged', 'dirtoleave'),
509
('change', 'unchanged', 'filetoleave'),
510
('change', 'modified', 'filetomodify'),
511
('change', 'added', 'newdir'),
512
('change', 'added', 'newfile'),
513
('renamed', 'renamed', 'dirtorename', 'renameddir'),
514
('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
515
('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
516
('renamed', 'renamed', 'filetorename', 'renamedfile'),
517
('deleted', 'dirtoremove'),
518
('deleted', 'filetoremove'),
522
def test_commit_removals_respects_filespec(self):
523
"""Commit respects the specified_files for removals."""
524
tree = self.make_branch_and_tree('.')
525
self.build_tree(['a', 'b'])
527
tree.commit('added a, b')
528
tree.remove(['a', 'b'])
529
tree.commit('removed a', specific_files='a')
530
basis = tree.basis_tree().inventory
531
self.assertIs(None, basis.path2id('a'))
532
self.assertFalse(basis.path2id('b') is None)
534
def test_commit_saves_1ms_timestamp(self):
535
"""Passing in a timestamp is saved with 1ms resolution"""
536
tree = self.make_branch_and_tree('.')
537
self.build_tree(['a'])
539
tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
542
rev = tree.branch.repository.get_revision('a1')
543
self.assertEqual(1153248633.419, rev.timestamp)
545
def test_commit_has_1ms_resolution(self):
546
"""Allowing commit to generate the timestamp also has 1ms resolution"""
547
tree = self.make_branch_and_tree('.')
548
self.build_tree(['a'])
550
tree.commit('added a', rev_id='a1')
552
rev = tree.branch.repository.get_revision('a1')
553
timestamp = rev.timestamp
554
timestamp_1ms = round(timestamp, 3)
555
self.assertEqual(timestamp_1ms, timestamp)
557
def test_commit_kind_changes(self):
558
if not osutils.has_symlinks():
559
raise tests.TestSkipped('Test requires symlink support')
560
tree = self.make_branch_and_tree('.')
561
os.symlink('target', 'name')
562
tree.add('name', 'a-file-id')
563
tree.commit('Added a symlink')
564
self.assertEqual('symlink', tree.basis_tree().kind('a-file-id'))
567
self.build_tree(['name'])
568
tree.commit('Changed symlink to file')
569
self.assertEqual('file', tree.basis_tree().kind('a-file-id'))
572
os.symlink('target', 'name')
573
tree.commit('file to symlink')
574
self.assertEqual('symlink', tree.basis_tree().kind('a-file-id'))
578
tree.commit('symlink to directory')
579
self.assertEqual('directory', tree.basis_tree().kind('a-file-id'))
582
os.symlink('target', 'name')
583
tree.commit('directory to symlink')
584
self.assertEqual('symlink', tree.basis_tree().kind('a-file-id'))
586
# prepare for directory <-> file tests
589
tree.commit('symlink to directory')
590
self.assertEqual('directory', tree.basis_tree().kind('a-file-id'))
593
self.build_tree(['name'])
594
tree.commit('Changed directory to file')
595
self.assertEqual('file', tree.basis_tree().kind('a-file-id'))
599
tree.commit('file to directory')
600
self.assertEqual('directory', tree.basis_tree().kind('a-file-id'))
602
def test_commit_unversioned_specified(self):
603
"""Commit should raise if specified files isn't in basis or worktree"""
604
tree = self.make_branch_and_tree('.')
605
self.assertRaises(errors.PathsNotVersionedError, tree.commit,
606
'message', specific_files=['bogus'])
608
class Callback(object):
610
def __init__(self, message, testcase):
612
self.message = message
613
self.testcase = testcase
615
def __call__(self, commit_obj):
617
self.testcase.assertTrue(isinstance(commit_obj, Commit))
620
def test_commit_callback(self):
621
"""Commit should invoke a callback to get the message"""
623
tree = self.make_branch_and_tree('.')
627
self.assertTrue(isinstance(e, BzrError))
628
self.assertEqual('The message or message_callback keyword'
629
' parameter is required for commit().', str(e))
631
self.fail('exception not raised')
632
cb = self.Callback(u'commit 1', self)
633
tree.commit(message_callback=cb)
634
self.assertTrue(cb.called)
635
repository = tree.branch.repository
636
message = repository.get_revision(tree.last_revision()).message
637
self.assertEqual('commit 1', message)
639
def test_no_callback_pointless(self):
640
"""Callback should not be invoked for pointless commit"""
641
tree = self.make_branch_and_tree('.')
642
cb = self.Callback(u'commit 2', self)
643
self.assertRaises(PointlessCommit, tree.commit, message_callback=cb,
644
allow_pointless=False)
645
self.assertFalse(cb.called)
647
def test_no_callback_netfailure(self):
648
"""Callback should not be invoked if connectivity fails"""
649
tree = self.make_branch_and_tree('.')
650
cb = self.Callback(u'commit 2', self)
651
repository = tree.branch.repository
652
# simulate network failure
653
def raise_(self, arg, arg2):
654
raise errors.NoSuchFile('foo')
655
repository.add_inventory = raise_
656
self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
657
self.assertFalse(cb.called)