/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_transform.py

  • Committer: Jelmer Vernooij
  • Date: 2020-07-05 12:50:01 UTC
  • mfrom: (7490.40.46 work)
  • mto: (7490.40.48 work)
  • mto: This revision was merged to the branch mainline in revision 7519.
  • Revision ID: jelmer@jelmer.uk-20200705125001-7s3vo0p55szbbws7
Merge lp:brz/3.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
36
36
from ..bzr import (
37
37
    generate_ids,
38
38
    )
39
 
from ..bzr.conflicts import (
 
39
from ..conflicts import (
40
40
    DeletingParent,
41
41
    DuplicateEntry,
42
42
    DuplicateID,
60
60
)
61
61
from ..merge import Merge3Merger, Merger
62
62
from ..mutabletree import MutableTree
 
63
from ..sixish import (
 
64
    BytesIO,
 
65
    PY3,
 
66
    text_type,
 
67
    )
63
68
from . import (
64
69
    features,
65
70
    TestCaseInTempDir,
70
75
    SymlinkFeature,
71
76
    )
72
77
from ..transform import (
 
78
    build_tree,
73
79
    create_from_tree,
 
80
    cook_conflicts,
74
81
    _FileMover,
75
82
    FinalPaths,
76
83
    resolve_conflicts,
 
84
    resolve_checkout,
77
85
    ROOT_PARENT,
78
86
    ImmortalLimbo,
79
87
    MalformedTransform,
81
89
    ReusingTransform,
82
90
    TransformRenameFailed,
83
91
)
 
92
from ..bzr.transform import (
 
93
    InventoryTreeTransform as TreeTransform,
 
94
    )
84
95
 
85
96
 
86
97
class TransformGroup(object):
291
302
        self.assertEqual(this.wt.id2path(b'i'), pathjoin('b/i1.OTHER'))
292
303
 
293
304
 
 
305
class TestBuildTree(tests.TestCaseWithTransport):
 
306
 
 
307
    def test_build_tree_with_symlinks(self):
 
308
        self.requireFeature(SymlinkFeature)
 
309
        os.mkdir('a')
 
310
        a = ControlDir.create_standalone_workingtree('a')
 
311
        os.mkdir('a/foo')
 
312
        with open('a/foo/bar', 'wb') as f:
 
313
            f.write(b'contents')
 
314
        os.symlink('a/foo/bar', 'a/foo/baz')
 
315
        a.add(['foo', 'foo/bar', 'foo/baz'])
 
316
        a.commit('initial commit')
 
317
        b = ControlDir.create_standalone_workingtree('b')
 
318
        basis = a.basis_tree()
 
319
        basis.lock_read()
 
320
        self.addCleanup(basis.unlock)
 
321
        build_tree(basis, b)
 
322
        self.assertIs(os.path.isdir('b/foo'), True)
 
323
        with open('b/foo/bar', 'rb') as f:
 
324
            self.assertEqual(f.read(), b"contents")
 
325
        self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
 
326
 
 
327
    def test_build_with_references(self):
 
328
        tree = self.make_branch_and_tree('source',
 
329
                                         format='development-subtree')
 
330
        subtree = self.make_branch_and_tree('source/subtree',
 
331
                                            format='development-subtree')
 
332
        tree.add_reference(subtree)
 
333
        tree.commit('a revision')
 
334
        tree.branch.create_checkout('target')
 
335
        self.assertPathExists('target')
 
336
        self.assertPathExists('target/subtree')
 
337
 
 
338
    def test_file_conflict_handling(self):
 
339
        """Ensure that when building trees, conflict handling is done"""
 
340
        source = self.make_branch_and_tree('source')
 
341
        target = self.make_branch_and_tree('target')
 
342
        self.build_tree(['source/file', 'target/file'])
 
343
        source.add('file', b'new-file')
 
344
        source.commit('added file')
 
345
        build_tree(source.basis_tree(), target)
 
346
        self.assertEqual(
 
347
            [DuplicateEntry('Moved existing file to', 'file.moved',
 
348
                            'file', None, 'new-file')],
 
349
            target.conflicts())
 
350
        target2 = self.make_branch_and_tree('target2')
 
351
        with open('target2/file', 'wb') as target_file, \
 
352
                open('source/file', 'rb') as source_file:
 
353
            target_file.write(source_file.read())
 
354
        build_tree(source.basis_tree(), target2)
 
355
        self.assertEqual([], target2.conflicts())
 
356
 
 
357
    def test_symlink_conflict_handling(self):
 
358
        """Ensure that when building trees, conflict handling is done"""
 
359
        self.requireFeature(SymlinkFeature)
 
360
        source = self.make_branch_and_tree('source')
 
361
        os.symlink('foo', 'source/symlink')
 
362
        source.add('symlink', b'new-symlink')
 
363
        source.commit('added file')
 
364
        target = self.make_branch_and_tree('target')
 
365
        os.symlink('bar', 'target/symlink')
 
366
        build_tree(source.basis_tree(), target)
 
367
        self.assertEqual(
 
368
            [DuplicateEntry('Moved existing file to', 'symlink.moved',
 
369
                            'symlink', None, 'new-symlink')],
 
370
            target.conflicts())
 
371
        target = self.make_branch_and_tree('target2')
 
372
        os.symlink('foo', 'target2/symlink')
 
373
        build_tree(source.basis_tree(), target)
 
374
        self.assertEqual([], target.conflicts())
 
375
 
 
376
    def test_directory_conflict_handling(self):
 
377
        """Ensure that when building trees, conflict handling is done"""
 
378
        source = self.make_branch_and_tree('source')
 
379
        target = self.make_branch_and_tree('target')
 
380
        self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
 
381
        source.add(['dir1', 'dir1/file'], [b'new-dir1', b'new-file'])
 
382
        source.commit('added file')
 
383
        build_tree(source.basis_tree(), target)
 
384
        self.assertEqual([], target.conflicts())
 
385
        self.assertPathExists('target/dir1/file')
 
386
 
 
387
        # Ensure contents are merged
 
388
        target = self.make_branch_and_tree('target2')
 
389
        self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
 
390
        build_tree(source.basis_tree(), target)
 
391
        self.assertEqual([], target.conflicts())
 
392
        self.assertPathExists('target2/dir1/file2')
 
393
        self.assertPathExists('target2/dir1/file')
 
394
 
 
395
        # Ensure new contents are suppressed for existing branches
 
396
        target = self.make_branch_and_tree('target3')
 
397
        self.make_branch('target3/dir1')
 
398
        self.build_tree(['target3/dir1/file2'])
 
399
        build_tree(source.basis_tree(), target)
 
400
        self.assertPathDoesNotExist('target3/dir1/file')
 
401
        self.assertPathExists('target3/dir1/file2')
 
402
        self.assertPathExists('target3/dir1.diverted/file')
 
403
        self.assertEqual(
 
404
            [DuplicateEntry('Diverted to', 'dir1.diverted',
 
405
                            'dir1', 'new-dir1', None)],
 
406
            target.conflicts())
 
407
 
 
408
        target = self.make_branch_and_tree('target4')
 
409
        self.build_tree(['target4/dir1/'])
 
410
        self.make_branch('target4/dir1/file')
 
411
        build_tree(source.basis_tree(), target)
 
412
        self.assertPathExists('target4/dir1/file')
 
413
        self.assertEqual('directory', file_kind('target4/dir1/file'))
 
414
        self.assertPathExists('target4/dir1/file.diverted')
 
415
        self.assertEqual(
 
416
            [DuplicateEntry('Diverted to', 'dir1/file.diverted',
 
417
                            'dir1/file', 'new-file', None)],
 
418
            target.conflicts())
 
419
 
 
420
    def test_mixed_conflict_handling(self):
 
421
        """Ensure that when building trees, conflict handling is done"""
 
422
        source = self.make_branch_and_tree('source')
 
423
        target = self.make_branch_and_tree('target')
 
424
        self.build_tree(['source/name', 'target/name/'])
 
425
        source.add('name', b'new-name')
 
426
        source.commit('added file')
 
427
        build_tree(source.basis_tree(), target)
 
428
        self.assertEqual(
 
429
            [DuplicateEntry('Moved existing file to',
 
430
                            'name.moved', 'name', None, 'new-name')],
 
431
            target.conflicts())
 
432
 
 
433
    def test_raises_in_populated(self):
 
434
        source = self.make_branch_and_tree('source')
 
435
        self.build_tree(['source/name'])
 
436
        source.add('name')
 
437
        source.commit('added name')
 
438
        target = self.make_branch_and_tree('target')
 
439
        self.build_tree(['target/name'])
 
440
        target.add('name')
 
441
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
 
442
                          build_tree, source.basis_tree(), target)
 
