~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Robert Collins
  • Date: 2007-04-30 05:13:58 UTC
  • mfrom: (2470 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2471.
  • Revision ID: robertc@robertcollins.net-20070430051358-8cp7kvp1q0tqhxx0
Merge Johns fix for bug 110399.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2006 Canonical Ltd
2
 
 
 
2
#
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.
7
 
 
 
7
#
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.
12
 
 
 
12
#
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
16
16
 
17
17
import os
 
18
import stat
 
19
import sys
18
20
 
 
21
from bzrlib import (
 
22
    errors,
 
23
    generate_ids,
 
24
    symbol_versioning,
 
25
    tests,
 
26
    urlutils,
 
27
    )
19
28
from bzrlib.bzrdir import BzrDir
20
29
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
21
 
                              UnversionedParent, ParentLoop)
 
30
                              UnversionedParent, ParentLoop, DeletingParent,)
22
31
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
23
 
                           ReusingTransform, CantMoveRoot, NotVersionedError,
24
 
                           ExistingLimbo, ImmortalLimbo, LockError)
 
32
                           ReusingTransform, CantMoveRoot, 
 
33
                           PathsNotVersionedError, ExistingLimbo,
 
34
                           ImmortalLimbo, LockError)
25
35
from bzrlib.osutils import file_kind, has_symlinks, pathjoin
26
36
from bzrlib.merge import Merge3Merger
27
37
from bzrlib.tests import TestCaseInTempDir, TestSkipped, TestCase
29
39
                              resolve_conflicts, cook_conflicts, 
30
40
                              find_interesting, build_tree, get_backup_name)
31
41
 
32
 
class TestTreeTransform(TestCaseInTempDir):
 
42
 
 
43
class TestTreeTransform(tests.TestCaseWithTransport):
 
44
 
33
45
    def setUp(self):
34
46
        super(TestTreeTransform, self).setUp()
35
 
        self.wt = BzrDir.create_standalone_workingtree('.')
 
47
        self.wt = self.make_branch_and_tree('.', format='dirstate-with-subtree')
36
48
        os.chdir('..')
37
49
 
38
50
    def get_transform(self):
39
51
        transform = TreeTransform(self.wt)
40
52
        #self.addCleanup(transform.finalize)
41
 
        return transform, transform.trans_id_tree_file_id(self.wt.get_root_id())
 
53
        return transform, transform.root
42
54
 
43
55
    def test_existing_limbo(self):
44
 
        limbo_name = self.wt._control_files.controlfilename('limbo')
 
56
        limbo_name = urlutils.local_path_from_url(
 
57
            self.wt._control_files.controlfilename('limbo'))
45
58
        transform, root = self.get_transform()
46
59
        os.mkdir(pathjoin(limbo_name, 'hehe'))
47
60
        self.assertRaises(ImmortalLimbo, transform.apply)
57
70
        transform, root = self.get_transform() 
58
71
        self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
59
72
        imaginary_id = transform.trans_id_tree_path('imaginary')
 
73
        imaginary_id2 = transform.trans_id_tree_path('imaginary/')
 
74
        self.assertEqual(imaginary_id, imaginary_id2)
60
75
        self.assertEqual(transform.get_tree_parent(imaginary_id), root)
61
76
        self.assertEqual(transform.final_kind(root), 'directory')
62
77
        self.assertEqual(transform.final_file_id(root), self.wt.get_root_id())
112
127
        self.assertEqual(self.wt.path2id('oz/dorothy'), 'dorothy-id')
113
128
        self.assertEqual(self.wt.path2id('oz/dorothy/toto'), 'toto-id')
114
129
 
