~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Martin Packman
  • Date: 2012-02-01 13:24:42 UTC
  • mto: (6437.23.4 2.5)
  • mto: This revision was merged to the branch mainline in revision 6462.
  • Revision ID: martin.packman@canonical.com-20120201132442-ela7jc4mxv4b058o
Treat path for .bzr.log as unicode

Show diffs side-by-side

added added

removed removed

Lines of Context:
60
60
    revision as _mod_revision,
61
61
    revisiontree,
62
62
    rio as _mod_rio,
63
 
    shelf,
64
63
    transform,
65
64
    transport,
66
65
    ui,
262
261
    def supports_views(self):
263
262
        return self.views.supports_views()
264
263
 
265
 
    def get_config_stack(self):
266
 
        """Retrieve the config stack for this tree.
267
 
 
268
 
        :return: A ``bzrlib.config.Stack``
269
 
        """
270
 
        # For the moment, just provide the branch config stack.
271
 
        return self.branch.get_config_stack()
272
 
 
273
264
    @staticmethod
274
265
    def open(path=None, _unsupported=False):
275
266
        """Open an existing working tree at path.
388
379
                                              list_current=list_current)
389
380
        return [tr for tr in iterator if tr is not None]
390
381
 
 
382
    def all_file_ids(self):
 
383
        """See Tree.iter_all_file_ids"""
 
384
        raise NotImplementedError(self.all_file_ids)
 
385
 
391
386
    def __repr__(self):
392
387
        return "<%s of %s>" % (self.__class__.__name__,
393
388
                               getattr(self, 'basedir', None))
535
530
        else:
536
531
            # TODO now merge from tree.last_revision to revision (to preserve
537
532
            # user local changes)
538
 
            try:
539
 
                other_tree = self.revision_tree(revision_id)
540
 
            except errors.NoSuchRevision:
541
 
                other_tree = self.branch.repository.revision_tree(revision_id)
542
 
 
543
 
            merge.transform_tree(tree, other_tree)
 
533
            merge.transform_tree(tree, self)
544
534
            if revision_id == _mod_revision.NULL_REVISION:
545
535
                new_parents = []
546
536
            else:
1357
1347
                basis_tree.unlock()
1358
1348
        return conflicts
1359
1349
 
1360
 
    @needs_write_lock
1361
 
    def store_uncommitted(self):
1362
 
        """Store uncommitted changes from the tree in the branch."""
1363
 
        target_tree = self.basis_tree()
1364
 
        shelf_creator = shelf.ShelfCreator(self, target_tree)
1365
 
        try:
1366
 
            if not shelf_creator.shelve_all():
1367
 
                return
1368
 
            self.branch.store_uncommitted(shelf_creator)
1369
 
            shelf_creator.transform()
1370
 
        finally:
1371
 
            shelf_creator.finalize()
1372
 
        note('Uncommitted changes stored in branch "%s".', self.branch.nick)
1373
 
 
1374
 
    @needs_write_lock
1375
 
    def restore_uncommitted(self):
1376
 
        """Restore uncommitted changes from the branch into the tree."""
1377
 
        unshelver = self.branch.get_unshelver(self)
1378
 
        if unshelver is None:
1379
 
            return
1380
 
        try:
1381
 
            merger = unshelver.make_merger()
1382
 
            merger.ignore_zero = True
1383
 
            merger.do_merge()
1384
 
            self.branch.store_uncommitted(None)
1385
 
        finally:
1386
 
            unshelver.finalize()
1387
 
 
1388
1350
    def revision_tree(self, revision_id):
1389
1351
        """See Tree.revision_tree.
1390
1352
 
1984
1946
            if entry.parent_id == orig_root_id:
1985
1947
                entry.parent_id = inv.root.file_id
1986
1948
 
 
1949
    def all_file_ids(self):
 
1950
        """See Tree.iter_all_file_ids"""
 
1951
        return set(self.inventory)
 
1952
 
1987
1953
    @needs_tree_write_lock
1988
1954
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1989
1955
        """See MutableTree.set_parent_trees."""
2008
1974
                # parent tree from the repository.
2009
1975
                self._cache_basis_inventory(leftmost_parent_id)
2010
1976
            else:
2011
 
                inv = leftmost_parent_tree.root_inventory
 
1977
                inv = leftmost_parent_tree.inventory
2012
1978
                xml = self._create_basis_xml_from_inventory(
2013
1979
                                        leftmost_parent_id, inv)
2014
1980
                self._write_basis_inventory(xml)
2111
2077
 
2112
2078
    def has_id(self, file_id):
2113
2079
        # files that have been deleted are excluded
2114
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2115
 
        if not inv.has_id(inv_file_id):
 
2080
        inv = self.inventory
 
2081
        if not inv.has_id(file_id):
2116
2082
            return False
2117
 
        path = inv.id2path(inv_file_id)
 
2083
        path = inv.id2path(file_id)
2118
2084
        return osutils.lexists(self.abspath(path))
2119
2085
 
2120
2086
    def has_or_had_id(self, file_id):
2121
 
        if file_id == self.get_root_id():
 
2087
        if file_id == self.inventory.root.file_id:
2122
2088
            return True
2123
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2124
 
        return inv.has_id(inv_file_id)
 
2089
        return self.inventory.has_id(file_id)
2125
2090
 
2126
 
    def all_file_ids(self):
 
2091
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
2092
    def __iter__(self):
2127
2093
        """Iterate through file_ids for this tree.
2128
2094
 
2129
2095
        file_ids are in a WorkingTree if they are in the working inventory
2130
2096
        and the working file exists.
2131
2097
        """
2132
 
        ret = set()
2133
 
        for path, ie in self.iter_entries_by_dir():
2134
 
            ret.add(ie.file_id)
2135
 
        return ret
 
2098
        inv = self._inventory
 
2099
        for path, ie in inv.iter_entries():
 
2100
            if osutils.lexists(self.abspath(path)):
 
2101
                yield ie.file_id
2136
2102
 
2137
2103
    @needs_tree_write_lock
2138
2104
    def set_last_revision(self, new_revision):
2194
2160
                _mod_revision.NULL_REVISION)
2195
2161
        else:
2196
2162
            rt = self.branch.repository.revision_tree(revision_ids[0])
2197
 
        self._write_inventory(rt.root_inventory)
 
2163
        self._write_inventory(rt.inventory)
2198
2164
        self.set_parent_ids(revision_ids)
2199
2165
 
2200
2166
    def flush(self):
2212
2178
    def get_file_mtime(self, file_id, path=None):
2213
2179
        """See Tree.get_file_mtime."""
2214
2180
        if not path:
2215
 
            path = self.id2path(file_id)
 
2181
            path = self.inventory.id2path(file_id)
2216
2182
        try:
2217
2183
            return os.lstat(self.abspath(path)).st_mtime
2218
2184
        except OSError, e:
2221
2187
            raise
2222
2188
 
2223
2189
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2224
 
        inv, file_id = self._path2inv_file_id(path)
 
2190
        file_id = self.path2id(path)
2225
2191
        if file_id is None:
2226
2192
            # For unversioned files on win32, we just assume they are not
2227
2193
            # executable
2228
2194
            return False
2229
 
        return inv[file_id].executable
 
2195
        return self._inventory[file_id].executable
2230
2196
 
2231
2197
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2232
2198
        mode = stat_result.st_mode
2234
2200
 
2235
2201
    def is_executable(self, file_id, path=None):
2236
2202
        if not self._supports_executable():
2237
 
            inv, inv_file_id = self._unpack_file_id(file_id)
2238
 
            return inv[inv_file_id].executable
 
2203
            return self._inventory[file_id].executable
2239
2204
        else:
2240
2205
            if not path:
2241
2206
                path = self.id2path(file_id)
2255
2220
        # should probably put it back with the previous ID.
2256
2221
        # the read and write working inventory should not occur in this
2257
2222
        # function - they should be part of lock_write and unlock.
2258
 
        # FIXME: nested trees
2259
 
        inv = self.root_inventory
 
2223
        inv = self.inventory
2260
2224
        for f, file_id, kind in zip(files, ids, kinds):
2261
2225
            if file_id is None:
2262
2226
                inv.add_path(f, kind=kind)
2303
2267
                parent_tree = self.branch.repository.revision_tree(parent_id)
2304
2268
            parent_tree.lock_read()
2305
2269
            try:
2306
 
                try:
2307
 
                    kind = parent_tree.kind(file_id)
2308
 
                except errors.NoSuchId:
 
2270
                if not parent_tree.has_id(file_id):
2309
2271
                    continue
2310
 
                if kind != 'file':
 
2272
                ie = parent_tree.inventory[file_id]
 
2273
                if ie.kind != 'file':
2311
2274
                    # Note: this is slightly unnecessary, because symlinks and
2312
2275
                    # directories have a "text" which is the empty text, and we
2313
2276
                    # know that won't mess up annotations. But it seems cleaner
2314
2277
                    continue
2315
 
                parent_text_key = (
2316
 
                    file_id, parent_tree.get_file_revision(file_id))
 
