~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

MergeĀ lp:bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
261
261
    def supports_views(self):
262
262
        return self.views.supports_views()
263
263
 
 
264
    def get_config_stack(self):
 
265
        """Retrieve the config stack for this tree.
 
266
 
 
267
        :return: A ``bzrlib.config.Stack``
 
268
        """
 
269
        # For the moment, just provide the branch config stack.
 
270
        return self.branch.get_config_stack()
 
271
 
264
272
    @staticmethod
265
273
    def open(path=None, _unsupported=False):
266
274
        """Open an existing working tree at path.
379
387
                                              list_current=list_current)
380
388
        return [tr for tr in iterator if tr is not None]
381
389
 
382
 
    def all_file_ids(self):
383
 
        """See Tree.iter_all_file_ids"""
384
 
        raise NotImplementedError(self.all_file_ids)
385
 
 
386
390
    def __repr__(self):
387
391
        return "<%s of %s>" % (self.__class__.__name__,
388
392
                               getattr(self, 'basedir', None))
1946
1950
            if entry.parent_id == orig_root_id:
1947
1951
                entry.parent_id = inv.root.file_id
1948
1952
 
1949
 
    def all_file_ids(self):
1950
 
        """See Tree.iter_all_file_ids"""
1951
 
        return set(self.inventory)
1952
 
 
1953
1953
    @needs_tree_write_lock
1954
1954
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1955
1955
        """See MutableTree.set_parent_trees."""
1974
1974
                # parent tree from the repository.
1975
1975
                self._cache_basis_inventory(leftmost_parent_id)
1976
1976
            else:
1977
 
                inv = leftmost_parent_tree.inventory
 
1977
                inv = leftmost_parent_tree.root_inventory
1978
1978
                xml = self._create_basis_xml_from_inventory(
1979
1979
                                        leftmost_parent_id, inv)
1980
1980
                self._write_basis_inventory(xml)
2077
2077
 
2078
2078
    def has_id(self, file_id):
2079
2079
        # files that have been deleted are excluded
2080
 
        inv = self.inventory
2081
 
        if not inv.has_id(file_id):
 
2080
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2081
        if not inv.has_id(inv_file_id):
2082
2082
            return False
2083
 
        path = inv.id2path(file_id)
 
2083
        path = inv.id2path(inv_file_id)
2084
2084
        return osutils.lexists(self.abspath(path))
2085
2085
 
2086
2086
    def has_or_had_id(self, file_id):
2087
 
        if file_id == self.inventory.root.file_id:
 
2087
        if file_id == self.get_root_id():
2088
2088
            return True
2089
 
        return self.inventory.has_id(file_id)
 
2089
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2090
        return inv.has_id(inv_file_id)
2090
2091
 
2091
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2092
 
    def __iter__(self):
 
2092
    def all_file_ids(self):
2093
2093
        """Iterate through file_ids for this tree.
2094
2094
 
2095
2095
        file_ids are in a WorkingTree if they are in the working inventory
2096
2096
        and the working file exists.
