/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: James Blackwell
  • Date: 2006-05-05 01:05:04 UTC
  • mfrom: (1697 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1700.
  • Revision ID: jblack@merconline.com-20060505010504-264cb48507e53b64
mergedĀ mainline

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
 
25
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
 
26
                            delete_any)
26
27
from bzrlib.progress import DummyProgress, ProgressPhase
27
28
from bzrlib.trace import mutter, warning
28
29
import bzrlib.ui 
286
287
        os.symlink(target, self._limbo_name(trans_id))
287
288
        unique_add(self._new_contents, trans_id, 'symlink')
288
289
 
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
 
 
300
290
    def cancel_creation(self, trans_id):
301
291
        """Cancel the creation of new file contents."""
302
292
        del self._new_contents[trans_id]
303
 
        self.delete_any(self._limbo_name(trans_id))
 
293
        delete_any(self._limbo_name(trans_id))
304
294
 
305
295
    def delete_contents(self, trans_id):
306
296
        """Schedule the contents of a path entry for deletion"""
438
428
        except KeyError:
439
429
            return os.path.basename(self._tree_id_paths[trans_id])
440
430
 
441
 
    def _by_parent(self):
 
431
    def by_parent(self):
442
432
        """Return a map of parent: children for known parents.
443
433
        
444
434
        Only new paths and parents of tree files with assigned ids are used.
465
455
        # ensure all children of all existent parents are known
466
456
        # all children of non-existent parents are known, by definition.
467
457
        self._add_tree_children()
468
 
        by_parent = self._by_parent()
 
458
        by_parent = self.by_parent()
469
459
        conflicts.extend(self._unversioned_parents(by_parent))
470
460
        conflicts.extend(self._parent_loops())
471
461
        conflicts.extend(self._duplicate_entries(by_parent))
482
472
        Active parents are those which gain children, and those which are
483
473
        removed.  This is a necessary first step in detecting conflicts.
484
474
        """
485
 
        parents = self._by_parent().keys()
 
475
        parents = self.by_parent().keys()
486
476
        parents.extend([t for t in self._removed_contents if 
487
477
                        self.tree_kind(t) == 'directory'])
488
478
        for trans_id in self._removed_id:
514
504
                continue
515
505
            yield self.trans_id_tree_path(childpath)
516
506
 
 
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
 
517
532
    def _parent_loops(self):
518
533
        """No entry should be its own ancestor"""
519
534
        conflicts = []
605
620
                if name == last_name:
606
621
                    conflicts.append(('duplicate', last_trans_id, trans_id,
607
622
                    name))
608
 
                last_name = name
609
 
                last_trans_id = trans_id
 
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
610
631
        return conflicts
611
632
 
612
633
    def _duplicate_ids(self):
699
720
                child_pb.update('removing file', num, len(tree_paths))
700
721
                full_path = self._tree.abspath(path)
701
722
                if trans_id in self._removed_contents:
702
 
                    self.delete_any(full_path)
 
723
                    delete_any(full_path)
703
724
                elif trans_id in self._new_name or trans_id in \
704
725
                    self._new_parent:
705
726
                    try:
884
905
def build_tree(tree, wt):
885
906
    """Create working tree for a branch, using a Transaction."""
886
907
    file_trans_id = {}
 
908
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
909
    pp = ProgressPhase("Build phase", 2, top_pb)
887
910
    tt = TreeTransform(wt)
888
911
    try:
 
912
        pp.next_phase()
889
913
        file_trans_id[wt.get_root_id()] = tt.trans_id_tree_file_id(wt.get_root_id())
890
914
        file_ids = topology_sorted_ids(tree)
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)
 
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()
899
930
        tt.apply()
900
931
    finally:
901
932
        tt.finalize()
 
933
        top_pb.finished()
902
934
 
903
935
def new_by_entry(tt, entry, parent_id, tree):
904
936
    """Create a new file according to its inventory entry"""
939
971
    else:
940
972
        interesting_ids = set()
941
973
        for tree_path in filenames:
 
974
            not_found = True
942
975
            for tree in (working_tree, target_tree):
943
 
                not_found = True
944
976
                file_id = tree.inventory.path2id(tree_path)
945
977
                if file_id is not None:
946
978
                    interesting_ids.add(file_id)
947
979
                    not_found = False
948
 
                if not_found:
949
 
                    raise NotVersionedError(path=tree_path)
 
980
            if not_found:
 
981
                raise NotVersionedError(path=tree_path)
950
982
    return interesting_ids
951
983
 
952
984
 
953
985
def change_entry(tt, file_id, working_tree, target_tree, 
954
 
                 trans_id_file_id, backups, trans_id):
 
986
                 trans_id_file_id, backups, trans_id, by_parent):
955
987
    """Replace a file_id's contents with those from a target tree."""
956
988
    e_trans_id = trans_id_file_id(file_id)
957
989
    entry = target_tree.inventory[file_id]
964
996
                tt.delete_contents(e_trans_id)
965
997
            else:
966
998
                parent_trans_id = trans_id_file_id(entry.parent_id)
967
 
                tt.adjust_path(entry.name+"~", parent_trans_id, e_trans_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)
968
1002
                tt.unversion_file(e_trans_id)
969
1003
                e_trans_id = tt.create_path(entry.name, parent_trans_id)
970
1004
                tt.version_file(file_id, e_trans_id)
988
1022
        tt.adjust_path(entry.name, parent_trans_id, e_trans_id)
989
1023
 
990
1024
 
 
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
 
991
1036
def _entry_changes(file_id, entry, working_tree):
992
1037
    """Determine in which ways the inventory entry has changed.
993
1038
 
1043
1088
                              interesting(i)]
1044
1089
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1045
1090
        try:
 
1091
            by_parent = tt.by_parent()
1046
1092
            for id_num, file_id in enumerate(sorted_interesting):
1047
1093
                child_pb.update("Reverting file", id_num+1, 
1048
1094
                                len(sorted_interesting))
1057
1103
                        backup_this = False
1058
1104
                        del merge_modified[file_id]
1059
1105
                    change_entry(tt, file_id, working_tree, target_tree, 
1060
 
                                 trans_id_file_id, backup_this, trans_id)
 
1106
                                 trans_id_file_id, backup_this, trans_id,
 
1107
                                 by_parent)
1061
1108
        finally:
1062
1109
            child_pb.finished()
1063
1110
        pp.next_phase()
1081
1128
            raw_conflicts = resolve_conflicts(tt, child_pb)
1082
1129
        finally:
1083
1130
            child_pb.finished()
1084
 
        for line in conflicts_strings(cook_conflicts(raw_conflicts, tt)):
1085
 
            warning(line)
 
1131
        conflicts = cook_conflicts(raw_conflicts, tt)
 
1132
        for conflict in conflicts:
 
1133
            warning(conflict)
1086
1134
        pp.next_phase()
1087
1135
        tt.apply()
1088
1136
        working_tree.set_merge_modified({})
1089
1137
    finally:
1090
1138
        tt.finalize()
1091
1139
        pb.clear()
 
1140
    return conflicts
1092
1141
 
1093
1142
 
1094
1143
def resolve_conflicts(tt, pb=DummyProgress()):
1148
1197
            new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
1149
1198
    return new_conflicts
1150
1199
 
 
1200
 
1151
1201
def cook_conflicts(raw_conflicts, tt):
1152
1202
    """Generate a list of cooked conflicts, sorted by file path"""
1153
 
    def key(conflict):
1154
 
        if conflict[2] is not None:
1155
 
            return conflict[2], conflict[0]
1156
 
        elif len(conflict) == 6:
1157
 
            return conflict[4], conflict[0]
1158
 
        else:
1159
 
            return None, conflict[0]
 
1203
    from bzrlib.conflicts import Conflict
 
1204
    conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
 
1205
    return sorted(conflict_iter, key=Conflict.sort_key)
1160
1206
 
1161
 
    return sorted(list(iter_cook_conflicts(raw_conflicts, tt)), key=key)
1162
1207
 
1163
1208
def iter_cook_conflicts(raw_conflicts, tt):
1164
 
    cooked_conflicts = []
 
1209
    from bzrlib.conflicts import Conflict
1165
1210
    fp = FinalPaths(tt)
1166
1211
    for conflict in raw_conflicts:
1167
1212
        c_type = conflict[0]
1169
1214
        modified_path = fp.get_path(conflict[2])
1170
1215
        modified_id = tt.final_file_id(conflict[2])
1171
1216
        if len(conflict) == 3:
1172
 
            yield c_type, action, modified_path, modified_id
 
1217
            yield Conflict.factory(c_type, action=action, path=modified_path,
 
1218
                                     file_id=modified_id)
 
1219
             
1173
1220
        else:
1174
1221
            conflicting_path = fp.get_path(conflict[3])
1175
1222
            conflicting_id = tt.final_file_id(conflict[3])
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
 
1223
            yield Conflict.factory(c_type, action=action, path=modified_path,
 
1224
                                   file_id=modified_id, 
 
1225
                                   conflict_path=conflicting_path,
 
1226
                                   conflict_file_id=conflicting_id)