/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: Martin Pool
  • Date: 2006-03-20 22:32:41 UTC
  • mto: This revision was merged to the branch mainline in revision 1621.
  • Revision ID: mbp@sourcefrog.net-20060320223241-95ba66352fd77026
Cleanup and document some check code

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
                           ReusingTransform, NotVersionedError, CantMoveRoot,
23
23
                           ExistingLimbo, ImmortalLimbo)
24
24
from bzrlib.inventory import InventoryEntry
25
 
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
26
 
                            delete_any)
 
25
from bzrlib.osutils import file_kind, supports_executable, pathjoin
27
26
from bzrlib.progress import DummyProgress, ProgressPhase
28
27
from bzrlib.trace import mutter, warning
29
28
import bzrlib.ui 
287
286
        os.symlink(target, self._limbo_name(trans_id))
288
287
        unique_add(self._new_contents, trans_id, 'symlink')
289
288
 
 
289
    @staticmethod
 
290
    def delete_any(full_path):
 
291
        """Delete a file or directory."""
 
292
        try:
 
293
            os.unlink(full_path)
 
294
        except OSError, e:
 
295
        # We may be renaming a dangling inventory id
 
296
            if e.errno not in (errno.EISDIR, errno.EACCES, errno.EPERM):
 
297
                raise
 
298
            os.rmdir(full_path)
 
299
 
290
300
    def cancel_creation(self, trans_id):
291
301
        """Cancel the creation of new file contents."""
292
302
        del self._new_contents[trans_id]
293
 
        delete_any(self._limbo_name(trans_id))
 
303
        self.delete_any(self._limbo_name(trans_id))
294
304
 
295
305
    def delete_contents(self, trans_id):
296
306
        """Schedule the contents of a path entry for deletion"""
428
438
        except KeyError:
429
439
            return os.path.basename(self._tree_id_paths[trans_id])
430
440
 
431
 
    def by_parent(self):
 
441
    def _by_parent(self):
432
442
        """Return a map of parent: children for known parents.
433
443
        
434
444
        Only new paths and parents of tree files with assigned ids are used.
455
465
        # ensure all children of all existent parents are known
456
466
        # all children of non-existent parents are known, by definition.
457
467
        self._add_tree_children()
458
 
        by_parent = self.by_parent()
 
468
        by_parent = self._by_parent()
459
469
        conflicts.extend(self._unversioned_parents(by_parent))
460
470
        conflicts.extend(self._parent_loops())
461
471
        conflicts.extend(self._duplicate_entries(by_parent))
472
482
        Active parents are those which gain children, and those which are
473
483
        removed.  This is a necessary first step in detecting conflicts.
474
484
        """
475
 
        parents = self.by_parent().keys()
 
485
        parents = self._by_parent().keys()
476
486
        parents.extend([t for t in self._removed_contents if 
477
487
                        self.tree_kind(t) == 'directory'])
478
488
        for trans_id in self._removed_id:
504
514
                continue
505
515
            yield self.trans_id_tree_path(childpath)
506
516
 
507
 
    def has_named_child(self, by_parent, parent_id, name):
508
 
        try:
509
 
            children = by_parent[parent_id]
510
 
        except KeyError:
511
 
            children = []
512
 
        for child in children:
513
 
            if self.final_name(child) == name:
514
 
                return True
515
 
        try:
516
 
            path = self._tree_id_paths[parent_id]
517
 
        except KeyError:
518
 
            return False
519
 
        childpath = joinpath(path, name)
520
 
        child_id = self._tree_path_ids.get(childpath)
521
 
        if child_id is None:
522
 
            return lexists(self._tree.abspath(childpath))
523
 
        else:
524
 
            if tt.final_parent(child_id) != parent_id:
525
 
                return False
526
 
            if child_id in tt._removed_contents:
527
 
                # XXX What about dangling file-ids?
528
 
                return False
529
 
            else:
530
 
                return True
531
 
 
532
517
    def _parent_loops(self):
533
518
        """No entry should be its own ancestor"""
534
519
        conflicts = []
620
605
                if name == last_name:
621
606
                    conflicts.append(('duplicate', last_trans_id, trans_id,
622
607
                    name))
623
 
                try:
624
 
                    kind = self.final_kind(trans_id)
625
 
                except NoSuchFile:
626
 
                    kind = None
627
 
                file_id = self.final_file_id(trans_id)
628
 
                if kind is not None or file_id is not None:
629
 
                    last_name = name
630
 
                    last_trans_id = trans_id
 
608
                last_name = name
 
609
                last_trans_id = trans_id
631
610
        return conflicts
632
611
 
633
612
    def _duplicate_ids(self):
720
699
                child_pb.update('removing file', num, len(tree_paths))
721
700
                full_path = self._tree.abspath(path)
722
701
                if trans_id in self._removed_contents:
723
 
                    delete_any(full_path)
 
702
                    self.delete_any(full_path)
724
703
                elif trans_id in self._new_name or trans_id in \
725
704
                    self._new_parent:
726
705
                    try:
905
884
def build_tree(tree, wt):
906
885
    """Create working tree for a branch, using a Transaction."""
907
886
    file_trans_id = {}
908
 
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
909
 
    pp = ProgressPhase("Build phase", 2, top_pb)
910
887
    tt = TreeTransform(wt)
911
888
    try:
912
 
        pp.next_phase()
913
889
        file_trans_id[wt.get_root_id()] = tt.trans_id_tree_file_id(wt.get_root_id())
914
890
        file_ids = topology_sorted_ids(tree)
915
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
916
 
        try:
917
 
            for num, file_id in enumerate(file_ids):
918
 
                pb.update("Building tree", num, len(file_ids))
919
 
                entry = tree.inventory[file_id]
920
 
                if entry.parent_id is None:
921
 
                    continue
922
 
                if entry.parent_id not in file_trans_id:
923
 
                    raise repr(entry.parent_id)
924
 
                parent_id = file_trans_id[entry.parent_id]
925
 
                file_trans_id[file_id] = new_by_entry(tt, entry, parent_id, 
926
 
                                                      tree)
927
 
        finally:
928
 
            pb.finished()
929
 
        pp.next_phase()
 
891
        for file_id in file_ids:
 
892
            entry = tree.inventory[file_id]
 
893
            if entry.parent_id is None:
 
894
                continue
 
895
            if entry.parent_id not in file_trans_id:
 
896
                raise repr(entry.parent_id)
 
897
            parent_id = file_trans_id[entry.parent_id]
 
898
            file_trans_id[file_id] = new_by_entry(tt, entry, parent_id, tree)
930
899
        tt.apply()
931
900
    finally:
932
901
        tt.finalize()
933
 
        top_pb.finished()
934
902
 
935
903
def new_by_entry(tt, entry, parent_id, tree):
936
904
    """Create a new file according to its inventory entry"""
971
939
    else:
972
940
        interesting_ids = set()
973
941
        for tree_path in filenames:
974
 
            not_found = True
975
942
            for tree in (working_tree, target_tree):
 
943
                not_found = True
976
944
                file_id = tree.inventory.path2id(tree_path)
977
945
                if file_id is not None:
978
946
                    interesting_ids.add(file_id)
979
947
                    not_found = False
980
 
            if not_found:
981
 
                raise NotVersionedError(path=tree_path)
 
948
                if not_found:
 
949
                    raise NotVersionedError(path=tree_path)
982
950
    return interesting_ids
983
951
 
984
952
 
985
953
def change_entry(tt, file_id, working_tree, target_tree, 
986
 
                 trans_id_file_id, backups, trans_id, by_parent):
 
954
                 trans_id_file_id, backups, trans_id):
987
955
    """Replace a file_id's contents with those from a target tree."""
988
956
    e_trans_id = trans_id_file_id(file_id)
989
957
    entry = target_tree.inventory[file_id]
996
964
                tt.delete_contents(e_trans_id)
997
965
            else:
998
966
                parent_trans_id = trans_id_file_id(entry.parent_id)
999
 
                backup_name = get_backup_name(entry, by_parent,
1000
 
                                              parent_trans_id, tt)
1001
 
                tt.adjust_path(backup_name, parent_trans_id, e_trans_id)
 
967
                tt.adjust_path(entry.name+"~", parent_trans_id, e_trans_id)
1002
968
                tt.unversion_file(e_trans_id)
1003
969
                e_trans_id = tt.create_path(entry.name, parent_trans_id)
1004
970
                tt.version_file(file_id, e_trans_id)
1022
988
        tt.adjust_path(entry.name, parent_trans_id, e_trans_id)
1023
989
 
1024
990
 
1025
 
def get_backup_name(entry, by_parent, parent_trans_id, tt):
1026
 
    """Produce a backup-style name that appears to be available"""
1027
 
    def name_gen():
1028
 
        counter = 1
1029
 
        while True:
1030
 
            yield "%s.~%d~" % (entry.name, counter)
1031
 
            counter += 1
1032
 
    for name in name_gen():
1033
 
        if not tt.has_named_child(by_parent, parent_trans_id, name):
1034
 
            return name
1035
 
 
1036
991
def _entry_changes(file_id, entry, working_tree):
1037
992
    """Determine in which ways the inventory entry has changed.
1038
993
 
1088
1043
                              interesting(i)]
1089
1044
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1090
1045
        try:
1091
 
            by_parent = tt.by_parent()
1092
1046
            for id_num, file_id in enumerate(sorted_interesting):
1093
1047
                child_pb.update("Reverting file", id_num+1, 
1094
1048
                                len(sorted_interesting))
1103
1057
                        backup_this = False
1104
1058
                        del merge_modified[file_id]
1105
1059
                    change_entry(tt, file_id, working_tree, target_tree, 
1106
 
                                 trans_id_file_id, backup_this, trans_id,
1107
 
                                 by_parent)
 
1060
                                 trans_id_file_id, backup_this, trans_id)
1108
1061
        finally:
1109
1062
            child_pb.finished()
1110
1063
        pp.next_phase()
1128
1081
            raw_conflicts = resolve_conflicts(tt, child_pb)
1129
1082
        finally:
1130
1083
            child_pb.finished()
1131
 
        conflicts = cook_conflicts(raw_conflicts, tt)
1132
 
        for conflict in conflicts:
1133
 
            warning(conflict)
 
1084
        for line in conflicts_strings(cook_conflicts(raw_conflicts, tt)):
 
1085
            warning(line)
1134
1086
        pp.next_phase()
1135
1087
        tt.apply()
1136
1088
        working_tree.set_merge_modified({})
1137
1089
    finally:
1138
1090
        tt.finalize()
1139
1091
        pb.clear()
1140
 
    return conflicts
1141
1092
 
1142
1093
 
1143
1094
def resolve_conflicts(tt, pb=DummyProgress()):
1200
1151
def cook_conflicts(raw_conflicts, tt):
1201
1152
    """Generate a list of cooked conflicts, sorted by file path"""
1202
1153
    def key(conflict):
1203
 
        if conflict.path is not None:
1204
 
            return conflict.path, conflict.typestring
1205
 
        elif getattr(conflict, "conflict_path", None) is not None:
1206
 
            return conflict.conflict_path, conflict.typestring
 
1154
        if conflict[2] is not None:
 
1155
            return conflict[2], conflict[0]
 
1156
        elif len(conflict) == 6:
 
1157
            return conflict[4], conflict[0]
1207
1158
        else:
1208
 
            return None, conflict.typestring
 
1159
            return None, conflict[0]
1209
1160
 
1210
1161
    return sorted(list(iter_cook_conflicts(raw_conflicts, tt)), key=key)
1211
1162
 
1212
1163
def iter_cook_conflicts(raw_conflicts, tt):
1213
 
    from bzrlib.conflicts import Conflict
 
1164
    cooked_conflicts = []
1214
1165
    fp = FinalPaths(tt)
1215
1166
    for conflict in raw_conflicts:
1216
1167
        c_type = conflict[0]
1218
1169
        modified_path = fp.get_path(conflict[2])
1219
1170
        modified_id = tt.final_file_id(conflict[2])
1220
1171
        if len(conflict) == 3:
1221
 
            yield Conflict.factory(c_type, action=action, path=modified_path,
1222
 
                                     file_id=modified_id)
1223
 
             
 
1172
            yield c_type, action, modified_path, modified_id
1224
1173
        else:
1225
1174
            conflicting_path = fp.get_path(conflict[3])
1226
1175
            conflicting_id = tt.final_file_id(conflict[3])
1227
 
            yield Conflict.factory(c_type, action=action, path=modified_path,
1228
 
                                   file_id=modified_id, 
1229
 
                                   conflict_path=conflicting_path,
1230
 
                                   conflict_file_id=conflicting_id)
 
1176
            yield (c_type, action, modified_path, modified_id, 
 
1177
                   conflicting_path, conflicting_id)
 
1178
 
 
1179
 
 
1180
def conflicts_strings(conflicts):
 
1181
    """Generate strings for the provided conflicts"""
 
1182
    for conflict in conflicts:
 
1183
        conflict_type = conflict[0]
 
1184
        if conflict_type == 'text conflict':
 
1185
            yield 'Text conflict in %s' % conflict[2]
 
1186
        elif conflict_type == 'contents conflict':
 
1187
            yield 'Contents conflict in %s' % conflict[2]
 
1188
        elif conflict_type == 'path conflict':
 
1189
            yield 'Path conflict: %s / %s' % conflict[2:]
 
1190
        elif conflict_type == 'duplicate id':
 
1191
            vals = (conflict[4], conflict[1], conflict[2])
 
1192
            yield 'Conflict adding id to %s.  %s %s.' % vals
 
1193
        elif conflict_type == 'duplicate':
 
1194
            vals = (conflict[4], conflict[1], conflict[2])
 
1195
            yield 'Conflict adding file %s.  %s %s.' % vals
 
1196
        elif conflict_type == 'parent loop':
 
1197
            vals = (conflict[4], conflict[2], conflict[1])
 
1198
            yield 'Conflict moving %s into %s.  %s.' % vals
 
1199
        elif conflict_type == 'unversioned parent':
 
1200
            vals = (conflict[2], conflict[1])
 
1201
            yield 'Conflict adding versioned files to %s.  %s.' % vals
 
1202
        elif conflict_type == 'missing parent':
 
1203
            vals = (conflict[2], conflict[1])
 
1204
            yield 'Conflict adding files to %s.  %s.' % vals