443
 
 
444
    def test_build_tree_rename_count(self):
 
445
        source = self.make_branch_and_tree('source')
 
446
        self.build_tree(['source/file1', 'source/dir1/'])
 
447
        source.add(['file1', 'dir1'])
 
448
        source.commit('add1')
 
449
        target1 = self.make_branch_and_tree('target1')
 
450
        transform_result = build_tree(source.basis_tree(), target1)
 
451
        self.assertEqual(2, transform_result.rename_count)
 
452
 
 
453
        self.build_tree(['source/dir1/file2'])
 
454
        source.add(['dir1/file2'])
 
455
        source.commit('add3')
 
456
        target2 = self.make_branch_and_tree('target2')
 
457
        transform_result = build_tree(source.basis_tree(), target2)
 
458
        # children of non-root directories should not be renamed
 
459
        self.assertEqual(2, transform_result.rename_count)
 
460
 
 
461
    def create_ab_tree(self):
 
462
        """Create a committed test tree with two files"""
 
463
        source = self.make_branch_and_tree('source')
 
464
        self.build_tree_contents([('source/file1', b'A')])
 
465
        self.build_tree_contents([('source/file2', b'B')])
 
466
        source.add(['file1', 'file2'], [b'file1-id', b'file2-id'])
 
467
        source.commit('commit files')
 
468
        source.lock_write()
 
469
        self.addCleanup(source.unlock)
 
470
        return source
 
471
 
 
472
    def test_build_tree_accelerator_tree(self):
 
473
        source = self.create_ab_tree()
 
474
        self.build_tree_contents([('source/file2', b'C')])
 
475
        calls = []
 
476
        real_source_get_file = source.get_file
 
477
 
 
478
        def get_file(path):
 
479
            calls.append(path)
 
480
            return real_source_get_file(path)
 
481
        source.get_file = get_file
 
482
        target = self.make_branch_and_tree('target')
 
483
        revision_tree = source.basis_tree()
 
484
        revision_tree.lock_read()
 
485
        self.addCleanup(revision_tree.unlock)
 
486
        build_tree(revision_tree, target, source)
 
487
        self.assertEqual(['file1'], calls)
 
488
        target.lock_read()
 
489
        self.addCleanup(target.unlock)
 
490
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
491
 
 
492
    def test_build_tree_accelerator_tree_observes_sha1(self):
 
493
        source = self.create_ab_tree()
 
494
        sha1 = osutils.sha_string(b'A')
 
495
        target = self.make_branch_and_tree('target')
 
496
        target.lock_write()
 
497
        self.addCleanup(target.unlock)
 
498
        state = target.current_dirstate()
 
499
        state._cutoff_time = time.time() + 60
 
500
        build_tree(source.basis_tree(), target, source)
 
501
        entry = state._get_entry(0, path_utf8=b'file1')
 
502
        self.assertEqual(sha1, entry[1][0][1])
 
503
 
 
504
    def test_build_tree_accelerator_tree_missing_file(self):
 
505
        source = self.create_ab_tree()
 
506
        os.unlink('source/file1')
 
507
        source.remove(['file2'])
 
508
        target = self.make_branch_and_tree('target')
 
509
        revision_tree = source.basis_tree()
 
510
        revision_tree.lock_read()
 
511
        self.addCleanup(revision_tree.unlock)
 
512
        build_tree(revision_tree, target, source)
 
513
        target.lock_read()
 
514
        self.addCleanup(target.unlock)
 
515
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
516
 
 
517
    def test_build_tree_accelerator_wrong_kind(self):
 
518
        self.requireFeature(SymlinkFeature)
 
519
        source = self.make_branch_and_tree('source')
 
520
        self.build_tree_contents([('source/file1', b'')])
 
521
        self.build_tree_contents([('source/file2', b'')])
 
522
        source.add(['file1', 'file2'], [b'file1-id', b'file2-id'])
 
523
        source.commit('commit files')
 
524
        os.unlink('source/file2')
 
525
        self.build_tree_contents([('source/file2/', b'C')])
 
526
        os.unlink('source/file1')
 
527
        os.symlink('file2', 'source/file1')
 
528
        calls = []
 
529
        real_source_get_file = source.get_file
 
530
 
 
531
        def get_file(path):
 
532
            calls.append(path)
 
533
            return real_source_get_file(path)
 
534
        source.get_file = get_file
 
535
        target = self.make_branch_and_tree('target')
 
536
        revision_tree = source.basis_tree()
 
537
        revision_tree.lock_read()
 
538
        self.addCleanup(revision_tree.unlock)
 
539
        build_tree(revision_tree, target, source)
 
540
        self.assertEqual([], calls)
 
541
        target.lock_read()
 
542
        self.addCleanup(target.unlock)
 
543
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
544
 
 
545
    def test_build_tree_hardlink(self):
 
546
        self.requireFeature(HardlinkFeature)
 
547
        source = self.create_ab_tree()
 
548
        target = self.make_branch_and_tree('target')
 
549
        revision_tree = source.basis_tree()
 
550
        revision_tree.lock_read()
 
551
        self.addCleanup(revision_tree.unlock)
 
552
        build_tree(revision_tree, target, source, hardlink=True)
 
553
        target.lock_read()
 
554
        self.addCleanup(target.unlock)
 
555
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
556
        source_stat = os.stat('source/file1')
 
557
        target_stat = os.stat('target/file1')
 
558
        self.assertEqual(source_stat, target_stat)
 
559
 
 
560
        # Explicitly disallowing hardlinks should prevent them.
 
561
        target2 = self.make_branch_and_tree('target2')
 
562
        build_tree(revision_tree, target2, source, hardlink=False)
 
563
        target2.lock_read()
 
564
        self.addCleanup(target2.unlock)
 
565
        self.assertEqual([], list(target2.iter_changes(revision_tree)))
 
566
        source_stat = os.stat('source/file1')
 
567
        target2_stat = os.stat('target2/file1')
 
568
        self.assertNotEqual(source_stat, target2_stat)
 
569
 
 
570
    def test_build_tree_accelerator_tree_moved(self):
 
571
        source = self.make_branch_and_tree('source')
 
572
        self.build_tree_contents([('source/file1', b'A')])
 
573
        source.add(['file1'], [b'file1-id'])
 
574
        source.commit('commit files')
 
575
        source.rename_one('file1', 'file2')
 
576
        source.lock_read()
 