115
 
        self.assertEqual('toto-contents', 
 
130
        self.assertEqual('toto-contents',
116
131
                         self.wt.get_file_byname('oz/dorothy/toto').read())
117
132
        self.assertIs(self.wt.is_executable('toto-id'), False)
118
133
 
 
134
    def test_tree_reference(self):
 
135
        transform, root = self.get_transform()
 
136
        tree = transform._tree
 
137
        trans_id = transform.new_directory('reference', root, 'subtree-id')
 
138
        transform.set_tree_reference('subtree-revision', trans_id)
 
139
        transform.apply()
 
140
        tree.lock_read()
 
141
        self.addCleanup(tree.unlock)
 
142
        self.assertEqual('subtree-revision',
 
143
                         tree.inventory['subtree-id'].reference_revision)
 
144
 
119
145
    def test_conflicts(self):
120
146
        transform, root = self.get_transform()
121
147
        trans_id = transform.new_file('name', root, 'contents', 
185
211
        transform3.delete_contents(oz_id)
186
212
        self.assertEqual(transform3.find_conflicts(), 
187
213
                         [('missing parent', oz_id)])
188
 
        root_id = transform3.trans_id_tree_file_id('TREE_ROOT')
 
214
        root_id = transform3.root
189
215
        tip_id = transform3.trans_id_tree_file_id('tip-id')
190
216
        transform3.adjust_path('tip', root_id, tip_id)
191
217
        transform3.apply()
217
243
    def test_name_invariants(self):
218
244
        create_tree, root = self.get_transform()
219
245
        # prepare tree
220
 
        root = create_tree.trans_id_tree_file_id('TREE_ROOT')
 
246
        root = create_tree.root
221
247
        create_tree.new_file('name1', root, 'hello1', 'name1')
222
248
        create_tree.new_file('name2', root, 'hello2', 'name2')
223
249
        ddir = create_tree.new_directory('dying_directory', root, 'ddir')
227
253
        create_tree.apply()
228
254
 
229
255
        mangle_tree,root = self.get_transform()
230
 
        root = mangle_tree.trans_id_tree_file_id('TREE_ROOT')
 
256
        root = mangle_tree.root
231
257
        #swap names
232
258
        name1 = mangle_tree.trans_id_tree_file_id('name1')
233
259
        name2 = mangle_tree.trans_id_tree_file_id('name2')
312
338
    def test_move_dangling_ie(self):
313
339
        create_tree, root = self.get_transform()
314
340
        # prepare tree
315
 
        root = create_tree.trans_id_tree_file_id('TREE_ROOT')
 
341
        root = create_tree.root
316
342
        create_tree.new_file('name1', root, 'hello1', 'name1')
317
343
        create_tree.apply()
318
344
        delete_contents, root = self.get_transform()
328
354
    def test_replace_dangling_ie(self):
329
355
        create_tree, root = self.get_transform()
330
356
        # prepare tree
331
 
        root = create_tree.trans_id_tree_file_id('TREE_ROOT')
 
357
        root = create_tree.root
332
358
        create_tree.new_file('name1', root, 'hello1', 'name1')
333
359
        create_tree.apply()
334
360
        delete_contents = TreeTransform(self.wt)
381
407
                                         'dorothy-id')
382
408
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
383
409
        oz = conflicts.trans_id_tree_file_id('oz-id')
384
 
        # set up missing, unversioned parent
 
410
        # set up DeletedParent parent conflict
385
411
        conflicts.delete_versioned(oz)
386
412
        emerald = conflicts.trans_id_tree_file_id('emerald-id')
 
413
        # set up MissingParent conflict
 
414
        munchkincity = conflicts.trans_id_file_id('munchkincity-id')
 
415
        conflicts.adjust_path('munchkincity', root, munchkincity)
 
416
        conflicts.new_directory('auntem', munchkincity, 'auntem-id')
387
417
        # set up parent loop
388
418
        conflicts.adjust_path('emeraldcity', emerald, emerald)
389
419
        return conflicts, emerald, oz, old_dorothy, new_dorothy
410
440
                                   'dorothy.moved', 'dorothy', None,
411
441
                                   'dorothy-id')
412
442
        self.assertEqual(cooked_conflicts[1], duplicate_id)
413
 
        missing_parent = MissingParent('Not deleting', 'oz', 'oz-id')
 
443
        missing_parent = MissingParent('Created directory', 'munchkincity',
 
444
                                       'munchkincity-id')
 
445
        deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
