~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Patch Queue Manager
  • Date: 2014-10-06 16:32:42 UTC
  • mfrom: (6597.2.4 split-diff-tests)
  • Revision ID: pqm@pqm.ubuntu.com-20141006163242-c2cll01cwc24grkk
(vila) Split some tests to be able to get finer grained failures (Vincent
 Ladeuil)

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