2278
                parent_text_key = (file_id, ie.revision)
2317
2279
                if parent_text_key not in maybe_file_parent_keys:
2318
2280
                    maybe_file_parent_keys.append(parent_text_key)
2319
2281
            finally:
2373
2335
            for s in _mod_rio.RioReader(hashfile):
2374
2336
                # RioReader reads in Unicode, so convert file_ids back to utf8
2375
2337
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2376
 
                if not self.has_id(file_id):
 
2338
                if not self.inventory.has_id(file_id):
2377
2339
                    continue
2378
2340
                text_hash = s.get("hash")
2379
2341
                if text_hash == self.get_file_sha1(file_id):
2409
2371
        other_tree.lock_tree_write()
2410
2372
        try:
2411
2373
            new_parents = other_tree.get_parent_ids()
2412
 
            other_root = other_tree.root_inventory.root
 
2374
            other_root = other_tree.inventory.root
2413
2375
            other_root.parent_id = new_root_parent
2414
2376
            other_root.name = osutils.basename(other_tree_path)
2415
 
            self.root_inventory.add(other_root)
2416
 
            add_children(self.root_inventory, other_root)
2417
 
            self._write_inventory(self.root_inventory)
 
2377
            self.inventory.add(other_root)
 
2378
            add_children(self.inventory, other_root)
 
2379
            self._write_inventory(self.inventory)
2418
2380
            # normally we don't want to fetch whole repositories, but i think
2419
2381
            # here we really do want to consolidate the whole thing.
2420
2382
            for parent_id in other_tree.get_parent_ids():
2463
2425
            tree_bzrdir = branch_bzrdir
2464
2426
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2465
2427
        wt.set_parent_ids(self.get_parent_ids())
2466
 
        # FIXME: Support nested trees
2467
 
        my_inv = self.root_inventory
 
2428
        my_inv = self.inventory
2468
2429
        child_inv = inventory.Inventory(root_id=None)
2469
2430
        new_root = my_inv[file_id]
2470
2431
        my_inv.remove_recursive_id(file_id)
2490
2451
        if not self.is_locked():
2491
2452
            raise errors.ObjectNotLocked(self)
2492
2453
 
 
2454
        inv = self.inventory
2493
2455
        if from_dir is None and include_root is True:
2494
 
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
 
2456
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2495
2457
        # Convert these into local objects to save lookup times
2496
2458
        pathjoin = osutils.pathjoin
2497
2459
        file_kind = self._kind
2504
2466
 
2505
2467
        # directory file_id, relative path, absolute path, reverse sorted children
2506
2468
        if from_dir is not None:
2507
 
            inv, from_dir_id = self._path2inv_file_id(from_dir)
 
2469
            from_dir_id = inv.path2id(from_dir)
2508
2470
            if from_dir_id is None:
2509
2471
                # Directory not versioned
2510
2472
                return
2511
2473
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2512
2474
        else:
2513
 
            inv = self.root_inventory
2514
2475
            from_dir_id = inv.root.file_id
2515
2476
            from_dir_abspath = self.basedir
2516
2477
        children = os.listdir(from_dir_abspath)
2639
2600
        rename_entries = []
2640
2601
        rename_tuples = []
2641
2602
 
2642
 
        invs_to_write = set()
2643
 
 
2644
2603
        # check for deprecated use of signature
2645
2604
        if to_dir is None:
2646
2605
            raise TypeError('You must supply a target directory')
2647
2606
        # check destination directory
2648
2607
        if isinstance(from_paths, basestring):
2649
2608
            raise ValueError()
 
2609
        inv = self.inventory
2650
2610
        to_abs = self.abspath(to_dir)
2651
2611
        if not isdir(to_abs):
2652
2612
            raise errors.BzrMoveFailedError('',to_dir,
2654
2614
        if not self.has_filename(to_dir):
2655
2615
            raise errors.BzrMoveFailedError('',to_dir,
2656
2616
                errors.NotInWorkingDirectory(to_dir))
2657
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2617
        to_dir_id = inv.path2id(to_dir)
2658
2618
        if to_dir_id is None:
2659
2619
            raise errors.BzrMoveFailedError('',to_dir,
2660
2620
                errors.NotVersionedError(path=to_dir))
2661
2621
 
2662
 
        to_dir_ie = to_inv[to_dir_id]
 
2622
        to_dir_ie = inv[to_dir_id]
2663
2623
        if to_dir_ie.kind != 'directory':
2664
2624
            raise errors.BzrMoveFailedError('',to_dir,
2665
2625
                errors.NotADirectory(to_abs))
2667
2627
        # create rename entries and tuples
2668
2628
        for from_rel in from_paths:
2669
2629
            from_tail = splitpath(from_rel)[-1]
2670
 
            from_inv, from_id = self._path2inv_file_id(from_rel)
 
2630
            from_id = inv.path2id(from_rel)
2671
2631
            if from_id is None:
2672
2632
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2673
2633
                    errors.NotVersionedError(path=from_rel))
