/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

  • Committer: Aaron Bentley
  • Date: 2006-08-29 18:17:41 UTC
  • mto: This revision was merged to the branch mainline in revision 1977.
  • Revision ID: abentley@panoramicfeedback.com-20060829181741-4360542e119ff839
Implement disk-content merge and conflict resolution for build_tree

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)
646
647
            last_name = None
647
648
            last_trans_id = None
648
649
            for name, trans_id in name_ids:
 
650
                try:
 
651
                    kind = self.final_kind(trans_id)
 
652
                except NoSuchFile:
 
653
                    kind = None
 
654
                file_id = self.final_file_id(trans_id)
 
655
                if kind is None and file_id is None:
 
656
                    continue
649
657
                if name == last_name:
650
658
                    conflicts.append(('duplicate', last_trans_id, trans_id,
651
659
                    name))
652
 
                try:
653
 
                    kind = self.final_kind(trans_id)
654
 
                except NoSuchFile:
655
 
                    kind = None
656
 
                file_id = self.final_file_id(trans_id)
657
 
                if kind is not None or file_id is not None:
658
 
                    last_name = name
659
 
                    last_trans_id = trans_id
 
660
                last_name = name
 
661
                last_trans_id = trans_id
660
662
        return conflicts
661
663
 
662
664
    def _duplicate_ids(self):
934
936
    file_ids.sort(key=tree.id2path)
935
937
    return file_ids
936
938
 
 
939
 
937
940
def build_tree(tree, wt):
938
 
    """Create working tree for a branch, using a Transaction."""
 
941
    """Create working tree for a branch, using a TreeTransform.
 
942
    
 
943
    Existing files are handled like so:
 
944
    
 
945
    - Existing bzrdirs take precedence over creating directories.  New 
 
946
      directory contents are silently dropped.
 
947
    - Otherwise, if the content on disk matches the content we are building,
 
948
      it is silently replaced.
 
949
    - Otherwise, conflict resolution will move the old file to 'oldname.moved'.
 
950
    """
939
951
    file_trans_id = {}
940
952
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
941
953
    pp = ProgressPhase("Build phase", 2, top_pb)
942
954
    tt = TreeTransform(wt)
943
955
    try:
944
956
        pp.next_phase()
945
 
        file_trans_id[wt.get_root_id()] = tt.trans_id_tree_file_id(wt.get_root_id())
 
957
        file_trans_id[wt.get_root_id()] = \
 
958
            tt.trans_id_tree_file_id(wt.get_root_id())
946
959
        file_ids = topology_sorted_ids(tree)
947
960
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
961
        suppress_children = None
948
962
        try:
949
963
            for num, file_id in enumerate(file_ids):
950
964
                pb.update("Building tree", num, len(file_ids))
951
965
                entry = tree.inventory[file_id]
952
966
                if entry.parent_id is None:
953
967
                    continue
 
968
                reparent = False
 
969
                tree_path = tree.id2path(file_id)
 
970
                if (suppress_children is not None and
 
971
                    tree_path.startswith(suppress_children)):
 
972
                    continue
 
973
                target_path = wt.abspath(tree.id2path(file_id)) 
 
974
                try:
 
975
                    kind = file_kind(target_path)
 
976
                except NoSuchFile:
 
977
                    pass
 
978
                else:
 
979
                    if kind == "directory":
 
980
                        try:
 
981
                            bzrdir.BzrDir.open(target_path)
 
982
                        except errors.NotBranchError:
 
983
                            pass
 
984
                        else:
 
985
                            suppress_children = tree_path + '/'
 
986
                            continue
 
987
                    if _content_match(tree, entry, file_id, 
 
988
                                      kind, target_path):
 
989
                        tt.delete_contents(tt.trans_id_tree_path(tree_path))
 
990
                        if kind == 'directory':
 
991
                            reparent = True
954
992
                if entry.parent_id not in file_trans_id:
955
993
                    raise repr(entry.parent_id)
956
994
                parent_id = file_trans_id[entry.parent_id]
