/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/transform.py

  • Committer: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2020-11-19 19:46:23 UTC
  • mfrom: (7524.2.5 merge-3.1)
  • Revision ID: breezy.the.bot@gmail.com-20201119194623-5tfi4z6ktdzo0z3y
Merge lp:brz/3.1.

Merged from https://code.launchpad.net/~jelmer/brz/merge-3.1/+merge/394038

Show diffs side-by-side

added added

removed removed

Lines of Context:
639
639
        return [(self.get_path(t), t) for t in trans_ids]
640
640
 
641
641
 
642
 
def build_tree(tree, wt, accelerator_tree=None, hardlink=False,
643
 
               delta_from_tree=False):
644
 
    """Create working tree for a branch, using a TreeTransform.
645
 
 
646
 
    This function should be used on empty trees, having a tree root at most.
647
 
    (see merge and revert functionality for working with existing trees)
648
 
 
649
 
    Existing files are handled like so:
650
 
 
651
 
    - Existing bzrdirs take precedence over creating new items.  They are
652
 
      created as '%s.diverted' % name.
653
 
    - Otherwise, if the content on disk matches the content we are building,
654
 
      it is silently replaced.
655
 
    - Otherwise, conflict resolution will move the old file to 'oldname.moved'.
656
 
 
657
 
    :param tree: The tree to convert wt into a copy of
658
 
    :param wt: The working tree that files will be placed into
659
 
    :param accelerator_tree: A tree which can be used for retrieving file
660
 
        contents more quickly than tree itself, i.e. a workingtree.  tree
661
 
        will be used for cases where accelerator_tree's content is different.
662
 
    :param hardlink: If true, hard-link files to accelerator_tree, where
663
 
        possible.  accelerator_tree must implement abspath, i.e. be a
664
 
        working tree.
665
 
    :param delta_from_tree: If true, build_tree may use the input Tree to
666
 
        generate the inventory delta.
667
 
    """
668
 
    with contextlib.ExitStack() as exit_stack:
669
 
        exit_stack.enter_context(wt.lock_tree_write())
670
 
        exit_stack.enter_context(tree.lock_read())
671
 
        if accelerator_tree is not None:
672
 
            exit_stack.enter_context(accelerator_tree.lock_read())
673
 
        return _build_tree(tree, wt, accelerator_tree, hardlink,
674
 
                           delta_from_tree)
675
 
 
676
 
 
677
 
def _build_tree(tree, wt, accelerator_tree, hardlink, delta_from_tree):
678
 
    """See build_tree."""
679
 
    for num, _unused in enumerate(wt.all_versioned_paths()):
680
 
        if num > 0:  # more than just a root
681
 
            raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
682
 
    file_trans_id = {}
683
 
    top_pb = ui.ui_factory.nested_progress_bar()
684
 
    pp = ProgressPhase("Build phase", 2, top_pb)
685
 
    if tree.path2id('') is not None:
686
 
        # This is kind of a hack: we should be altering the root
687
 
        # as part of the regular tree shape diff logic.
688
 
        # The conditional test here is to avoid doing an
689
 
        # expensive operation (flush) every time the root id
690
 
        # is set within the tree, nor setting the root and thus
691
 
        # marking the tree as dirty, because we use two different
692
 
        # idioms here: tree interfaces and inventory interfaces.
693
 
        if wt.path2id('') != tree.path2id(''):
694
 
            wt.set_root_id(tree.path2id(''))
695
 
            wt.flush()
696
 
    tt = wt.transform()
697
 
    divert = set()
698
 
    try:
699
 
        pp.next_phase()
700
 
        file_trans_id[find_previous_path(wt, tree, '')] = tt.trans_id_tree_path('')
701
 
        with ui.ui_factory.nested_progress_bar() as pb:
702
 
            deferred_contents = []
703
 
            num = 0
704
 
            total = len(tree.all_versioned_paths())
705
 
            if delta_from_tree:
706
 
                precomputed_delta = []
707
 
            else:
708
 
                precomputed_delta = None
709
 
            # Check if tree inventory has content. If so, we populate
710
 
            # existing_files with the directory content. If there are no
711
 
            # entries we skip populating existing_files as its not used.
712
 
            # This improves performance and unncessary work on large
713
 
            # directory trees. (#501307)
714
 
            if total > 0:
715
 
                existing_files = set()
