~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-31 16:12:57 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060731161257-91a231523255332c
new official bzr.ico

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
22
21
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
23
22
                           ReusingTransform, NotVersionedError, CantMoveRoot,
24
23
                           ExistingLimbo, ImmortalLimbo)
73
72
     * set_executability
74
73
    """
75
74
    def __init__(self, tree, pb=DummyProgress()):
76
 
        """Note: a tree_write lock is taken on the tree.
 
75
        """Note: a write lock is taken on the tree.
77
76
        
78
77
        Use TreeTransform.finalize() to release the lock
79
78
        """
80
79
        object.__init__(self)
81
80
        self._tree = tree
82
 
        self._tree.lock_tree_write()
 
81
        self._tree.lock_write()
83
82
        try:
84
83
            control_files = self._tree._control_files
85
84
            self._limbodir = urlutils.local_path_from_url(
292
291
        except KeyError:
293
292
            return
294
293
        try:
295
 
            mode = os.stat(self._tree.abspath(old_path)).st_mode
 
294
            mode = os.stat(old_path).st_mode
296
295
        except OSError, e:
297
296
            if e.errno == errno.ENOENT:
298
297
                return
476
475
 
477
476
    def path_changed(self, trans_id):
478
477
        """Return True if a trans_id's path has changed."""
479
 
        return (trans_id in self._new_name) or (trans_id in self._new_parent)
480
 
 
481
 
    def new_contents(self, trans_id):
482
 
        return (trans_id in self._new_contents)
 
478
        return trans_id in self._new_name or trans_id in self._new_parent
483
479
 
484
480
    def find_conflicts(self):
485
481
        """Find any violations of inventory or filesystem invariants"""
511
507
                        self.tree_kind(t) == 'directory'])
512
508
        for trans_id in self._removed_id:
513
509
            file_id = self.tree_file_id(trans_id)
514
 
            if self._tree.inventory[file_id].kind == 'directory':
 
510
            if self._tree.inventory[file_id].kind in ('directory', 
 
511
                                                      'root_directory'):
515
512
                parents.append(trans_id)
516
513
 
517
514
        for parent_id in parents:
650
647
            last_name = None
651
648
            last_trans_id = None
652
649
            for name, trans_id in name_ids:
 
650
                if name == last_name:
 
651
                    conflicts.append(('duplicate', last_trans_id, trans_id,
 
652
                    name))
653
653
                try:
654
654
                    kind = self.final_kind(trans_id)
655
655
                except NoSuchFile:
656
656
                    kind = None
657
657
                file_id = self.final_file_id(trans_id)
658
 
                if kind is None and file_id is None:
659
 
                    continue
660
 
                if name == last_name:
661
 
                    conflicts.append(('duplicate', last_trans_id, trans_id,
662
 
                    name))
663
 
                last_name = name
664
 
                last_trans_id = trans_id
 
658
                if kind is not None or file_id is not None:
 
659
                    last_name = name
 
660
                    last_trans_id = trans_id
665
661
        return conflicts
666
662
 
667
663
    def _duplicate_ids(self):
939
935
    file_ids.sort(key=tree.id2path)
940
936
    return file_ids
941
937
 
942
 
 
943
938
def build_tree(tree, wt):
944
 
    """Create working tree for a branch, using a TreeTransform.
945
 
    
946
 
    This function should be used on empty trees, having a tree root at most.
947
 
    (see merge and revert functionality for working with existing trees)
948
 
 
949
 
    Existing files are handled like so:
950
 
    
951
 
    - Existing bzrdirs take precedence over creating new items.  They are
952
 
      created as '%s.diverted' % name.
953
 
    - Otherwise, if the content on disk matches the content we are building,
954
 
      it is silently replaced.
955
 
    - Otherwise, conflict resolution will move the old file to 'oldname.moved'.
956
 
    """
957
 
    assert 2 > len(wt.inventory)
 
939
    """Create working tree for a branch, using a Transaction."""
958
940
    file_trans_id = {}
959
941
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
960
942
    pp = ProgressPhase("Build phase", 2, top_pb)
961
943
    tt = TreeTransform(wt)
962
 
    divert = set()
963
944
    try:
964
945
        pp.next_phase()
965
 
        file_trans_id[wt.get_root_id()] = \
966
 
            tt.trans_id_tree_file_id(wt.get_root_id())
 
946
        file_trans_id[wt.get_root_id()] = tt.trans_id_tree_file_id(wt.get_root_id())
 
947
        file_ids = topology_sorted_ids(tree)
967
948
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
968
949
        try:
969
 
            for num, (tree_path, entry) in \
970
 
                enumerate(tree.inventory.iter_entries_by_dir()):
971
 
                pb.update("Building tree", num, len(tree.inventory))
 
950
            for num, file_id in enumerate(file_ids):
 
