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

Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
import errno
19
19
from stat import S_ISREG
20
20
 
 
21
from bzrlib import bzrdir, errors
21
22
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
22
23
                           ReusingTransform, NotVersionedError, CantMoveRoot,
23
24
                           ExistingLimbo, ImmortalLimbo, NoFinalPath)
478
479
 
479
480
    def path_changed(self, trans_id):
480
481
        """Return True if a trans_id's path has changed."""
481
 
        return trans_id in self._new_name or trans_id in self._new_parent
 
482
        return (trans_id in self._new_name) or (trans_id in self._new_parent)
 
483
 
 
484
    def new_contents(self, trans_id):
 
485
        return (trans_id in self._new_contents)
482
486
 
483
487
    def find_conflicts(self):
484
488
        """Find any violations of inventory or filesystem invariants"""
652
656
            last_name = None
653
657
            last_trans_id = None
654
658
            for name, trans_id in name_ids:
 
659
                try:
 
660
                    kind = self.final_kind(trans_id)
 
661
                except NoSuchFile:
 
662
                    kind = None
 
663
                file_id = self.final_file_id(trans_id)
 
664
                if kind is None and file_id is None:
 
665
                    continue
655
666
                if name == last_name:
656
667
                    conflicts.append(('duplicate', last_trans_id, trans_id,
657
668
                    name))
658
 
                try:
659
 
                    kind = self.final_kind(trans_id)
660
 
                except NoSuchFile:
661
 
                    kind = None
662
 
                file_id = self.final_file_id(trans_id)
663
 
                if kind is not None or file_id is not None:
664
 
                    last_name = name
665
 
                    last_trans_id = trans_id
 
669
                last_name = name
 
670
                last_trans_id = trans_id
666
671
        return conflicts
667
672
 
668
673
    def _duplicate_ids(self):
940
945
    file_ids.sort(key=tree.id2path)
941
946
    return file_ids
942
947
 
 
948
 
943
949
def build_tree(tree, wt):
944
 
    """Create working tree for a branch, using a Transaction."""
 
950
    """Create working tree for a branch, using a TreeTransform.
 
951
    
 
952
    This function should be used on empty trees, having a tree root at most.
 
953
    (see merge and revert functionality for working with existing trees)
 
954
 
 
955
    Existing files are handled like so:
 
956
    
 
957
    - Existing bzrdirs take precedence over creating new items.  They are
 
958
      created as '%s.diverted' % name.
 
959
    - Otherwise, if the content on disk matches the content we are building,
 
960
      it is silently replaced.
 
961
    - Otherwise, conflict resolution will move the old file to 'oldname.moved'.
 
962
    """
 
963
    assert 2 > len(wt.inventory)
945
964
    file_trans_id = {}
946
965
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
947
966
    pp = ProgressPhase("Build phase", 2, top_pb)
948
967
    if tree.inventory.root is not None:
949
968
        wt.set_root_id(tree.inventory.root.file_id)
950
969
    tt = TreeTransform(wt)
 
970
    divert = set()
951
971
    try:
952
972
        pp.next_phase()
953
 
        file_trans_id[wt.get_root_id()] = tt.trans_id_tree_file_id(wt.get_root_id())
954
 
        file_ids = topology_sorted_ids(tree)
 
973
        file_trans_id[wt.get_root_id()] = \
 
974
            tt.trans_id_tree_file_id(wt.get_root_id())
955
975
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
956
976
        try:
957
 
            for num, file_id in enumerate(file_ids):
958
 
                pb.update("Building tree", num, len(file_ids))
959
 
                entry = tree.inventory[file_id]
 
977
            for num, (tree_path, entry) in \
 
978
                enumerate(tree.inventory.iter_entries_by_dir()):
 
979
                pb.update("Building tree", num, len(tree.inventory))
960
980
                if entry.parent_id is None:
961
981
                    continue
 
982
                reparent = False
 
983
                file_id = entry.file_id
 
984
                target_path = wt.abspath(tree_path)
 
985
                try:
 
986
                    kind = file_kind(target_path)
 
987
                except NoSuchFile:
 
988
                    pass
 
989
                else:
 
990
                    if kind == "directory":
 
991
                        try:
 
992
                            bzrdir.BzrDir.open(target_path)
 
993
                        except errors.NotBranchError:
 
994
                            pass
 
995
                        else:
 
996
                            divert.add(file_id)
 
997
                    if (file_id not in divert and
 
998
                        _content_match(tree, entry, file_id, kind,
 
999
                        target_path)):
 
1000
                        tt.delete_contents(tt.trans_id_tree_path(tree_path))
 
1001
                        if kind == 'directory':
 
1002
                            reparent = True
962
1003
                if entry.parent_id not in file_trans_id:
963
1004
                    raise repr(entry.parent_id)
964
1005
                parent_id = file_trans_id[entry.parent_id]
965
 
                file_trans_id[file_id] = new_by_entry(tt, entry, parent_id, 
 
1006
                file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
966
1007
                                                      tree)
 
1008
                if reparent:
 
1009
                    new_trans_id = file_trans_id[file_id]
 
1010
                    old_parent = tt.trans_id_tree_path(tree_path)
 
1011
                    _reparent_children(tt, old_parent, new_trans_id)
967
1012
        finally:
968
1013
            pb.finished()
969
1014
        pp.next_phase()
 
1015
        divert_trans = set(file_trans_id[f] for f in divert)
 