716
 
                for dir, files in wt.walkdirs():
717
 
                    existing_files.update(f[0] for f in files)
718
 
            for num, (tree_path, entry) in \
719
 
                    enumerate(tree.iter_entries_by_dir()):
720
 
                pb.update(gettext("Building tree"), num
721
 
                          - len(deferred_contents), total)
722
 
                if entry.parent_id is None:
723
 
                    continue
724
 
                reparent = False
725
 
                file_id = entry.file_id
726
 
                if delta_from_tree:
727
 
                    precomputed_delta.append((None, tree_path, file_id, entry))
728
 
                if tree_path in existing_files:
729
 
                    target_path = wt.abspath(tree_path)
730
 
                    kind = file_kind(target_path)
731
 
                    if kind == "directory":
732
 
                        try:
733
 
                            controldir.ControlDir.open(target_path)
734
 
                        except errors.NotBranchError:
735
 
                            pass
736
 
                        else:
737
 
                            divert.add(tree_path)
738
 
                    if (tree_path not in divert
739
 
                        and _content_match(
740
 
                            tree, entry, tree_path, kind, target_path)):
741
 
                        tt.delete_contents(tt.trans_id_tree_path(tree_path))
742
 
                        if kind == 'directory':
743
 
                            reparent = True
744
 
                parent_id = file_trans_id[osutils.dirname(tree_path)]
745
 
                if entry.kind == 'file':
746
 
                    # We *almost* replicate new_by_entry, so that we can defer
747
 
                    # getting the file text, and get them all at once.
748
 
                    trans_id = tt.create_path(entry.name, parent_id)
749
 
                    file_trans_id[tree_path] = trans_id
750
 
                    tt.version_file(trans_id, file_id=file_id)
751
 
                    executable = tree.is_executable(tree_path)
752
 
                    if executable:
753
 
                        tt.set_executability(executable, trans_id)
754
 
                    trans_data = (trans_id, tree_path, entry.text_sha1)
755
 
                    deferred_contents.append((tree_path, trans_data))
756
 
                else:
757
 
                    file_trans_id[tree_path] = new_by_entry(
758
 
                        tree_path, tt, entry, parent_id, tree)
759
 
                if reparent:
760
 
                    new_trans_id = file_trans_id[tree_path]
761
 
                    old_parent = tt.trans_id_tree_path(tree_path)
762
 
                    _reparent_children(tt, old_parent, new_trans_id)
763
 
            offset = num + 1 - len(deferred_contents)
764
 
            _create_files(tt, tree, deferred_contents, pb, offset,
765
 
                          accelerator_tree, hardlink)
766
 
        pp.next_phase()
767
 
        divert_trans = set(file_trans_id[f] for f in divert)
768
 
 
769
 
        def resolver(t, c):
770
 
            return resolve_checkout(t, c, divert_trans)
771
 
        raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
772
 
        if len(raw_conflicts) > 0:
773
 
            precomputed_delta = None
774
 
        conflicts = tt.cook_conflicts(raw_conflicts)
775
 
        for conflict in conflicts:
776
 
            trace.warning(str(conflict))
777
 
        try:
778
 
            wt.add_conflicts(conflicts)
779
 
        except errors.UnsupportedOperation:
780
 
            pass
781
 
        result = tt.apply(no_conflicts=True,
782
 
                          precomputed_delta=precomputed_delta)
783
 
    finally:
784
 
        tt.finalize()
785
 
        top_pb.finished()
786
 
    return result
787
 
 
788
 
 
789
 
def _create_files(tt, tree, desired_files, pb, offset, accelerator_tree,
790
 
                  hardlink):
791
 
    total = len(desired_files) + offset
792
 
    wt = tt._tree
793
 
    if accelerator_tree is None:
794
 
        new_desired_files = desired_files
795
 
    else:
796
 
        iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
797
 
        unchanged = [
798
 
            change.path for change in iter
799
 
            if not (change.changed_content or change.executable[0] != change.executable[1])]
800
 
        if accelerator_tree.supports_content_filtering():
801
 
            unchanged = [(tp, ap) for (tp, ap) in unchanged
802
 
                         if not next(accelerator_tree.iter_search_rules([ap]))]
803
 
        unchanged = dict(unchanged)
804
 
        new_desired_files = []
805
 
        count = 0