414
446
        self.assertEqual(cooked_conflicts[2], missing_parent)
415
 
        unversioned_parent = UnversionedParent('Versioned directory', 'oz',
 
447
        unversioned_parent = UnversionedParent('Versioned directory',
 
448
                                               'munchkincity',
 
449
                                               'munchkincity-id')
 
450
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
416
451
                                               'oz-id')
417
452
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
418
453
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
419
454
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
420
 
        self.assertEqual(cooked_conflicts[4], parent_loop)
421
 
        self.assertEqual(len(cooked_conflicts), 5)
 
455
        self.assertEqual(cooked_conflicts[4], deleted_parent)
 
456
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
 
457
        self.assertEqual(cooked_conflicts[6], parent_loop)
 
458
        self.assertEqual(len(cooked_conflicts), 7)
422
459
        tt.finalize()
423
460
 
424
461
    def test_string_conflicts(self):
434
471
        self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy.  '
435
472
                                         'Unversioned existing file '
436
473
                                         'dorothy.moved.')
437
 
        self.assertEqual(conflicts_s[2], 'Conflict adding files to oz.  '
438
 
                                         'Not deleting.')
439
 
        self.assertEqual(conflicts_s[3], 'Conflict adding versioned files to '
440
 
                                         'oz.  Versioned directory.')
441
 
        self.assertEqual(conflicts_s[4], 'Conflict moving oz/emeraldcity into'
 
474
        self.assertEqual(conflicts_s[2], 'Conflict adding files to'
 
475
                                         ' munchkincity.  Created directory.')
 
476
        self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
 
477
                                         ' versioned, but has versioned'
 
478
                                         ' children.  Versioned directory.')
 
479
        self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
 
480
                                         " is not empty.  Not deleting.")
 
481
        self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
 
482
                                         ' versioned, but has versioned'
 
483
                                         ' children.  Versioned directory.')
 
484
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
442
485
                                         ' oz/emeraldcity.  Cancelled move.')
443
486
 
444
487
    def test_moving_versioned_directories(self):
485
528
        create.new_file('vfile', root, 'myfile-text', 'myfile-id')
486
529
        create.new_file('uvfile', root, 'othertext')
487
530
        create.apply()
488
 
        self.assertEqual(find_interesting(wt, wt, ['vfile']),
489
 
                         set(['myfile-id']))
490
 
        self.assertRaises(NotVersionedError, find_interesting, wt, wt,
491
 
                          ['uvfile'])
492
 
 
 
531
        result = self.applyDeprecated(symbol_versioning.zero_fifteen,
 
532
            find_interesting, wt, wt, ['vfile'])
 
533
        self.assertEqual(result, set(['myfile-id']))
 
534
 
 
535
    def test_set_executability_order(self):
 
536
        """Ensure that executability behaves the same, no matter what order.
 
537
        
 
538
        - create file and set executability simultaneously
 
539
        - create file and set executability afterward
 
540
        - unsetting the executability of a file whose executability has not been
 
541
        declared should throw an exception (this may happen when a
 
542
        merge attempts to create a file with a duplicate ID)
 
543
        """
 
544
        transform, root = self.get_transform()
 
545
        wt = transform._tree
 
546
        transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
 
547
                           True)
 
548
        sac = transform.new_file('set_after_creation', root, 'Set after creation', 'sac')
 
549
        transform.set_executability(True, sac)
 
550
        uws = transform.new_file('unset_without_set', root, 'Unset badly', 'uws')
 
551
        self.assertRaises(KeyError, transform.set_executability, None, uws)
 
552
        transform.apply()
 
553
        self.assertTrue(wt.is_executable('soc'))
 
554
        self.assertTrue(wt.is_executable('sac'))
 
555
 
 
556
    def test_preserve_mode(self):
 
557
        """File mode is preserved when replacing content"""
 
558
        if sys.platform == 'win32':
 
559
            raise TestSkipped('chmod has no effect on win32')
 
560
        transform, root = self.get_transform()
 
561
        transform.new_file('file1', root, 'contents', 'file1-id', True)
 
562
        transform.apply()
 
563
        self.assertTrue(self.wt.is_executable('file1-id'))
 
564
        transform, root = self.get_transform()
 
565
        file1_id = transform.trans_id_tree_file_id('file1-id')
 
566
        transform.delete_contents(file1_id)
 
567
        transform.create_file('contents2', file1_id)
 
568
        transform.apply()
 
569
        self.assertTrue(self.wt.is_executable('file1-id'))
 
570
 
 
571
    def test__set_mode_stats_correctly(self):
 
572
        """_set_mode stats to determine file mode."""
 
573
        if sys.platform == 'win32':
 
574
            raise TestSkipped('chmod has no effect on win32')
 
575
 
 
576
        stat_paths = []
 
577
        real_stat = os.stat
 
578
        def instrumented_stat(path):
 
579
            stat_paths.append(path)
 
580
            return real_stat(path)
 
581
 
 
582
        transform, root = self.get_transform()
 
583
 
 
584
        bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
 
585
                                     file_id='bar-id-1', executable=False)
 
586
        transform.apply()
 
587
 
 
588
        transform, root = self.get_transform()
 
589
        bar1_id = transform.trans_id_tree_path('bar')
 
590
        bar2_id = transform.trans_id_tree_path('bar2')
 
591
        try:
 
592
            os.stat = instrumented_stat
 
593
            transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
 
594
        finally:
 
595
            os.stat = real_stat
 
596
            transform.finalize()
 
597
 
 
598
        bar1_abspath = self.wt.abspath('bar')
 
599
        self.assertEqual([bar1_abspath], stat_paths)
 
600
 
 
601
    def test_iter_changes(self):
 
602
        self.wt.set_root_id('eert_toor')
 
603
        transform, root = self.get_transform()
 
604
        transform.new_file('old', root, 'blah', 'id-1', True)
 
605
        transform.apply()
 
606
        transform, root = self.get_transform()
 
607
        try:
 
608
            self.assertEqual([], list(transform._iter_changes()))
 
609
            old = transform.trans_id_tree_file_id('id-1')
 
610
            transform.unversion_file(old)
 
611
            self.assertEqual([('id-1', ('old', None), False, (True, False),
 
612
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
613
                (True, True))], list(transform._iter_changes()))
 
614
            transform.new_directory('new', root, 'id-1')
 
615
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
 
616
                ('eert_toor', 'eert_toor'), ('old', 'new'),
 
617
                ('file', 'directory'),
 
618
                (True, False))], list(transform._iter_changes()))
 
619
        finally:
 
620
            transform.finalize()
 
621
 
 
622
    def test_iter_changes_new(self):
 
623
        self.wt.set_root_id('eert_toor')
 
624
        transform, root = self.get_transform()
 
625
        transform.new_file('old', root, 'blah')
 
626
        transform.apply()
 
627
        transform, root = self.get_transform()
 
628
        try:
 
629
            old = transform.trans_id_tree_path('old')
 
630
            transform.version_file('id-1', old)
 
631
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
 
632
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
633
                (False, False))], list(transform._iter_changes()))
 
634
        finally:
 
635
            transform.finalize()
 
636
 
 
637
    def test_iter_changes_modifications(self):
 
638
        self.wt.set_root_id('eert_toor')
 
639
        transform, root = self.get_transform()
 
640
        transform.new_file('old', root, 'blah', 'id-1')
 
641
        transform.new_file('new', root, 'blah')
 
642
        transform.new_directory('subdir', root, 'subdir-id')
 
643
        transform.apply()
 
644
        transform, root = self.get_transform()
 
645
        try:
 
646
            old = transform.trans_id_tree_path('old')
 
647
            subdir = transform.trans_id_tree_file_id('subdir-id')
 
648
            new = transform.trans_id_tree_path('new')
 
649
            self.assertEqual([], list(transform._iter_changes()))
 
650
 
 
651
            #content deletion
 
652
            transform.delete_contents(old)
 
653
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
 
654
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
 
655
                (False, False))], list(transform._iter_changes()))
 
656
 
 
657
            #content change
 
658
            transform.create_file('blah', old)
 
659
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
 
660
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
661
                (False, False))], list(transform._iter_changes()))
 
662
            transform.cancel_deletion(old)
 
663
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
 
664
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
665
                (False, False))], list(transform._iter_changes()))
 
666
            transform.cancel_creation(old)
 
667
 
 
668
            # move file_id to a different file
 
669
            self.assertEqual([], list(transform._iter_changes()))
 
670
            transform.unversion_file(old)
 
671
            transform.version_file('id-1', new)
 
672
            transform.adjust_path('old', root, new)
 
673
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
 
674
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
675
                (False, False))], list(transform._iter_changes()))
 
676
            transform.cancel_versioning(new)
 
677
            transform._removed_id = set()
 
678
 
 
679
            #execute bit
 
680
            self.assertEqual([], list(transform._iter_changes()))
 
681
            transform.set_executability(True, old)
 
682
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
 
683
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
 
684
                (False, True))], list(transform._iter_changes()))
 
685
            transform.set_executability(None, old)
 
686
 
 
687
            # filename
 
688
            self.assertEqual([], list(transform._iter_changes()))
 
689
            transform.adjust_path('new', root, old)
 
690
            transform._new_parent = {}
 
691
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
 
692
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
 
693
                (False, False))], list(transform._iter_changes()))
 
694
            transform._new_name = {}
 
695
 
 
696
            # parent directory
 
697
            self.assertEqual([], list(transform._iter_changes()))
 
698
            transform.adjust_path('new', subdir, old)
 
699
            transform._new_name = {}
 
700
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
 
701
                (True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
 
702
                ('file', 'file'), (False, False))],
 
703
                list(transform._iter_changes()))
 
704
            transform._new_path = {}
 
705
 
 
706
        finally:
 
707
            transform.finalize()
 
708
 
 
709
    def test_iter_changes_modified_bleed(self):
 
710
        self.wt.set_root_id('eert_toor')
 
711
        """Modified flag should not bleed from one change to another"""
 
712
        # unfortunately, we have no guarantee that file1 (which is modified)
 
713
        # will be applied before file2.  And if it's applied after file2, it
 
714
        # obviously can't bleed into file2's change output.  But for now, it
 
715
        # works.
 
716
        transform, root = self.get_transform()
 
717
        transform.new_file('file1', root, 'blah', 'id-1')
 
718
        transform.new_file('file2', root, 'blah', 'id-2')
 
719
        transform.apply()
 
720
        transform, root = self.get_transform()
 
721
        try:
 
722
            transform.delete_contents(transform.trans_id_file_id('id-1'))
 
723
            transform.set_executability(True,
 
724
            transform.trans_id_file_id('id-2'))
 
725
            self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
 
726
                ('eert_toor', 'eert_toor'), ('file1', u'file1'),
 
727
                ('file', None), (False, False)),
 
728
                ('id-2', (u'file2', u'file2'), False, (True, True),
 
729
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
 
730
                ('file', 'file'), (False, True))],
 
731
                list(transform._iter_changes()))
 
732
        finally:
 
733
            transform.finalize()
 
734
 
 
735
    def test_iter_changes_move_missing(self):
 
736
        """Test moving ids with no files around"""
 
737
        self.wt.set_root_id('toor_eert')
 
738
        # Need two steps because versioning a non-existant file is a conflict.
 
739
        transform, root = self.get_transform()
 
740
        transform.new_directory('floater', root, 'floater-id')
 
741
        transform.apply()
 
742
        transform, root = self.get_transform()
 
743
        transform.delete_contents(transform.trans_id_tree_path('floater'))
 
744
        transform.apply()
 
745
        transform, root = self.get_transform()
 
746
        floater = transform.trans_id_tree_path('floater')
 
747
        try:
 
748
            transform.adjust_path('flitter', root, floater)
 
749
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
 
750
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
 
751
            (None, None), (False, False))], list(transform._iter_changes()))
 
752
        finally:
 
