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)
1020
1051
osutils.rename(self.abspath(f), self.abspath(dest_path))
1021
1052
except OSError, e:
1022
1053
raise BzrError("failed to rename %r to %r: %s" %
1023
(f, dest_path, e[1]),
1024
["rename rolled back"])
1054
(f, dest_path, e[1]))
1026
1056
# restore the inventory on error
1027
self._set_inventory(orig_inv)
1057
self._set_inventory(orig_inv, dirty=original_modified)
1029
1059
self._write_inventory(inv)
1073
1103
except OSError, e:
1074
1104
inv.rename(file_id, from_parent, from_name)
1075
1105
raise BzrError("failed to rename %r to %r: %s"
1076
% (from_abs, to_abs, e[1]),
1077
["rename rolled back"])
1106
% (from_abs, to_abs, e[1]))
1078
1107
self._write_inventory(inv)
1080
1109
@needs_read_lock
1472
1504
@needs_read_lock
1473
1505
def read_working_inventory(self):
1474
"""Read the working inventory."""
1506
"""Read the working inventory.
1508
:raises errors.InventoryModified: read_working_inventory will fail
1509
when the current in memory inventory has been modified.
1511
# conceptually this should be an implementation detail of the tree.
1512
# XXX: Deprecate this.
1475
1513
# ElementTree does its own conversion from UTF-8, so open in
1515
if self._inventory_is_modified:
1516
raise errors.InventoryModified(self)
1477
1517
result = xml5.serializer_v5.read_inventory(
1478
1518
self._control_files.get('inventory'))
1479
self._set_inventory(result)
1519
self._set_inventory(result, dirty=False)
1482
1522
@needs_tree_write_lock
1562
1602
@needs_tree_write_lock
1563
1603
def set_root_id(self, file_id):
1564
1604
"""Set the root id for this tree."""
1565
inv = self.read_working_inventory()
1607
symbol_versioning.warn(symbol_versioning.zero_twelve
1608
% 'WorkingTree.set_root_id with fileid=None',
1612
inv = self._inventory
1566
1613
orig_root_id = inv.root.file_id
1614
# TODO: it might be nice to exit early if there was nothing
1615
# to do, saving us from trigger a sync on unlock.
1616
self._inventory_is_modified = True
1617
# we preserve the root inventory entry object, but
1618
# unlinkit from the byid index
1567
1619
del inv._byid[inv.root.file_id]
1568
1620
inv.root.file_id = file_id
1621
# and link it into the index with the new changed id.
1569
1622
inv._byid[inv.root.file_id] = inv.root
1623
# and finally update all children to reference the new id.
1624
# XXX: this should be safe to just look at the root.children
1625
# list, not the WHOLE INVENTORY.
1570
1626
for fid in inv:
1571
1627
entry = inv[fid]
1572
1628
if entry.parent_id == orig_root_id:
1573
1629
entry.parent_id = inv.root.file_id
1574
self._write_inventory(inv)
1576
1631
def unlock(self):
1577
1632
"""See Branch.unlock.
1585
1640
raise NotImplementedError(self.unlock)
1588
1642
def update(self):
1589
1643
"""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.
1645
This will update the branch if its bound too, which means we have
1646
multiple trees involved:
1648
- The new basis tree of the master.
1649
- The old basis tree of the branch.
1650
- The old basis tree of the working tree.
1651
- The current working tree state.
1653
Pathologically, all three may be different, and non-ancestors of each
1654
other. Conceptually we want to:
1656
- Preserve the wt.basis->wt.state changes
1657
- Transform the wt.basis to the new master basis.
1658
- Apply a merge of the old branch basis to get any 'local' changes from
1660
- Restore the wt.basis->wt.state changes.
1603
1662
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()
1663
- Merge current state -> basis tree of the master w.r.t. the old tree
1665
- Do a 'normal' merge of the old branch basis if it is relevant.
1667
if self.branch.get_master_branch() is not None:
1669
update_branch = True
1671
self.lock_tree_write()
1672
update_branch = False
1675
old_tip = self.branch.update()
1678
return self._update_tree(old_tip)
1682
@needs_tree_write_lock
1683
def _update_tree(self, old_tip=None):
1684
"""Update a tree to the master branch.
1686
:param old_tip: if supplied, the previous tip revision the branch,
1687
before it was changed to the master branch's tip.
1608
1689
# here if old_tip is not None, it is the old tip of the branch before
1609
1690
# it was updated from the master branch. This should become a pending
1610
1691
# merge in the working tree to preserve the user existing work. we
1624
1705
# merge tree state up to new branch tip.
1625
1706
basis = self.basis_tree()
1626
1707
to_tree = self.branch.basis_tree()
1708
if basis.inventory.root is None:
1709
self.set_root_id(to_tree.inventory.root.file_id)
1627
1710
result += merge.merge_inner(
1676
1759
@needs_tree_write_lock
1677
1760
def _write_inventory(self, inv):
1678
1761
"""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')
1762
self._set_inventory(inv, dirty=True)
1686
1765
def set_conflicts(self, arg):
1687
1766
raise UnsupportedOperation(self.set_conflicts, self)
1740
1819
def unlock(self):
1741
1820
# we share control files:
1742
if self._hashcache.needs_write and self._control_files._lock_count==3:
1743
self._hashcache.write()
1821
if self._control_files._lock_count == 3:
1822
# _inventory_is_modified is always False during a read lock.
1823
if self._inventory_is_modified:
1825
if self._hashcache.needs_write:
1826
self._hashcache.write()
1744
1827
# reverse order of locking.
1746
1829
return self._control_files.unlock()
1804
1887
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1806
1889
def unlock(self):
1807
if self._hashcache.needs_write and self._control_files._lock_count==1:
1808
self._hashcache.write()
1890
if self._control_files._lock_count == 1:
1891
# _inventory_is_modified is always False during a read lock.
1892
if self._inventory_is_modified:
1894
if self._hashcache.needs_write:
1895
self._hashcache.write()
1809
1896
# reverse order of locking.
1811
1898
return self._control_files.unlock()
1957
2044
_internal=True,
1959
2046
_bzrdir=a_bzrdir)
1960
wt._write_inventory(inv)
1961
wt.set_root_id(inv.root.file_id)
1962
2047
basis_tree = branch.repository.revision_tree(revision)
2048
if basis_tree.inventory.root is not None:
2049
wt.set_root_id(basis_tree.inventory.root.file_id)
2050
# set the parent list and cache the basis tree.
1963
2051
wt.set_parent_trees([(revision, basis_tree)])
1964
2052
transform.build_tree(basis_tree, wt)
2029
2117
branch = a_bzrdir.open_branch()
2030
2118
if revision_id is None:
2031
2119
revision_id = branch.last_revision()
2120
# WorkingTree3 can handle an inventory which has a unique root id.
2121
# as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2122
# those trees. And because there isn't a format bump inbetween, we
2123
# are maintaining compatibility with older clients.
2124
# inv = Inventory(root_id=gen_root_id())
2033
2126
wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2039
2132
_control_files=control_files)
2040
2133
wt.lock_tree_write()
2042
wt._write_inventory(inv)
2043
wt.set_root_id(inv.root.file_id)
2044
2135
basis_tree = branch.repository.revision_tree(revision_id)
2136
# only set an explicit root id if there is one to set.
2137
if basis_tree.inventory.root is not None:
2138
wt.set_root_id(basis_tree.inventory.root.file_id)
2045
2139
if revision_id == NULL_REVISION:
2046
2140
wt.set_parent_trees([])
2048
2142
wt.set_parent_trees([(revision_id, basis_tree)])
2049
2143
transform.build_tree(basis_tree, wt)
2145
# Unlock in this order so that the unlock-triggers-flush in
2146
# WorkingTree is given a chance to fire.
2147
control_files.unlock()
2052
control_files.unlock()
2055
2151
def __init__(self):