~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-12-03 05:08:05 UTC
  • mfrom: (4854.1.1 merge-2.0-into-devel)
  • Revision ID: pqm@pqm.ubuntu.com-20091203050805-s70sybyrsrbbs10c
(andrew) Merge lp:bzr/2.0 into lp:bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
859
859
    def get_preview_tree(self):
860
860
        """Return a tree representing the result of the transform.
861
861
 
862
 
        This tree only supports the subset of Tree functionality required
863
 
        by show_diff_trees.  It must only be compared to tt._tree.
 
862
        The tree is a snapshot, and altering the TreeTransform will invalidate
 
863
        it.
864
864
        """
865
865
        return _PreviewTree(self)
866
866
 
1054
1054
    def _limbo_name(self, trans_id):
1055
1055
        """Generate the limbo name of a file"""
1056
1056
        limbo_name = self._limbo_files.get(trans_id)
1057
 
        if limbo_name is not None:
1058
 
            return limbo_name
1059
 
        parent = self._new_parent.get(trans_id)
1060
 
        # if the parent directory is already in limbo (e.g. when building a
1061
 
        # tree), choose a limbo name inside the parent, to reduce further
1062
 
        # renames.
1063
 
        use_direct_path = False
1064
 
        if self._new_contents.get(parent) == 'directory':
1065
 
            filename = self._new_name.get(trans_id)
1066
 
            if filename is not None:
1067
 
                if parent not in self._limbo_children:
1068
 
                    self._limbo_children[parent] = set()
1069
 
                    self._limbo_children_names[parent] = {}
1070
 
                    use_direct_path = True
1071
 
                # the direct path can only be used if no other file has
1072
 
                # already taken this pathname, i.e. if the name is unused, or
1073
 
                # if it is already associated with this trans_id.
1074
 
                elif self._case_sensitive_target:
1075
 
                    if (self._limbo_children_names[parent].get(filename)
1076
 
                        in (trans_id, None)):
1077
 
                        use_direct_path = True
1078
 
                else:
1079
 
                    for l_filename, l_trans_id in\
1080
 
                        self._limbo_children_names[parent].iteritems():
1081
 
                        if l_trans_id == trans_id:
1082
 
                            continue
1083
 
                        if l_filename.lower() == filename.lower():
1084
 
                            break
1085
 
                    else:
1086
 
                        use_direct_path = True
1087
 
 
1088
 
        if use_direct_path:
1089
 
            limbo_name = pathjoin(self._limbo_files[parent], filename)
1090
 
            self._limbo_children[parent].add(trans_id)
1091
 
            self._limbo_children_names[parent][filename] = trans_id
1092
 
        else:
1093
 
            limbo_name = pathjoin(self._limbodir, trans_id)
1094
 
            self._needs_rename.add(trans_id)
1095
 
        self._limbo_files[trans_id] = limbo_name
 
1057
        if limbo_name is None:
 
1058
            limbo_name = self._generate_limbo_path(trans_id)
 
1059
            self._limbo_files[trans_id] = limbo_name
1096
1060
        return limbo_name
1097
1061
 
 
1062
    def _generate_limbo_path(self, trans_id):
 
1063
        """Generate a limbo path using the trans_id as the relative path.
 
1064
 
 
1065
        This is suitable as a fallback, and when the transform should not be
 
1066
        sensitive to the path encoding of the limbo directory.
 
1067
        """
 
1068
        self._needs_rename.add(trans_id)
 
1069
        return pathjoin(self._limbodir, trans_id)
 
1070
 
1098
1071
    def adjust_path(self, name, parent, trans_id):
1099
1072
        previous_parent = self._new_parent.get(trans_id)
1100
1073
        previous_name = self._new_name.get(trans_id)
1122
1095
                continue
1123
1096
            new_path = self._limbo_name(trans_id)
1124
1097
            os.rename(old_path, new_path)
 
1098
            for descendant in self._limbo_descendants(trans_id):
 
1099
                desc_path = self._limbo_files[descendant]
 
1100
                desc_path = new_path + desc_path[len(old_path):]
 
1101
                self._limbo_files[descendant] = desc_path
 
1102
 
 
1103
    def _limbo_descendants(self, trans_id):
 
1104
        """Return the set of trans_ids whose limbo paths descend from this."""
 
1105
        descendants = set(self._limbo_children.get(trans_id, []))
 
1106
        for descendant in list(descendants):
 
1107
            descendants.update(self._limbo_descendants(descendant))
 
1108
        return descendants
1125
1109
 
1126
1110
    def create_file(self, contents, trans_id, mode_id=None):
1127
1111
        """Schedule creation of a new file.
1396
1380
                continue
1397
1381
            yield self.trans_id_tree_path(childpath)
1398
1382
 
 
1383
    def _generate_limbo_path(self, trans_id):
 
1384
        """Generate a limbo path using the final path if possible.
 
1385
 
 
1386
        This optimizes the performance of applying the tree transform by
 
1387
        avoiding renames.  These renames can be avoided only when the parent
 
1388
        directory is already scheduled for creation.
 
1389
 
 
1390
        If the final path cannot be used, falls back to using the trans_id as
 
1391
        the relpath.
 
1392
        """
 
1393
        parent = self._new_parent.get(trans_id)
 
1394
        # if the parent directory is already in limbo (e.g. when building a
 
1395
        # tree), choose a limbo name inside the parent, to reduce further
 
1396
        # renames.
 
1397
        use_direct_path = False
 
1398
        if self._new_contents.get(parent) == 'directory':
 
1399
            filename = self._new_name.get(trans_id)
 
1400
            if filename is not None:
 
1401
                if parent not in self._limbo_children:
 
1402
                    self._limbo_children[parent] = set()
 
1403
                    self._limbo_children_names[parent] = {}
 
1404
                    use_direct_path = True
 
1405
                # the direct path can only be used if no other file has
 
1406
                # already taken this pathname, i.e. if the name is unused, or
 
1407
                # if it is already associated with this trans_id.
 
1408
                elif self._case_sensitive_target:
 
1409
                    if (self._limbo_children_names[parent].get(filename)
 
1410
                        in (trans_id, None)):
 
1411
                        use_direct_path = True
 
1412
                else:
 
1413
                    for l_filename, l_trans_id in\
 
1414
                        self._limbo_children_names[parent].iteritems():
 
1415
                        if l_trans_id == trans_id:
 
1416
                            continue
 
1417
                        if l_filename.lower() == filename.lower():
 
1418
                            break
 
1419
                    else:
 
1420
                        use_direct_path = True
 
1421
 
 
1422
        if not use_direct_path:
 
1423
            return DiskTreeTransform._generate_limbo_path(self, trans_id)
 
1424
 
 
1425
        limbo_name = pathjoin(self._limbo_files[parent], filename)
 
1426
        self._limbo_children[parent].add(trans_id)
 
1427
        self._limbo_children_names[parent][filename] = trans_id
 
1428
        return limbo_name
 
1429
 
 
1430
 
1399
1431
    def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1400
1432
        """Apply all changes to the inventory and filesystem.
1401
1433
 
1635
1667
        self._all_children_cache = {}
1636
1668
        self._path2trans_id_cache = {}
1637
1669
        self._final_name_cache = {}
1638
 
 
1639
 
    def _changes(self, file_id):
1640
 
        for changes in self._transform.iter_changes():
1641
 
            if changes[0] == file_id:
1642
 
                return changes
 
1670
        self._iter_changes_cache = dict((c[0], c) for c in
 
1671
                                        self._transform.iter_changes())
1643
1672
 
1644
1673
    def _content_change(self, file_id):
1645
1674
        """Return True if the content of this file changed"""
1646
 
        changes = self._changes(file_id)
 
1675
        changes = self._iter_changes_cache.get(file_id)
1647
1676
        # changes[2] is true if the file content changed.  See
1648
1677
        # InterTree.iter_changes.
1649
1678
        return (changes is not None and changes[2])
1884
1913
    def get_file_mtime(self, file_id, path=None):
1885
1914
        """See Tree.get_file_mtime"""
1886
1915
        if not self._content_change(file_id):
1887
 
            return self._transform._tree.get_file_mtime(file_id, path)
 
1916
            return self._transform._tree.get_file_mtime(file_id)
1888
1917
        return self._stat_limbo_file(file_id).st_mtime
1889
1918
 
1890
1919
    def _file_size(self, entry, stat_value):
1944
1973
                statval = os.lstat(limbo_name)
1945
1974
                size = statval.st_size
1946
1975
                if not supports_executable():
1947
 
                    executable = None
 
1976
                    executable = False
1948
1977
                else:
1949
1978
                    executable = statval.st_mode & S_IEXEC
1950
1979
            else:
1952
1981
                executable = None
1953
1982
            if kind == 'symlink':
1954
1983
                link_or_sha1 = os.readlink(limbo_name).decode(osutils._fs_enc)
1955
 
        if supports_executable():
1956
 
            executable = tt._new_executability.get(trans_id, executable)
 
1984
        executable = tt._new_executability.get(trans_id, executable)
1957
1985
        return kind, size, executable, link_or_sha1
1958
1986
 
1959
1987
    def iter_changes(self, from_tree, include_unchanged=False,
1990
2018
 
1991
2019
    def annotate_iter(self, file_id,
1992
2020
                      default_revision=_mod_revision.CURRENT_REVISION):
1993
 
        changes = self._changes(file_id)
 
2021
        changes = self._iter_changes_cache.get(file_id)
1994
2022
        if changes is None:
1995
2023
            get_old = True
1996
2024
        else:
2621
2649
                tt.adjust_path(name[1], parent_trans, trans_id)
2622
2650
            if executable[0] != executable[1] and kind[1] == "file":
2623
2651
                tt.set_executability(executable[1], trans_id)
2624
 
        for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2625
 
            deferred_files):
2626
 
            tt.create_file(bytes, trans_id, mode_id)
 
2652
        if working_tree.supports_content_filtering():
 
2653
            for index, ((trans_id, mode_id), bytes) in enumerate(
 
2654
                target_tree.iter_files_bytes(deferred_files)):
 
2655
                file_id = deferred_files[index][0]
 
2656
                # We're reverting a tree to the target tree so using the
 
2657
                # target tree to find the file path seems the best choice
 
2658
                # here IMO - Ian C 27/Oct/2009
 
2659
                filter_tree_path = target_tree.id2path(file_id)
 
2660
                filters = working_tree._content_filter_stack(filter_tree_path)
 
2661
                bytes = filtered_output_bytes(bytes, filters,
 
2662
                    ContentFilterContext(filter_tree_path, working_tree))
 
2663
                tt.create_file(bytes, trans_id, mode_id)
 
2664
        else:
 
2665
            for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
 
2666
                deferred_files):
 
2667
                tt.create_file(bytes, trans_id, mode_id)
2627
2668
    finally:
2628
2669
        if basis_tree is not None:
2629
2670
            basis_tree.unlock()