806
 
        for unused_tree_path, (trans_id, tree_path, text_sha1) in desired_files:
807
 
            accelerator_path = unchanged.get(tree_path)
808
 
            if accelerator_path is None:
809
 
                new_desired_files.append((tree_path,
810
 
                                          (trans_id, tree_path, text_sha1)))
811
 
                continue
812
 
            pb.update(gettext('Adding file contents'), count + offset, total)
813
 
            if hardlink:
814
 
                tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
815
 
                                   trans_id)
816
 
            else:
817
 
                with accelerator_tree.get_file(accelerator_path) as f:
818
 
                    chunks = osutils.file_iterator(f)
819
 
                    if wt.supports_content_filtering():
820
 
                        filters = wt._content_filter_stack(tree_path)
821
 
                        chunks = filtered_output_bytes(chunks, filters,
822
 
                                                       ContentFilterContext(tree_path, tree))
823
 
                    tt.create_file(chunks, trans_id, sha1=text_sha1)
824
 
            count += 1
825
 
        offset += count
826
 
    for count, ((trans_id, tree_path, text_sha1), contents) in enumerate(
827
 
            tree.iter_files_bytes(new_desired_files)):
828
 
        if wt.supports_content_filtering():
829
 
            filters = wt._content_filter_stack(tree_path)
830
 
            contents = filtered_output_bytes(contents, filters,
831
 
                                             ContentFilterContext(tree_path, tree))
832
 
        tt.create_file(contents, trans_id, sha1=text_sha1)
833
 
        pb.update(gettext('Adding file contents'), count + offset, total)
834
 
 
835
 
 
836
642
def _reparent_children(tt, old_parent, new_parent):
837
643
    for child in tt.iter_tree_children(old_parent):
838
644
        tt.adjust_path(tt.final_name(child), new_parent, child)
845
651
    return by_parent[old_parent]
846
652
 
847
653
 
848
 
def _content_match(tree, entry, tree_path, kind, target_path):
849
 
    if entry.kind != kind:
850
 
        return False
851
 
    if entry.kind == "directory":
852
 
        return True
853
 
    if entry.kind == "file":
854
 
        with open(target_path, 'rb') as f1, \
855
 
                tree.get_file(tree_path) as f2:
856
 
            if osutils.compare_files(f1, f2):
857
 
                return True
858
 
    elif entry.kind == "symlink":
859
 
        if tree.get_symlink_target(tree_path) == os.readlink(target_path):
860
 
            return True
861
 
    return False
862
 
 
863
 
 
864
 
def resolve_checkout(tt, conflicts, divert):
865
 
    new_conflicts = set()
866
 
    for c_type, conflict in ((c[0], c) for c in conflicts):
867
 
        # Anything but a 'duplicate' would indicate programmer error
868
 
        if c_type != 'duplicate':
869
 
            raise AssertionError(c_type)
870
 
        # Now figure out which is new and which is old
871
 
        if tt.new_contents(conflict[1]):
872
 
            new_file = conflict[1]
873
 
            old_file = conflict[2]
874
 
        else:
875
 
            new_file = conflict[2]
876
 
            old_file = conflict[1]
877
 
 
878
 
        # We should only get here if the conflict wasn't completely
879
 
        # resolved
880
 
        final_parent = tt.final_parent(old_file)
881
 
        if new_file in divert:
882
 
            new_name = tt.final_name(old_file) + '.diverted'
883
 
            tt.adjust_path(new_name, final_parent, new_file)
884
 
            new_conflicts.add((c_type, 'Diverted to',
885
 
                               new_file, old_file))
886
 
        else:
887
 
            new_name = tt.final_name(old_file) + '.moved'
888
 
            tt.adjust_path(new_name, final_parent, old_file)
889
 
            new_conflicts.add((c_type, 'Moved existing file to',
890
 
                               old_file, new_file))
891
 
    return new_conflicts
892
 
 
893
 
 
894
654
def new_by_entry(path, tt, entry, parent_id, tree):
895
655
    """Create a new file according to its inventory entry"""
896
656
    name = entry.name
953
713
        tt.set_executability(entry.executable, trans_id)
954
714
 
955
715
 
956
 
def revert(working_tree, target_tree, filenames, backups=False,
957
 
           pb=None, change_reporter=None):
958
 
    """Revert a working tree's contents to those of a target tree."""