2097
2097
        """
2098
 
        inv = self._inventory
2099
 
        for path, ie in inv.iter_entries():
2100
 
            if osutils.lexists(self.abspath(path)):
2101
 
                yield ie.file_id
 
2098
        ret = set()
 
2099
        for path, ie in self.iter_entries_by_dir():
 
2100
            ret.add(ie.file_id)
 
2101
        return ret
2102
2102
 
2103
2103
    @needs_tree_write_lock
2104
2104
    def set_last_revision(self, new_revision):
2160
2160
                _mod_revision.NULL_REVISION)
2161
2161
        else:
2162
2162
            rt = self.branch.repository.revision_tree(revision_ids[0])
2163
 
        self._write_inventory(rt.inventory)
 
2163
        self._write_inventory(rt.root_inventory)
2164
2164
        self.set_parent_ids(revision_ids)
2165
2165
 
2166
2166
    def flush(self):
2178
2178
    def get_file_mtime(self, file_id, path=None):
2179
2179
        """See Tree.get_file_mtime."""
2180
2180
        if not path:
2181
 
            path = self.inventory.id2path(file_id)
 
2181
            path = self.id2path(file_id)
2182
2182
        try:
2183
2183
            return os.lstat(self.abspath(path)).st_mtime
2184
2184
        except OSError, e:
2187
2187
            raise
2188
2188
 
2189
2189
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2190
 
        file_id = self.path2id(path)
 
2190
        inv, file_id = self._path2inv_file_id(path)
2191
2191
        if file_id is None:
2192
2192
            # For unversioned files on win32, we just assume they are not
2193
2193
            # executable
2194
2194
            return False
2195
 
        return self._inventory[file_id].executable
 
2195
        return inv[file_id].executable
2196
2196
 
2197
2197
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2198
2198
        mode = stat_result.st_mode
2200
2200
 
2201
2201
    def is_executable(self, file_id, path=None):
2202
2202
        if not self._supports_executable():
2203
 
            return self._inventory[file_id].executable
 
2203
            inv, inv_file_id = self._unpack_file_id(file_id)
 
2204
            return inv[inv_file_id].executable
2204
2205
        else:
2205
2206
            if not path:
2206
2207
                path = self.id2path(file_id)
2220
2221
        # should probably put it back with the previous ID.
2221
2222
        # the read and write working inventory should not occur in this
2222
2223
        # function - they should be part of lock_write and unlock.
2223
 
        inv = self.inventory
 
2224
        # FIXME: nested trees
 
2225
        inv = self.root_inventory
2224
2226
        for f, file_id, kind in zip(files, ids, kinds):
2225
2227
            if file_id is None:
2226
2228
                inv.add_path(f, kind=kind)
2267
2269
                parent_tree = self.branch.repository.revision_tree(parent_id)
2268
2270
            parent_tree.lock_read()
2269
2271
            try:
2270
 
                if not parent_tree.has_id(file_id):
 
2272
                try:
 
2273
                    kind = parent_tree.kind(file_id)
 
2274
                except errors.NoSuchId:
2271
2275
                    continue
2272
 
                ie = parent_tree.inventory[file_id]
2273
 
                if ie.kind != 'file':
 
2276
                if kind != 'file':
2274
2277
                    # Note: this is slightly unnecessary, because symlinks and
2275
2278
                    # directories have a "text" which is the empty text, and we
2276
2279
                    # know that won't mess up annotations. But it seems cleaner
2277
2280
                    continue
2278
 
                parent_text_key = (file_id, ie.revision)
 
2281
                parent_text_key = (
 
2282
                    file_id, parent_tree.get_file_revision(file_id))
2279
2283
                if parent_text_key not in maybe_file_parent_keys:
2280
2284
                    maybe_file_parent_keys.append(parent_text_key)
2281
2285
            finally:
2335
2339
            for s in _mod_rio.RioReader(hashfile):
2336
2340
                # RioReader reads in Unicode, so convert file_ids back to utf8
2337
2341
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2338
 
                if not self.inventory.has_id(file_id):
 
2342
                if not self.has_id(file_id):
2339
2343
                    continue
2340
2344
                text_hash = s.get("hash")
2341
2345
                if text_hash == self.get_file_sha1(file_id):
2371
2375
        other_tree.lock_tree_write()
2372
2376
        try:
2373
2377
            new_parents = other_tree.get_parent_ids()
2374
 
            other_root = other_tree.inventory.root
 
2378
            other_root = other_tree.root_inventory.root
2375
2379
            other_root.parent_id = new_root_parent
2376
2380
            other_root.name = osutils.basename(other_tree_path)
2377
 
            self.inventory.add(other_root)
2378
 
            add_children(self.inventory, other_root)
2379
 
            self._write_inventory(self.inventory)
 
2381
            self.root_inventory.add(other_root)
 
2382
            add_children(self.root_inventory, other_root)
 
2383
            self._write_inventory(self.root_inventory)
2380
2384
            # normally we don't want to fetch whole repositories, but i think
2381
2385
            # here we really do want to consolidate the whole thing.
2382
2386
            for parent_id in other_tree.get_parent_ids():
2425
2429
            tree_bzrdir = branch_bzrdir
2426
2430
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2427
2431
        wt.set_parent_ids(self.get_parent_ids())
2428
 
        my_inv = self.inventory
 
2432
        # FIXME: Support nested trees
 
2433
        my_inv = self.root_inventory
2429
2434
        child_inv = inventory.Inventory(root_id=None)
2430
2435
        new_root = my_inv[file_id]
2431
2436
        my_inv.remove_recursive_id(file_id)
2451
2456
        if not self.is_locked():
2452
2457
            raise errors.ObjectNotLocked(self)
2453
2458
 
2454
 
        inv = self.inventory
2455
2459
        if from_dir is None and include_root is True:
2456
 
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
 
2460
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
2457
2461
        # Convert these into local objects to save lookup times
2458
2462
        pathjoin = osutils.pathjoin
2459
2463
        file_kind = self._kind
2466
2470
 
2467
2471
        # directory file_id, relative path, absolute path, reverse sorted children
2468
2472
        if from_dir is not None:
2469
 
            from_dir_id = inv.path2id(from_dir)
 
2473
            inv, from_dir_id = self._path2inv_file_id(from_dir)
2470
2474
            if from_dir_id is None:
2471
2475
                # Directory not versioned
2472
2476
                return
2473
2477
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2474
2478
        else:
 
2479
            inv = self.root_inventory
2475
2480
            from_dir_id = inv.root.file_id
2476
2481
            from_dir_abspath = self.basedir
2477
2482
        children = os.listdir(from_dir_abspath)
2600
2605
        rename_entries = []
2601
2606
        rename_tuples = []
2602
2607
 
 
2608
        invs_to_write = set()
 
2609
 
2603
2610
        # check for deprecated use of signature
2604
2611
        if to_dir is None:
2605
2612
            raise TypeError('You must supply a target directory')
2606
2613
        # check destination directory
2607
2614
        if isinstance(from_paths, basestring):
2608
2615
            raise ValueError()
2609
 
        inv = self.inventory
2610
2616
        to_abs = self.abspath(to_dir)
2611
2617
        if not isdir(to_abs):
2612
2618
            raise errors.BzrMoveFailedError('',to_dir,
2614
2620
        if not self.has_filename(to_dir):
2615
2621
            raise errors.BzrMoveFailedError('',to_dir,
2616
2622
                errors.NotInWorkingDirectory(to_dir))
2617
 
        to_dir_id = inv.path2id(to_dir)
 
2623
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2618
2624
        if to_dir_id is None:
2619
2625
            raise errors.BzrMoveFailedError('',to_dir,
2620
2626
                errors.NotVersionedError(path=to_dir))
2621
2627
 
2622
 
        to_dir_ie = inv[to_dir_id]
 
2628
        to_dir_ie = to_inv[to_dir_id]
2623
2629
        if to_dir_ie.kind != 'directory':
2624
2630
            raise errors.BzrMoveFailedError('',to_dir,
2625
2631
                errors.NotADirectory(to_abs))
2627
2633
        # create rename entries and tuples
2628
2634
        for from_rel in from_paths:
2629
2635
            from_tail = splitpath(from_rel)[-1]
2630
 
            from_id = inv.path2id(from_rel)
 
2636
            from_inv, from_id = self._path2inv_file_id(from_rel)
2631
2637
            if from_id is None:
2632
2638
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2633
2639
                    errors.NotVersionedError(path=from_rel))
2634
2640
 
2635
 
            from_entry = inv[from_id]
 
2641
            from_entry = from_inv[from_id]
2636
2642
            from_parent_id = from_entry.parent_id
2637
2643
            to_rel = pathjoin(to_dir, from_tail)
2638
2644
            rename_entry = InventoryWorkingTree._RenameEntry(
2657
2663
            # restore the inventory on error
2658
2664
            self._inventory_is_modified = original_modified
2659
2665
            raise
2660
 
        self._write_inventory(inv)
 
2666
        #FIXME: Should potentially also write the from_invs
 
2667
        self._write_inventory(to_inv)
2661
2668
        return rename_tuples
2662
2669
 
2663
2670
    @needs_tree_write_lock
2683
2690
 
2684
2691
        Everything else results in an error.
2685
2692
        """
2686
 
        inv = self.inventory
2687
2693
        rename_entries = []
2688
2694
 
2689
2695
        # create rename entries and tuples
2690
2696
        from_tail = splitpath(from_rel)[-1]
2691
 
        from_id = inv.path2id(from_rel)
 
2697
        from_inv, from_id = self._path2inv_file_id(from_rel)
2692
2698
        if from_id is None:
2693
2699
            # if file is missing in the inventory maybe it's in the basis_tree
2694
2700
            basis_tree = self.branch.basis_tree()
2697
2703
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2698
2704
                    errors.NotVersionedError(path=from_rel))
2699
2705
            # put entry back in the inventory so we can rename it
2700
 
            from_entry = basis_tree.inventory[from_id].copy()
2701
 
            inv.add(from_entry)
 
2706
            from_entry = basis_tree.root_inventory[from_id].copy()
 
2707
            from_inv.add(from_entry)
2702
2708
        else:
2703
 
            from_entry = inv[from_id]
 
2709
            from_inv, from_inv_id = self._unpack_file_id(from_id)
 
2710
            from_entry = from_inv[from_inv_id]
2704
2711
        from_parent_id = from_entry.parent_id
2705
2712
        to_dir, to_tail = os.path.split(to_rel)
2706
 
        to_dir_id = inv.path2id(to_dir)
 
2713
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2707
2714
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2708
2715
                                     from_id=from_id,
2709
2716
                                     from_tail=from_tail,
2731
2738
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2732
2739
 
2733
2740
        self._move(rename_entries)
2734
 
        self._write_inventory(inv)
 
2741
        self._write_inventory(to_inv)
2735
2742
 
2736
2743
    class _RenameEntry(object):
2737
2744
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2753
2760
 
2754
2761
        Also does basic plausability tests.
2755
2762
        """
2756
 
        inv = self.inventory
 
2763
        # FIXME: Handling of nested trees
 
2764
        inv = self.root_inventory
2757
2765
 
2758
2766
        for rename_entry in rename_entries:
2759
2767
            # store to local variables for easier reference
2817
2825
        Depending on the value of the flag 'only_change_inv', the
2818
2826
        file will be moved on the file system or not.
2819
2827
        """
2820
 
        inv = self.inventory
2821
2828
        moved = []
2822
2829
 
2823
2830
        for entry in rename_entries:
2830
2837
 
2831
2838
    def _rollback_move(self, moved):
2832
2839
        """Try to rollback a previous move in case of an filesystem error."""
2833
 
        inv = self.inventory
2834
2840
        for entry in moved:
2835
2841
            try:
2836
2842
                self._move_entry(WorkingTree._RenameEntry(
2845
2851
                        " Error message is: %s" % e)
2846
2852
 
2847
2853
    def _move_entry(self, entry):
2848
 
        inv = self.inventory
 
2854
        inv = self.root_inventory
2849
2855
        from_rel_abs = self.abspath(entry.from_rel)
2850
2856
        to_rel_abs = self.abspath(entry.to_rel)
2851
2857
        if from_rel_abs == to_rel_abs:
2892
2898
 
2893
2899
    def stored_kind(self, file_id):
2894
2900
        """See Tree.stored_kind"""
2895
 
        return self.inventory[file_id].kind
 
2901
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2902
        return inv[inv_file_id].kind
2896
2903
 
2897
2904
    def extras(self):
2898
2905
        """Yield all unversioned files in this WorkingTree.
2905
2912
        This is the same order used by 'osutils.walkdirs'.
2906
2913
        """
2907
2914
        ## TODO: Work from given directory downwards
2908
 
        for path, dir_entry in self.inventory.directories():
 
2915
        for path, dir_entry in self.iter_entries_by_dir():
 
2916
            if dir_entry.kind != 'directory':
 
2917
                continue
2909
2918
            # mutter("search for unknowns in %r", path)
2910
2919
            dirabs = self.abspath(path)
2911
2920
            if not isdir(dirabs):
2948
2957
        """
2949
2958
        _directory = 'directory'
2950
2959
        # get the root in the inventory
2951
 
        inv = self.inventory
2952
 
        top_id = inv.path2id(prefix)
 
2960
        inv, top_id = self._path2inv_file_id(prefix)
2953
2961
        if top_id is None:
2954
2962
            pending = []
2955
2963
        else: