~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
import os
18
18
import stat
 
19
from StringIO import StringIO
19
20
import sys
20
21
 
21
22
from bzrlib import (
22
23
    errors,
23
24
    generate_ids,
 
25
    osutils,
 
26
    progress,
 
27
    revision as _mod_revision,
24
28
    symbol_versioning,
25
29
    tests,
26
30
    urlutils,
27
31
    )
28
32
from bzrlib.bzrdir import BzrDir
29
33
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
30
 
                              UnversionedParent, ParentLoop, DeletingParent,)
 
34
                              UnversionedParent, ParentLoop, DeletingParent,
 
35
                              NonDirectoryParent)
 
36
from bzrlib.diff import show_diff_trees
31
37
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
32
38
                           ReusingTransform, CantMoveRoot, 
33
39
                           PathsNotVersionedError, ExistingLimbo,
34
40
                           ExistingPendingDeletion, ImmortalLimbo,
35
41
                           ImmortalPendingDeletion, LockError)
36
 
from bzrlib.osutils import file_kind, has_symlinks, pathjoin
 
42
from bzrlib.osutils import file_kind, pathjoin
37
43
from bzrlib.merge import Merge3Merger
38
 
from bzrlib.tests import TestCaseInTempDir, TestSkipped, TestCase
 
44
from bzrlib.tests import (
 
45
    CaseInsensitiveFilesystemFeature,
 
46
    HardlinkFeature,
 
47
    SymlinkFeature,
 
48
    TestCase,
 
49
    TestCaseInTempDir,
 
50
    TestSkipped,
 
51
    )
39
52
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
40
53
                              resolve_conflicts, cook_conflicts, 
41
54
                              find_interesting, build_tree, get_backup_name,
42
 
                              change_entry, _FileMover)
43
 
 
 
55
                              change_entry, _FileMover, resolve_checkout,
 
56
                              TransformPreview)
44
57
 
45
58
class TestTreeTransform(tests.TestCaseWithTransport):
46
59
 
79
92
        self.assertRaises(ExistingPendingDeletion, self.get_transform)
80
93
 
81
94
    def test_build(self):
82
 
        transform, root = self.get_transform() 
 
95
        transform, root = self.get_transform()
 
96
        self.wt.lock_tree_write()
 
97
        self.addCleanup(self.wt.unlock)
83
98
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
84
99
        imaginary_id = transform.trans_id_tree_path('imaginary')
85
100
        imaginary_id2 = transform.trans_id_tree_path('imaginary/')
120
135
        transform.finalize()
121
136
        transform.finalize()
122
137
 
 
138
    def test_hardlink(self):
 
139
        self.requireFeature(HardlinkFeature)
 
140
        transform, root = self.get_transform()
 
141
        transform.new_file('file1', root, 'contents')
 
142
        transform.apply()
 
143
        target = self.make_branch_and_tree('target')
 
144
        target_transform = TreeTransform(target)
 
145
        trans_id = target_transform.create_path('file1', target_transform.root)
 
146
        target_transform.create_hardlink(self.wt.abspath('file1'), trans_id)
 
147
        target_transform.apply()
 
148
        self.failUnlessExists('target/file1')
 
149
        source_stat = os.stat(self.wt.abspath('file1'))
 
150
        target_stat = os.stat('target/file1')
 
151
        self.assertEqual(source_stat, target_stat)
 
152
 
123
153
    def test_convenience(self):
124
154
        transform, root = self.get_transform()
 
155
        self.wt.lock_tree_write()
 
156
        self.addCleanup(self.wt.unlock)
125
157
        trans_id = transform.new_file('name', root, 'contents', 
126
158
                                      'my_pretties', True)
127
159
        oz = transform.new_directory('oz', root, 'oz-id')
228
260
        transform3.adjust_path('tip', root_id, tip_id)
229
261
        transform3.apply()
230
262
 
 
263
    def test_conflict_on_case_insensitive(self):
 
264
        tree = self.make_branch_and_tree('tree')
 
265
        # Don't try this at home, kids!
 
266
        # Force the tree to report that it is case sensitive, for conflict
 
267
        # resolution tests
 
268
        tree.case_sensitive = True
 
269
        transform = TreeTransform(tree)
 
270
        self.addCleanup(transform.finalize)
 
271
        transform.new_file('file', transform.root, 'content')
 
272
        transform.new_file('FiLe', transform.root, 'content')
 
273
        result = transform.find_conflicts()
 
274
        self.assertEqual([], result)
 
275
        transform.finalize()
 
276
        # Force the tree to report that it is case insensitive, for conflict
 
277
        # generation tests
 
278
        tree.case_sensitive = False
 
279
        transform = TreeTransform(tree)
 
280
        self.addCleanup(transform.finalize)
 
281
        transform.new_file('file', transform.root, 'content')
 
282
        transform.new_file('FiLe', transform.root, 'content')
 
283
        result = transform.find_conflicts()
 
284
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
 
285
 
 
286
    def test_conflict_on_case_insensitive_existing(self):
 