577
        self.addCleanup(source.unlock)
 
578
        target = self.make_branch_and_tree('target')
 
579
        revision_tree = source.basis_tree()
 
580
        revision_tree.lock_read()
 
581
        self.addCleanup(revision_tree.unlock)
 
582
        build_tree(revision_tree, target, source)
 
583
        target.lock_read()
 
584
        self.addCleanup(target.unlock)
 
585
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
586
 
 
587
    def test_build_tree_hardlinks_preserve_execute(self):
 
588
        self.requireFeature(HardlinkFeature)
 
589
        source = self.create_ab_tree()
 
590
        tt = source.transform()
 
591
        trans_id = tt.trans_id_tree_path('file1')
 
592
        tt.set_executability(True, trans_id)
 
593
        tt.apply()
 
594
        self.assertTrue(source.is_executable('file1'))
 
595
        target = self.make_branch_and_tree('target')
 
596
        revision_tree = source.basis_tree()
 
597
        revision_tree.lock_read()
 
598
        self.addCleanup(revision_tree.unlock)
 
599
        build_tree(revision_tree, target, source, hardlink=True)
 
600
        target.lock_read()
 
601
        self.addCleanup(target.unlock)
 
602
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
603
        self.assertTrue(source.is_executable('file1'))
 
604
 
 
605
    def install_rot13_content_filter(self, pattern):
 
606
        # We could use
 
607
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
 
608
        # below, but that looks a bit... hard to read even if it's exactly
 
609
        # the same thing.
 
610
        original_registry = filters._reset_registry()
 
611
 
 
612
        def restore_registry():
 
613
            filters._reset_registry(original_registry)
 
614
        self.addCleanup(restore_registry)
 
615
 
 
616
        def rot13(chunks, context=None):
 
617
            return [
 
618
                codecs.encode(chunk.decode('ascii'), 'rot13').encode('ascii')
 
619
                for chunk in chunks]
 
620
        rot13filter = filters.ContentFilter(rot13, rot13)
 
621
        filters.filter_stacks_registry.register(
 
622
            'rot13', {'yes': [rot13filter]}.get)
 
623
        os.mkdir(self.test_home_dir + '/.bazaar')
 
624
        rules_filename = self.test_home_dir + '/.bazaar/rules'
 
625
        with open(rules_filename, 'wb') as f:
 
626
            f.write(b'[name %s]\nrot13=yes\n' % (pattern,))
 
627
 
 
628
        def uninstall_rules():
 
629
            os.remove(rules_filename)
 
630
            rules.reset_rules()
 
631
        self.addCleanup(uninstall_rules)
 
632
        rules.reset_rules()
 
633
 
 
634
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
 
635
        """build_tree will not hardlink files that have content filtering rules
 
636
        applied to them (but will still hardlink other files from the same tree
 
637
        if it can).
 
638
        """
 
639
        self.requireFeature(HardlinkFeature)
 
640
        self.install_rot13_content_filter(b'file1')
 
641
        source = self.create_ab_tree()
 
642
        target = self.make_branch_and_tree('target')
 
643
        revision_tree = source.basis_tree()
 
644
        revision_tree.lock_read()
 
645
        self.addCleanup(revision_tree.unlock)
 
646
        build_tree(revision_tree, target, source, hardlink=True)
 
647
        target.lock_read()
 
648
        self.addCleanup(target.unlock)
 
649
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
650
        source_stat = os.stat('source/file1')
 
651
        target_stat = os.stat('target/file1')
 
652
        self.assertNotEqual(source_stat, target_stat)
 
653
        source_stat = os.stat('source/file2')
 
654
        target_stat = os.stat('target/file2')
 
655
        self.assertEqualStat(source_stat, target_stat)
 
656
 
 
657
    def test_case_insensitive_build_tree_inventory(self):
 
658
        if (features.CaseInsensitiveFilesystemFeature.available()
 
659
                or features.CaseInsCasePresFilenameFeature.available()):
 
660
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
 
661
        source = self.make_branch_and_tree('source')
 
662
        self.build_tree(['source/file', 'source/FILE'])
 
663
        source.add(['file', 'FILE'], [b'lower-id', b'upper-id'])
 
664
        source.commit('added files')
 
665
        # Don't try this at home, kids!
 
666
        # Force the tree to report that it is case insensitive
 
667
        target = self.make_branch_and_tree('target')
 
668
        target.case_sensitive = False
 
669
        build_tree(source.basis_tree(), target, source, delta_from_tree=True)
 
670
        self.assertEqual('file.moved', target.id2path(b'lower-id'))
 
671
        self.assertEqual('FILE', target.id2path(b'upper-id'))
 
672
 
 
673
    def test_build_tree_observes_sha(self):
 
674
        source = self.make_branch_and_tree('source')
 
675
        self.build_tree(['source/file1', 'source/dir/', 'source/dir/file2'])
 
676
        source.add(['file1', 'dir', 'dir/file2'],
 
677
                   [b'file1-id', b'dir-id', b'file2-id'])
 
678
        source.commit('new files')
 
679
        target = self.make_branch_and_tree('target')
 
680
        target.lock_write()
 
681
        self.addCleanup(target.unlock)
 
682
        # We make use of the fact that DirState caches its cutoff time. So we
 
683
        # set the 'safe' time to one minute in the future.
 
684
        state = target.current_dirstate()
 
685
        state._cutoff_time = time.time() + 60
 
686
        build_tree(source.basis_tree(), target)
 
687
        entry1_sha = osutils.sha_file_by_name('source/file1')
 
688
        entry2_sha = osutils.sha_file_by_name('source/dir/file2')
 
689
        # entry[1] is the state information, entry[1][0] is the state of the
 
690
        # working tree, entry[1][0][1] is the sha value for the current working
 
691
        # tree
 
692
        entry1 = state._get_entry(0, path_utf8=b'file1')
 
693
        self.assertEqual(entry1_sha, entry1[1][0][1])
 
694
        # The 'size' field must also be set.
 
695
        self.assertEqual(25, entry1[1][0][2])
 
696
        entry1_state = entry1[1][0]
 
697
        entry2 = state._get_entry(0, path_utf8=b'dir/file2')
 
698
        self.assertEqual(entry2_sha, entry2[1][0][1])
 
699
        self.assertEqual(29, entry2[1][0][2])
 
700
        entry2_state = entry2[1][0]
 
701
        # Now, make sure that we don't have to re-read the content. The
 
702
        # packed_stat should match exactly.
 
703
        self.assertEqual(entry1_sha, target.get_file_sha1('file1'))
 
704
        self.assertEqual(entry2_sha, target.get_file_sha1('dir/file2'))
 
705
        self.assertEqual(entry1_state, entry1[1][0])
 
706
        self.assertEqual(entry2_state, entry2[1][0])
 
707
 
 
708
 
294
709
class TestCommitTransform(tests.TestCaseWithTransport):
295
710
 
296
711
    def get_branch(self):
559
974
        new_globals.update(globals)
560
975
        new_func = types.FunctionType(func.__code__, new_globals,
561
976
                                      func.__name__, func.__defaults__)
562
 
        setattr(instance, method_name,
563
 
                types.MethodType(new_func, instance))
 
977
        if PY3:
 
978
            setattr(instance, method_name,
 
979
                    types.MethodType(new_func, instance))
 
980
        else:
 
981
            setattr(instance, method_name,
 
982
                    types.MethodType(new_func, instance, instance.__class__))
564
983
        self.addCleanup(delattr, instance, method_name)
565
984
 
566
985
    @staticmethod
711
1130
                         conflicts.pop())
712
1131
 
713
1132
 
 
1133
A_ENTRY = (b'a-id', ('a', 'a'), True, (True, True),
 
1134
           (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
 
1135
           (False, False), False)
 
1136
ROOT_ENTRY = (b'TREE_ROOT', ('', ''), False, (True, True), (None, None),
 
1137
              ('', ''), ('directory', 'directory'), (False, False), False)
 
1138
 
 
1139
 
 
1140
class TestTransformPreview(tests.TestCaseWithTransport):
 
1141
 
 
1142
    def create_tree(self):
 
1143
        tree = self.make_branch_and_tree('.')
 
1144
        self.build_tree_contents([('a', b'content 1')])
 
1145
        tree.set_root_id(b'TREE_ROOT')
 
1146
        tree.add('a', b'a-id')
 
1147
        tree.commit('rev1', rev_id=b'rev1')
 
1148
        return tree.branch.repository.revision_tree(b'rev1')
 
1149
 
 
1150
    def get_empty_preview(self):
 
1151
        repository = self.make_repository('repo')
 
1152
        tree = repository.revision_tree(_mod_revision.NULL_REVISION)
 
1153
        preview = tree.preview_transform()
 
1154
        self.addCleanup(preview.finalize)
 
1155
        return preview
 
1156
 
 
1157
    def test_transform_preview(self):
 
1158
        revision_tree = self.create_tree()
 
1159
        preview = revision_tree.preview_transform()
 
1160
        self.addCleanup(preview.finalize)
 
1161
 
 
1162
    def test_transform_preview_tree(self):
 
1163
        revision_tree = self.create_tree()
 
1164
        preview = revision_tree.preview_transform()
 
1165
        self.addCleanup(preview.finalize)
 
1166
        preview.get_preview_tree()
 
1167
 
 
1168
    def test_transform_new_file(self):
 
1169
        revision_tree = self.create_tree()
 
1170
        preview = revision_tree.preview_transform()
 
1171
        self.addCleanup(preview.finalize)
 
1172
        preview.new_file('file2', preview.root, [b'content B\n'], b'file2-id')
 
1173
        preview_tree = preview.get_preview_tree()
 
1174
        self.assertEqual(preview_tree.kind('file2'), 'file')
 
1175
        with preview_tree.get_file('file2') as f:
 
1176
            self.assertEqual(f.read(), b'content B\n')
 
1177
 
 
1178
    def test_diff_preview_tree(self):
 
1179
        revision_tree = self.create_tree()
 
1180
        preview = revision_tree.preview_transform()
 
1181
        self.addCleanup(preview.finalize)
 
1182
        preview.new_file('file2', preview.root, [b'content B\n'], b'file2-id')
 
1183
        preview_tree = preview.get_preview_tree()
 
1184
        out = BytesIO()
 
1185
        show_diff_trees(revision_tree, preview_tree, out)
 
1186
        lines = out.getvalue().splitlines()
 
1187
        self.assertEqual(lines[0], b"=== added file 'file2'")
 
1188
        # 3 lines of diff administrivia
 
1189
        self.assertEqual(lines[4], b"+content B")
 
1190
 
 
1191
    def test_unsupported_symlink_diff(self):
 
1192
        self.requireFeature(SymlinkFeature)
 
1193
        tree = self.make_branch_and_tree('.')
 
1194
        self.build_tree_contents([('a', 'content 1')])
 
1195
        tree.set_root_id(b'TREE_ROOT')
 
1196
        tree.add('a', b'a-id')
 
1197
        os.symlink('a', 'foo')
 
1198
        tree.add('foo', b'foo-id')
 
1199
        tree.commit('rev1', rev_id=b'rev1')
 
1200
        revision_tree = tree.branch.repository.revision_tree(b'rev1')
 
1201
        preview = revision_tree.preview_transform()
 
1202
        self.addCleanup(preview.finalize)
 
1203
        preview.delete_versioned(preview.trans_id_tree_path('foo'))
 
1204
        preview_tree = preview.get_preview_tree()
 
1205
        out = BytesIO()
 
1206
        log = BytesIO()
 
1207
        trace.push_log_file(log)
 
1208
        os_symlink = getattr(os, 'symlink', None)
 
1209
        os.symlink = None
 
1210
        try:
 
1211
            show_diff_trees(revision_tree, preview_tree, out)
 
1212
            lines = out.getvalue().splitlines()
 
1213
        finally:
 
1214
            os.symlink = os_symlink
 
1215
        self.assertContainsRe(
 
1216
            log.getvalue(),
 
1217
            b'Ignoring "foo" as symlinks are not supported on this filesystem')
 
1218
 
 
1219
    def test_transform_conflicts(self):
 
1220
        revision_tree = self.create_tree()
 
1221
        preview = revision_tree.preview_transform()
 
1222
        self.addCleanup(preview.finalize)
 
1223
        preview.new_file('a', preview.root, [b'content 2'])
 
1224
        resolve_conflicts(preview)
 
1225
        trans_id = preview.trans_id_file_id(b'a-id')
 
1226
        self.assertEqual('a.moved', preview.final_name(trans_id))
 
1227
 
 
1228
    def get_tree_and_preview_tree(self):
 
1229
        revision_tree = self.create_tree()
 
1230
        preview = revision_tree.preview_transform()
 
1231
        self.addCleanup(preview.finalize)
 
1232
        a_trans_id = preview.trans_id_file_id(b'a-id')
 
1233
        preview.delete_contents(a_trans_id)
 
1234
        preview.create_file([b'b content'], a_trans_id)
 
1235
        preview_tree = preview.get_preview_tree()
 
1236
        return revision_tree, preview_tree
 
1237
 
 
1238
    def test_iter_changes(self):
 
1239
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1240
        root = revision_tree.path2id('')
 
1241
        self.assertEqual([(b'a-id', ('a', 'a'), True, (True, True),
 
1242
                           (root, root), ('a', 'a'), ('file', 'file'),
 
1243
                           (False, False), False)],
 
1244
                         list(preview_tree.iter_changes(revision_tree)))
 
1245
 
 
1246
    def test_include_unchanged_succeeds(self):
 
1247
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1248
        changes = preview_tree.iter_changes(revision_tree,
 
1249
                                            include_unchanged=True)
 
1250
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
 
1251
 
 
1252
    def test_specific_files(self):
 
1253
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1254
        changes = preview_tree.iter_changes(revision_tree,
 
1255
                                            specific_files=[''])
 
1256
        self.assertEqual([A_ENTRY], list(changes))
 
1257
 
 
1258
    def test_want_unversioned(self):
 
1259
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1260
        changes = preview_tree.iter_changes(revision_tree,
 
1261
                                            want_unversioned=True)
 
1262
        self.assertEqual([A_ENTRY], list(changes))
 
1263
 
 
1264
    def test_ignore_extra_trees_no_specific_files(self):
 
1265
        # extra_trees is harmless without specific_files, so we'll silently
 
1266
        # accept it, even though we won't use it.
 
1267
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1268
        preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
 
1269
 
 
1270
    def test_ignore_require_versioned_no_specific_files(self):
 
1271
        # require_versioned is meaningless without specific_files.
 
1272
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1273
        preview_tree.iter_changes(revision_tree, require_versioned=False)
 
1274
 
 
1275
    def test_ignore_pb(self):
 
1276
        # pb could be supported, but TT.iter_changes doesn't support it.
 
1277
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1278
        preview_tree.iter_changes(revision_tree)
 
1279
 
 
1280
    def test_kind(self):
 
1281
        revision_tree = self.create_tree()
 
1282
        preview = revision_tree.preview_transform()
 
1283
        self.addCleanup(preview.finalize)
 
1284
        preview.new_file('file', preview.root, [b'contents'], b'file-id')
 
1285
        preview.new_directory('directory', preview.root, b'dir-id')
 
1286
        preview_tree = preview.get_preview_tree()
 
1287
        self.assertEqual('file', preview_tree.kind('file'))
 
1288
        self.assertEqual('directory', preview_tree.kind('directory'))
 
1289
 
 
1290
    def test_get_file_mtime(self):
 
1291
        preview = self.get_empty_preview()
 
1292
        file_trans_id = preview.new_file('file', preview.root, [b'contents'],
 
1293
                                         b'file-id')
 
1294
        limbo_path = preview._limbo_name(file_trans_id)
 
1295
        preview_tree = preview.get_preview_tree()
 
1296
        self.assertEqual(os.stat(limbo_path).st_mtime,
 
1297
                         preview_tree.get_file_mtime('file'))
 
1298
 
 
1299
    def test_get_file_mtime_renamed(self):
 
1300
        work_tree = self.make_branch_and_tree('tree')
 
1301
        self.build_tree(['tree/file'])
 
1302
        work_tree.add('file', b'file-id')
 
1303
        preview = work_tree.preview_transform()
 
1304
        self.addCleanup(preview.finalize)
 
1305
        file_trans_id = preview.trans_id_tree_path('file')
 
1306
        preview.adjust_path('renamed', preview.root, file_trans_id)
 
1307
        preview_tree = preview.get_preview_tree()
 
1308
        preview_mtime = preview_tree.get_file_mtime('renamed')
 
1309
        work_mtime = work_tree.get_file_mtime('file')
 
1310
 
 
1311
    def test_get_file_size(self):
 
1312
        work_tree = self.make_branch_and_tree('tree')
 
1313
        self.build_tree_contents([('tree/old', b'old')])
 
1314
        work_tree.add('old', b'old-id')
 
1315
        preview = work_tree.preview_transform()
 
1316
        self.addCleanup(preview.finalize)
 
1317
        preview.new_file('name', preview.root, [b'contents'], b'new-id',
 
1318
                         'executable')
 
1319
        tree = preview.get_preview_tree()
 
1320
        self.assertEqual(len('old'), tree.get_file_size('old'))
 
1321
        self.assertEqual(len('contents'), tree.get_file_size('name'))
 
1322
 
 
1323
    def test_get_file(self):
 
1324
        preview = self.get_empty_preview()
 
1325
        preview.new_file('file', preview.root, [b'contents'], b'file-id')
 
1326
        preview_tree = preview.get_preview_tree()
 
1327
        with preview_tree.get_file('file') as tree_file:
 
1328
            self.assertEqual(b'contents', tree_file.read())
 
1329
 
 
1330
    def test_get_symlink_target(self):
 
1331
        self.requireFeature(SymlinkFeature)
 
1332
        preview = self.get_empty_preview()
 
1333
        preview.new_symlink('symlink', preview.root, 'target', b'symlink-id')
 
1334
        preview_tree = preview.get_preview_tree()
 
1335
        self.assertEqual('target',
 
1336
                         preview_tree.get_symlink_target('symlink'))
 
1337
 
 
1338
    def test_all_file_ids(self):
 
1339
        tree = self.make_branch_and_tree('tree')
 
1340
        self.build_tree(['tree/a', 'tree/b', 'tree/c'])
 
1341
        tree.add(['a', 'b', 'c'], [b'a-id', b'b-id', b'c-id'])
 
1342
        preview = tree.preview_transform()
 
1343
        self.addCleanup(preview.finalize)
 
1344
        preview.unversion_file(preview.trans_id_file_id(b'b-id'))
 
1345
        c_trans_id = preview.trans_id_file_id(b'c-id')
 
1346
        preview.unversion_file(c_trans_id)
 
1347
        preview.version_file(c_trans_id, file_id=b'c-id')
 
1348
        preview_tree = preview.get_preview_tree()
 
1349
        self.assertEqual({b'a-id', b'c-id', tree.path2id('')},
 
1350
                         preview_tree.all_file_ids())
 
1351
 
 
1352
    def test_path2id_deleted_unchanged(self):
 
1353
        tree = self.make_branch_and_tree('tree')
 
1354
        self.build_tree(['tree/unchanged', 'tree/deleted'])
 
1355
        tree.add(['unchanged', 'deleted'], [b'unchanged-id', b'deleted-id'])
 
1356
        preview = tree.preview_transform()
 
1357
        self.addCleanup(preview.finalize)
 
1358
        preview.unversion_file(preview.trans_id_file_id(b'deleted-id'))
 
1359
        preview_tree = preview.get_preview_tree()
 
1360
        self.assertEqual(b'unchanged-id', preview_tree.path2id('unchanged'))
 
1361
        self.assertFalse(preview_tree.is_versioned('deleted'))
 
1362
 
 
1363
    def test_path2id_created(self):
 
1364
        tree = self.make_branch_and_tree('tree')
 
1365
        self.build_tree(['tree/unchanged'])
 
1366
        tree.add(['unchanged'], [b'unchanged-id'])
 
1367
        preview = tree.preview_transform()
 
1368
        self.addCleanup(preview.finalize)
 
1369
        preview.new_file('new', preview.trans_id_file_id(b'unchanged-id'),
 
1370
                         [b'contents'], b'new-id')
 
1371
        preview_tree = preview.get_preview_tree()
 
1372
        self.assertEqual(b'new-id', preview_tree.path2id('unchanged/new'))
 
1373
 
 
1374
    def test_path2id_moved(self):
 
1375
        tree = self.make_branch_and_tree('tree')
 
1376
        self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
 
1377
        tree.add(['old_parent', 'old_parent/child'],
 
1378
                 [b'old_parent-id', b'child-id'])
 
1379
        preview = tree.preview_transform()
 
1380
        self.addCleanup(preview.finalize)
 
1381
        new_parent = preview.new_directory('new_parent', preview.root,
 
1382
                                           b'new_parent-id')
 
1383
        preview.adjust_path('child', new_parent,
 
1384
                            preview.trans_id_file_id(b'child-id'))
 
1385
        preview_tree = preview.get_preview_tree()
 
1386
        self.assertFalse(preview_tree.is_versioned('old_parent/child'))
 
1387
        self.assertEqual(b'child-id', preview_tree.path2id('new_parent/child'))
 
1388
 
 
1389
    def test_path2id_renamed_parent(self):
 
1390
        tree = self.make_branch_and_tree('tree')
 
1391
        self.build_tree(['tree/old_name/', 'tree/old_name/child'])
 
1392
        tree.add(['old_name', 'old_name/child'],
 
1393
                 [b'parent-id', b'child-id'])
 
1394
        preview = tree.preview_transform()
 
1395
        self.addCleanup(preview.finalize)
 
1396
        preview.adjust_path('new_name', preview.root,
 
1397
                            preview.trans_id_file_id(b'parent-id'))
 
1398
        preview_tree = preview.get_preview_tree()
 
1399
        self.assertFalse(preview_tree.is_versioned('old_name/child'))
 
1400
        self.assertEqual(b'child-id', preview_tree.path2id('new_name/child'))
 
1401
 
 
1402
    def assertMatchingIterEntries(self, tt, specific_files=None):
 
1403
        preview_tree = tt.get_preview_tree()
 
1404
        preview_result = list(preview_tree.iter_entries_by_dir(
 
1405
                              specific_files=specific_files))
 
1406
        tree = tt._tree
 
1407
        tt.apply()
 
1408
        actual_result = list(tree.iter_entries_by_dir(
 
1409
            specific_files=specific_files))
 
1410
        self.assertEqual(actual_result, preview_result)
 
1411
 
 
1412
    def test_iter_entries_by_dir_new(self):
 
1413
        tree = self.make_branch_and_tree('tree')
 
1414
        tt = tree.transform()
 
1415
        tt.new_file('new', tt.root, [b'contents'], b'new-id')
 
1416
        self.assertMatchingIterEntries(tt)
 
1417
 
 
1418
    def test_iter_entries_by_dir_deleted(self):
 
1419
        tree = self.make_branch_and_tree('tree')
 
1420
        self.build_tree(['tree/deleted'])
 
1421
        tree.add('deleted', b'deleted-id')
 
1422
        tt = tree.transform()
 
1423
        tt.delete_contents(tt.trans_id_file_id(b'deleted-id'))
 
1424
        self.assertMatchingIterEntries(tt)
 
1425
 
 
1426
    def test_iter_entries_by_dir_unversioned(self):
 
1427
        tree = self.make_branch_and_tree('tree')
 
1428
        self.build_tree(['tree/removed'])
 
1429
        tree.add('removed', b'removed-id')
 
1430
        tt = tree.transform()
 
1431
        tt.unversion_file(tt.trans_id_file_id(b'removed-id'))
 
1432
        self.assertMatchingIterEntries(tt)
 
1433
 
 
1434
    def test_iter_entries_by_dir_moved(self):
 
1435
        tree = self.make_branch_and_tree('tree')
 
1436
        self.build_tree(['tree/moved', 'tree/new_parent/'])
 
1437
        tree.add(['moved', 'new_parent'], [b'moved-id', b'new_parent-id'])
 
1438
        tt = tree.transform()
 
1439
        tt.adjust_path('moved', tt.trans_id_file_id(b'new_parent-id'),
 
1440
                       tt.trans_id_file_id(b'moved-id'))
 
1441
        self.assertMatchingIterEntries(tt)
 
1442
 
 
1443
    def test_iter_entries_by_dir_specific_files(self):
 
1444
        tree = self.make_branch_and_tree('tree')
 
1445
        tree.set_root_id(b'tree-root-id')
 
1446
        self.build_tree(['tree/parent/', 'tree/parent/child'])
 
1447
        tree.add(['parent', 'parent/child'], [b'parent-id', b'child-id'])
 
1448
        tt = tree.transform()
 
1449
        self.assertMatchingIterEntries(tt, ['', 'parent/child'])
 
1450
 
 
1451
    def test_symlink_content_summary(self):
 
1452
        self.requireFeature(SymlinkFeature)
 
1453
        preview = self.get_empty_preview()
 
1454
        preview.new_symlink('path', preview.root, 'target', b'path-id')
 
1455
        summary = preview.get_preview_tree().path_content_summary('path')
 
1456
        self.assertEqual(('symlink', None, None, 'target'), summary)
 
1457
 
 
1458
    def test_missing_content_summary(self):
 
1459
        preview = self.get_empty_preview()
 
1460
        summary = preview.get_preview_tree().path_content_summary('path')
 
1461
        self.assertEqual(('missing', None, None, None), summary)
 
1462
 
 
1463
    def test_deleted_content_summary(self):
 
1464
        tree = self.make_branch_and_tree('tree')
 
1465
        self.build_tree(['tree/path/'])
 
1466
        tree.add('path')
 
1467
        preview = tree.preview_transform()
 
1468
        self.addCleanup(preview.finalize)
 
1469
        preview.delete_contents(preview.trans_id_tree_path('path'))
 
1470
        summary = preview.get_preview_tree().path_content_summary('path')
 
1471
        self.assertEqual(('missing', None, None, None), summary)
 
1472
 
 
1473
    def test_file_content_summary_executable(self):
 
1474
        preview = self.get_empty_preview()
 
1475
        path_id = preview.new_file('path', preview.root, [
 
1476
                                   b'contents'], b'path-id')
 
1477
        preview.set_executability(True, path_id)
 
1478
        summary = preview.get_preview_tree().path_content_summary('path')
 
1479
        self.assertEqual(4, len(summary))
 
1480
        self.assertEqual('file', summary[0])
 
1481
        # size must be known
 
1482
        self.assertEqual(len('contents'), summary[1])
 
1483
        # executable
 
1484
        self.assertEqual(True, summary[2])
 
1485
        # will not have hash (not cheap to determine)
 
1486
        self.assertIs(None, summary[3])
 
1487
 
 
1488
    def test_change_executability(self):
 
1489
        tree = self.make_branch_and_tree('tree')
 
1490
        self.build_tree(['tree/path'])
 
1491
        tree.add('path')
 
1492
        preview = tree.preview_transform()
 
1493
        self.addCleanup(preview.finalize)
 
1494
        path_id = preview.trans_id_tree_path('path')
 
1495
        preview.set_executability(True, path_id)
 
1496
        summary = preview.get_preview_tree().path_content_summary('path')
 
1497
        self.assertEqual(True, summary[2])
 
1498
 
 
1499
    def test_file_content_summary_non_exec(self):
 
1500
        preview = self.get_empty_preview()
 
1501
        preview.new_file('path', preview.root, [b'contents'], b'path-id')
 
1502
        summary = preview.get_preview_tree().path_content_summary('path')
 
1503
        self.assertEqual(4, len(summary))
 
1504
        self.assertEqual('file', summary[0])
 
1505
        # size must be known
 
1506
        self.assertEqual(len('contents'), summary[1])
 
1507
        # not executable
 
1508
        self.assertEqual(False, summary[2])
 
1509
        # will not have hash (not cheap to determine)
 
1510
        self.assertIs(None, summary[3])
 
1511
 
 
1512
    def test_dir_content_summary(self):
 
1513
        preview = self.get_empty_preview()
 
1514
        preview.new_directory('path', preview.root, b'path-id')
 
1515
        summary = preview.get_preview_tree().path_content_summary('path')
 
1516
        self.assertEqual(('directory', None, None, None), summary)
 
1517
 
 
1518
    def test_tree_content_summary(self):
 
1519
        preview = self.get_empty_preview()
 
1520
        path = preview.new_directory('path', preview.root, b'path-id')
 
1521
        preview.set_tree_reference(b'rev-1', path)
 
1522
        summary = preview.get_preview_tree().path_content_summary('path')
 
1523
        self.assertEqual(4, len(summary))
 
1524
        self.assertEqual('tree-reference', summary[0])
 
1525
 
 
1526
    def test_annotate(self):
 
1527
        tree = self.make_branch_and_tree('tree')
 
1528
        self.build_tree_contents([('tree/file', b'a\n')])
 
1529
        tree.add('file', b'file-id')
 
1530
        tree.commit('a', rev_id=b'one')
 
1531
        self.build_tree_contents([('tree/file', b'a\nb\n')])
 
1532
        preview = tree.preview_transform()
 
1533
        self.addCleanup(preview.finalize)
 
1534
        file_trans_id = preview.trans_id_file_id(b'file-id')
 
1535
        preview.delete_contents(file_trans_id)
 
1536
        preview.create_file([b'a\nb\nc\n'], file_trans_id)
 
1537
        preview_tree = preview.get_preview_tree()
 
1538
        expected = [
 
1539
            (b'one', b'a\n'),
 
1540
            (b'me:', b'b\n'),
 
1541
            (b'me:', b'c\n'),
 
1542
        ]
 
1543
        annotation = preview_tree.annotate_iter(
 
1544
            'file', default_revision=b'me:')
 
1545
        self.assertEqual(expected, annotation)
 
1546
 
 
1547
    def test_annotate_missing(self):
 
1548
        preview = self.get_empty_preview()
 
1549
        preview.new_file('file', preview.root, [b'a\nb\nc\n'], b'file-id')
 
1550
        preview_tree = preview.get_preview_tree()
 
1551
        expected = [
 
1552
            (b'me:', b'a\n'),
 
1553
            (b'me:', b'b\n'),
 
1554
            (b'me:', b'c\n'),
 
1555
            ]
 
1556
        annotation = preview_tree.annotate_iter(
 
1557
            'file', default_revision=b'me:')
 
1558
        self.assertEqual(expected, annotation)
 
1559
 
 
1560
    def test_annotate_rename(self):
 
1561
        tree = self.make_branch_and_tree('tree')
 
1562
        self.build_tree_contents([('tree/file', b'a\n')])
 
1563
        tree.add('file', b'file-id')
 
1564
        tree.commit('a', rev_id=b'one')
 
1565
        preview = tree.preview_transform()
 
1566
        self.addCleanup(preview.finalize)
 
1567
        file_trans_id = preview.trans_id_file_id(b'file-id')
 
1568
        preview.adjust_path('newname', preview.root, file_trans_id)
 
1569
        preview_tree = preview.get_preview_tree()
 
1570
        expected = [
 
1571
            (b'one', b'a\n'),
 
1572
        ]
 
1573
        annotation = preview_tree.annotate_iter(
 
1574
            'file', default_revision=b'me:')
 
1575
        self.assertEqual(expected, annotation)
 
1576
 
 
1577
    def test_annotate_deleted(self):
 
1578
        tree = self.make_branch_and_tree('tree')
 
1579
        self.build_tree_contents([('tree/file', b'a\n')])
 
1580
        tree.add('file', b'file-id')
 
1581
        tree.commit('a', rev_id=b'one')
 
1582
        self.build_tree_contents([('tree/file', b'a\nb\n')])
 
1583
        preview = tree.preview_transform()
 
1584
        self.addCleanup(preview.finalize)
 
1585
        file_trans_id = preview.trans_id_file_id(b'file-id')
 
1586
        preview.delete_contents(file_trans_id)
 
1587
        preview_tree = preview.get_preview_tree()
 
1588
        annotation = preview_tree.annotate_iter(
 
1589
            'file', default_revision=b'me:')
 
1590
        self.assertIs(None, annotation)
 
1591
 
 
1592
    def test_stored_kind(self):
 
1593
        preview = self.get_empty_preview()
 
1594
        preview.new_file('file', preview.root, [b'a\nb\nc\n'], b'file-id')
 
1595
        preview_tree = preview.get_preview_tree()
 
1596
        self.assertEqual('file', preview_tree.stored_kind('file'))
 
1597
 
 
1598
    def test_is_executable(self):
 
1599
        preview = self.get_empty_preview()
 
1600
        preview.new_file('file', preview.root, [b'a\nb\nc\n'], b'file-id')
 
1601
        preview.set_executability(True, preview.trans_id_file_id(b'file-id'))
 
1602
        preview_tree = preview.get_preview_tree()
 
1603
        self.assertEqual(True, preview_tree.is_executable('file'))
 
1604
 
 
1605
    def test_get_set_parent_ids(self):
 
1606
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
1607
        self.assertEqual([], preview_tree.get_parent_ids())
 
1608
        preview_tree.set_parent_ids([b'rev-1'])
 
1609
        self.assertEqual([b'rev-1'], preview_tree.get_parent_ids())
 
1610
 
 
1611
    def test_plan_file_merge(self):
 
1612
        work_a = self.make_branch_and_tree('wta')
 
1613
        self.build_tree_contents([('wta/file', b'a\nb\nc\nd\n')])
 
1614
        work_a.add('file', b'file-id')
 
1615
        base_id = work_a.commit('base version')
 
1616
        tree_b = work_a.controldir.sprout('wtb').open_workingtree()
 
1617
        preview = work_a.preview_transform()
 
1618
        self.addCleanup(preview.finalize)
 
1619
        trans_id = preview.trans_id_file_id(b'file-id')
 
1620
        preview.delete_contents(trans_id)
 
1621
        preview.create_file([b'b\nc\nd\ne\n'], trans_id)
 
1622
        self.build_tree_contents([('wtb/file', b'a\nc\nd\nf\n')])
 
1623
        tree_a = preview.get_preview_tree()
 
1624
        tree_a.set_parent_ids([base_id])
 
1625
        self.assertEqual([
 
1626
            ('killed-a', b'a\n'),
 
1627
            ('killed-b', b'b\n'),
 
1628
            ('unchanged', b'c\n'),
 
1629
            ('unchanged', b'd\n'),
 
1630
            ('new-a', b'e\n'),
 
1631
            ('new-b', b'f\n'),
 
1632
        ], list(tree_a.plan_file_merge('file', tree_b)))
 
1633
 
 
1634
    def test_plan_file_merge_revision_tree(self):
 
1635
        work_a = self.make_branch_and_tree('wta')
 
1636
        self.build_tree_contents([('wta/file', b'a\nb\nc\nd\n')])
 
1637
        work_a.add('file', b'file-id')
 
1638
        base_id = work_a.commit('base version')
 
1639
        tree_b = work_a.controldir.sprout('wtb').open_workingtree()
 
1640
        preview = work_a.basis_tree().preview_transform()
 
1641
        self.addCleanup(preview.finalize)
 
1642
        trans_id = preview.trans_id_file_id(b'file-id')
 
1643
        preview.delete_contents(trans_id)
 
1644
        preview.create_file([b'b\nc\nd\ne\n'], trans_id)
 
1645
        self.build_tree_contents([('wtb/file', b'a\nc\nd\nf\n')])
 
1646
        tree_a = preview.get_preview_tree()
 
1647
        tree_a.set_parent_ids([base_id])
 
1648
        self.assertEqual([
 
1649
            ('killed-a', b'a\n'),
 
1650
            ('killed-b', b'b\n'),
 
1651
            ('unchanged', b'c\n'),
 
1652
            ('unchanged', b'd\n'),
 
1653
            ('new-a', b'e\n'),
 
1654
            ('new-b', b'f\n'),
 
1655
        ], list(tree_a.plan_file_merge('file', tree_b)))
 
1656
 
 
1657
    def test_walkdirs(self):
 
1658
        preview = self.get_empty_preview()
 
1659
        preview.new_directory('', ROOT_PARENT, b'tree-root')
 
1660
        # FIXME: new_directory should mark root.
 
1661
        preview.fixup_new_roots()
 
1662
        preview_tree = preview.get_preview_tree()
 
1663
        preview.new_file('a', preview.root, [b'contents'], b'a-id')
 
1664
        expected = [(('', b'tree-root'),
 
1665
                     [('a', 'a', 'file', None, b'a-id', 'file')])]
 
1666
        self.assertEqual(expected, list(preview_tree.walkdirs()))
 
1667
 
 
1668
    def test_extras(self):
 
1669
        work_tree = self.make_branch_and_tree('tree')
 
1670
        self.build_tree(['tree/removed-file', 'tree/existing-file',
 
1671
                         'tree/not-removed-file'])
 
1672
        work_tree.add(['removed-file', 'not-removed-file'])
 
1673
        preview = work_tree.preview_transform()
 
1674
        self.addCleanup(preview.finalize)
 
1675
        preview.new_file('new-file', preview.root, [b'contents'])
 
1676
        preview.new_file('new-versioned-file', preview.root, [b'contents'],
 
1677
                         b'new-versioned-id')
 
1678
        tree = preview.get_preview_tree()
 
1679
        preview.unversion_file(preview.trans_id_tree_path('removed-file'))
 
1680
        self.assertEqual({'new-file', 'removed-file', 'existing-file'},
 
1681
                         set(tree.extras()))
 
1682
 
 
1683
    def test_merge_into_preview(self):
 
1684
        work_tree = self.make_branch_and_tree('tree')
 
1685
        self.build_tree_contents([('tree/file', b'b\n')])
 
1686
        work_tree.add('file', b'file-id')
 
1687
        work_tree.commit('first commit')
 
1688
        child_tree = work_tree.controldir.sprout('child').open_workingtree()
 
1689
        self.build_tree_contents([('child/file', b'b\nc\n')])
 
1690
        child_tree.commit('child commit')
 
1691
        child_tree.lock_write()
 
1692
        self.addCleanup(child_tree.unlock)
 
1693
        work_tree.lock_write()
 
1694
        self.addCleanup(work_tree.unlock)
 
1695
        preview = work_tree.preview_transform()
 
1696
        self.addCleanup(preview.finalize)
 
1697
        file_trans_id = preview.trans_id_file_id(b'file-id')
 
1698
        preview.delete_contents(file_trans_id)
 
1699
        preview.create_file([b'a\nb\n'], file_trans_id)
 
1700
        preview_tree = preview.get_preview_tree()
 
1701
        merger = Merger.from_revision_ids(preview_tree,
 
1702
                                          child_tree.branch.last_revision(),
 
1703
                                          other_branch=child_tree.branch,
 
1704
                                          tree_branch=work_tree.branch)
 
1705
        merger.merge_type = Merge3Merger
 
1706
        tt = merger.make_merger().make_preview_transform()
 
1707
        self.addCleanup(tt.finalize)
 
1708
        final_tree = tt.get_preview_tree()
 
1709
        self.assertEqual(
 
1710
            b'a\nb\nc\n',
 
1711
            final_tree.get_file_text(final_tree.id2path(b'file-id')))
 
1712
 
 
1713
    def test_merge_preview_into_workingtree(self):
 
1714
        tree = self.make_branch_and_tree('tree')
 
1715
        tree.set_root_id(b'TREE_ROOT')
 
1716
        tt = tree.preview_transform()
 
1717
        self.addCleanup(tt.finalize)
 
1718
        tt.new_file('name', tt.root, [b'content'], b'file-id')
 
1719
        tree2 = self.make_branch_and_tree('tree2')
 
1720
        tree2.set_root_id(b'TREE_ROOT')
 
1721
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
 
1722
                                         tree.basis_tree())
 