2674
2634
 
2675
 
            from_entry = from_inv[from_id]
 
2635
            from_entry = inv[from_id]
2676
2636
            from_parent_id = from_entry.parent_id
2677
2637
            to_rel = pathjoin(to_dir, from_tail)
2678
2638
            rename_entry = InventoryWorkingTree._RenameEntry(
2697
2657
            # restore the inventory on error
2698
2658
            self._inventory_is_modified = original_modified
2699
2659
            raise
2700
 
        #FIXME: Should potentially also write the from_invs
2701
 
        self._write_inventory(to_inv)
 
2660
        self._write_inventory(inv)
2702
2661
        return rename_tuples
2703
2662
 
2704
2663
    @needs_tree_write_lock
2724
2683
 
2725
2684
        Everything else results in an error.
2726
2685
        """
 
2686
        inv = self.inventory
2727
2687
        rename_entries = []
2728
2688
 
2729
2689
        # create rename entries and tuples
2730
2690
        from_tail = splitpath(from_rel)[-1]
2731
 
        from_inv, from_id = self._path2inv_file_id(from_rel)
 
2691
        from_id = inv.path2id(from_rel)
2732
2692
        if from_id is None:
2733
2693
            # if file is missing in the inventory maybe it's in the basis_tree
2734
2694
            basis_tree = self.branch.basis_tree()
2737
2697
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2738
2698
                    errors.NotVersionedError(path=from_rel))
2739
2699
            # put entry back in the inventory so we can rename it
2740
 
            from_entry = basis_tree.root_inventory[from_id].copy()
2741
 
            from_inv.add(from_entry)
 
2700
            from_entry = basis_tree.inventory[from_id].copy()
 
2701
            inv.add(from_entry)
2742
2702
        else:
2743
 
            from_inv, from_inv_id = self._unpack_file_id(from_id)
2744
 
            from_entry = from_inv[from_inv_id]
 
2703
            from_entry = inv[from_id]
2745
2704
        from_parent_id = from_entry.parent_id
2746
2705
        to_dir, to_tail = os.path.split(to_rel)
2747
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2706
        to_dir_id = inv.path2id(to_dir)
2748
2707
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2749
2708
                                     from_id=from_id,
2750
2709
                                     from_tail=from_tail,
2772
2731
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2773
2732
 
2774
2733
        self._move(rename_entries)
2775
 
        self._write_inventory(to_inv)
 
2734
        self._write_inventory(inv)
2776
2735
 
2777
2736
    class _RenameEntry(object):
2778
2737
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2794
2753
 
2795
2754
        Also does basic plausability tests.
2796
2755
        """
2797
 
        # FIXME: Handling of nested trees
2798
 
        inv = self.root_inventory
 
2756
        inv = self.inventory
2799
2757
 
2800
2758
        for rename_entry in rename_entries:
2801
2759
            # store to local variables for easier reference
2859
2817
        Depending on the value of the flag 'only_change_inv', the
2860
2818
        file will be moved on the file system or not.
2861
2819
        """
 
2820
        inv = self.inventory
2862
2821
        moved = []
2863
2822
 
2864
2823
        for entry in rename_entries:
2871
2830
 
2872
2831
    def _rollback_move(self, moved):
2873
2832
        """Try to rollback a previous move in case of an filesystem error."""
 
2833
        inv = self.inventory
2874
2834
        for entry in moved:
2875
2835
            try:
2876
2836
                self._move_entry(WorkingTree._RenameEntry(
2885
2845
                        " Error message is: %s" % e)
2886
2846
 
2887
2847
    def _move_entry(self, entry):
2888
 
        inv = self.root_inventory
 
2848
        inv = self.inventory
2889
2849
        from_rel_abs = self.abspath(entry.from_rel)
2890
2850
        to_rel_abs = self.abspath(entry.to_rel)
2891
2851
        if from_rel_abs == to_rel_abs:
2932
2892
 
2933
2893
    def stored_kind(self, file_id):
2934
2894
        """See Tree.stored_kind"""
2935
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2936
 
        return inv[inv_file_id].kind
 
2895
        return self.inventory[file_id].kind
2937
2896
 
2938
2897
    def extras(self):
2939
2898
        """Yield all unversioned files in this WorkingTree.
2946
2905
        This is the same order used by 'osutils.walkdirs'.
2947
2906
        """
2948
2907
        ## TODO: Work from given directory downwards
2949
 
        for path, dir_entry in self.iter_entries_by_dir():
2950
 
            if dir_entry.kind != 'directory':
2951
 
                continue
 
2908
        for path, dir_entry in self.inventory.directories():
2952
2909
            # mutter("search for unknowns in %r", path)
2953
2910
            dirabs = self.abspath(path)
2954
2911
            if not isdir(dirabs):
2991
2948
        """
2992
2949
        _directory = 'directory'
2993
2950
        # get the root in the inventory
2994
 
        inv, top_id = self._path2inv_file_id(prefix)
 
2951
        inv = self.inventory
 
2952
        top_id = inv.path2id(prefix)
2995
2953
        if top_id is None:
2996
2954
            pending = []
2997
2955
        else:
3112
3070
    def __ne__(self, other):
3113
3071
        return not (self == other)
3114
3072
 
 
3073
    @classmethod
 
3074
    @symbol_versioning.deprecated_method(
 
3075
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3076
    def get_default_format(klass):
 
3077
        """Return the current default format."""
 
3078
        return format_registry.get_default()
 
3079
 
3115
3080
    def get_format_description(self):
3116
3081
        """Return the short description for this format."""
3117
3082
        raise NotImplementedError(self.get_format_description)
3133
3098
        """True if this format supports stored views."""
3134
3099
        return False
3135
3100
 
3136
 
    def get_controldir_for_branch(self):
3137
 
        """Get the control directory format for creating branches.
3138
 
 
3139
 
        This is to support testing of working tree formats that can not exist
3140
 
        in the same control directory as a branch.
3141
 
        """
3142
 
        return self._matchingbzrdir
3143
 
 
3144
 
 
3145
 
class WorkingTreeFormatMetaDir(bzrdir.BzrFormat, WorkingTreeFormat):
3146
 
    """Base class for working trees that live in bzr meta directories."""
3147
 
 
3148
 
    def __init__(self):
3149
 
        WorkingTreeFormat.__init__(self)
3150
 
        bzrdir.BzrFormat.__init__(self)
3151
 
 
3152
 
    @classmethod
3153
 
    def find_format_string(klass, controldir):
3154
 
        """Return format name for the working tree object in controldir."""
3155
 
        try:
3156
 
            transport = controldir.get_workingtree_transport(None)
3157
 
            return transport.get_bytes("format")
3158
 
        except errors.NoSuchFile:
3159
 
            raise errors.NoWorkingTree(base=transport.base)
3160
 
 
3161
 
    @classmethod
3162
 
    def find_format(klass, controldir):
3163
 
        """Return the format for the working tree object in controldir."""
3164
 
        format_string = klass.find_format_string(controldir)
3165
 
        return klass._find_format(format_registry, 'working tree',
3166
 
                format_string)
3167
 
 
3168
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
3169
 
            basedir=None):
3170
 
        WorkingTreeFormat.check_support_status(self,
3171
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
3172
 
            basedir=basedir)
3173
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
3174
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
 
3101
    @classmethod
 
3102
    @symbol_versioning.deprecated_method(
 
3103
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3104
    def register_format(klass, format):
 
3105
        format_registry.register(format)
 
3106
 
 
3107
    @classmethod
 
3108
    @symbol_versioning.deprecated_method(
 
3109
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3110
    def register_extra_format(klass, format):
 
3111
        format_registry.register_extra(format)
 
3112
 
 
3113
    @classmethod
 
3114
    @symbol_versioning.deprecated_method(
 
3115
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3116
    def unregister_extra_format(klass, format):
 
3117
        format_registry.unregister_extra(format)
 
3118
 
 
3119
    @classmethod
 
3120
    @symbol_versioning.deprecated_method(
 
3121
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3122
    def get_formats(klass):
 
3123
        return format_registry._get_all()
 
3124
 
 
3125
    @classmethod
 
3126
    @symbol_versioning.deprecated_method(
 
3127
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3128
    def set_default_format(klass, format):
 
3129
        format_registry.set_default(format)
 
3130
 
 
3131
    @classmethod
 
3132
    @symbol_versioning.deprecated_method(
 
3133
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3134
    def unregister_format(klass, format):
 
3135
        format_registry.remove(format)
3175
3136
 
3176
3137
    def get_controldir_for_branch(self):
3177
3138
        """Get the control directory format for creating branches.