~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

[merge] bzr.dev 2294

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
from stat import S_ISREG
20
20
 
21
21
from bzrlib import bzrdir, errors
 
22
from bzrlib.lazy_import import lazy_import
 
23
lazy_import(globals(), """
 
24
from bzrlib import delta
 
25
""")
22
26
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
23
27
                           ReusingTransform, NotVersionedError, CantMoveRoot,
24
28
                           ExistingLimbo, ImmortalLimbo, NoFinalPath)
903
907
        self.create_symlink(target, trans_id)
904
908
        return trans_id
905
909
 
 
910
    def _affected_ids(self):
 
911
        """Return the set of transform ids affected by the transform"""
 
912
        trans_ids = set(self._removed_id)
 
913
        trans_ids.update(self._new_id.keys())
 
914
        trans_ids.update(self._removed_contents)
 
915
        trans_ids.update(self._new_contents.keys())
 
916
        trans_ids.update(self._new_executability.keys())
 
917
        trans_ids.update(self._new_name.keys())
 
918
        trans_ids.update(self._new_parent.keys())
 
919
        return trans_ids
 
920
 
 
921
    def _get_file_id_maps(self):
 
922
        """Return mapping of file_ids to trans_ids in the to and from states"""
 
923
        trans_ids = self._affected_ids()
 
924
        from_trans_ids = {}
 
925
        to_trans_ids = {}
 
926
        # Build up two dicts: trans_ids associated with file ids in the
 
927
        # FROM state, vs the TO state.
 
928
        for trans_id in trans_ids:
 
929
            from_file_id = self.tree_file_id(trans_id)
 
930
            if from_file_id is not None:
 
931
                from_trans_ids[from_file_id] = trans_id
 
932
            to_file_id = self.final_file_id(trans_id)
 
933
            if to_file_id is not None:
 
934
                to_trans_ids[to_file_id] = trans_id
 
935
        return from_trans_ids, to_trans_ids
 
936
 
 
937
    def _from_file_data(self, from_trans_id, from_versioned, file_id):
 
938
        """Get data about a file in the from (tree) state
 
939
 
 
940
        Return a (name, parent, kind, executable) tuple
 
941
        """
 
942
        from_path = self._tree_id_paths.get(from_trans_id)
 
943
        if from_versioned:
 
944
            # get data from working tree if versioned
 
945
            from_entry = self._tree.inventory[file_id]
 
946
            from_name = from_entry.name
 
947
            from_parent = from_entry.parent_id
 
948
        else:
 
949
            from_entry = None
 
950
            if from_path is None:
 
951
                # File does not exist in FROM state
 
952
                from_name = None
 
953
                from_parent = None
 
954
            else:
 
955
                # File exists, but is not versioned.  Have to use path-
 
956
                # splitting stuff
 
957
                from_name = os.path.basename(from_path)
 
958
                tree_parent = self.get_tree_parent(from_trans_id)
 
959
                from_parent = self.tree_file_id(tree_parent)
 
960
        if from_path is not None:
 
961
            from_kind, from_executable, from_stats = \
 
962
                self._tree._comparison_data(from_entry, from_path)
 
963
        else:
 
964
            from_kind = None
 
965
            from_executable = False
 
966
        return from_name, from_parent, from_kind, from_executable
 
967
 
 
968
    def _to_file_data(self, to_trans_id, from_trans_id, from_executable):
 
969
        """Get data about a file in the to (target) state
 
970
 
 
971
        Return a (name, parent, kind, executable) tuple
 
972
        """
 
973
        to_name = self.final_name(to_trans_id)
 
974
        try:
 
975
            to_kind = self.final_kind(to_trans_id)
 
976
        except NoSuchFile:
 
977
            to_kind = None
 
978
        to_parent = self.final_file_id(self.final_parent(to_trans_id))
 
979
        if to_trans_id in self._new_executability:
 
980
            to_executable = self._new_executability[to_trans_id]
 
981
        elif to_trans_id == from_trans_id:
 
982
            to_executable = from_executable
 
983
        else:
 
984
            to_executable = False
 
985
        return to_name, to_parent, to_kind, to_executable
 
986
 
 
987
    def _iter_changes(self):
 
988
        """Produce output in the same format as Tree._iter_changes.
 
989
 
 
990
        Will produce nonsensical results if invoked while inventory/filesystem
 
991
        conflicts (as reported by TreeTransform.find_conflicts()) are present.
 
992
 
 
993
        This reads the Transform, but only reproduces changes involving a
 
994
        file_id.  Files that are not versioned in either of the FROM or TO
 
995
        states are not reflected.
 
996
        """
 