957
995
                file_trans_id[file_id] = new_by_entry(tt, entry, parent_id, 
958
996
                                                      tree)
 
997
                if reparent:
 
998
                    new_trans_id = file_trans_id[file_id]
 
999
                    old_parent = tt.trans_id_tree_path(tree_path)
 
1000
                    _reparent_children(tt, old_parent, new_trans_id)
959
1001
        finally:
960
1002
            pb.finished()
961
1003
        pp.next_phase()
 
1004
        raw_conflicts = resolve_conflicts(tt, pass_func=resolve_checkout)
 
1005
        conflicts = cook_conflicts(raw_conflicts, tt)
 
1006
        for conflict in conflicts:
 
1007
            warning(conflict)
 
1008
        try:
 
1009
            wt.add_conflicts(conflicts)
 
1010
        except errors.UnsupportedOperation:
 
1011
            pass
962
1012
        tt.apply()
963
1013
    finally:
964
1014
        tt.finalize()
965
1015
        top_pb.finished()
966
1016
 
 
1017
 
 
1018
def _reparent_children(tt, old_parent, new_parent):
 
1019
    for child in tt.iter_tree_children(old_parent):
 
1020
        tt.adjust_path(tt.final_name(child), new_parent, child)
 
1021
 
 
1022
 
 
1023
def _content_match(tree, entry, file_id, kind, target_path):
 
1024
    if entry.kind != kind:
 
1025
        return False
 
1026
    if entry.kind == "directory":
 
1027
        return True
 
1028
    if entry.kind == "file":
 
1029
        if tree.get_file(file_id).read() == file(target_path, 'rb').read():
 
1030
            return True
 
1031
    elif entry.kind == "symlink":
 
1032
        if tree.get_symlink_target(file_id) == os.readlink(target_path):
 
1033
            return True
 
1034
    return False
 
1035
 
 
1036
 
 
1037
def resolve_checkout(tt, conflicts):
 
1038
    new_conflicts = set()
 
1039
    for c_type, conflict in ((c[0], c) for c in conflicts):
 
1040
        # Anything but a 'duplicate' would indicate programmer error
 
1041
        assert c_type == 'duplicate', c_type
 
1042
        # Now figure out which is new and which is old
 
1043
        if tt.path_changed(conflict[1]):
 
1044
            new_file = conflict[1]
 
1045
            old_file = conflict[2]
 
1046
        else:
 
1047
            new_file = conflict[2]
 
1048
            old_file = conflict[1]
 
1049
 
 
1050
        # We should only get here if the conflict wasn't completely
 
1051
        # resolved
 
1052
        final_parent = tt.final_parent(old_file)
 
1053
        new_name = tt.final_name(old_file)+'.moved'
 
1054
        tt.adjust_path(new_name, final_parent, old_file)
 
1055
        new_conflicts.add((c_type, 'Moved existing file to',
 
1056
                           old_file, new_file))
 
1057
    return new_conflicts
 
1058
 
 
1059
 
967
1060
def new_by_entry(tt, entry, parent_id, tree):
968
1061
    """Create a new file according to its inventory entry"""
969
1062
    name = entry.name
1172
1265
    return conflicts
1173
1266
 
1174
1267
 
1175
 
def resolve_conflicts(tt, pb=DummyProgress()):
 
1268
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
1176
1269
    """Make many conflict-resolution attempts, but die if they fail"""
 
1270
    if pass_func is None:
 
1271
        pass_func = conflict_pass
1177
1272
    new_conflicts = set()
1178
1273
    try:
1179
1274
        for n in range(10):
1181
1276
            conflicts = tt.find_conflicts()
1182
1277
            if len(conflicts) == 0:
1183
1278
                return new_conflicts
1184
 
            new_conflicts.update(conflict_pass(tt, conflicts))
 
1279
            new_conflicts.update(pass_func(tt, conflicts))
1185
1280
        raise MalformedTransform(conflicts=conflicts)
1186
1281
    finally:
1187
1282
        pb.clear()