959
 
    pb = ui.ui_factory.nested_progress_bar()
960
 
    try:
961
 
        with target_tree.lock_read(), working_tree.transform(pb) as tt:
962
 
            pp = ProgressPhase("Revert phase", 3, pb)
963
 
            conflicts, merge_modified = _prepare_revert_transform(
964
 
                working_tree, target_tree, tt, filenames, backups, pp)
965
 
            if change_reporter:
966
 
                from . import delta
967
 
                change_reporter = delta._ChangeReporter(
968
 
                    unversioned_filter=working_tree.is_ignored)
969
 
                delta.report_changes(tt.iter_changes(), change_reporter)
970
 
            for conflict in conflicts:
971
 
                trace.warning(str(conflict))
972
 
            pp.next_phase()
973
 
            tt.apply()
974
 
            if working_tree.supports_merge_modified():
975
 
                working_tree.set_merge_modified(merge_modified)
976
 
    finally:
977
 
        pb.clear()
978
 
    return conflicts
979
 
 
980
 
 
981
 
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
 
716
def _prepare_revert_transform(es, working_tree, target_tree, tt, filenames,
982
717
                              backups, pp, basis_tree=None,
983
718
                              merge_modified=None):
984
719
    with ui.ui_factory.nested_progress_bar() as child_pb:
985
720
        if merge_modified is None:
986
721
            merge_modified = working_tree.merge_modified()