753
            transform.finalize()
 
754
 
 
755
    def test_iter_changes_pointless(self):
 
756
        """Ensure that no-ops are not treated as modifications"""
 
757
        self.wt.set_root_id('eert_toor')
 
758
        transform, root = self.get_transform()
 
759
        transform.new_file('old', root, 'blah', 'id-1')
 
760
        transform.new_directory('subdir', root, 'subdir-id')
 
761
        transform.apply()
 
762
        transform, root = self.get_transform()
 
763
        try:
 
764
            old = transform.trans_id_tree_path('old')
 
765
            subdir = transform.trans_id_tree_file_id('subdir-id')
 
766
            self.assertEqual([], list(transform._iter_changes()))
 
767
            transform.delete_contents(subdir)
 
768
            transform.create_directory(subdir)
 
769
            transform.set_executability(False, old)
 
770
            transform.unversion_file(old)
 
771
            transform.version_file('id-1', old)
 
772
            transform.adjust_path('old', root, old)
 
773
            self.assertEqual([], list(transform._iter_changes()))
 
774
        finally:
 
775
            transform.finalize()
493
776
 
494
777
class TransformGroup(object):
495
 
    def __init__(self, dirname):
 
778
    def __init__(self, dirname, root_id):
496
779
        self.name = dirname
497
780
        os.mkdir(dirname)
498
781
        self.wt = BzrDir.create_standalone_workingtree(dirname)
 
782
        self.wt.set_root_id(root_id)
499
783
        self.b = self.wt.branch
500
784
        self.tt = TreeTransform(self.wt)
501
785
        self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
502
786
 
 
787
 
503
788
def conflict_text(tree, merge):
504
789
    template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
505
790
    return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
507
792
 
508
793
class TestTransformMerge(TestCaseInTempDir):
509
794
    def test_text_merge(self):
510
 
        base = TransformGroup("base")
 
795
        root_id = generate_ids.gen_root_id()
 
796
        base = TransformGroup("base", root_id)
511
797
        base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
512
798
        base.tt.new_file('b', base.root, 'b1', 'b')
513
799
        base.tt.new_file('c', base.root, 'c', 'c')
517
803
        base.tt.new_directory('g', base.root, 'g')
518
804
        base.tt.new_directory('h', base.root, 'h')
519
805
        base.tt.apply()
520
 
        other = TransformGroup("other")
 
806
        other = TransformGroup("other", root_id)
521
807
        other.tt.new_file('a', other.root, 'y\nb\nc\nd\be\n', 'a')
522
808
        other.tt.new_file('b', other.root, 'b2', 'b')
523
809
        other.tt.new_file('c', other.root, 'c2', 'c')
528
814
        other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
529
815
        other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
530
816
        other.tt.apply()
531
 
        this = TransformGroup("this")
 
817
        this = TransformGroup("this", root_id)
532
818
        this.tt.new_file('a', this.root, 'a\nb\nc\nd\bz\n', 'a')
533
819
        this.tt.new_file('b', this.root, 'b', 'b')
534
820
        this.tt.new_file('c', this.root, 'c', 'c')
585
871
    def test_file_merge(self):
586
872
        if not has_symlinks():
587
873
            raise TestSkipped('Symlinks are not supported on this platform')
588
 
        base = TransformGroup("BASE")
589
 
        this = TransformGroup("THIS")
590
 
        other = TransformGroup("OTHER")
 
874
        root_id = generate_ids.gen_root_id()
 
875
        base = TransformGroup("BASE", root_id)
 
876
        this = TransformGroup("THIS", root_id)
 
877
        other = TransformGroup("OTHER", root_id)
591
878
        for tg in this, base, other:
592
879
            tg.tt.new_directory('a', tg.root, 'a')
593
880
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
625
912
        self.assertIs(os.path.lexists(this.wt.abspath('h.OTHER')), True)
626
913
 
627
914
    def test_filename_merge(self):
628
 
        base = TransformGroup("BASE")
629
 
        this = TransformGroup("THIS")
630
 
        other = TransformGroup("OTHER")
 
