80
81
MergeModifiedFormatError,
81
82
UnsupportedOperation,
83
from bzrlib.inventory import InventoryEntry, Inventory
84
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
84
85
from bzrlib.lockable_files import LockableFiles, TransportLock
85
86
from bzrlib.lockdir import LockDir
86
87
import bzrlib.mutabletree
259
260
self.basedir = wt.basedir
260
261
self._control_files = wt._control_files
261
262
self._hashcache = wt._hashcache
262
self._set_inventory(wt._inventory)
263
self._set_inventory(wt._inventory, dirty=False)
263
264
self._format = wt._format
264
265
self.bzrdir = wt.bzrdir
265
266
from bzrlib.hashcache import HashCache
309
310
if _inventory is None:
310
self._set_inventory(self.read_working_inventory())
311
self._inventory_is_modified = False
312
self.read_working_inventory()
312
self._set_inventory(_inventory)
314
# the caller of __init__ has provided an inventory,
315
# we assume they know what they are doing - as its only
316
# the Format factory and creation methods that are
317
# permitted to do this.
318
self._set_inventory(_inventory, dirty=False)
314
320
branch = property(
315
321
fget=lambda self: self._branch,
330
336
self._control_files.break_lock()
331
337
self.branch.break_lock()
333
def _set_inventory(self, inv):
339
def _set_inventory(self, inv, dirty):
340
"""Set the internal cached inventory.
342
:param inv: The inventory to set.
343
:param dirty: A boolean indicating whether the inventory is the same
344
logical inventory as whats on disk. If True the inventory is not
345
the same and should be written to disk or data will be lost, if
346
False then the inventory is the same as that on disk and any
347
serialisation would be unneeded overhead.
334
349
assert inv.root is not None
335
350
self._inventory = inv
351
self._inventory_is_modified = dirty
338
354
def open(path=None, _unsupported=False):
407
423
xml = self.read_basis_inventory()
408
424
inv = xml6.serializer_v6.read_inventory_from_string(xml)
409
425
if inv is not None and inv.revision_id == revision_id:
410
return bzrlib.tree.RevisionTree(self.branch.repository,
426
return bzrlib.revisiontree.RevisionTree(
427
self.branch.repository, inv, revision_id)
412
428
except (NoSuchFile, errors.BadInventoryFormat):
414
430
# No cached copy available, retrieve from the repository.
503
519
parents.append(l.rstrip('\n'))
506
523
def get_root_id(self):
507
524
"""Return the id of this trees root"""
508
inv = self.read_working_inventory()
509
return inv.root.file_id
525
return self._inventory.root.file_id
511
527
def _get_store_filename(self, file_id):
512
528
## XXX: badly named; this is not in the store at all
870
"""Write the in memory inventory to disk."""
871
# TODO: Maybe this should only write on dirty ?
872
if self._control_files._lock_mode != 'w':
873
raise errors.NotWriteLocked(self)
875
xml5.serializer_v5.write_inventory(self._inventory, sio)
877
self._control_files.put('inventory', sio)
878
self._inventory_is_modified = False
852
880
def list_files(self, include_root=False):
853
881
"""Recursively list all files as (path, class, kind, id, entry).
1010
1038
# create a file in this interval and then the rename might be
1011
1039
# left half-done. But we should have caught most problems.
1012
1040
orig_inv = deepcopy(self.inventory)
1041
original_modified = self._inventory_is_modified
1044
self._inventory_is_modified = True
1014
1045
for f in from_paths:
1015
1046
name_tail = splitpath(f)[-1]
1016
1047
dest_path = pathjoin(to_name, name_tail)
1472
1506
@needs_read_lock
1473
1507
def read_working_inventory(self):
1474
"""Read the working inventory."""
1508
"""Read the working inventory.
1510
:raises errors.InventoryModified: read_working_inventory will fail
1511
when the current in memory inventory has been modified.
1513
# conceptually this should be an implementation detail of the tree.
1514
# XXX: Deprecate this.
1475
1515
# ElementTree does its own conversion from UTF-8, so open in
1517
if self._inventory_is_modified:
1518
raise errors.InventoryModified(self)
1477
1519
result = xml5.serializer_v5.read_inventory(
1478
1520
self._control_files.get('inventory'))
1479
self._set_inventory(result)
1521
self._set_inventory(result, dirty=False)
1482
1524
@needs_tree_write_lock
1562
1604
@needs_tree_write_lock
1563
1605
def set_root_id(self, file_id):
1564
1606
"""Set the root id for this tree."""
1565
inv = self.read_working_inventory()
1609
symbol_versioning.warn(symbol_versioning.zero_twelve
1610
% 'WorkingTree.set_root_id with fileid=None',
1614
inv = self._inventory
1566
1615
orig_root_id = inv.root.file_id
1616
# TODO: it might be nice to exit early if there was nothing
1617
# to do, saving us from trigger a sync on unlock.
1618
self._inventory_is_modified = True
1619
# we preserve the root inventory entry object, but
1620
# unlinkit from the byid index
1567
1621
del inv._byid[inv.root.file_id]
1568
1622
inv.root.file_id = file_id
1623
# and link it into the index with the new changed id.
1569
1624
inv._byid[inv.root.file_id] = inv.root
1625
# and finally update all children to reference the new id.
1626
# XXX: this should be safe to just look at the root.children
1627
# list, not the WHOLE INVENTORY.
1570
1628
for fid in inv:
1571
1629
entry = inv[fid]
1572
1630
if entry.parent_id == orig_root_id:
1573
1631
entry.parent_id = inv.root.file_id
1574
self._write_inventory(inv)
1576
1633
def unlock(self):
1577
1634
"""See Branch.unlock.
1585
1642
raise NotImplementedError(self.unlock)
1588
1644
def update(self):
1589
1645
"""Update a working tree along its branch.
1591
This will update the branch if its bound too, which means we have multiple trees involved:
1592
The new basis tree of the master.
1593
The old basis tree of the branch.
1594
The old basis tree of the working tree.
1595
The current working tree state.
1596
pathologically all three may be different, and non ancestors of each other.
1597
Conceptually we want to:
1598
Preserve the wt.basis->wt.state changes
1599
Transform the wt.basis to the new master basis.
1600
Apply a merge of the old branch basis to get any 'local' changes from it into the tree.
1601
Restore the wt.basis->wt.state changes.
1647
This will update the branch if its bound too, which means we have
1648
multiple trees involved:
1650
- The new basis tree of the master.
1651
- The old basis tree of the branch.
1652
- The old basis tree of the working tree.
1653
- The current working tree state.
1655
Pathologically, all three may be different, and non-ancestors of each
1656
other. Conceptually we want to:
1658
- Preserve the wt.basis->wt.state changes
1659
- Transform the wt.basis to the new master basis.
1660
- Apply a merge of the old branch basis to get any 'local' changes from
1662
- Restore the wt.basis->wt.state changes.
1603
1664
There isn't a single operation at the moment to do that, so we:
1604
Merge current state -> basis tree of the master w.r.t. the old tree basis.
1605
Do a 'normal' merge of the old branch basis if it is relevant.
1607
old_tip = self.branch.update()
1665
- Merge current state -> basis tree of the master w.r.t. the old tree
1667
- Do a 'normal' merge of the old branch basis if it is relevant.
1669
if self.branch.get_master_branch() is not None:
1671
update_branch = True
1673
self.lock_tree_write()
1674
update_branch = False
1677
old_tip = self.branch.update()
1680
return self._update_tree(old_tip)
1684
@needs_tree_write_lock
1685
def _update_tree(self, old_tip=None):
1686
"""Update a tree to the master branch.
1688
:param old_tip: if supplied, the previous tip revision the branch,
1689
before it was changed to the master branch's tip.
1608
1691
# here if old_tip is not None, it is the old tip of the branch before
1609
1692
# it was updated from the master branch. This should become a pending
1610
1693
# merge in the working tree to preserve the user existing work. we
1624
1707
# merge tree state up to new branch tip.
1625
1708
basis = self.basis_tree()
1626
1709
to_tree = self.branch.basis_tree()
1710
if basis.inventory.root is None:
1711
self.set_root_id(to_tree.inventory.root.file_id)
1627
1712
result += merge.merge_inner(
1676
1761
@needs_tree_write_lock
1677
1762
def _write_inventory(self, inv):
1678
1763
"""Write inventory as the current inventory."""
1680
xml5.serializer_v5.write_inventory(inv, sio)
1682
self._control_files.put('inventory', sio)
1683
self._set_inventory(inv)
1684
mutter('wrote working inventory')
1764
self._set_inventory(inv, dirty=True)
1686
1767
def set_conflicts(self, arg):
1687
1768
raise UnsupportedOperation(self.set_conflicts, self)
1740
1821
def unlock(self):
1741
1822
# we share control files:
1742
if self._hashcache.needs_write and self._control_files._lock_count==3:
1743
self._hashcache.write()
1823
if self._control_files._lock_count == 3:
1824
# _inventory_is_modified is always False during a read lock.
1825
if self._inventory_is_modified:
1827
if self._hashcache.needs_write:
1828
self._hashcache.write()
1744
1829
# reverse order of locking.
1746
1831
return self._control_files.unlock()
1804
1889
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1806
1891
def unlock(self):
1807
if self._hashcache.needs_write and self._control_files._lock_count==1:
1808
self._hashcache.write()
1892
if self._control_files._lock_count == 1:
1893
# _inventory_is_modified is always False during a read lock.
1894
if self._inventory_is_modified:
1896
if self._hashcache.needs_write:
1897
self._hashcache.write()
1809
1898
# reverse order of locking.
1811
1900
return self._control_files.unlock()
1957
2046
_internal=True,
1959
2048
_bzrdir=a_bzrdir)
1960
wt._write_inventory(inv)
1961
wt.set_root_id(inv.root.file_id)
1962
2049
basis_tree = branch.repository.revision_tree(revision)
2050
if basis_tree.inventory.root is not None:
2051
wt.set_root_id(basis_tree.inventory.root.file_id)
2052
# set the parent list and cache the basis tree.
1963
2053
wt.set_parent_trees([(revision, basis_tree)])
1964
2054
transform.build_tree(basis_tree, wt)
2029
2119
branch = a_bzrdir.open_branch()
2030
2120
if revision_id is None:
2031
2121
revision_id = branch.last_revision()
2122
# WorkingTree3 can handle an inventory which has a unique root id.
2123
# as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2124
# those trees. And because there isn't a format bump inbetween, we
2125
# are maintaining compatibility with older clients.
2126
# inv = Inventory(root_id=gen_root_id())
2033
2128
wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2039
2134
_control_files=control_files)
2040
2135
wt.lock_tree_write()
2042
wt._write_inventory(inv)
2043
wt.set_root_id(inv.root.file_id)
2044
2137
basis_tree = branch.repository.revision_tree(revision_id)
2138
# only set an explicit root id if there is one to set.
2139
if basis_tree.inventory.root is not None:
2140
wt.set_root_id(basis_tree.inventory.root.file_id)
2045
2141
if revision_id == NULL_REVISION:
2046
2142
wt.set_parent_trees([])
2048
2144
wt.set_parent_trees([(revision_id, basis_tree)])
2049
2145
transform.build_tree(basis_tree, wt)
2147
# Unlock in this order so that the unlock-triggers-flush in
2148
# WorkingTree is given a chance to fire.
2149
control_files.unlock()
2052
control_files.unlock()
2055
2153
def __init__(self):