951
                pb.update("Building tree", num, len(file_ids))
 
952
                entry = tree.inventory[file_id]
972
953
                if entry.parent_id is None:
973
954
                    continue
974
 
                reparent = False
975
 
                file_id = entry.file_id
976
 
                target_path = wt.abspath(tree_path)
977
 
                try:
978
 
                    kind = file_kind(target_path)
979
 
                except NoSuchFile:
980
 
                    pass
981
 
                else:
982
 
                    if kind == "directory":
983
 
                        try:
984
 
                            bzrdir.BzrDir.open(target_path)
985
 
                        except errors.NotBranchError:
986
 
                            pass
987
 
                        else:
988
 
                            divert.add(file_id)
989
 
                    if (file_id not in divert and
990
 
                        _content_match(tree, entry, file_id, kind,
991
 
                        target_path)):
992
 
                        tt.delete_contents(tt.trans_id_tree_path(tree_path))
993
 
                        if kind == 'directory':
994
 
                            reparent = True
995
955
                if entry.parent_id not in file_trans_id:
996
956
                    raise repr(entry.parent_id)
997
957
                parent_id = file_trans_id[entry.parent_id]
998
 
                file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
 
958
                file_trans_id[file_id] = new_by_entry(tt, entry, parent_id, 
999
959
                                                      tree)
1000
 
                if reparent:
1001
 
                    new_trans_id = file_trans_id[file_id]
1002
 
                    old_parent = tt.trans_id_tree_path(tree_path)
1003
 
                    _reparent_children(tt, old_parent, new_trans_id)
1004
960
        finally:
1005
961
            pb.finished()
1006
962
        pp.next_phase()
1007
 
        divert_trans = set(file_trans_id[f] for f in divert)
1008
 
        resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1009
 
        raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
1010
 
        conflicts = cook_conflicts(raw_conflicts, tt)
1011
 
        for conflict in conflicts:
1012
 
            warning(conflict)
1013
 
        try:
1014
 
            wt.add_conflicts(conflicts)
1015
 
        except errors.UnsupportedOperation:
1016
 
            pass
1017
963
        tt.apply()
1018
964
    finally:
1019
965
        tt.finalize()
1020
966
        top_pb.finished()
1021
967
 
1022
 
 
1023
 
def _reparent_children(tt, old_parent, new_parent):
1024
 
    for child in tt.iter_tree_children(old_parent):
1025
 
        tt.adjust_path(tt.final_name(child), new_parent, child)
1026
 
 
1027
 
 
1028
 
def _content_match(tree, entry, file_id, kind, target_path):
1029
 
    if entry.kind != kind:
1030
 
        return False
1031
 
    if entry.kind == "directory":
1032
 
        return True
1033
 
    if entry.kind == "file":
1034
 
        if tree.get_file(file_id).read() == file(target_path, 'rb').read():
1035
 
            return True
1036
 
    elif entry.kind == "symlink":
1037
 
        if tree.get_symlink_target(file_id) == os.readlink(target_path):
1038
 
            return True
1039
 
    return False
1040
 
 
1041
 
 
1042
 
def resolve_checkout(tt, conflicts, divert):
1043
 
    new_conflicts = set()
1044
 
    for c_type, conflict in ((c[0], c) for c in conflicts):
1045
 
        # Anything but a 'duplicate' would indicate programmer error
1046
 
        assert c_type == 'duplicate', c_type
1047
 
        # Now figure out which is new and which is old
1048
 
        if tt.new_contents(conflict[1]):
1049
 
            new_file = conflict[1]
1050
 
            old_file = conflict[2]
1051
 
        else:
1052
 
            new_file = conflict[2]
1053
 
            old_file = conflict[1]
1054
 
 
1055
 
        # We should only get here if the conflict wasn't completely
1056
 
        # resolved
1057
 
        final_parent = tt.final_parent(old_file)
1058
 
        if new_file in divert:
1059
 
            new_name = tt.final_name(old_file)+'.diverted'
1060
 
            tt.adjust_path(new_name, final_parent, new_file)
1061
 
            new_conflicts.add((c_type, 'Diverted to',
1062
 
                               new_file, old_file))
1063
 
        else:
1064
 
            new_name = tt.final_name(old_file)+'.moved'
1065
 
            tt.adjust_path(new_name, final_parent, old_file)
1066
 
            new_conflicts.add((c_type, 'Moved existing file to',
1067
 
                               old_file, new_file))
1068
 
    return new_conflicts
1069
 
 
1070
 
 
1071
968
def new_by_entry(tt, entry, parent_id, tree):
1072
969
    """Create a new file according to its inventory entry"""
1073
970
    name = entry.name
1086
983
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
1087
984
    """Create new file contents according to an inventory entry."""