1723
        merger.merge_type = Merge3Merger
 
1724
        merger.do_merge()
 
1725
 
 
1726
    def test_merge_preview_into_workingtree_handles_conflicts(self):
 
1727
        tree = self.make_branch_and_tree('tree')
 
1728
        self.build_tree_contents([('tree/foo', b'bar')])
 
1729
        tree.add('foo', b'foo-id')
 
1730
        tree.commit('foo')
 
1731
        tt = tree.preview_transform()
 
1732
        self.addCleanup(tt.finalize)
 
1733
        trans_id = tt.trans_id_file_id(b'foo-id')
 
1734
        tt.delete_contents(trans_id)
 
1735
        tt.create_file([b'baz'], trans_id)
 
1736
        tree2 = tree.controldir.sprout('tree2').open_workingtree()
 
1737
        self.build_tree_contents([('tree2/foo', b'qux')])
 
1738
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
 
1739
                                         tree.basis_tree())
 
1740
        merger.merge_type = Merge3Merger
 
1741
        merger.do_merge()
 
1742
 
 
1743
    def test_has_filename(self):
 
1744
        wt = self.make_branch_and_tree('tree')
 
1745
        self.build_tree(['tree/unmodified', 'tree/removed', 'tree/modified'])
 
1746
        tt = wt.preview_transform()
 
