72
73
# Explicitly import bzrlib.bzrdir so that the BzrProber
73
74
# is guaranteed to be registered.
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
233
233
"""See `Tree.has_versioned_directories`."""
234
234
return self._format.supports_versioned_directories
236
def _supports_executable(self):
237
if sys.platform == 'win32':
239
# FIXME: Ideally this should check the file system
242
236
def break_lock(self):
243
237
"""Break a lock if one is present from another instance.
261
255
def supports_views(self):
262
256
return self.views.supports_views()
264
def get_config_stack(self):
265
"""Retrieve the config stack for this tree.
267
:return: A ``bzrlib.config.Stack``
269
# For the moment, just provide the branch config stack.
270
return self.branch.get_config_stack()
273
259
def open(path=None, _unsupported=False):
274
260
"""Open an existing working tree at path.
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)
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]
376
def all_file_ids(self):
377
"""See Tree.iter_all_file_ids"""
378
raise NotImplementedError(self.all_file_ids)
390
380
def __repr__(self):
391
381
return "<%s of %s>" % (self.__class__.__name__,
392
382
getattr(self, 'basedir', None))
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
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
1943
def all_file_ids(self):
1944
"""See Tree.iter_all_file_ids"""
1945
return set(self.inventory)
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)
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)
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):
2083
path = inv.id2path(inv_file_id)
2077
path = inv.id2path(file_id)
2084
2078
return osutils.lexists(self.abspath(path))
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:
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)
2092
def all_file_ids(self):
2085
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2093
2087
"""Iterate through file_ids for this tree.
2095
2089
file_ids are in a WorkingTree if they are in the working inventory
2096
2090
and the working file exists.
2099
for path, ie in self.iter_entries_by_dir():
2092
inv = self._inventory
2093
for path, ie in inv.iter_entries():
2094
if osutils.lexists(self.abspath(path)):
2103
2097
@needs_tree_write_lock
2104
2098
def set_last_revision(self, new_revision):
2160
2154
_mod_revision.NULL_REVISION)
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)
2166
2160
def flush(self):
2178
2172
def get_file_mtime(self, file_id, path=None):
2179
2173
"""See Tree.get_file_mtime."""
2181
path = self.id2path(file_id)
2175
path = self.inventory.id2path(file_id)
2183
2177
return os.lstat(self.abspath(path)).st_mtime
2184
2178
except OSError, e:
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
2195
return inv[file_id].executable
2189
return self._inventory[file_id].executable
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)
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
2195
if not supports_executable():
2196
def is_executable(self, file_id, path=None):
2197
return self._inventory[file_id].executable
2199
_is_executable_from_path_and_stat = \
2200
_is_executable_from_path_and_stat_from_basis
2202
def is_executable(self, file_id, path=None):
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)
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)
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
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()
2273
kind = parent_tree.kind(file_id)
2274
except errors.NoSuchId:
2265
if not parent_tree.has_id(file_id):
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
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)
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):
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()
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)
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)
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
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
2477
2469
from_dir_abspath = pathjoin(self.basedir, from_dir)
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 = []
2608
invs_to_write = set()
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))
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))
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
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
2670
2659
@needs_tree_write_lock
2691
2680
Everything else results in an error.
2682
inv = self.inventory
2693
2683
rename_entries = []
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()
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)
2740
2729
self._move(rename_entries)
2741
self._write_inventory(to_inv)
2730
self._write_inventory(inv)
2743
2732
class _RenameEntry(object):
2744
2733
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2761
2750
Also does basic plausability tests.
2763
# FIXME: Handling of nested trees
2764
inv = self.root_inventory
2752
inv = self.inventory
2766
2754
for rename_entry in rename_entries:
2767
2755
# store to local variables for easier reference
2851
2841
" Error message is: %s" % e)
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:
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
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'.
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':
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):
2984
2972
if dir[2] == _directory:
2985
2973
pending.append(dir)
2988
def update_feature_flags(self, updated_flags):
2989
"""Update the feature flags for this branch.
2991
:param updated_flags: Dictionary mapping feature names to necessities
2992
A necessity can be None to indicate the feature should be removed
2994
self._format._update_feature_flags(updated_flags)
2995
self.control_transport.put_bytes('format', self._format.as_string())
2998
2976
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2999
2977
"""Registry for working tree formats."""
3151
3129
return self._matchingbzrdir
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."""
3157
3135
def __init__(self):
3158
3136
WorkingTreeFormat.__init__(self)
3159
bzrdir.BzrFormat.__init__(self)
3137
bzrdir.BzrDirMetaComponentFormat.__init__(self)
3162
3140
def find_format_string(klass, controldir):
3174
3152
return klass._find_format(format_registry, 'working tree',
3177
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
3179
WorkingTreeFormat.check_support_status(self,
3180
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
3182
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
3183
recommend_upgrade=recommend_upgrade, basedir=basedir)
3186
3156
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3187
3157
"bzrlib.workingtree_4", "WorkingTreeFormat4")