987
 
        merge_modified = _alter_files(working_tree, target_tree, tt,
 
722
        merge_modified = _alter_files(es, working_tree, target_tree, tt,
988
723
                                      child_pb, filenames, backups,
989
724
                                      merge_modified, basis_tree)
990
725
    with ui.ui_factory.nested_progress_bar() as child_pb:
994
729
    return conflicts, merge_modified
995
730
 
996
731
 
997
 
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
 
732
def revert(working_tree, target_tree, filenames, backups=False,
 
733
           pb=None, change_reporter=None, merge_modified=None, basis_tree=None):
 
734
    """Revert a working tree's contents to those of a target tree."""
 
735
    with contextlib.ExitStack() as es:
 
736
        pb = es.enter_context(ui.ui_factory.nested_progress_bar())
 
737
        es.enter_context(target_tree.lock_read())
 
738
        tt = es.enter_context(working_tree.transform(pb))
 
739
        pp = ProgressPhase("Revert phase", 3, pb)
 
740
        conflicts, merge_modified = _prepare_revert_transform(
 
741
            es, working_tree, target_tree, tt, filenames, backups, pp)
 
742
        if change_reporter:
 
743
            from . import delta
 
744
            change_reporter = delta._ChangeReporter(
 
745
                unversioned_filter=working_tree.is_ignored)
 
746
            delta.report_changes(tt.iter_changes(), change_reporter)
 
747
        for conflict in conflicts:
 
748
            trace.warning(str(conflict))
 
749
        pp.next_phase()
 
750
        tt.apply()
 
751
        if working_tree.supports_merge_modified():
 
752
            working_tree.set_merge_modified(merge_modified)
 
753
    return conflicts
 
754
 
 
755
 
 
756
def _alter_files(es, working_tree, target_tree, tt, pb, specific_files,
998
757
                 backups, merge_modified, basis_tree=None):
999
758
    if basis_tree is not None:
1000
 
        basis_tree.lock_read()
 
759
        es.enter_context(basis_tree.lock_read())
1001
760
    # We ask the working_tree for its changes relative to the target, rather
1002
761
    # than the target changes relative to the working tree. Because WT4 has an
1003
762
    # optimizer to compare itself to a target, but no optimizer for the
1008
767
        skip_root = True
1009
768
    else:
1010
769
        skip_root = False
1011
 
    try:
1012
 
        deferred_files = []
1013
 
        for id_num, change in enumerate(change_list):
1014
 
            target_path, wt_path = change.path
1015
 
            target_versioned, wt_versioned = change.versioned
1016
 
            target_parent = change.parent_id[0]
1017
 
            target_name, wt_name = change.name
1018
 
            target_kind, wt_kind = change.kind
1019
 
            target_executable, wt_executable = change.executable
1020
 
            if skip_root and wt_path == '':
1021
 
                continue
1022
 
            trans_id = tt.trans_id_file_id(change.file_id)
1023
 
            mode_id = None
1024
 
            if change.changed_content:
1025
 
                keep_content = False
1026
 
                if wt_kind == 'file' and (backups or target_kind is None):
1027
 
                    wt_sha1 = working_tree.get_file_sha1(wt_path)
1028
 
                    if merge_modified.get(wt_path) != wt_sha1:
1029
 
                        # acquire the basis tree lazily to prevent the
1030
 
                        # expense of accessing it when it's not needed ?
1031
 
                        # (Guessing, RBC, 200702)
1032
 
                        if basis_tree is None:
1033
 
                            basis_tree = working_tree.basis_tree()
1034
 
                            basis_tree.lock_read()
1035
 
                        basis_inter = InterTree.get(basis_tree, working_tree)
1036
 
                        basis_path = basis_inter.find_source_path(wt_path)
1037
 
                        if basis_path is None:
1038
 
                            if target_kind is None and not target_versioned:
1039
 
                                keep_content = True
1040
 
                        else:
1041
 
                            if wt_sha1 != basis_tree.get_file_sha1(basis_path):
1042
 
                                keep_content = True
1043
 
                if wt_kind is not None:
1044
 
                    if not keep_content:
1045
 
                        tt.delete_contents(trans_id)
1046
 
                    elif target_kind is not None:
1047
 
                        parent_trans_id = tt.trans_id_tree_path(osutils.dirname(wt_path))
1048
 
                        backup_name = tt._available_backup_name(
1049
 
                            wt_name, parent_trans_id)
1050
 
                        tt.adjust_path(backup_name, parent_trans_id, trans_id)
1051
 
                        new_trans_id = tt.create_path(wt_name, parent_trans_id)
1052
 
                        if wt_versioned and target_versioned:
1053
 
                            tt.unversion_file(trans_id)
1054
 
                            tt.version_file(
1055
 
                                new_trans_id, file_id=getattr(change, 'file_id', None))
1056
 
                        # New contents should have the same unix perms as old
1057
 
                        # contents
1058
 
                        mode_id = trans_id
1059
 
                        trans_id = new_trans_id
1060
 
                if target_kind in ('directory', 'tree-reference'):
1061
 
                    tt.create_directory(trans_id)
1062
 
                    if target_kind == 'tree-reference':
1063
 
                        revision = target_tree.get_reference_revision(
1064
 
                            target_path)
1065
 
                        tt.set_tree_reference(revision, trans_id)
1066
 
                elif target_kind == 'symlink':
1067
 
                    tt.create_symlink(target_tree.get_symlink_target(
1068
 
                        target_path), trans_id)
1069
 
                elif target_kind == 'file':
1070
 
                    deferred_files.append(
1071
 
                        (target_path, (trans_id, mode_id, target_path)))
 
770
    deferred_files = []
 
771
    for id_num, change in enumerate(change_list):
 
772
        target_path, wt_path = change.path
 
773
        target_versioned, wt_versioned = change.versioned
 
774
        target_parent = change.parent_id[0]
 
775
        target_name, wt_name = change.name
 
776
        target_kind, wt_kind = change.kind
 
777
        target_executable, wt_executable = change.executable
 
778
        if skip_root and wt_path == '':
 
779
            continue
 
780
        mode_id = None
 
781
        if wt_path is not None:
 
782
            trans_id = tt.trans_id_tree_path(wt_path)
 
783
        else:
 
784
            trans_id = tt.assign_id()
 
785
        if change.changed_content:
 
786
            keep_content = False
 
787
            if wt_kind == 'file' and (backups or target_kind is None):
 
788
                wt_sha1 = working_tree.get_file_sha1(wt_path)
 
789
                if merge_modified.get(wt_path) != wt_sha1:
 
790
                    # acquire the basis tree lazily to prevent the
 
791
                    # expense of accessing it when it's not needed ?
 
792
                    # (Guessing, RBC, 200702)
1072
793
                    if basis_tree is None:
1073
794
                        basis_tree = working_tree.basis_tree()
1074
 
                        basis_tree.lock_read()
1075
 
                    new_sha1 = target_tree.get_file_sha1(target_path)
1076
 
                    basis_inter = InterTree.get(basis_tree, target_tree)
1077
 
                    basis_path = basis_inter.find_source_path(target_path)
1078
 
                    if (basis_path is not None and
1079
 
                            new_sha1 == basis_tree.get_file_sha1(basis_path)):
1080
 
                        # If the new contents of the file match what is in basis,
1081
 
                        # then there is no need to store in merge_modified.
1082
 
                        if basis_path in merge_modified:
1083
 
                            del merge_modified[basis_path]
 
795
                        es.enter_context(basis_tree.lock_read())
 
796
                    basis_inter = InterTree.get(basis_tree, working_tree)
 
797
                    basis_path = basis_inter.find_source_path(wt_path)
 
798
                    if basis_path is None:
 
799
                        if target_kind is None and not target_versioned:
 
800
                            keep_content = True
1084
801
                    else:
1085
 
                        merge_modified[target_path] = new_sha1
1086
 
 
1087
 
                    # preserve the execute bit when backing up
1088
 
                    if keep_content and wt_executable == target_executable:
1089
 
                        tt.set_executability(target_executable, trans_id)
 
802
                        if wt_sha1 != basis_tree.get_file_sha1(basis_path):
 
803
                            keep_content = True
 
804
            if wt_kind is not None:
 
805
                if not keep_content:
 
806
                    tt.delete_contents(trans_id)
1090
807
                elif target_kind is not None:
1091
 
                    raise AssertionError(target_kind)
1092
 
            if not wt_versioned and target_versioned:
1093
 
                tt.version_file(
1094
 
                    trans_id, file_id=getattr(change, 'file_id', None))
1095
 
            if wt_versioned and not target_versioned:
1096
 
                tt.unversion_file(trans_id)
1097
 
            if (target_name is not None
1098
 
                    and (wt_name != target_name or change.is_reparented())):
1099
 
                if target_path == '':
1100
 
                    parent_trans = ROOT_PARENT
1101
 
                else:
1102
 
                    parent_trans = tt.trans_id_file_id(target_parent)
1103
 
                if wt_path == '' and wt_versioned:
1104
 
                    tt.adjust_root_path(target_name, parent_trans)
1105
 
                else:
1106
 
                    tt.adjust_path(target_name, parent_trans, trans_id)
1107
 
            if wt_executable != target_executable and target_kind == "file":
1108
 
                tt.set_executability(target_executable, trans_id)
1109
 
        if working_tree.supports_content_filtering():
1110
 
            for (trans_id, mode_id, target_path), bytes in (
1111
 
                    target_tree.iter_files_bytes(deferred_files)):
1112
 
                # We're reverting a tree to the target tree so using the
1113
 
                # target tree to find the file path seems the best choice
1114
 
                # here IMO - Ian C 27/Oct/2009
1115
 
                filters = working_tree._content_filter_stack(target_path)
1116
 
                bytes = filtered_output_bytes(
1117
 
                    bytes, filters,
1118
 
                    ContentFilterContext(target_path, working_tree))
1119
 
                tt.create_file(bytes, trans_id, mode_id)
1120
 
        else:
1121
 
            for (trans_id, mode_id, target_path), bytes in target_tree.iter_files_bytes(
1122
 
                    deferred_files):
1123
 
                tt.create_file(bytes, trans_id, mode_id)
1124
 
        tt.fixup_new_roots()
1125
 
    finally:
1126
 
        if basis_tree is not None:
1127
 
            basis_tree.unlock()
 
808
                    parent_trans_id = tt.trans_id_tree_path(osutils.dirname(wt_path))
 
809
                    backup_name = tt._available_backup_name(
 
810
                        wt_name, parent_trans_id)
 
811
                    tt.adjust_path(backup_name, parent_trans_id, trans_id)
 
812
                    new_trans_id = tt.create_path(wt_name, parent_trans_id)
 
813
                    if wt_versioned and target_versioned:
 
814
                        tt.unversion_file(trans_id)
 
815
                        tt.version_file(
 
816
                            new_trans_id, file_id=getattr(change, 'file_id', None))
 
817
                    # New contents should have the same unix perms as old
 
818
                    # contents
 
819
                    mode_id = trans_id
 
820
                    trans_id = new_trans_id
 
821
            if target_kind in ('directory', 'tree-reference'):
 
822
                tt.create_directory(trans_id)
 
823
                if target_kind == 'tree-reference':
 
824
                    revision = target_tree.get_reference_revision(
 
825
                        target_path)
 
826
                    tt.set_tree_reference(revision, trans_id)
 
827
            elif target_kind == 'symlink':
 
828
                tt.create_symlink(target_tree.get_symlink_target(
 
829
                    target_path), trans_id)
 
830
            elif target_kind == 'file':
 
831
                deferred_files.append(
 
832
                    (target_path, (trans_id, mode_id, target_path)))
 
833
                if basis_tree is None:
 
834
                    basis_tree = working_tree.basis_tree()
 
835
                    es.enter_context(basis_tree.lock_read())
 
836
                new_sha1 = target_tree.get_file_sha1(target_path)
 
837
                basis_inter = InterTree.get(basis_tree, target_tree)
 
838
                basis_path = basis_inter.find_source_path(target_path)
 
839
                if (basis_path is not None and
 
840
                        new_sha1 == basis_tree.get_file_sha1(basis_path)):
 
841
                    # If the new contents of the file match what is in basis,
 
842
                    # then there is no need to store in merge_modified.
 
843
                    if basis_path in merge_modified:
 
844
                        del merge_modified[basis_path]
 
845
                else:
 
846
                    merge_modified[target_path] = new_sha1
 
847
 
 
848
                # preserve the execute bit when backing up
 
849
                if keep_content and wt_executable == target_executable:
 
850
                    tt.set_executability(target_executable, trans_id)
 
851
            elif target_kind is not None:
 
852
                raise AssertionError(target_kind)
 
853
        if not wt_versioned and target_versioned:
 
854
            tt.version_file(
 
855
                trans_id, file_id=getattr(change, 'file_id', None))
 
856
        if wt_versioned and not target_versioned:
 
857
            tt.unversion_file(trans_id)
 
858
        if (target_name is not None
 
859
                and (wt_name != target_name or change.is_reparented())):
 
860
            if target_path == '':
 
861
                parent_trans = ROOT_PARENT
 
862
            else:
 
863
                parent_trans = tt.trans_id_file_id(target_parent)
 
864
            if wt_path == '' and wt_versioned:
 
865
                tt.adjust_root_path(target_name, parent_trans)
 
866
            else:
 
867
                tt.adjust_path(target_name, parent_trans, trans_id)
 
868
        if wt_executable != target_executable and target_kind == "file":
 
869
            tt.set_executability(target_executable, trans_id)
 
870
    if working_tree.supports_content_filtering():
 
871
        for (trans_id, mode_id, target_path), bytes in (
 
872
                target_tree.iter_files_bytes(deferred_files)):
 
873
            # We're reverting a tree to the target tree so using the
 
874
            # target tree to find the file path seems the best choice
 
875
            # here IMO - Ian C 27/Oct/2009
 
876
            filters = working_tree._content_filter_stack(target_path)
 
877
            bytes = filtered_output_bytes(
 
878
                bytes, filters,
 
879
                ContentFilterContext(target_path, working_tree))
 
880
            tt.create_file(bytes, trans_id, mode_id)
 
881
    else:
 
882
        for (trans_id, mode_id, target_path), bytes in target_tree.iter_files_bytes(
 
883
                deferred_files):
 
884
            tt.create_file(bytes, trans_id, mode_id)
 
885
    tt.fixup_new_roots()
1128
886
    return merge_modified
1129
887
 
1130
888
 
1155
913
        existing_file, new_file = trans_id, last_trans_id
1156
914
    else:
1157
915
        existing_file, new_file = last_trans_id, trans_id
1158
 
    new_name = tt.final_name(existing_file) + '.moved'
1159
 
    tt.adjust_path(new_name, final_parent, existing_file)
1160
 
    yield (c_type, 'Moved existing file to', existing_file, new_file)
 
916
    if (not tt._tree.has_versioned_directories() and
 
917
            tt.final_kind(trans_id) == 'directory' and
 
918
            tt.final_kind(last_trans_id) == 'directory'):
 
919
        _reparent_transform_children(tt, existing_file, new_file)
 
920
        tt.delete_contents(existing_file)
 
921
        tt.unversion_file(existing_file)
 
922
        tt.cancel_creation(existing_file)
 
923
    else:
 
924
        new_name = tt.final_name(existing_file) + '.moved'
 
925
        tt.adjust_path(new_name, final_parent, existing_file)
 
926
        yield (c_type, 'Moved existing file to', existing_file, new_file)
1161
927
 
1162
928
 
1163
929
def resolve_parent_loop(tt, path_tree, c_type, cur):