1747
        removed_id = tt.trans_id_tree_path('removed')
 
1748
        tt.delete_contents(removed_id)
 
1749
        tt.new_file('new', tt.root, [b'contents'])
 
1750
        modified_id = tt.trans_id_tree_path('modified')
 
1751
        tt.delete_contents(modified_id)
 
1752
        tt.create_file([b'modified-contents'], modified_id)
 
1753
        self.addCleanup(tt.finalize)
 
1754
        tree = tt.get_preview_tree()
 
1755
        self.assertTrue(tree.has_filename('unmodified'))
 
1756
        self.assertFalse(tree.has_filename('not-present'))
 
1757
        self.assertFalse(tree.has_filename('removed'))
 
1758
        self.assertTrue(tree.has_filename('new'))
 
1759
        self.assertTrue(tree.has_filename('modified'))
 
1760
 
 
1761
    def test_is_executable(self):
 
1762
        tree = self.make_branch_and_tree('tree')
 
1763
        preview = tree.preview_transform()
 
1764
        self.addCleanup(preview.finalize)
 
1765
        preview.new_file('foo', preview.root, [b'bar'], b'baz-id')
 
1766
        preview_tree = preview.get_preview_tree()
 
1767
        self.assertEqual(False, preview_tree.is_executable('tree/foo'))
 
