1
1
# Copyright (C) 2006 Canonical Ltd
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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
27
from bzrlib.bzrdir import BzrDir
20
28
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
21
UnversionedParent, ParentLoop)
29
UnversionedParent, ParentLoop, DeletingParent,)
22
30
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
23
ReusingTransform, CantMoveRoot, NotVersionedError,
24
ExistingLimbo, ImmortalLimbo, LockError)
31
ReusingTransform, CantMoveRoot,
32
PathsNotVersionedError, ExistingLimbo,
33
ImmortalLimbo, LockError)
25
34
from bzrlib.osutils import file_kind, has_symlinks, pathjoin
26
35
from bzrlib.merge import Merge3Merger
27
36
from bzrlib.tests import TestCaseInTempDir, TestSkipped, TestCase
28
37
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
29
38
resolve_conflicts, cook_conflicts,
30
39
find_interesting, build_tree, get_backup_name)
31
import bzrlib.urlutils as urlutils
33
42
class TestTreeTransform(TestCaseInTempDir):
415
428
'dorothy.moved', 'dorothy', None,
417
430
self.assertEqual(cooked_conflicts[1], duplicate_id)
418
missing_parent = MissingParent('Not deleting', 'oz', 'oz-id')
431
missing_parent = MissingParent('Created directory', 'munchkincity',
433
deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
419
434
self.assertEqual(cooked_conflicts[2], missing_parent)
420
unversioned_parent = UnversionedParent('Versioned directory', 'oz',
435
unversioned_parent = UnversionedParent('Versioned directory',
438
unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
422
440
self.assertEqual(cooked_conflicts[3], unversioned_parent)
423
441
parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
424
442
'oz/emeraldcity', 'emerald-id', 'emerald-id')
425
self.assertEqual(cooked_conflicts[4], parent_loop)
426
self.assertEqual(len(cooked_conflicts), 5)
443
self.assertEqual(cooked_conflicts[4], deleted_parent)
444
self.assertEqual(cooked_conflicts[5], unversioned_parent2)
445
self.assertEqual(cooked_conflicts[6], parent_loop)
446
self.assertEqual(len(cooked_conflicts), 7)
429
449
def test_string_conflicts(self):
439
459
self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy. '
440
460
'Unversioned existing file '
441
461
'dorothy.moved.')
442
self.assertEqual(conflicts_s[2], 'Conflict adding files to oz. '
444
self.assertEqual(conflicts_s[3], 'Conflict adding versioned files to '
445
'oz. Versioned directory.')
446
self.assertEqual(conflicts_s[4], 'Conflict moving oz/emeraldcity into'
462
self.assertEqual(conflicts_s[2], 'Conflict adding files to'
463
' munchkincity. Created directory.')
464
self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
465
' versioned, but has versioned'
466
' children. Versioned directory.')
467
self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
468
" is not empty. Not deleting.")
469
self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
470
' versioned, but has versioned'
471
' children. Versioned directory.')
472
self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
447
473
' oz/emeraldcity. Cancelled move.')
449
475
def test_moving_versioned_directories(self):
516
542
self.assertTrue(wt.is_executable('soc'))
517
543
self.assertTrue(wt.is_executable('sac'))
545
def test_preserve_mode(self):
546
"""File mode is preserved when replacing content"""
547
if sys.platform == 'win32':
548
raise TestSkipped('chmod has no effect on win32')
549
transform, root = self.get_transform()
550
transform.new_file('file1', root, 'contents', 'file1-id', True)
552
self.assertTrue(self.wt.is_executable('file1-id'))
553
transform, root = self.get_transform()
554
file1_id = transform.trans_id_tree_file_id('file1-id')
555
transform.delete_contents(file1_id)
556
transform.create_file('contents2', file1_id)
558
self.assertTrue(self.wt.is_executable('file1-id'))
560
def test__set_mode_stats_correctly(self):
561
"""_set_mode stats to determine file mode."""
562
if sys.platform == 'win32':
563
raise TestSkipped('chmod has no effect on win32')
567
def instrumented_stat(path):
568
stat_paths.append(path)
569
return real_stat(path)
571
transform, root = self.get_transform()
573
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
574
file_id='bar-id-1', executable=False)
577
transform, root = self.get_transform()
578
bar1_id = transform.trans_id_tree_path('bar')
579
bar2_id = transform.trans_id_tree_path('bar2')
581
os.stat = instrumented_stat
582
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
587
bar1_abspath = self.wt.abspath('bar')
588
self.assertEqual([bar1_abspath], stat_paths)
520
591
class TransformGroup(object):
521
def __init__(self, dirname):
592
def __init__(self, dirname, root_id):
522
593
self.name = dirname
523
594
os.mkdir(dirname)
524
595
self.wt = BzrDir.create_standalone_workingtree(dirname)
596
self.wt.set_root_id(root_id)
525
597
self.b = self.wt.branch
526
598
self.tt = TreeTransform(self.wt)
527
599
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
728
806
self.assertIs(os.path.isdir('b/foo'), True)
729
807
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
730
808
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
810
def test_file_conflict_handling(self):
811
"""Ensure that when building trees, conflict handling is done"""
812
source = self.make_branch_and_tree('source')
813
target = self.make_branch_and_tree('target')
814
self.build_tree(['source/file', 'target/file'])
815
source.add('file', 'new-file')
816
source.commit('added file')
817
build_tree(source.basis_tree(), target)
818
self.assertEqual([DuplicateEntry('Moved existing file to',
819
'file.moved', 'file', None, 'new-file')],
821
target2 = self.make_branch_and_tree('target2')
822
target_file = file('target2/file', 'wb')
824
source_file = file('source/file', 'rb')
826
target_file.write(source_file.read())
831
build_tree(source.basis_tree(), target2)
832
self.assertEqual([], target2.conflicts())
834
def test_symlink_conflict_handling(self):
835
"""Ensure that when building trees, conflict handling is done"""
836
if not has_symlinks():
837
raise TestSkipped('Test requires symlink support')
838
source = self.make_branch_and_tree('source')
839
os.symlink('foo', 'source/symlink')
840
source.add('symlink', 'new-symlink')
841
source.commit('added file')
842
target = self.make_branch_and_tree('target')
843
os.symlink('bar', 'target/symlink')
844
build_tree(source.basis_tree(), target)
845
self.assertEqual([DuplicateEntry('Moved existing file to',
846
'symlink.moved', 'symlink', None, 'new-symlink')],
848
target = self.make_branch_and_tree('target2')
849
os.symlink('foo', 'target2/symlink')
850
build_tree(source.basis_tree(), target)
851
self.assertEqual([], target.conflicts())
853
def test_directory_conflict_handling(self):
854
"""Ensure that when building trees, conflict handling is done"""
855
source = self.make_branch_and_tree('source')
856
target = self.make_branch_and_tree('target')
857
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
858
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
859
source.commit('added file')
860
build_tree(source.basis_tree(), target)
861
self.assertEqual([], target.conflicts())
862
self.failUnlessExists('target/dir1/file')
864
# Ensure contents are merged
865
target = self.make_branch_and_tree('target2')
866
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
867
build_tree(source.basis_tree(), target)
868
self.assertEqual([], target.conflicts())
869
self.failUnlessExists('target2/dir1/file2')
870
self.failUnlessExists('target2/dir1/file')
872
# Ensure new contents are suppressed for existing branches
873
target = self.make_branch_and_tree('target3')
874
self.make_branch('target3/dir1')
875
self.build_tree(['target3/dir1/file2'])
876
build_tree(source.basis_tree(), target)
877
self.failIfExists('target3/dir1/file')
878
self.failUnlessExists('target3/dir1/file2')
879
self.failUnlessExists('target3/dir1.diverted/file')
880
self.assertEqual([DuplicateEntry('Diverted to',
881
'dir1.diverted', 'dir1', 'new-dir1', None)],
884
target = self.make_branch_and_tree('target4')
885
self.build_tree(['target4/dir1/'])
886
self.make_branch('target4/dir1/file')
887
build_tree(source.basis_tree(), target)
888
self.failUnlessExists('target4/dir1/file')
889
self.assertEqual('directory', file_kind('target4/dir1/file'))
890
self.failUnlessExists('target4/dir1/file.diverted')
891
self.assertEqual([DuplicateEntry('Diverted to',
892
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
895
def test_mixed_conflict_handling(self):
896
"""Ensure that when building trees, conflict handling is done"""
897
source = self.make_branch_and_tree('source')
898
target = self.make_branch_and_tree('target')
899
self.build_tree(['source/name', 'target/name/'])
900
source.add('name', 'new-name')
901
source.commit('added file')
902
build_tree(source.basis_tree(), target)
903
self.assertEqual([DuplicateEntry('Moved existing file to',
904
'name.moved', 'name', None, 'new-name')], target.conflicts())
906
def test_raises_in_populated(self):
907
source = self.make_branch_and_tree('source')
908
self.build_tree(['source/name'])
910
source.commit('added name')
911
target = self.make_branch_and_tree('target')
912
self.build_tree(['target/name'])
914
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
915
build_tree, source.basis_tree(), target)
732
918
class MockTransform(object):
734
920
def has_named_child(self, by_parent, parent_id, name):