287
        tree = self.make_branch_and_tree('tree')
 
288
        self.build_tree(['tree/FiLe'])
 
289
        # Don't try this at home, kids!
 
290
        # Force the tree to report that it is case sensitive, for conflict
 
291
        # resolution tests
 
292
        tree.case_sensitive = True
 
293
        transform = TreeTransform(tree)
 
294
        self.addCleanup(transform.finalize)
 
295
        transform.new_file('file', transform.root, 'content')
 
296
        result = transform.find_conflicts()
 
297
        self.assertEqual([], result)
 
298
        transform.finalize()
 
299
        # Force the tree to report that it is case insensitive, for conflict
 
300
        # generation tests
 
301
        tree.case_sensitive = False
 
302
        transform = TreeTransform(tree)
 
303
        self.addCleanup(transform.finalize)
 
304
        transform.new_file('file', transform.root, 'content')
 
305
        result = transform.find_conflicts()
 
306
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
 
307
 
 
308
    def test_resolve_case_insensitive_conflict(self):
 
309
        tree = self.make_branch_and_tree('tree')
 
310
        # Don't try this at home, kids!
 
311
        # Force the tree to report that it is case insensitive, for conflict
 
312
        # resolution tests
 
313
        tree.case_sensitive = False
 
314
        transform = TreeTransform(tree)
 
315
        self.addCleanup(transform.finalize)
 
316
        transform.new_file('file', transform.root, 'content')
 
317
        transform.new_file('FiLe', transform.root, 'content')
 
318
        resolve_conflicts(transform)
 
319
        transform.apply()
 
320
        self.failUnlessExists('tree/file')
 
321
        self.failUnlessExists('tree/FiLe.moved')
 
322
 
 
323
    def test_resolve_checkout_case_conflict(self):
 
324
        tree = self.make_branch_and_tree('tree')
 
325
        # Don't try this at home, kids!
 
326
        # Force the tree to report that it is case insensitive, for conflict
 
327
        # resolution tests
 
328
        tree.case_sensitive = False
 
329
        transform = TreeTransform(tree)
 
330
        self.addCleanup(transform.finalize)
 
331
        transform.new_file('file', transform.root, 'content')
 
332
        transform.new_file('FiLe', transform.root, 'content')
 
333
        resolve_conflicts(transform,
 
334
                          pass_func=lambda t, c: resolve_checkout(t, c, []))
 
335
        transform.apply()
 
336
        self.failUnlessExists('tree/file')
 
337
        self.failUnlessExists('tree/FiLe.moved')
 
338
 
 
339
    def test_apply_case_conflict(self):
 
340
        """Ensure that a transform with case conflicts can always be applied"""
 
341
        tree = self.make_branch_and_tree('tree')
 
342
        transform = TreeTransform(tree)
 
343
        self.addCleanup(transform.finalize)
 
344
        transform.new_file('file', transform.root, 'content')
 
345
        transform.new_file('FiLe', transform.root, 'content')
 
346
        dir = transform.new_directory('dir', transform.root)
 
347
        transform.new_file('dirfile', dir, 'content')
 
348
        transform.new_file('dirFiLe', dir, 'content')
 
349
        resolve_conflicts(transform)
 
350
        transform.apply()
 
351
        self.failUnlessExists('tree/file')
 
352
        if not os.path.exists('tree/FiLe.moved'):
 
353
            self.failUnlessExists('tree/FiLe')
 
354
        self.failUnlessExists('tree/dir/dirfile')
 
355
        if not os.path.exists('tree/dir/dirFiLe.moved'):
 
356
            self.failUnlessExists('tree/dir/dirFiLe')
 
357
 
 
358
    def test_case_insensitive_limbo(self):
 
359
        tree = self.make_branch_and_tree('tree')
 
360
        # Don't try this at home, kids!
 
361
        # Force the tree to report that it is case insensitive
 
362
        tree.case_sensitive = False
 
363
        transform = TreeTransform(tree)
 
364
        self.addCleanup(transform.finalize)
 
365
        dir = transform.new_directory('dir', transform.root)
 
366
        first = transform.new_file('file', dir, 'content')
 
367
        second = transform.new_file('FiLe', dir, 'content')
 
368
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
 
369
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
 
370
 
231
371
    def test_add_del(self):
232
372
        start, root = self.get_transform()
233
373
        start.new_directory('a', root, 'a')
385
525
        replace.apply()
386
526
 
387
527
    def test_symlinks(self):
388
 
        if not has_symlinks():
389
 
            raise TestSkipped('Symlinks are not supported on this platform')
 
528
        self.requireFeature(SymlinkFeature)
390
529
        transform,root = self.get_transform()
391
530
        oz_id = transform.new_directory('oz', root, 'oz-id')
392
531
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
406
545
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
407
546
                         'wizard-target')
408
547
 
 
548
    def test_unable_create_symlink(self):
 
549
        def tt_helper():
 
550
            wt = self.make_branch_and_tree('.')
 
551
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
552
            try:
 
553
                tt.new_symlink('foo', tt.root, 'bar')
 