1088
985
    if entry.kind == "file":
1089
 
        if lines is None:
 
986
        if lines == None:
1090
987
            lines = tree.get_file(entry.file_id).readlines()
1091
988
        tt.create_file(lines, trans_id, mode_id=mode_id)
1092
989
    elif entry.kind == "symlink":
1174
1071
        contents_mod = True
1175
1072
        meta_mod = False
1176
1073
    if has_contents is True:
1177
 
        if entry.kind != working_kind:
 
1074
        real_e_kind = entry.kind
 
1075
        if real_e_kind == 'root_directory':
 
1076
            real_e_kind = 'directory'
 
1077
        if real_e_kind != working_kind:
1178
1078
            contents_mod, meta_mod = True, False
1179
1079
        else:
1180
1080
            cur_entry._read_tree_state(working_tree.id2path(file_id), 
1189
1089
    """Revert a working tree's contents to those of a target tree."""
1190
1090
    interesting_ids = find_interesting(working_tree, target_tree, filenames)
1191
1091
    def interesting(file_id):
1192
 
        return interesting_ids is None or (file_id in interesting_ids)
 
1092
        return interesting_ids is None or file_id in interesting_ids
1193
1093
 
1194
1094
    tt = TreeTransform(working_tree, pb)
1195
1095
    try:
1229
1129
        pp.next_phase()
1230
1130
        wt_interesting = [i for i in working_tree.inventory if interesting(i)]
1231
1131
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1232
 
        basis_tree = None
1233
1132
        try:
1234
1133
            for id_num, file_id in enumerate(wt_interesting):
1235
1134
                child_pb.update("New file check", id_num+1, 
1237
1136
                if file_id not in target_tree:
1238
1137
                    trans_id = tt.trans_id_tree_file_id(file_id)
1239
1138
                    tt.unversion_file(trans_id)
1240
 
                    try:
1241
 
                        file_kind = working_tree.kind(file_id)
1242
 
                    except NoSuchFile:
1243
 
                        file_kind = None
1244
 
                    delete_merge_modified = (file_id in merge_modified)
1245
 
                    if file_kind != 'file' and file_kind is not None:
1246
 
                        keep_contents = False
1247
 
                    else:
1248
 
                        if basis_tree is None:
1249
 
                            basis_tree = working_tree.basis_tree()
1250
 
                        wt_sha1 = working_tree.get_file_sha1(file_id)
1251
 
                        if (file_id in merge_modified and 
1252
 
                            merge_modified[file_id] == wt_sha1):
1253
 
                            keep_contents = False
1254
 
                        elif (file_id in basis_tree and 
1255
 
                            basis_tree.get_file_sha1(file_id) == wt_sha1):
1256
 
                            keep_contents = False
1257
 
                        else:
1258
 
                            keep_contents = True
1259
 
                    if not keep_contents:
 
1139
                    if file_id in merge_modified:
1260
1140
                        tt.delete_contents(trans_id)
1261
 
                    if delete_merge_modified:
1262
1141
                        del merge_modified[file_id]
1263
1142
        finally:
1264
1143
            child_pb.finished()
1280
1159
    return conflicts
1281
1160
 
1282
1161
 
1283
 
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
 
1162
def resolve_conflicts(tt, pb=DummyProgress()):
1284
1163
    """Make many conflict-resolution attempts, but die if they fail"""
1285
 
    if pass_func is None:
1286
 
        pass_func = conflict_pass
1287
1164
    new_conflicts = set()
1288
1165
    try:
1289
1166
        for n in range(10):
1291
1168
            conflicts = tt.find_conflicts()
1292
1169
            if len(conflicts) == 0:
1293
1170
                return new_conflicts
1294
 
            new_conflicts.update(pass_func(tt, conflicts))
 
1171
            new_conflicts.update(conflict_pass(tt, conflicts))
1295
1172
        raise MalformedTransform(conflicts=conflicts)
1296
1173
    finally:
1297
1174
        pb.clear()
1330
1207
            trans_id = conflict[1]
1331
1208
            try:
1332
1209
                tt.cancel_deletion(trans_id)
1333
 
                new_conflicts.add(('deleting parent', 'Not deleting', 
1334
 
                                   trans_id))
 
1210
                new_conflicts.add((c_type, 'Not deleting', trans_id))
1335
1211
            except KeyError:
1336
1212
                tt.create_directory(trans_id)
1337
 
                new_conflicts.add((c_type, 'Created directory', trans_id))
 
1213
                new_conflicts.add((c_type, 'Created directory.', trans_id))
1338
1214
        elif c_type == 'unversioned parent':
1339
1215
            tt.version_file(tt.inactive_file_id(conflict[1]), conflict[1])
1340
1216
            new_conflicts.add((c_type, 'Versioned directory', conflict[1]))