1768
 
 
1769
    def test_commit_preview_tree(self):
 
1770
        tree = self.make_branch_and_tree('tree')
 
1771
        rev_id = tree.commit('rev1')
 
1772
        tree.branch.lock_write()
 
1773
        self.addCleanup(tree.branch.unlock)
 
1774
        tt = tree.preview_transform()
 
1775
        tt.new_file('file', tt.root, [b'contents'], b'file_id')
 
1776
        self.addCleanup(tt.finalize)
 
1777
        preview = tt.get_preview_tree()
 
1778
        preview.set_parent_ids([rev_id])
 
1779
        builder = tree.branch.get_commit_builder([rev_id])
 
1780
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
 
1781
        builder.finish_inventory()
 
1782
        rev2_id = builder.commit('rev2')
 
1783
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
 
1784
        self.assertEqual(b'contents', rev2_tree.get_file_text('file'))
 
1785
 
 
1786
    def test_ascii_limbo_paths(self):
 
1787
        self.requireFeature(features.UnicodeFilenameFeature)
 
1788
        branch = self.make_branch('any')
 
1789
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
 
1790
        tt = tree.preview_transform()
 
1791
        self.addCleanup(tt.finalize)
 
1792
        foo_id = tt.new_directory('', ROOT_PARENT)
 
1793
        bar_id = tt.new_file(u'\u1234bar', foo_id, [b'contents'])
 
1794
        limbo_path = tt._limbo_name(bar_id)
 
1795
        self.assertEqual(limbo_path, limbo_path)
 
1796
 
 
1797
 
714
1798
class FakeSerializer(object):
715
1799
    """Serializer implementation that simply returns the input.
716
1800
 
1007
2091
        tt.delete_contents(dir_tid)
1008
2092
        tt.unversion_file(dir_tid)
1009
2093
        # There should be a conflict because dir still contain foo
1010
 
        raw_conflicts = tt.find_raw_conflicts()
 
2094
        raw_conflicts = tt.find_conflicts()
1011
2095
        self.assertLength(1, raw_conflicts)
1012
2096
        self.assertEqual(('missing parent', 'new-1'), raw_conflicts[0])
1013
2097
        return tt, orphan_tid