997
        final_paths = FinalPaths(self)
 
998
        from_trans_ids, to_trans_ids = self._get_file_id_maps()
 
999
        results = []
 
1000
        # Now iterate through all active file_ids
 
1001
        for file_id in set(from_trans_ids.keys() + to_trans_ids.keys()):
 
1002
            modified = False
 
1003
            from_trans_id = from_trans_ids.get(file_id)
 
1004
            # find file ids, and determine versioning state
 
1005
            if from_trans_id is None:
 
1006
                from_versioned = False
 
1007
                from_trans_id = to_trans_ids[file_id]
 
1008
            else:
 
1009
                from_versioned = True
 
1010
            to_trans_id = to_trans_ids.get(file_id)
 
1011
            if to_trans_id is None:
 
1012
                to_versioned = False
 
1013
                to_trans_id = from_trans_id
 
1014
            else:
 
1015
                to_versioned = True
 
1016
 
 
1017
            from_name, from_parent, from_kind, from_executable = \
 
1018
                self._from_file_data(from_trans_id, from_versioned, file_id)
 
1019
 
 
1020
            to_name, to_parent, to_kind, to_executable = \
 
1021
                self._to_file_data(to_trans_id, from_trans_id, from_executable)
 
1022
 
 
1023
            to_path = final_paths.get_path(to_trans_id)
 
1024
            if from_kind != to_kind:
 
1025
                modified = True
 
1026
            elif to_kind in ('file' or 'symlink') and (
 
1027
                to_trans_id != from_trans_id or
 
1028
                to_trans_id in self._new_contents):
 
1029
                modified = True
 
1030
            if (not modified and from_versioned == to_versioned and
 
1031
                from_parent==to_parent and from_name == to_name and
 
1032
                from_executable == to_executable):
 
1033
                continue
 
1034
            results.append((file_id, to_path, modified,
 
1035
                   (from_versioned, to_versioned),
 
1036
                   (from_parent, to_parent),
 
1037
                   (from_name, to_name),
 
1038
                   (from_kind, to_kind),
 
1039
                   (from_executable, to_executable)))
 
1040
        return iter(sorted(results, key=lambda x:x[1]))
 
1041
 
 
1042
 
906
1043
def joinpath(parent, child):
907
1044
    """Join tree-relative paths, handling the tree root specially"""
908
1045
    if parent is None or parent == "":
1209
1346
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1210
1347
        try:
1211
1348
            _alter_files(working_tree, target_tree, tt, child_pb,
1212
 
                         interesting_ids, backups, change_reporter)
 
1349
                         interesting_ids, backups)
1213
1350
        finally:
1214
1351
            child_pb.finished()
1215
1352
        pp.next_phase()
1219
1356
        finally:
1220
1357
            child_pb.finished()
1221
1358
        conflicts = cook_conflicts(raw_conflicts, tt)
 
1359
        if change_reporter:
 
1360
            change_reporter = delta.ChangeReporter(working_tree.inventory)
 
1361
            delta.report_changes(tt._iter_changes(), change_reporter)
1222
1362
        for conflict in conflicts:
1223
1363
            warning(conflict)
1224
1364
        pp.next_phase()
1231
1371
    return conflicts
1232
1372
 
1233
1373
 
1234
 
def _alter_files(working_tree, target_tree, tt, pb, interesting_ids, backups,
1235
 
                 report_changes):
1236
 
    from bzrlib import delta
 
1374
def _alter_files(working_tree, target_tree, tt, pb, interesting_ids,
 
1375
                 backups):
1237
1376
    merge_modified = working_tree.merge_modified()
1238
 
    change_list = list(target_tree._iter_changes(working_tree,
1239
 
        specific_file_ids=interesting_ids, pb=pb))
 
1377
    change_list = target_tree._iter_changes(working_tree,
 
1378
        specific_file_ids=interesting_ids, pb=pb)
1240
1379
    if target_tree.inventory.root is None:
1241
1380
        skip_root = True
1242
1381
    else:
1243
1382
        skip_root = False
1244
1383
    basis_tree = None
1245
1384
    try:
1246
 
        if report_changes:
1247
 
            change_reporter = delta.ChangeReporter(working_tree.inventory)
1248
 
            delta.report_changes(change_list, change_reporter)
1249
1385
        for id_num, (file_id, path, changed_content, versioned, parent, name,
1250
1386
                kind, executable) in enumerate(change_list):
1251
1387
            if skip_root and file_id[0] is not None and parent[0] is None: