~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Patch Queue Manager
  • Date: 2011-12-19 17:14:34 UTC
  • mfrom: (6378.1.5 config-si-unit)
  • Revision ID: pqm@pqm.ubuntu.com-20111219171434-i0b4ir0invs9il2v
(vila) Migrate add.maximum_file_size configuration option. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
47
47
 
48
48
from bzrlib import (
49
49
    branch,
 
50
    bzrdir,
50
51
    conflicts as _mod_conflicts,
51
52
    controldir,
52
53
    errors,
71
72
 
72
73
# Explicitly import bzrlib.bzrdir so that the BzrProber
73
74
# is guaranteed to be registered.
74
 
from bzrlib import (
75
 
    bzrdir,
76
 
    symbol_versioning,
77
 
    )
 
75
import bzrlib.bzrdir
78
76
 
 
77
from bzrlib import symbol_versioning
79
78
from bzrlib.decorators import needs_read_lock, needs_write_lock
80
79
from bzrlib.i18n import gettext
81
80
from bzrlib.lock import LogicalLockResult
90
89
    realpath,
91
90
    safe_unicode,
92
91
    splitpath,
 
92
    supports_executable,
93
93
    )
94
94
from bzrlib.trace import mutter, note
95
95
from bzrlib.revision import CURRENT_REVISION
233
233
        """See `Tree.has_versioned_directories`."""
234
234
        return self._format.supports_versioned_directories
235
235
 
236
 
    def _supports_executable(self):
237
 
        if sys.platform == 'win32':
238
 
            return False
239
 
        # FIXME: Ideally this should check the file system
240
 
        return True
241
 
 
242
236
    def break_lock(self):
243
237
        """Break a lock if one is present from another instance.
244
238
 
261
255
    def supports_views(self):
262
256
        return self.views.supports_views()
263
257
 
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
 
 
272
258
    @staticmethod
273
259
    def open(path=None, _unsupported=False):
274
260
        """Open an existing working tree at path.
276
262
        """
277
263
        if path is None:
278
264
            path = osutils.getcwd()
279
 
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
280
 
        return control.open_workingtree(unsupported=_unsupported)
 
265
        control = controldir.ControlDir.open(path, _unsupported)
 
266
        return control.open_workingtree(_unsupported)
281
267
 
282
268
    @staticmethod
283
269
    def open_containing(path=None):
387
373
                                              list_current=list_current)
388
374
        return [tr for tr in iterator if tr is not None]
389
375
 
 
376
    def all_file_ids(self):
 
377
        """See Tree.iter_all_file_ids"""
 
378
        raise NotImplementedError(self.all_file_ids)
 
379
 
390
380
    def __repr__(self):
391
381
        return "<%s of %s>" % (self.__class__.__name__,
392
382
                               getattr(self, 'basedir', None))
1127
1117
        else:
1128
1118
            mode = stat_value.st_mode
1129
1119
            kind = osutils.file_kind_from_stat_mode(mode)
1130
 
            if not self._supports_executable():
 
1120
            if not supports_executable():
1131
1121
                executable = entry is not None and entry.executable
1132
1122
            else:
1133
1123
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1950
1940
            if entry.parent_id == orig_root_id:
1951
1941
                entry.parent_id = inv.root.file_id
1952
1942
 
 
1943
    def all_file_ids(self):
 
1944
        """See Tree.iter_all_file_ids"""
 
1945
        return set(self.inventory)
 
1946
 
1953
1947
    @needs_tree_write_lock
1954
1948
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1955
1949
        """See MutableTree.set_parent_trees."""
1974
1968
                # parent tree from the repository.
1975
1969
                self._cache_basis_inventory(leftmost_parent_id)
1976
1970
            else:
1977
 
                inv = leftmost_parent_tree.root_inventory
 
1971
                inv = leftmost_parent_tree.inventory
1978
1972
                xml = self._create_basis_xml_from_inventory(
1979
1973
                                        leftmost_parent_id, inv)
1980
1974
                self._write_basis_inventory(xml)
2077
2071
 
2078
2072
    def has_id(self, file_id):
2079
2073
        # files that have been deleted are excluded
2080
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2081
 
        if not inv.has_id(inv_file_id):
 
2074
        inv = self.inventory
 
2075
        if not inv.has_id(file_id):
2082
2076
            return False
2083
 
        path = inv.id2path(inv_file_id)
 
2077
        path = inv.id2path(file_id)
2084
2078
        return osutils.lexists(self.abspath(path))
2085
2079
 
2086
2080
    def has_or_had_id(self, file_id):
2087
 
        if file_id == self.get_root_id():
 
2081
        if file_id == self.inventory.root.file_id:
2088
2082
            return True
2089
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2090
 
        return inv.has_id(inv_file_id)
 
2083
        return self.inventory.has_id(file_id)
2091
2084
 
2092
 
    def all_file_ids(self):
 
2085
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
2086
    def __iter__(self):
2093
2087
        """Iterate through file_ids for this tree.
2094
2088
 
2095
2089
        file_ids are in a WorkingTree if they are in the working inventory
2096
2090
        and the working file exists.
2097
2091
        """
2098
 
        ret = set()
2099
 
        for path, ie in self.iter_entries_by_dir():
2100
 
            ret.add(ie.file_id)
2101
 
        return ret
 
2092
        inv = self._inventory
 
2093
        for path, ie in inv.iter_entries():
 
2094
            if osutils.lexists(self.abspath(path)):
 
2095
                yield ie.file_id
2102
2096
 
2103
2097
    @needs_tree_write_lock
2104
2098
    def set_last_revision(self, new_revision):
2160
2154
                _mod_revision.NULL_REVISION)
2161
2155
        else:
2162
2156
            rt = self.branch.repository.revision_tree(revision_ids[0])
2163
 
        self._write_inventory(rt.root_inventory)
 
2157
        self._write_inventory(rt.inventory)
2164
2158
        self.set_parent_ids(revision_ids)
2165
2159
 
2166
2160
    def flush(self):
2178
2172
    def get_file_mtime(self, file_id, path=None):
2179
2173
        """See Tree.get_file_mtime."""
2180
2174
        if not path:
2181
 
            path = self.id2path(file_id)
 
2175
            path = self.inventory.id2path(file_id)
2182
2176
        try:
2183
2177
            return os.lstat(self.abspath(path)).st_mtime
2184
2178
        except OSError, e:
2187
2181
            raise
2188
2182
 
2189
2183
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2190
 
        inv, file_id = self._path2inv_file_id(path)
 
2184
        file_id = self.path2id(path)
2191
2185
        if file_id is None:
2192
2186
            # For unversioned files on win32, we just assume they are not
2193
2187
            # executable
2194
2188
            return False
2195
 
        return inv[file_id].executable
 
2189
        return self._inventory[file_id].executable
2196
2190
 
2197
2191
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2198
2192
        mode = stat_result.st_mode
2199
2193
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2200
2194
 
2201
 
    def is_executable(self, file_id, path=None):
2202
 
        if not self._supports_executable():
2203
 
            inv, inv_file_id = self._unpack_file_id(file_id)
2204
 
            return inv[inv_file_id].executable
2205
 
        else:
 
2195
    if not supports_executable():
 
2196
        def is_executable(self, file_id, path=None):
 
2197
            return self._inventory[file_id].executable
 
2198
 
 
2199
        _is_executable_from_path_and_stat = \
 
2200
            _is_executable_from_path_and_stat_from_basis
 
2201
    else:
 
2202
        def is_executable(self, file_id, path=None):
2206
2203
            if not path:
2207
2204
                path = self.id2path(file_id)
2208
2205
            mode = os.lstat(self.abspath(path)).st_mode
2209
2206
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2210
2207
 
2211
 
    def _is_executable_from_path_and_stat(self, path, stat_result):
2212
 
        if not self._supports_executable():
2213
 
            return self._is_executable_from_path_and_stat_from_basis(path, stat_result)
2214
 
        else:
2215
 
            return self._is_executable_from_path_and_stat_from_stat(path, stat_result)
 
2208
        _is_executable_from_path_and_stat = \
 
2209
            _is_executable_from_path_and_stat_from_stat
2216
2210
 
2217
2211
    @needs_tree_write_lock
2218
2212
    def _add(self, files, ids, kinds):
2221
2215
        # should probably put it back with the previous ID.
2222
2216
        # the read and write working inventory should not occur in this
2223
2217
        # function - they should be part of lock_write and unlock.
2224
 
        # FIXME: nested trees
2225
 
        inv = self.root_inventory
 
2218
        inv = self.inventory
2226
2219
        for f, file_id, kind in zip(files, ids, kinds):
2227
2220
            if file_id is None:
2228
2221
                inv.add_path(f, kind=kind)
2269
2262
                parent_tree = self.branch.repository.revision_tree(parent_id)
2270
2263
            parent_tree.lock_read()
2271
2264
            try:
2272
 
                try:
2273
 
                    kind = parent_tree.kind(file_id)
2274
 
                except errors.NoSuchId:
 
2265
                if not parent_tree.has_id(file_id):
2275
2266
                    continue
2276
 
                if kind != 'file':
 
2267
                ie = parent_tree.inventory[file_id]
 
2268
                if ie.kind != 'file':
2277
2269
                    # Note: this is slightly unnecessary, because symlinks and
2278
2270
                    # directories have a "text" which is the empty text, and we
2279
2271
                    # know that won't mess up annotations. But it seems cleaner
2280
2272
                    continue
2281
 
                parent_text_key = (
2282
 
                    file_id, parent_tree.get_file_revision(file_id))
 
2273
                parent_text_key = (file_id, ie.revision)
2283
2274
                if parent_text_key not in maybe_file_parent_keys:
2284
2275
                    maybe_file_parent_keys.append(parent_text_key)
2285
2276
            finally:
2339
2330
            for s in _mod_rio.RioReader(hashfile):
2340
2331
                # RioReader reads in Unicode, so convert file_ids back to utf8
2341
2332
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2342
 
                if not self.has_id(file_id):
 
2333
                if not self.inventory.has_id(file_id):
2343
2334
                    continue
2344
2335
                text_hash = s.get("hash")
2345
2336
                if text_hash == self.get_file_sha1(file_id):
2375
2366
        other_tree.lock_tree_write()
2376
2367
        try:
2377
2368
            new_parents = other_tree.get_parent_ids()
2378
 
            other_root = other_tree.root_inventory.root
 
2369
            other_root = other_tree.inventory.root
2379
2370
            other_root.parent_id = new_root_parent
2380
2371
            other_root.name = osutils.basename(other_tree_path)
2381
 
            self.root_inventory.add(other_root)
2382
 
            add_children(self.root_inventory, other_root)
2383
 
            self._write_inventory(self.root_inventory)
 
2372
            self.inventory.add(other_root)
 
2373
            add_children(self.inventory, other_root)
 
2374
            self._write_inventory(self.inventory)
2384
2375
            # normally we don't want to fetch whole repositories, but i think
2385
2376
            # here we really do want to consolidate the whole thing.
2386
2377
            for parent_id in other_tree.get_parent_ids():
2424
2415
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2425
2416
        if tree_transport.base != branch_transport.base:
2426
2417
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2427
 
            tree_bzrdir.set_branch_reference(new_branch)
 
2418
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
 
2419
                target_branch=new_branch)
2428
2420
        else:
2429
2421
            tree_bzrdir = branch_bzrdir
2430
2422
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2431
2423
        wt.set_parent_ids(self.get_parent_ids())
2432
 
        # FIXME: Support nested trees
2433
 
        my_inv = self.root_inventory
 
2424
        my_inv = self.inventory
2434
2425
        child_inv = inventory.Inventory(root_id=None)
2435
2426
        new_root = my_inv[file_id]
2436
2427
        my_inv.remove_recursive_id(file_id)
2456
2447
        if not self.is_locked():
2457
2448
            raise errors.ObjectNotLocked(self)
2458
2449
 
 
2450
        inv = self.inventory
2459
2451
        if from_dir is None and include_root is True:
2460
 
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
 
2452
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2461
2453
        # Convert these into local objects to save lookup times
2462
2454
        pathjoin = osutils.pathjoin
2463
2455
        file_kind = self._kind
2470
2462
 
2471
2463
        # directory file_id, relative path, absolute path, reverse sorted children
2472
2464
        if from_dir is not None:
2473
 
            inv, from_dir_id = self._path2inv_file_id(from_dir)
 
2465
            from_dir_id = inv.path2id(from_dir)
2474
2466
            if from_dir_id is None:
2475
2467
                # Directory not versioned
2476
2468
                return
2477
2469
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2478
2470
        else:
2479
 
            inv = self.root_inventory
2480
2471
            from_dir_id = inv.root.file_id
2481
2472
            from_dir_abspath = self.basedir
2482
2473
        children = os.listdir(from_dir_abspath)
2605
2596
        rename_entries = []
2606
2597
        rename_tuples = []
2607
2598
 
2608
 
        invs_to_write = set()
2609
 
 
2610
2599
        # check for deprecated use of signature
2611
2600
        if to_dir is None:
2612
2601
            raise TypeError('You must supply a target directory')
2613
2602
        # check destination directory
2614
2603
        if isinstance(from_paths, basestring):
2615
2604
            raise ValueError()
 
2605
        inv = self.inventory
2616
2606
        to_abs = self.abspath(to_dir)
2617
2607
        if not isdir(to_abs):
2618
2608
            raise errors.BzrMoveFailedError('',to_dir,
2620
2610
        if not self.has_filename(to_dir):
2621
2611
            raise errors.BzrMoveFailedError('',to_dir,
2622
2612
                errors.NotInWorkingDirectory(to_dir))
2623
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2613
        to_dir_id = inv.path2id(to_dir)
2624
2614
        if to_dir_id is None:
2625
2615
            raise errors.BzrMoveFailedError('',to_dir,
2626
2616
                errors.NotVersionedError(path=to_dir))
2627
2617
 
2628
 
        to_dir_ie = to_inv[to_dir_id]
 
2618
        to_dir_ie = inv[to_dir_id]
2629
2619
        if to_dir_ie.kind != 'directory':
2630
2620
            raise errors.BzrMoveFailedError('',to_dir,
2631
2621
                errors.NotADirectory(to_abs))
2633
2623
        # create rename entries and tuples
2634
2624
        for from_rel in from_paths:
2635
2625
            from_tail = splitpath(from_rel)[-1]
2636
 
            from_inv, from_id = self._path2inv_file_id(from_rel)
 
2626
            from_id = inv.path2id(from_rel)
2637
2627
            if from_id is None:
2638
2628
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2639
2629
                    errors.NotVersionedError(path=from_rel))
2640
2630
 
2641
 
            from_entry = from_inv[from_id]
 
2631
            from_entry = inv[from_id]
2642
2632
            from_parent_id = from_entry.parent_id
2643
2633
            to_rel = pathjoin(to_dir, from_tail)
2644
2634
            rename_entry = InventoryWorkingTree._RenameEntry(
2663
2653
            # restore the inventory on error
2664
2654
            self._inventory_is_modified = original_modified
2665
2655
            raise
2666
 
        #FIXME: Should potentially also write the from_invs
2667
 
        self._write_inventory(to_inv)
 
2656
        self._write_inventory(inv)
2668
2657
        return rename_tuples
2669
2658
 
2670
2659
    @needs_tree_write_lock
2690
2679
 
2691
2680
        Everything else results in an error.
2692
2681
        """
 
2682
        inv = self.inventory
2693
2683
        rename_entries = []
2694
2684
 
2695
2685
        # create rename entries and tuples
2696
2686
        from_tail = splitpath(from_rel)[-1]
2697
 
        from_inv, from_id = self._path2inv_file_id(from_rel)
 
2687
        from_id = inv.path2id(from_rel)
2698
2688
        if from_id is None:
2699
2689
            # if file is missing in the inventory maybe it's in the basis_tree
2700
2690
            basis_tree = self.branch.basis_tree()
2703
2693
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2704
2694
                    errors.NotVersionedError(path=from_rel))
2705
2695
            # put entry back in the inventory so we can rename it
2706
 
            from_entry = basis_tree.root_inventory[from_id].copy()
2707
 
            from_inv.add(from_entry)
 
2696
            from_entry = basis_tree.inventory[from_id].copy()
 
2697
            inv.add(from_entry)
2708
2698
        else:
2709
 
            from_inv, from_inv_id = self._unpack_file_id(from_id)
2710
 
            from_entry = from_inv[from_inv_id]
 
2699
            from_entry = inv[from_id]
2711
2700
        from_parent_id = from_entry.parent_id
2712
2701
        to_dir, to_tail = os.path.split(to_rel)
2713
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2702
        to_dir_id = inv.path2id(to_dir)
2714
2703
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2715
2704
                                     from_id=from_id,
2716
2705
                                     from_tail=from_tail,
2738
2727
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2739
2728
 
2740
2729
        self._move(rename_entries)
2741
 
        self._write_inventory(to_inv)
 
2730
        self._write_inventory(inv)
2742
2731
 
2743
2732
    class _RenameEntry(object):
2744
2733
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2760
2749
 
2761
2750
        Also does basic plausability tests.
2762
2751
        """
2763
 
        # FIXME: Handling of nested trees
2764
 
        inv = self.root_inventory
 
2752
        inv = self.inventory
2765
2753
 
2766
2754
        for rename_entry in rename_entries:
2767
2755
            # store to local variables for easier reference
2825
2813
        Depending on the value of the flag 'only_change_inv', the
2826
2814
        file will be moved on the file system or not.
2827
2815
        """
 
2816
        inv = self.inventory
2828
2817
        moved = []
2829
2818
 
2830
2819
        for entry in rename_entries:
2837
2826
 
2838
2827
    def _rollback_move(self, moved):
2839
2828
        """Try to rollback a previous move in case of an filesystem error."""
 
2829
        inv = self.inventory
2840
2830
        for entry in moved:
2841
2831
            try:
2842
2832
                self._move_entry(WorkingTree._RenameEntry(
2851
2841
                        " Error message is: %s" % e)
2852
2842
 
2853
2843
    def _move_entry(self, entry):
2854
 
        inv = self.root_inventory
 
2844
        inv = self.inventory
2855
2845
        from_rel_abs = self.abspath(entry.from_rel)
2856
2846
        to_rel_abs = self.abspath(entry.to_rel)
2857
2847
        if from_rel_abs == to_rel_abs:
2898
2888
 
2899
2889
    def stored_kind(self, file_id):
2900
2890
        """See Tree.stored_kind"""
2901
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2902
 
        return inv[inv_file_id].kind
 
2891
        return self.inventory[file_id].kind
2903
2892
 
2904
2893
    def extras(self):
2905
2894
        """Yield all unversioned files in this WorkingTree.
2912
2901
        This is the same order used by 'osutils.walkdirs'.
2913
2902
        """
2914
2903
        ## TODO: Work from given directory downwards
2915
 
        for path, dir_entry in self.iter_entries_by_dir():
2916
 
            if dir_entry.kind != 'directory':
2917
 
                continue
 
2904
        for path, dir_entry in self.inventory.directories():
2918
2905
            # mutter("search for unknowns in %r", path)
2919
2906
            dirabs = self.abspath(path)
2920
2907
            if not isdir(dirabs):
2957
2944
        """
2958
2945
        _directory = 'directory'
2959
2946
        # get the root in the inventory
2960
 
        inv, top_id = self._path2inv_file_id(prefix)
 
2947
        inv = self.inventory
 
2948
        top_id = inv.path2id(prefix)
2961
2949
        if top_id is None:
2962
2950
            pending = []
2963
2951
        else:
2984
2972
                if dir[2] == _directory:
2985
2973
                    pending.append(dir)
2986
2974
 
2987
 
    @needs_write_lock
2988
 
    def update_feature_flags(self, updated_flags):
2989
 
        """Update the feature flags for this branch.
2990
 
 
2991
 
        :param updated_flags: Dictionary mapping feature names to necessities
2992
 
            A necessity can be None to indicate the feature should be removed
2993
 
        """
2994
 
        self._format._update_feature_flags(updated_flags)
2995
 
        self.control_transport.put_bytes('format', self._format.as_string())
2996
 
 
2997
2975
 
2998
2976
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2999
2977
    """Registry for working tree formats."""
3151
3129
        return self._matchingbzrdir
3152
3130
 
3153
3131
 
3154
 
class WorkingTreeFormatMetaDir(bzrdir.BzrFormat, WorkingTreeFormat):
 
3132
class WorkingTreeFormatMetaDir(bzrdir.BzrDirMetaComponentFormat, WorkingTreeFormat):
3155
3133
    """Base class for working trees that live in bzr meta directories."""
3156
3134
 
3157
3135
    def __init__(self):
3158
3136
        WorkingTreeFormat.__init__(self)
3159
 
        bzrdir.BzrFormat.__init__(self)
 
3137
        bzrdir.BzrDirMetaComponentFormat.__init__(self)
3160
3138
 
3161
3139
    @classmethod
3162
3140
    def find_format_string(klass, controldir):
3174
3152
        return klass._find_format(format_registry, 'working tree',
3175
3153
                format_string)
3176
3154
 
3177
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
3178
 
            basedir=None):
3179
 
        WorkingTreeFormat.check_support_status(self,
3180
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
3181
 
            basedir=basedir)
3182
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
3183
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
3184
 
 
3185
3155
 
3186
3156
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3187
3157
    "bzrlib.workingtree_4", "WorkingTreeFormat4")