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):
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
539
555
def copy_content_into(self, tree, revision_id=None):
540
556
"""Copy the current content and user files of this tree into tree."""
557
tree.set_root_id(self.get_root_id())
541
558
if revision_id is None:
542
559
merge.transform_tree(tree, self)
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)
1024
1055
["rename rolled back"])
1026
1057
# restore the inventory on error
1027
self._set_inventory(orig_inv)
1058
self._set_inventory(orig_inv, dirty=original_modified)
1029
1060
self._write_inventory(inv)
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.
1588
1645
def update(self):
1589
1646
"""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.
1648
This will update the branch if its bound too, which means we have
1649
multiple trees involved:
1651
- The new basis tree of the master.
1652
- The old basis tree of the branch.
1653
- The old basis tree of the working tree.
1654
- The current working tree state.
1656
Pathologically, all three may be different, and non-ancestors of each
1657
other. Conceptually we want to:
1659
- Preserve the wt.basis->wt.state changes
1660
- Transform the wt.basis to the new master basis.
1661
- Apply a merge of the old branch basis to get any 'local' changes from
1663
- Restore the wt.basis->wt.state changes.
1603
1665
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.
1666
- Merge current state -> basis tree of the master w.r.t. the old tree
1668
- Do a 'normal' merge of the old branch basis if it is relevant.
1607
1670
old_tip = self.branch.update()
1608
1672
# here if old_tip is not None, it is the old tip of the branch before
1609
1673
# it was updated from the master branch. This should become a pending
1610
1674
# merge in the working tree to preserve the user existing work. we
1624
1688
# merge tree state up to new branch tip.
1625
1689
basis = self.basis_tree()
1626
1690
to_tree = self.branch.basis_tree()
1691
if basis.inventory.root is None:
1692
self.set_root_id(to_tree.inventory.root.file_id)
1627
1693
result += merge.merge_inner(
1676
1742
@needs_tree_write_lock
1677
1743
def _write_inventory(self, inv):
1678
1744
"""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')
1745
self._set_inventory(inv, dirty=True)
1686
1748
def set_conflicts(self, arg):
1687
1749
raise UnsupportedOperation(self.set_conflicts, self)
1740
1802
def unlock(self):
1741
1803
# we share control files:
1742
if self._hashcache.needs_write and self._control_files._lock_count==3:
1743
self._hashcache.write()
1804
if self._control_files._lock_count == 3:
1805
# _inventory_is_modified is always False during a read lock.
1806
if self._inventory_is_modified:
1808
if self._hashcache.needs_write:
1809
self._hashcache.write()
1744
1810
# reverse order of locking.
1746
1812
return self._control_files.unlock()
1804
1870
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1806
1872
def unlock(self):
1807
if self._hashcache.needs_write and self._control_files._lock_count==1:
1808
self._hashcache.write()
1873
if self._control_files._lock_count == 1:
1874
# _inventory_is_modified is always False during a read lock.
1875
if self._inventory_is_modified:
1877
if self._hashcache.needs_write:
1878
self._hashcache.write()
1809
1879
# reverse order of locking.
1811
1881
return self._control_files.unlock()
1957
2027
_internal=True,
1959
2029
_bzrdir=a_bzrdir)
1960
wt._write_inventory(inv)
1961
wt.set_root_id(inv.root.file_id)
1962
2030
basis_tree = branch.repository.revision_tree(revision)
2031
if basis_tree.inventory.root is not None:
2032
wt.set_root_id(basis_tree.inventory.root.file_id)
2033
# set the parent list and cache the basis tree.
1963
2034
wt.set_parent_trees([(revision, basis_tree)])
1964
2035
transform.build_tree(basis_tree, wt)
2029
2100
branch = a_bzrdir.open_branch()
2030
2101
if revision_id is None:
2031
2102
revision_id = branch.last_revision()
2103
inv = Inventory(root_id=gen_root_id())
2033
2104
wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2039
2110
_control_files=control_files)
2040
2111
wt.lock_tree_write()
2042
wt._write_inventory(inv)
2043
wt.set_root_id(inv.root.file_id)
2044
2113
basis_tree = branch.repository.revision_tree(revision_id)
2114
# only set an explicit root id if there is one to set.
2115
if basis_tree.inventory.root is not None:
2116
wt.set_root_id(basis_tree.inventory.root.file_id)
2045
2117
if revision_id == NULL_REVISION:
2046
2118
wt.set_parent_trees([])
2048
2120
wt.set_parent_trees([(revision_id, basis_tree)])
2049
2121
transform.build_tree(basis_tree, wt)
2123
# Unlock in this order so that the unlock-triggers-flush in
2124
# WorkingTree is given a chance to fire.
2125
control_files.unlock()
2052
control_files.unlock()
2055
2129
def __init__(self):