~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

Use numbered backup files

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
26
from bzrlib.progress import DummyProgress, ProgressPhase
27
27
from bzrlib.trace import mutter, warning
28
28
import bzrlib.ui 
438
438
        except KeyError:
439
439
            return os.path.basename(self._tree_id_paths[trans_id])
440
440
 
441
 
    def _by_parent(self):
 
441
    def by_parent(self):
442
442
        """Return a map of parent: children for known parents.
443
443
        
444
444
        Only new paths and parents of tree files with assigned ids are used.
465
465
        # ensure all children of all existent parents are known
466
466
        # all children of non-existent parents are known, by definition.
467
467
        self._add_tree_children()
468
 
        by_parent = self._by_parent()
 
468
        by_parent = self.by_parent()
469
469
        conflicts.extend(self._unversioned_parents(by_parent))
470
470
        conflicts.extend(self._parent_loops())
471
471
        conflicts.extend(self._duplicate_entries(by_parent))
482
482
        Active parents are those which gain children, and those which are
483
483
        removed.  This is a necessary first step in detecting conflicts.
484
484
        """
485
 
        parents = self._by_parent().keys()
 
485
        parents = self.by_parent().keys()
486
486
        parents.extend([t for t in self._removed_contents if 
487
487
                        self.tree_kind(t) == 'directory'])
488
488
        for trans_id in self._removed_id:
514
514
                continue
515
515
            yield self.trans_id_tree_path(childpath)
516
516
 
 
517
    def has_named_child(self, by_parent, parent_id, name):
 
518
        try:
 
519
            children = by_parent[parent_id]
 
520
        except KeyError:
 
521
            children = []
 
522
        for child in children:
 
523
            if self.final_name(child) == name:
 
524
                return True
 
525
        try:
 
526
            path = self._tree_id_paths[parent_id]
 
527
        except KeyError:
 
528
            return False
 
529
        childpath = joinpath(path, name)
 
530
        child_id = self._tree_path_ids.get(childpath)
 
531
        if child_id is None:
 
532
            return lexists(self._tree.abspath(childpath))
 
533
        else:
 
534
            if tt.final_parent(child_id) != parent_id:
 
535
                return False
 
536
            if child_id in tt._removed_contents:
 
537
                # XXX What about dangling file-ids?
 
538
                return False
 
539
            else:
 
540
                return True
 
541
 
517
542
    def _parent_loops(self):
518
543
        """No entry should be its own ancestor"""
519
544
        conflicts = []
968
993
 
969
994
 
970
995
def change_entry(tt, file_id, working_tree, target_tree, 
971
 
                 trans_id_file_id, backups, trans_id):
 
996
                 trans_id_file_id, backups, trans_id, by_parent):
972
997
    """Replace a file_id's contents with those from a target tree."""
973
998
    e_trans_id = trans_id_file_id(file_id)
974
999
    entry = target_tree.inventory[file_id]
981
1006
                tt.delete_contents(e_trans_id)
982
1007
            else:
983
1008
                parent_trans_id = trans_id_file_id(entry.parent_id)
984
 
                tt.adjust_path(entry.name+"~", parent_trans_id, e_trans_id)
 
1009
                backup_name = get_backup_name(entry, by_parent,
 
1010
                                              parent_trans_id, tt)
 
1011
                tt.adjust_path(backup_name, parent_trans_id, e_trans_id)
985
1012
                tt.unversion_file(e_trans_id)
986
1013
                e_trans_id = tt.create_path(entry.name, parent_trans_id)
987
1014
                tt.version_file(file_id, e_trans_id)
1005
1032
        tt.adjust_path(entry.name, parent_trans_id, e_trans_id)
1006
1033
 
1007
1034
 
 
1035
def get_backup_name(entry, by_parent, parent_trans_id, tt):
 
1036
    """Produce a backup-style name that appears to be available"""
 
1037
    def name_gen():
 
1038
        yield entry.name+"~"
 
1039
        counter = 1
 
1040
        while True:
 
1041
            yield "%s~%d~" % (entry.name, counter)
 
1042
            counter += 1
 
1043
    for name in name_gen():
 
1044
        if not tt.has_named_child(by_parent, parent_trans_id, name):
 
1045
            return name
 
1046
 
1008
1047
def _entry_changes(file_id, entry, working_tree):
1009
1048
    """Determine in which ways the inventory entry has changed.
1010
1049
 
1060
1099
                              interesting(i)]
1061
1100
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1062
1101
        try:
 
1102
            by_parent = tt.by_parent()
1063
1103
            for id_num, file_id in enumerate(sorted_interesting):
1064
1104
                child_pb.update("Reverting file", id_num+1, 
1065
1105
                                len(sorted_interesting))
1074
1114
                        backup_this = False
1075
1115
                        del merge_modified[file_id]
1076
1116
                    change_entry(tt, file_id, working_tree, target_tree, 
1077
 
                                 trans_id_file_id, backup_this, trans_id)
 
1117
                                 trans_id_file_id, backup_this, trans_id,
 
1118
                                 by_parent)
1078
1119
        finally:
1079
1120
            child_pb.finished()
1080
1121
        pp.next_phase()