1016
        resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
 
1017
        raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
 
1018
        conflicts = cook_conflicts(raw_conflicts, tt)
 
1019
        for conflict in conflicts:
 
1020
            warning(conflict)
 
1021
        try:
 
1022
            wt.add_conflicts(conflicts)
 
1023
        except errors.UnsupportedOperation:
 
1024
            pass
970
1025
        tt.apply()
971
1026
    finally:
972
1027
        tt.finalize()
973
1028
        top_pb.finished()
974
1029
 
 
1030
 
 
1031
def _reparent_children(tt, old_parent, new_parent):
 
1032
    for child in tt.iter_tree_children(old_parent):
 
1033
        tt.adjust_path(tt.final_name(child), new_parent, child)
 
1034
 
 
1035
 
 
1036
def _content_match(tree, entry, file_id, kind, target_path):
 
1037
    if entry.kind != kind:
 
1038
        return False
 
1039
    if entry.kind == "directory":
 
1040
        return True
 
1041
    if entry.kind == "file":
 
1042
        if tree.get_file(file_id).read() == file(target_path, 'rb').read():
 
1043
            return True
 
1044
    elif entry.kind == "symlink":
 
1045
        if tree.get_symlink_target(file_id) == os.readlink(target_path):
 
1046
            return True
 
1047
    return False
 
1048
 
 
1049
 
 
1050
def resolve_checkout(tt, conflicts, divert):
 
1051
    new_conflicts = set()
 
1052
    for c_type, conflict in ((c[0], c) for c in conflicts):
 
1053
        # Anything but a 'duplicate' would indicate programmer error
 
1054
        assert c_type == 'duplicate', c_type
 
1055
        # Now figure out which is new and which is old
 
1056
        if tt.new_contents(conflict[1]):
 
1057
            new_file = conflict[1]
 
1058
            old_file = conflict[2]
 
1059
        else:
 
1060
            new_file = conflict[2]
 
1061
            old_file = conflict[1]
 
1062
 
 
1063
        # We should only get here if the conflict wasn't completely
 
1064
        # resolved
 
1065
        final_parent = tt.final_parent(old_file)
 
1066
        if new_file in divert:
 
1067
            new_name = tt.final_name(old_file)+'.diverted'
 
1068
            tt.adjust_path(new_name, final_parent, new_file)
 
1069
            new_conflicts.add((c_type, 'Diverted to',
 
1070
                               new_file, old_file))
 
1071
        else:
 
1072
            new_name = tt.final_name(old_file)+'.moved'
 
1073
            tt.adjust_path(new_name, final_parent, old_file)
 
1074
            new_conflicts.add((c_type, 'Moved existing file to',
 
1075
                               old_file, new_file))
 
1076
    return new_conflicts
 
1077
 
 
1078
 
975
1079
def new_by_entry(tt, entry, parent_id, tree):
976
1080
    """Create a new file according to its inventory entry"""
977
1081
    name = entry.name
1093
1197
    """Revert a working tree's contents to those of a target tree."""
1094
1198
    interesting_ids = find_interesting(working_tree, target_tree, filenames)
1095
1199
    def interesting(file_id):
1096
 
        return interesting_ids is None or file_id in interesting_ids
 
1200
        return interesting_ids is None or (file_id in interesting_ids)
1097
1201
 
1098
1202
    tt = TreeTransform(working_tree, pb)
1099
1203
    try:
1143
1247
                if file_id not in target_tree:
1144
1248
                    trans_id = tt.trans_id_tree_file_id(file_id)
1145
1249
                    tt.unversion_file(trans_id)
1146
 
                    if file_id in merge_modified:
 
1250
                    try:
 
1251
                        file_kind = working_tree.kind(file_id)
 
1252
                    except NoSuchFile:
 
1253
                        file_kind = None
 
1254
                    if file_kind != 'file' and file_kind is not None:
 
1255
                        keep_contents = False
 
1256
                        delete_merge_modified = False
 
1257
                    else:
 
1258
                        if (file_id in merge_modified and 
 
1259
                            merge_modified[file_id] == 
 
1260
                            working_tree.get_file_sha1(file_id)):
 
1261
                            keep_contents = False
 
1262
                            delete_merge_modified = True
 
1263
                        else:
 
1264
                            keep_contents = True
 
1265
                            delete_merge_modified = False
 
1266
                    if not keep_contents:
1147
1267
                        tt.delete_contents(trans_id)
 
1268
                    if delete_merge_modified:
1148
1269
                        del merge_modified[file_id]
1149
1270
        finally:
1150
1271
            child_pb.finished()
1166
1287
    return conflicts
1167
1288
 
1168
1289
 
1169
 
def resolve_conflicts(tt, pb=DummyProgress()):
 
1290
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
1170
1291
    """Make many conflict-resolution attempts, but die if they fail"""
 
1292
    if pass_func is None:
 
1293
        pass_func = conflict_pass
1171
1294
    new_conflicts = set()
1172
1295
    try:
1173
1296
        for n in range(10):
1175
1298
            conflicts = tt.find_conflicts()
1176
1299
            if len(conflicts) == 0:
1177
1300
                return new_conflicts
1178
 
            new_conflicts.update(conflict_pass(tt, conflicts))
 
1301
            new_conflicts.update(pass_func(tt, conflicts))
1179
1302
        raise MalformedTransform(conflicts=conflicts)
1180
1303
    finally:
1181
1304
        pb.clear()