554
                tt.apply()
 
555
            finally:
 
556
                wt.unlock()
 
557
        os_symlink = getattr(os, 'symlink', None)
 
558
        os.symlink = None
 
559
        try:
 
560
            err = self.assertRaises(errors.UnableCreateSymlink, tt_helper)
 
561
            self.assertEquals(
 
562
                "Unable to create symlink 'foo' on this platform",
 
563
                str(err))
 
564
        finally:
 
565
            if os_symlink:
 
566
                os.symlink = os_symlink
 
567
 
409
568
    def get_conflicted(self):
410
569
        create,root = self.get_transform()
411
570
        create.new_file('dorothy', root, 'dorothy', 'dorothy-id')
495
654
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
496
655
                                         ' oz/emeraldcity.  Cancelled move.')
497
656
 
 
657
    def prepare_wrong_parent_kind(self):
 
658
        tt, root = self.get_transform()
 
659
        tt.new_file('parent', root, 'contents', 'parent-id')
 
660
        tt.apply()
 
661
        tt, root = self.get_transform()
 
662
        parent_id = tt.trans_id_file_id('parent-id')
 
663
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
 
664
        return tt
 
665
 
 
666
    def test_find_conflicts_wrong_parent_kind(self):
 
667
        tt = self.prepare_wrong_parent_kind()
 
668
        tt.find_conflicts()
 
669
 
 
670
    def test_resolve_conflicts_wrong_existing_parent_kind(self):
 
671
        tt = self.prepare_wrong_parent_kind()
 
672
        raw_conflicts = resolve_conflicts(tt)
 
673
        self.assertEqual(set([('non-directory parent', 'Created directory',
 
674
                         'new-3')]), raw_conflicts)
 
675
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
 
676
        self.assertEqual([NonDirectoryParent('Created directory', 'parent.new',
 
677
        'parent-id')], cooked_conflicts)
 
678
        tt.apply()
 
679
        self.assertEqual(None, self.wt.path2id('parent'))
 
680
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
 
681
 
 
682
    def test_resolve_conflicts_wrong_new_parent_kind(self):
 
683
        tt, root = self.get_transform()
 
684
        parent_id = tt.new_directory('parent', root, 'parent-id')
 
685
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
 
686
        tt.apply()
 
687
        tt, root = self.get_transform()
 
688
        parent_id = tt.trans_id_file_id('parent-id')
 
689
        tt.delete_contents(parent_id)
 
690
        tt.create_file('contents', parent_id)
 
691
        raw_conflicts = resolve_conflicts(tt)
 
692
        self.assertEqual(set([('non-directory parent', 'Created directory',
 
693
                         'new-3')]), raw_conflicts)
 
694
        tt.apply()
 
695
        self.assertEqual(None, self.wt.path2id('parent'))
 
696
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
 
697
 
 
698
    def test_resolve_conflicts_wrong_parent_kind_unversioned(self):
 
699
        tt, root = self.get_transform()
 
700
        parent_id = tt.new_directory('parent', root)
 
701
        tt.new_file('child,', parent_id, 'contents2')
 
702
        tt.apply()
 
703
        tt, root = self.get_transform()
 
704
        parent_id = tt.trans_id_tree_path('parent')
 
705
        tt.delete_contents(parent_id)
 
706
        tt.create_file('contents', parent_id)
 
707
        resolve_conflicts(tt)
 
708
        tt.apply()
 
709
        self.assertIs(None, self.wt.path2id('parent'))
 
710
        self.assertIs(None, self.wt.path2id('parent.new'))
 
711
 
498
712
    def test_moving_versioned_directories(self):
499
713
        create, root = self.get_transform()
500
714
        kansas = create.new_directory('kansas', root, 'kansas-id')