915
        root_id = generate_ids.gen_root_id()
 
916
        base = TransformGroup("BASE", root_id)
 
917
        this = TransformGroup("THIS", root_id)
 
918
        other = TransformGroup("OTHER", root_id)
631
919
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
632
920
                                   for t in [base, this, other]]
633
921
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
657
945
        self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
658
946
 
659
947
    def test_filename_merge_conflicts(self):
660
 
        base = TransformGroup("BASE")
661
 
        this = TransformGroup("THIS")
662
 
        other = TransformGroup("OTHER")
 
948
        root_id = generate_ids.gen_root_id()
 
949
        base = TransformGroup("BASE", root_id)
 
950
        this = TransformGroup("THIS", root_id)
 
951
        other = TransformGroup("OTHER", root_id)
663
952
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
664
953
                                   for t in [base, this, other]]
665
954
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
686
975
        self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
687
976
        self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
688
977
 
689
 
class TestBuildTree(TestCaseInTempDir):
 
978
 
 
979
class TestBuildTree(tests.TestCaseWithTransport):
 
980
 
690
981
    def test_build_tree(self):
691
982
        if not has_symlinks():
692
983
            raise TestSkipped('Test requires symlink support')
698
989
        a.add(['foo', 'foo/bar', 'foo/baz'])
699
990
        a.commit('initial commit')
700
991
        b = BzrDir.create_standalone_workingtree('b')
701
 
        build_tree(a.basis_tree(), b)
 
992
        basis = a.basis_tree()
 
993
        basis.lock_read()
 
994
        self.addCleanup(basis.unlock)
 
995
        build_tree(basis, b)
702
996
        self.assertIs(os.path.isdir('b/foo'), True)
703
997
        self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
998
        self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
 
999
 
 
1000
    def test_build_with_references(self):
 
1001
        tree = self.make_branch_and_tree('source',
 
1002
            format='dirstate-with-subtree')
 
1003
        subtree = self.make_branch_and_tree('source/subtree',
 
1004
            format='dirstate-with-subtree')
 
1005
        tree.add_reference(subtree)
 
1006
        tree.commit('a revision')
 
1007
        tree.branch.create_checkout('target')
 
1008
        self.failUnlessExists('target')
 
1009
        self.failUnlessExists('target/subtree')
 
1010
 
 
1011
    def test_file_conflict_handling(self):
 
1012
        """Ensure that when building trees, conflict handling is done"""
 
1013
        source = self.make_branch_and_tree('source')
 
1014
        target = self.make_branch_and_tree('target')
 
1015
        self.build_tree(['source/file', 'target/file'])
 
1016
        source.add('file', 'new-file')
 
1017
        source.commit('added file')
 
1018
        build_tree(source.basis_tree(), target)
 
1019
        self.assertEqual([DuplicateEntry('Moved existing file to',
 
1020
                          'file.moved', 'file', None, 'new-file')],
 
1021
                         target.conflicts())
 
1022
        target2 = self.make_branch_and_tree('target2')
 
1023
        target_file = file('target2/file', 'wb')
 
1024
        try:
 
1025
            source_file = file('source/file', 'rb')
 
1026
            try:
 
1027
                target_file.write(source_file.read())
 
1028
            finally:
 
1029
                source_file.close()
 
1030
        finally:
 
1031
            target_file.close()
 
1032
        build_tree(source.basis_tree(), target2)
 
1033
        self.assertEqual([], target2.conflicts())
 
1034
 
 
1035
    def test_symlink_conflict_handling(self):
 
1036
        """Ensure that when building trees, conflict handling is done"""
 
1037
        if not has_symlinks():
 
1038
            raise TestSkipped('Test requires symlink support')
 
1039
        source = self.make_branch_and_tree('source')
 
1040
        os.symlink('foo', 'source/symlink')
 
1041
        source.add('symlink', 'new-symlink')
 
1042
        source.commit('added file')
 
1043
        target = self.make_branch_and_tree('target')
 
1044
        os.symlink('bar', 'target/symlink')
 
1045
        build_tree(source.basis_tree(), target)
 
1046
        self.assertEqual([DuplicateEntry('Moved existing file to',
 
1047
            'symlink.moved', 'symlink', None, 'new-symlink')],
 
1048
            target.conflicts())
 
1049
        target = self.make_branch_and_tree('target2')
 
1050
        os.symlink('foo', 'target2/symlink')
 
1051
        build_tree(source.basis_tree(), target)
 
1052
        self.assertEqual([], target.conflicts())
705
1053
        
 
1054
    def test_directory_conflict_handling(self):
 
1055
        """Ensure that when building trees, conflict handling is done"""
 
1056
        source = self.make_branch_and_tree('source')
 
1057
        target = self.make_branch_and_tree('target')
 
1058
        self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
 
1059
        source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
 
1060
        source.commit('added file')
 
1061
        build_tree(source.basis_tree(), target)
 
1062
        self.assertEqual([], target.conflicts())
 
1063
        self.failUnlessExists('target/dir1/file')
 
1064
 
 
1065
        # Ensure contents are merged
 
1066
        target = self.make_branch_and_tree('target2')
 
1067
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
 
1068
        build_tree(source.basis_tree(), target)
 
1069
        self.assertEqual([], target.conflicts())
 
1070
        self.failUnlessExists('target2/dir1/file2')
 
1071
        self.failUnlessExists('target2/dir1/file')
 
1072
 
 
1073
        # Ensure new contents are suppressed for existing branches
 
1074
        target = self.make_branch_and_tree('target3')
 
1075
        self.make_branch('target3/dir1')
 
1076
        self.build_tree(['target3/dir1/file2'])
 
1077
        build_tree(source.basis_tree(), target)
 
1078
        self.failIfExists('target3/dir1/file')
 
1079
        self.failUnlessExists('target3/dir1/file2')
 
1080
        self.failUnlessExists('target3/dir1.diverted/file')
 
1081
        self.assertEqual([DuplicateEntry('Diverted to',
 
1082
            'dir1.diverted', 'dir1', 'new-dir1', None)],
 
1083
            target.conflicts())
 
1084
 
 
1085
        target = self.make_branch_and_tree('target4')
 
1086
        self.build_tree(['target4/dir1/'])
 
1087
        self.make_branch('target4/dir1/file')
 
1088
        build_tree(source.basis_tree(), target)
 
1089
        self.failUnlessExists('target4/dir1/file')
 
1090
        self.assertEqual('directory', file_kind('target4/dir1/file'))
 
1091
        self.failUnlessExists('target4/dir1/file.diverted')
 
1092
        self.assertEqual([DuplicateEntry('Diverted to',
 
1093
            'dir1/file.diverted', 'dir1/file', 'new-file', None)],
 
1094
            target.conflicts())
 
1095
 
 
1096
    def test_mixed_conflict_handling(self):
 
1097
        """Ensure that when building trees, conflict handling is done"""
 
1098
        source = self.make_branch_and_tree('source')
 
1099
        target = self.make_branch_and_tree('target')
 
1100
        self.build_tree(['source/name', 'target/name/'])
 
1101
        source.add('name', 'new-name')
 
1102
        source.commit('added file')
 
1103
        build_tree(source.basis_tree(), target)
 
1104
        self.assertEqual([DuplicateEntry('Moved existing file to',
 
1105
            'name.moved', 'name', None, 'new-name')], target.conflicts())
 
1106
 
 
1107
    def test_raises_in_populated(self):
 
1108
        source = self.make_branch_and_tree('source')
 
1109
        self.build_tree(['source/name'])
 
1110
        source.add('name')
 
1111
        source.commit('added name')
 
1112
        target = self.make_branch_and_tree('target')
 
1113
        self.build_tree(['target/name'])
 
1114
        target.add('name')
 
1115
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
 
1116
            build_tree, source.basis_tree(), target)
 
1117
 
 
1118
 
706
1119
class MockTransform(object):
707
1120
 
708
1121
    def has_named_child(self, by_parent, parent_id, name):