554
768
        """
555
769
        transform, root = self.get_transform()
556
770
        wt = transform._tree
 
771
        wt.lock_read()
 
772
        self.addCleanup(wt.unlock)
557
773
        transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
558
774
                           True)
559
775
        sac = transform.new_file('set_after_creation', root,
573
789
        transform, root = self.get_transform()
574
790
        transform.new_file('file1', root, 'contents', 'file1-id', True)
575
791
        transform.apply()
 
792
        self.wt.lock_write()
 
793
        self.addCleanup(self.wt.unlock)
576
794
        self.assertTrue(self.wt.is_executable('file1-id'))
577
795
        transform, root = self.get_transform()
578
796
        file1_id = transform.trans_id_tree_file_id('file1-id')
618
836
        transform.apply()
619
837
        transform, root = self.get_transform()
620
838
        try:
621
 
            self.assertEqual([], list(transform._iter_changes()))
 
839
            self.assertEqual([], list(transform.iter_changes()))
622
840
            old = transform.trans_id_tree_file_id('id-1')
623
841
            transform.unversion_file(old)
624
842
            self.assertEqual([('id-1', ('old', None), False, (True, False),
625
843
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
626
 
                (True, True))], list(transform._iter_changes()))
 
844
                (True, True))], list(transform.iter_changes()))
627
845
            transform.new_directory('new', root, 'id-1')
628
846
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
629
847
                ('eert_toor', 'eert_toor'), ('old', 'new'),
630
848
                ('file', 'directory'),
631
 
                (True, False))], list(transform._iter_changes()))
 
849
                (True, False))], list(transform.iter_changes()))
632
850
        finally:
633
851
            transform.finalize()
634
852
 
643
861
            transform.version_file('id-1', old)
644
862
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
645
863
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
646
 
                (False, False))], list(transform._iter_changes()))
 
864
                (False, False))], list(transform.iter_changes()))
647
865
        finally:
648
866
            transform.finalize()
649
867
 
659
877
            old = transform.trans_id_tree_path('old')
660
878
            subdir = transform.trans_id_tree_file_id('subdir-id')
661
879
            new = transform.trans_id_tree_path('new')
662
 
            self.assertEqual([], list(transform._iter_changes()))
 
880
            self.assertEqual([], list(transform.iter_changes()))
663
881
 
664
882
            #content deletion
665
883
            transform.delete_contents(old)
666
884
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
667
885
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
668
 
                (False, False))], list(transform._iter_changes()))
 
886
                (False, False))], list(transform.iter_changes()))
669
887
 
670
888
            #content change
671
889
            transform.create_file('blah', old)
672
890
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
673
891
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
674
 
                (False, False))], list(transform._iter_changes()))
 
892
                (False, False))], list(transform.iter_changes()))
675
893
            transform.cancel_deletion(old)
676
894
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
677
895
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
678
 
                (False, False))], list(transform._iter_changes()))
 
896
                (False, False))], list(transform.iter_changes()))
679
897
            transform.cancel_creation(old)
680
898
 
681
899
            # move file_id to a different file
682
 
            self.assertEqual([], list(transform._iter_changes()))
 
900
            self.assertEqual([], list(transform.iter_changes()))
683
901
            transform.unversion_file(old)
684
902
            transform.version_file('id-1', new)
685
903
            transform.adjust_path('old', root, new)
686
904
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
687
905
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
688
 
                (False, False))], list(transform._iter_changes()))
 
906
                (False, False))], list(transform.iter_changes()))
689
907
            transform.cancel_versioning(new)
690
908
            transform._removed_id = set()
691
909
 
692
910
            #execute bit
693
 
            self.assertEqual([], list(transform._iter_changes()))
 
911
            self.assertEqual([], list(transform.iter_changes()))
694
912
            transform.set_executability(True, old)
695
913
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
696
914
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
697
 
                (False, True))], list(transform._iter_changes()))
 
915
                (False, True))], list(transform.iter_changes()))
698
916
            transform.set_executability(None, old)
699
917
 
700
918
            # filename
701
 
            self.assertEqual([], list(transform._iter_changes()))
 
919
            self.assertEqual([], list(transform.iter_changes()))
702
920
            transform.adjust_path('new', root, old)
703
921
            transform._new_parent = {}
704
922
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
705
923
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
706
 
                (False, False))], list(transform._iter_changes()))
 
924
                (False, False))], list(transform.iter_changes()))
707
925
            transform._new_name = {}
708
926
 
709
927
            # parent directory
710
 
            self.assertEqual([], list(transform._iter_changes()))
 
928
            self.assertEqual([], list(transform.iter_changes()))
711
929
            transform.adjust_path('new', subdir, old)
712
930
            transform._new_name = {}
713
931
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
714
932
                (True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
715
933
                ('file', 'file'), (False, False))],
716
 
                list(transform._iter_changes()))
 
934
                list(transform.iter_changes()))
717
935
            transform._new_path = {}
718
936
 
719
937
        finally:
741
959
                ('id-2', (u'file2', u'file2'), False, (True, True),
742
960
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
743
961
                ('file', 'file'), (False, True))],
744
 
                list(transform._iter_changes()))
 
962
                list(transform.iter_changes()))
745
963
        finally:
746
964
            transform.finalize()
747
965
 
761
979
            transform.adjust_path('flitter', root, floater)
762
980
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
763
981
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
764
 
            (None, None), (False, False))], list(transform._iter_changes()))
 
982
            (None, None), (False, False))], list(transform.iter_changes()))
765
983
        finally:
766
984
            transform.finalize()
767
985
 
776
994
        try:
777
995
            old = transform.trans_id_tree_path('old')
778
996
            subdir = transform.trans_id_tree_file_id('subdir-id')
779
 
            self.assertEqual([], list(transform._iter_changes()))
 
997
            self.assertEqual([], list(transform.iter_changes()))
780
998
            transform.delete_contents(subdir)
781
999
            transform.create_directory(subdir)
782
1000
            transform.set_executability(False, old)
783
1001
            transform.unversion_file(old)
784
1002
            transform.version_file('id-1', old)
785
1003
            transform.adjust_path('old', root, old)
786
 
            self.assertEqual([], list(transform._iter_changes()))
 
1004
            self.assertEqual([], list(transform.iter_changes()))
787
1005
        finally:
788
1006
            transform.finalize()
789
1007
 
959
1177
        self.callDeprecated([txt], change_entry, None, None, None, None, None,
960
1178
            None, None, None)
961
1179
 
 
1180
    def test_case_insensitive_clash(self):
 
1181
        self.requireFeature(CaseInsensitiveFilesystemFeature)
 
1182
        def tt_helper():
 
1183
            wt = self.make_branch_and_tree('.')
 
1184
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
1185
            try:
 
1186
                tt.new_file('foo', tt.root, 'bar')
 
1187
                tt.new_file('Foo', tt.root, 'spam')
 
1188
                # Lie to tt that we've already resolved all conflicts.
 
1189
                tt.apply(no_conflicts=True)
 
1190
            except:
 
1191
                wt.unlock()
 
1192
                raise
 
1193
        err = self.assertRaises(errors.FileExists, tt_helper)
 
1194
        self.assertContainsRe(str(err),
 
1195
            "^File exists: .+/foo")
 
1196
 
 
1197
    def test_two_directories_clash(self):
 
1198
        def tt_helper():
 
1199
            wt = self.make_branch_and_tree('.')
 
1200
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
1201
            try:
 
1202
                foo_1 = tt.new_directory('foo', tt.root)
 
1203
                tt.new_directory('bar', foo_1)
 
1204
                foo_2 = tt.new_directory('foo', tt.root)
 
1205
                tt.new_directory('baz', foo_2)
 
1206
                # Lie to tt that we've already resolved all conflicts.
 
1207
                tt.apply(no_conflicts=True)
 
1208
            except:
 
1209
                wt.unlock()
 
1210
                raise
 
1211
        err = self.assertRaises(errors.FileExists, tt_helper)
 
1212
        self.assertContainsRe(str(err),
 
1213
            "^File exists: .+/foo")
 
1214
 
 
1215
    def test_two_directories_clash_finalize(self):
 
1216
        def tt_helper():
 
1217
            wt = self.make_branch_and_tree('.')
 
1218
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
 
1219
            try:
 
1220
                foo_1 = tt.new_directory('foo', tt.root)
 
1221
                tt.new_directory('bar', foo_1)
 
1222
                foo_2 = tt.new_directory('foo', tt.root)
 
1223
                tt.new_directory('baz', foo_2)
 
1224
                # Lie to tt that we've already resolved all conflicts.
 
1225
                tt.apply(no_conflicts=True)
 
1226
            except:
 
1227
                tt.finalize()
 
1228
                raise
 
1229
        err = self.assertRaises(errors.FileExists, tt_helper)
 
1230
        self.assertContainsRe(str(err),
 
1231
            "^File exists: .+/foo")
 
1232
 
962
1233
 
963
1234
class TransformGroup(object):
 
1235
 
964
1236
    def __init__(self, dirname, root_id):
965
1237
        self.name = dirname
966
1238
        os.mkdir(dirname)
977
1249
 
978
1250
 
979
1251
class TestTransformMerge(TestCaseInTempDir):
 
1252
 
980
1253
    def test_text_merge(self):
981
1254
        root_id = generate_ids.gen_root_id()
982
1255
        base = TransformGroup("base", root_id)
1012
1285
        this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
1013
1286
        this.tt.apply()
1014
1287
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
 
1288
 
1015
1289
        # textual merge
1016
1290
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1017
1291
        # three-way text conflict
1055
1329
        this.wt.revert()
1056
1330
 
1057
1331
    def test_file_merge(self):
1058
 
        if not has_symlinks():
1059
 
            raise TestSkipped('Symlinks are not supported on this platform')
 
1332
        self.requireFeature(SymlinkFeature)
1060
1333
        root_id = generate_ids.gen_root_id()
1061
1334
        base = TransformGroup("BASE", root_id)
1062
1335
        this = TransformGroup("THIS", root_id)
1164
1437
 
1165
1438
class TestBuildTree(tests.TestCaseWithTransport):
1166
1439
 
1167
 
    def test_build_tree(self):
1168
 
        if not has_symlinks():
1169
 
            raise TestSkipped('Test requires symlink support')
 
1440
    def test_build_tree_with_symlinks(self):
 
1441
        self.requireFeature(SymlinkFeature)
1170
1442
        os.mkdir('a')
1171
1443
        a = BzrDir.create_standalone_workingtree('a')
1172
1444
        os.mkdir('a/foo')
1220
1492
 
1221
1493
    def test_symlink_conflict_handling(self):
1222
1494
        """Ensure that when building trees, conflict handling is done"""
1223
 
        if not has_symlinks():
1224
 
            raise TestSkipped('Test requires symlink support')
 
1495
        self.requireFeature(SymlinkFeature)
1225
1496
        source = self.make_branch_and_tree('source')
1226
1497
        os.symlink('foo', 'source/symlink')
1227
1498
        source.add('symlink', 'new-symlink')
1318
1589
        # children of non-root directories should not be renamed
1319
1590
        self.assertEqual(2, transform_result.rename_count)
1320
1591
 
 
1592
    def create_ab_tree(self):
 
1593
        """Create a committed test tree with two files"""
 
1594
        source = self.make_branch_and_tree('source')
 
1595
        self.build_tree_contents([('source/file1', 'A')])
 
1596
        self.build_tree_contents([('source/file2', 'B')])
 
1597
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
 
1598
        source.commit('commit files')
 
1599
        source.lock_write()
 
1600
        self.addCleanup(source.unlock)
 
1601
        return source
 
1602
 
 
1603
    def test_build_tree_accelerator_tree(self):
 
1604
        source = self.create_ab_tree()
 
1605
        self.build_tree_contents([('source/file2', 'C')])
 
1606
        calls = []
 
1607
        real_source_get_file = source.get_file
 
1608
        def get_file(file_id, path=None):
 
1609
            calls.append(file_id)
 
1610
            return real_source_get_file(file_id, path)
 
1611
        source.get_file = get_file
 
1612
        target = self.make_branch_and_tree('target')
 
1613
        revision_tree = source.basis_tree()
 
1614
        revision_tree.lock_read()
 
1615
        self.addCleanup(revision_tree.unlock)
 
1616
        build_tree(revision_tree, target, source)
 
1617
        self.assertEqual(['file1-id'], calls)
 
1618
        target.lock_read()
 
1619
        self.addCleanup(target.unlock)
 
1620
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1621
 
 
1622
    def test_build_tree_accelerator_tree_missing_file(self):
 
1623
        source = self.create_ab_tree()
 
1624
        os.unlink('source/file1')
 
1625
        source.remove(['file2'])
 
1626
        target = self.make_branch_and_tree('target')
 
1627
        revision_tree = source.basis_tree()
 
1628
        revision_tree.lock_read()
 
1629
        self.addCleanup(revision_tree.unlock)
 
1630
        build_tree(revision_tree, target, source)
 
1631
        target.lock_read()
 
1632
        self.addCleanup(target.unlock)
 
1633
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1634
 
 
1635
    def test_build_tree_accelerator_wrong_kind(self):
 
1636
        self.requireFeature(SymlinkFeature)
 
1637
        source = self.make_branch_and_tree('source')
 
1638
        self.build_tree_contents([('source/file1', '')])
 
1639
        self.build_tree_contents([('source/file2', '')])
 
1640
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
 
1641
        source.commit('commit files')
 
1642
        os.unlink('source/file2')
 
1643
        self.build_tree_contents([('source/file2/', 'C')])
 
1644
        os.unlink('source/file1')
 
1645
        os.symlink('file2', 'source/file1')
 
1646
        calls = []
 
1647
        real_source_get_file = source.get_file
 
1648
        def get_file(file_id, path=None):
 
1649
            calls.append(file_id)
 
1650
            return real_source_get_file(file_id, path)
 
1651
        source.get_file = get_file
 
1652
        target = self.make_branch_and_tree('target')
 
1653
        revision_tree = source.basis_tree()
 
1654
        revision_tree.lock_read()
 
1655
        self.addCleanup(revision_tree.unlock)
 
1656
        build_tree(revision_tree, target, source)
 
1657
        self.assertEqual([], calls)
 
1658
        target.lock_read()
 
1659
        self.addCleanup(target.unlock)
 
1660
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1661
 
 
1662
    def test_build_tree_hardlink(self):
 
1663
        self.requireFeature(HardlinkFeature)
 
1664
        source = self.create_ab_tree()
 
1665
        target = self.make_branch_and_tree('target')
 
1666
        revision_tree = source.basis_tree()
 
1667
        revision_tree.lock_read()
 
1668
        self.addCleanup(revision_tree.unlock)
 
1669
        build_tree(revision_tree, target, source, hardlink=True)
 
1670
        target.lock_read()
 
1671
        self.addCleanup(target.unlock)
 
1672
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1673
        source_stat = os.stat('source/file1')
 
1674
        target_stat = os.stat('target/file1')
 
1675
        self.assertEqual(source_stat, target_stat)
 
1676
 
 
1677
        # Explicitly disallowing hardlinks should prevent them.
 
1678
        target2 = self.make_branch_and_tree('target2')
 
1679
        build_tree(revision_tree, target2, source, hardlink=False)
 
1680
        target2.lock_read()
 
1681
        self.addCleanup(target2.unlock)
 
1682
        self.assertEqual([], list(target2.iter_changes(revision_tree)))
 
1683
        source_stat = os.stat('source/file1')
 
1684
        target2_stat = os.stat('target2/file1')
 
1685
        self.assertNotEqual(source_stat, target2_stat)
 
1686
 
 
1687
    def test_build_tree_accelerator_tree_moved(self):
 
1688
        source = self.make_branch_and_tree('source')
 
1689
        self.build_tree_contents([('source/file1', 'A')])
 
1690
        source.add(['file1'], ['file1-id'])
 
1691
        source.commit('commit files')
 
1692
        source.rename_one('file1', 'file2')
 
1693
        source.lock_read()
 
1694
        self.addCleanup(source.unlock)
 
1695
        target = self.make_branch_and_tree('target')
 
1696
        revision_tree = source.basis_tree()
 
1697
        revision_tree.lock_read()
 
1698
        self.addCleanup(revision_tree.unlock)
 
1699
        build_tree(revision_tree, target, source)
 
1700
        target.lock_read()
 
1701
        self.addCleanup(target.unlock)
 
1702
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1703
 
 
1704
    def test_build_tree_hardlinks_preserve_execute(self):
 
1705
        self.requireFeature(HardlinkFeature)
 
1706
        source = self.create_ab_tree()
 
1707
        tt = TreeTransform(source)
 
1708
        trans_id = tt.trans_id_tree_file_id('file1-id')
 
1709
        tt.set_executability(True, trans_id)
 
1710
        tt.apply()
 
1711
        self.assertTrue(source.is_executable('file1-id'))
 
1712
        target = self.make_branch_and_tree('target')
 
1713
        revision_tree = source.basis_tree()
 
1714
        revision_tree.lock_read()
 
1715
        self.addCleanup(revision_tree.unlock)
 
1716
        build_tree(revision_tree, target, source, hardlink=True)
 
1717
        target.lock_read()
 
1718
        self.addCleanup(target.unlock)
 
1719
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1720
        self.assertTrue(source.is_executable('file1-id'))
 
1721
 
1321
1722
 
1322
1723
class MockTransform(object):
1323
1724
 
1336
1737
        object.__init__(self)
1337
1738
        self.name = "name"
1338
1739
 
 
1740
 
1339
1741
class TestGetBackupName(TestCase):
1340
1742
    def test_get_backup_name(self):
1341
1743
        tt = MockTransform()
1395
1797
        mover.rename('c/e', 'c/d')
1396
1798
        try:
1397
1799
            mover.rename('a', 'c')
1398
 
        except OSError, e:
 
1800
        except errors.FileExists, e:
1399
1801
            mover.rollback()
1400
1802
        self.failUnlessExists('a')
1401
1803
        self.failUnlessExists('c/d')
1468
1870
                          _mover=self.ExceptionFileMover(bad_target='d'))
1469
1871
        self.failUnlessExists('a')
1470
1872
        self.failUnlessExists('a/b')
 
1873
 
 
1874
    def test_resolve_no_parent(self):
 
1875
        wt = self.make_branch_and_tree('.')
 
1876
        tt = TreeTransform(wt)
 
1877
        self.addCleanup(tt.finalize)
 
1878
        parent = tt.trans_id_file_id('parent-id')
 
1879
        tt.new_file('file', parent, 'Contents')
 
1880
        resolve_conflicts(tt)
 
1881
 
 
1882
 
 
1883
class TestTransformPreview(tests.TestCaseWithTransport):
 
1884
 
 
1885
    def create_tree(self):
 
1886
        tree = self.make_branch_and_tree('.')
 
1887
        self.build_tree_contents([('a', 'content 1')])
 
1888
        tree.add('a', 'a-id')
 
1889
        tree.commit('rev1', rev_id='rev1')
 
1890
        return tree.branch.repository.revision_tree('rev1')
 
1891
 
 
1892
    def get_empty_preview(self):
 
1893
        repository = self.make_repository('repo')
 
1894
        tree = repository.revision_tree(_mod_revision.NULL_REVISION)
 
1895
        preview = TransformPreview(tree)
 
1896
        self.addCleanup(preview.finalize)
 
1897
        return preview
 
1898
 
 
1899
    def test_transform_preview(self):
 
1900
        revision_tree = self.create_tree()
 
1901
        preview = TransformPreview(revision_tree)
 
1902
        self.addCleanup(preview.finalize)
 
1903
 
 
1904
    def test_transform_preview_tree(self):
 
1905
        revision_tree = self.create_tree()
 
1906
        preview = TransformPreview(revision_tree)
 
1907
        self.addCleanup(preview.finalize)
 
1908
        preview.get_preview_tree()
 
1909
 
 
1910
    def test_transform_new_file(self):
 
1911
        revision_tree = self.create_tree()
 
1912
        preview = TransformPreview(revision_tree)
 
1913
        self.addCleanup(preview.finalize)
 
1914
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
 
1915
        preview_tree = preview.get_preview_tree()
 
1916
        self.assertEqual(preview_tree.kind('file2-id'), 'file')
 
1917
        self.assertEqual(
 
1918
            preview_tree.get_file('file2-id').read(), 'content B\n')
 
1919
 
 
1920
    def test_diff_preview_tree(self):
 
1921
        revision_tree = self.create_tree()
 
1922
        preview = TransformPreview(revision_tree)
 
1923
        self.addCleanup(preview.finalize)
 
1924
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
 
1925
        preview_tree = preview.get_preview_tree()
 
1926
        out = StringIO()
 
1927
        show_diff_trees(revision_tree, preview_tree, out)
 
1928
        lines = out.getvalue().splitlines()
 
1929
        self.assertEqual(lines[0], "=== added file 'file2'")
 
1930
        # 3 lines of diff administrivia
 
1931
        self.assertEqual(lines[4], "+content B")
 
1932
 
 
1933
    def test_transform_conflicts(self):
 
1934
        revision_tree = self.create_tree()
 
1935
        preview = TransformPreview(revision_tree)
 
1936
        self.addCleanup(preview.finalize)
 
1937
        preview.new_file('a', preview.root, 'content 2')
 
1938
        resolve_conflicts(preview)
 
1939
        trans_id = preview.trans_id_file_id('a-id')
 
1940
        self.assertEqual('a.moved', preview.final_name(trans_id))
 
1941
 
 
1942
    def get_tree_and_preview_tree(self):
 
1943
        revision_tree = self.create_tree()
 
1944
        preview = TransformPreview(revision_tree)
 
1945
        self.addCleanup(preview.finalize)
 
1946
        a_trans_id = preview.trans_id_file_id('a-id')
 
1947
        preview.delete_contents(a_trans_id)
 
1948
        preview.create_file('b content', a_trans_id)
 
1949
        preview_tree = preview.get_preview_tree()
 
1950
        return revision_tree, preview_tree
 
1951
 
 
1952
    def test_iter_changes(self):
 
1953
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1954
        root = revision_tree.inventory.root.file_id
 
1955
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
 
1956
                          (root, root), ('a', 'a'), ('file', 'file'),
 
1957
                          (False, False))],
 
1958
                          list(preview_tree.iter_changes(revision_tree)))
 
1959
 
 
1960
    def test_wrong_tree_value_error(self):
 
1961
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1962
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
 
1963
                              preview_tree)
 
1964
        self.assertEqual('from_tree must be transform source tree.', str(e))
 
1965
 
 
1966
    def test_include_unchanged_value_error(self):
 
1967
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1968
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
 
1969
                              revision_tree, include_unchanged=True)
 
1970
        self.assertEqual('include_unchanged is not supported', str(e))
 
1971
 
 
1972
    def test_specific_files(self):
 
1973
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1974
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
 
1975
                              revision_tree, specific_files=['pete'])
 
1976
        self.assertEqual('specific_files is not supported', str(e))
 
1977
 
 
1978
    def test_want_unversioned_value_error(self):
 
1979
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1980
        e = self.assertRaises(ValueError, preview_tree.iter_changes,
 
1981
                              revision_tree, want_unversioned=True)
 
1982
        self.assertEqual('want_unversioned is not supported', str(e))
 
1983
 
 
1984
    def test_ignore_extra_trees_no_specific_files(self):
 
1985
        # extra_trees is harmless without specific_files, so we'll silently
 
1986
        # accept it, even though we won't use it.
 
1987
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1988
        preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
 
1989
 
 
1990
    def test_ignore_require_versioned_no_specific_files(self):
 
1991
        # require_versioned is meaningless without specific_files.
 
1992
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1993
        preview_tree.iter_changes(revision_tree, require_versioned=False)
 
1994
 
 
1995
    def test_ignore_pb(self):
 
1996
        # pb could be supported, but TT.iter_changes doesn't support it.
 
1997
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1998
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
 
1999
 
 
2000
    def test_kind(self):
 
2001
        revision_tree = self.create_tree()
 
2002
        preview = TransformPreview(revision_tree)
 
2003
        self.addCleanup(preview.finalize)
 
2004
        preview.new_file('file', preview.root, 'contents', 'file-id')
 
2005
        preview.new_directory('directory', preview.root, 'dir-id')
 
2006
        preview_tree = preview.get_preview_tree()
 
2007
        self.assertEqual('file', preview_tree.kind('file-id'))
 
2008
        self.assertEqual('directory', preview_tree.kind('dir-id'))
 
2009
 
 
2010
    def test_get_file_mtime(self):
 
2011
        preview = self.get_empty_preview()
 
2012
        file_trans_id = preview.new_file('file', preview.root, 'contents',
 
2013
                                         'file-id')
 
2014
        limbo_path = preview._limbo_name(file_trans_id)
 
2015
        preview_tree = preview.get_preview_tree()
 
2016
        self.assertEqual(os.stat(limbo_path).st_mtime,
 
2017
                         preview_tree.get_file_mtime('file-id'))
 
2018
 
 
2019
    def test_get_file(self):
 
2020
        preview = self.get_empty_preview()
 
2021
        preview.new_file('file', preview.root, 'contents', 'file-id')
 
2022
        preview_tree = preview.get_preview_tree()
 
2023
        tree_file = preview_tree.get_file('file-id')
 
2024
        try:
 
2025
            self.assertEqual('contents', tree_file.read())
 
2026
        finally:
 
2027
            tree_file.close()
 
2028
 
 
2029
    def test_get_symlink_target(self):
 
2030
        self.requireFeature(SymlinkFeature)
 
2031
        preview = self.get_empty_preview()
 
2032
        preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
 
2033
        preview_tree = preview.get_preview_tree()
 
2034
        self.assertEqual('target',
 
2035
                         preview_tree.get_symlink_target('symlink-id'))