29
29
WorkingTree.open(dir).
32
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
33
CONFLICT_HEADER_1 = "BZR conflict list format 1"
35
32
# TODO: Give the workingtree sole responsibility for the working inventory;
36
33
# remove the variable and references to it from the branch. This may require
37
34
# updating the commit code so as to update the inventory within the working
39
36
# At the moment they may alias the inventory and have old copies of it in
40
37
# memory. (Now done? -- mbp 20060309)
42
from binascii import hexlify
39
from cStringIO import StringIO
43
from bzrlib.lazy_import import lazy_import
44
lazy_import(globals(), """
44
46
from copy import deepcopy
45
from cStringIO import StringIO
51
50
from time import time
55
from bzrlib import bzrdir, errors, ignores, osutils, urlutils
56
from bzrlib.atomicfile import AtomicFile
56
conflicts as _mod_conflicts,
57
67
import bzrlib.branch
58
from bzrlib.conflicts import Conflict, ConflictList, CONFLICT_SUFFIXES
68
from bzrlib.transport import get_transport
72
from bzrlib import symbol_versioning
59
73
from bzrlib.decorators import needs_read_lock, needs_write_lock
60
74
from bzrlib.errors import (BzrCheckError,
67
81
MergeModifiedFormatError,
68
82
UnsupportedOperation,
70
from bzrlib.inventory import InventoryEntry, Inventory
84
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
71
85
from bzrlib.lockable_files import LockableFiles, TransportLock
72
86
from bzrlib.lockdir import LockDir
73
from bzrlib.merge import merge_inner, transform_tree
74
87
import bzrlib.mutabletree
75
88
from bzrlib.mutabletree import needs_tree_write_lock
76
89
from bzrlib.osutils import (
101
from bzrlib.trace import mutter, note
102
from bzrlib.transport.local import LocalTransport
93
104
from bzrlib.progress import DummyProgress, ProgressPhase
94
105
from bzrlib.revision import NULL_REVISION
95
106
import bzrlib.revisiontree
104
from bzrlib.trace import mutter, note
105
from bzrlib.transform import build_tree
106
from bzrlib.transport import get_transport
107
from bzrlib.transport.local import LocalTransport
108
from bzrlib.textui import show_status
117
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
118
CONFLICT_HEADER_1 = "BZR conflict list format 1"
113
120
# the regex removes any weird characters; we don't escape them
114
121
# but rather just pull them out
253
260
self.basedir = wt.basedir
254
261
self._control_files = wt._control_files
255
262
self._hashcache = wt._hashcache
256
self._set_inventory(wt._inventory)
263
self._set_inventory(wt._inventory, dirty=False)
257
264
self._format = wt._format
258
265
self.bzrdir = wt.bzrdir
259
266
from bzrlib.hashcache import HashCache
303
310
if _inventory is None:
304
self._set_inventory(self.read_working_inventory())
311
self._inventory_is_modified = False
312
self.read_working_inventory()
306
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)
308
320
branch = property(
309
321
fget=lambda self: self._branch,
324
336
self._control_files.break_lock()
325
337
self.branch.break_lock()
327
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.
328
349
assert inv.root is not None
329
350
self._inventory = inv
351
self._inventory_is_modified = dirty
332
354
def open(path=None, _unsupported=False):
401
423
xml = self.read_basis_inventory()
402
inv = bzrlib.xml6.serializer_v6.read_inventory_from_string(xml)
424
inv = xml6.serializer_v6.read_inventory_from_string(xml)
403
425
if inv is not None and inv.revision_id == revision_id:
404
return bzrlib.tree.RevisionTree(self.branch.repository,
426
return bzrlib.tree.RevisionTree(self.branch.repository,
405
427
inv, revision_id)
406
428
except (NoSuchFile, errors.BadInventoryFormat):
463
485
The path may be absolute or relative. If its a relative path it is
464
486
interpreted relative to the python current working directory.
466
return relpath(self.basedir, path)
488
return osutils.relpath(self.basedir, path)
468
490
def has_filename(self, filename):
469
491
return osutils.lexists(self.abspath(filename))
497
519
parents.append(l.rstrip('\n'))
500
523
def get_root_id(self):
501
524
"""Return the id of this trees root"""
502
inv = self.read_working_inventory()
503
return inv.root.file_id
525
return self._inventory.root.file_id
505
527
def _get_store_filename(self, file_id):
506
528
## XXX: badly named; this is not in the store at all
533
555
def copy_content_into(self, tree, revision_id=None):
534
556
"""Copy the current content and user files of this tree into tree."""
557
tree.set_root_id(self.get_root_id())
535
558
if revision_id is None:
536
transform_tree(tree, self)
559
merge.transform_tree(tree, self)
538
561
# TODO now merge from tree.last_revision to revision (to preserve
539
562
# user local changes)
540
transform_tree(tree, self)
563
merge.transform_tree(tree, self)
541
564
tree.set_parent_ids([revision_id])
543
566
def id2abspath(self, file_id):
676
699
return self.get_parent_ids()[1:]
701
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
702
"""Common ghost checking functionality from set_parent_*.
704
This checks that the left hand-parent exists if there are any
707
if len(revision_ids) > 0:
708
leftmost_id = revision_ids[0]
709
if (not allow_leftmost_as_ghost and not
710
self.branch.repository.has_revision(leftmost_id)):
711
raise errors.GhostRevisionUnusableHere(leftmost_id)
713
def _set_merges_from_parent_ids(self, parent_ids):
714
merges = parent_ids[1:]
715
self._control_files.put_utf8('pending-merges', '\n'.join(merges))
678
717
@needs_tree_write_lock
679
718
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
680
719
"""Set the parent ids to revision_ids.
688
727
:param revision_ids: The revision_ids to set as the parent ids of this
689
728
working tree. Any of these may be ghosts.
730
self._check_parents_for_ghosts(revision_ids,
731
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
691
733
if len(revision_ids) > 0:
692
leftmost_id = revision_ids[0]
693
if (not allow_leftmost_as_ghost and not
694
self.branch.repository.has_revision(leftmost_id)):
695
raise errors.GhostRevisionUnusableHere(leftmost_id)
696
self.set_last_revision(leftmost_id)
734
self.set_last_revision(revision_ids[0])
698
736
self.set_last_revision(None)
699
merges = revision_ids[1:]
700
self._control_files.put_utf8('pending-merges', '\n'.join(merges))
738
self._set_merges_from_parent_ids(revision_ids)
702
740
@needs_tree_write_lock
703
741
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
704
742
"""See MutableTree.set_parent_trees."""
705
# parent trees are not used in current format trees, delegate to
707
self.set_parent_ids([rev for (rev, tree) in parents_list],
743
parent_ids = [rev for (rev, tree) in parents_list]
745
self._check_parents_for_ghosts(parent_ids,
708
746
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
748
if len(parent_ids) == 0:
749
leftmost_parent_id = None
750
leftmost_parent_tree = None
752
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
754
if self._change_last_revision(leftmost_parent_id):
755
if leftmost_parent_tree is None:
756
# If we don't have a tree, fall back to reading the
757
# parent tree from the repository.
758
self._cache_basis_inventory(leftmost_parent_id)
760
inv = leftmost_parent_tree.inventory
761
xml = self._create_basis_xml_from_inventory(
762
leftmost_parent_id, inv)
763
self._write_basis_inventory(xml)
764
self._set_merges_from_parent_ids(parent_ids)
710
766
@needs_tree_write_lock
711
767
def set_pending_merges(self, rev_list):
712
768
parents = self.get_parent_ids()
813
def list_files(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
880
def list_files(self, include_root=False):
814
881
"""Recursively list all files as (path, class, kind, id, entry).
816
883
Lists, but does not descend into unversioned directories.
821
888
Skips the control directory.
823
890
inv = self._inventory
891
if include_root is True:
892
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
824
893
# Convert these into local objects to save lookup times
825
894
pathjoin = osutils.pathjoin
826
895
file_kind = osutils.file_kind
969
1038
# create a file in this interval and then the rename might be
970
1039
# left half-done. But we should have caught most problems.
971
1040
orig_inv = deepcopy(self.inventory)
1041
original_modified = self._inventory_is_modified
1044
self._inventory_is_modified = True
973
1045
for f in from_paths:
974
1046
name_tail = splitpath(f)[-1]
975
1047
dest_path = pathjoin(to_name, name_tail)
976
1048
result.append((f, dest_path))
977
1049
inv.rename(inv.path2id(f), to_dir_id, name_tail)
979
rename(self.abspath(f), self.abspath(dest_path))
1051
osutils.rename(self.abspath(f), self.abspath(dest_path))
980
1052
except OSError, e:
981
1053
raise BzrError("failed to rename %r to %r: %s" %
982
1054
(f, dest_path, e[1]),
983
1055
["rename rolled back"])
985
1057
# restore the inventory on error
986
self._set_inventory(orig_inv)
1058
self._set_inventory(orig_inv, dirty=original_modified)
988
1060
self._write_inventory(inv)
1028
1100
from_abs = self.abspath(from_rel)
1029
1101
to_abs = self.abspath(to_rel)
1031
rename(from_abs, to_abs)
1103
osutils.rename(from_abs, to_abs)
1032
1104
except OSError, e:
1033
1105
inv.rename(file_id, from_parent, from_name)
1034
1106
raise BzrError("failed to rename %r to %r: %s"
1111
1183
pb = bzrlib.ui.ui_factory.nested_progress_bar()
1113
1185
new_basis_tree = self.branch.basis_tree()
1114
merge_inner(self.branch,
1115
1188
new_basis_tree,
1117
1190
this_tree=self,
1192
if (basis_tree.inventory.root is None and
1193
new_basis_tree.inventory.root is not None):
1194
self.set_root_id(new_basis_tree.inventory.root.file_id)
1121
1197
# TODO - dedup parents list with things merged by pull ?
1383
1459
self.branch.set_revision_history([new_revision])
1462
def _write_basis_inventory(self, xml):
1463
"""Write the basis inventory XML to the basis-inventory file"""
1464
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1465
path = self._basis_inventory_name()
1467
self._control_files.put(path, sio)
1469
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1470
"""Create the text that will be saved in basis-inventory"""
1471
inventory.revision_id = revision_id
1472
return xml6.serializer_v6.write_inventory_to_string(inventory)
1386
1474
def _cache_basis_inventory(self, new_revision):
1387
1475
"""Cache new_revision as the basis inventory."""
1388
1476
# TODO: this should allow the ready-to-use inventory to be passed in,
1405
1493
'format="6"' not in firstline):
1406
1494
inv = self.branch.repository.deserialise_inventory(
1407
1495
new_revision, xml)
1408
inv.revision_id = new_revision
1409
xml = bzrlib.xml6.serializer_v6.write_inventory_to_string(inv)
1410
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1411
path = self._basis_inventory_name()
1413
self._control_files.put(path, sio)
1496
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1497
self._write_basis_inventory(xml)
1414
1498
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1422
1506
@needs_read_lock
1423
1507
def read_working_inventory(self):
1424
"""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.
1425
1515
# ElementTree does its own conversion from UTF-8, so open in
1427
result = bzrlib.xml5.serializer_v5.read_inventory(
1517
if self._inventory_is_modified:
1518
raise errors.InventoryModified(self)
1519
result = xml5.serializer_v5.read_inventory(
1428
1520
self._control_files.get('inventory'))
1429
self._set_inventory(result)
1521
self._set_inventory(result, dirty=False)
1432
1524
@needs_tree_write_lock
1464
1556
new_status = 'I'
1466
1558
new_status = '?'
1467
show_status(new_status, inv[fid].kind, f, to_file=to_file)
1559
textui.show_status(new_status, inv[fid].kind, f,
1470
1563
self._write_inventory(inv)
1472
1565
@needs_tree_write_lock
1473
1566
def revert(self, filenames, old_tree=None, backups=True,
1474
1567
pb=DummyProgress()):
1475
from transform import revert
1476
from conflicts import resolve
1568
from bzrlib.conflicts import resolve
1477
1569
if old_tree is None:
1478
1570
old_tree = self.basis_tree()
1479
conflicts = revert(self, old_tree, filenames, backups, pb)
1571
conflicts = transform.revert(self, old_tree, filenames, backups, pb)
1480
1572
if not len(filenames):
1481
1573
self.set_parent_ids(self.get_parent_ids()[:1])
1512
1604
@needs_tree_write_lock
1513
1605
def set_root_id(self, file_id):
1514
1606
"""Set the root id for this tree."""
1515
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
1516
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
1517
1621
del inv._byid[inv.root.file_id]
1518
1622
inv.root.file_id = file_id
1623
# and link it into the index with the new changed id.
1519
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.
1520
1628
for fid in inv:
1521
1629
entry = inv[fid]
1522
1630
if entry.parent_id == orig_root_id:
1523
1631
entry.parent_id = inv.root.file_id
1524
self._write_inventory(inv)
1526
1633
def unlock(self):
1527
1634
"""See Branch.unlock.
1538
1645
def update(self):
1539
1646
"""Update a working tree along its branch.
1541
This will update the branch if its bound too, which means we have multiple trees involved:
1542
The new basis tree of the master.
1543
The old basis tree of the branch.
1544
The old basis tree of the working tree.
1545
The current working tree state.
1546
pathologically all three may be different, and non ancestors of each other.
1547
Conceptually we want to:
1548
Preserve the wt.basis->wt.state changes
1549
Transform the wt.basis to the new master basis.
1550
Apply a merge of the old branch basis to get any 'local' changes from it into the tree.
1551
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.
1553
1665
There isn't a single operation at the moment to do that, so we:
1554
Merge current state -> basis tree of the master w.r.t. the old tree basis.
1555
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.
1557
1670
old_tip = self.branch.update()
1558
1672
# here if old_tip is not None, it is the old tip of the branch before
1559
1673
# it was updated from the master branch. This should become a pending
1560
1674
# merge in the working tree to preserve the user existing work. we
1574
1688
# merge tree state up to new branch tip.
1575
1689
basis = self.basis_tree()
1576
1690
to_tree = self.branch.basis_tree()
1577
result += merge_inner(self.branch,
1691
if basis.inventory.root is None:
1692
self.set_root_id(to_tree.inventory.root.file_id)
1693
result += merge.merge_inner(
1580
1697
this_tree=self)
1624
1742
@needs_tree_write_lock
1625
1743
def _write_inventory(self, inv):
1626
1744
"""Write inventory as the current inventory."""
1628
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
1630
self._control_files.put('inventory', sio)
1631
self._set_inventory(inv)
1632
mutter('wrote working inventory')
1745
self._set_inventory(inv, dirty=True)
1634
1748
def set_conflicts(self, arg):
1635
1749
raise UnsupportedOperation(self.set_conflicts, self)
1658
1772
if text == False:
1660
1774
ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1661
conflicts.append(Conflict.factory(ctype, path=conflicted,
1775
conflicts.append(_mod_conflicts.Conflict.factory(ctype,
1662
1777
file_id=self.path2id(conflicted)))
1663
1778
return conflicts
1687
1802
def unlock(self):
1688
1803
# we share control files:
1689
if self._hashcache.needs_write and self._control_files._lock_count==3:
1690
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()
1691
1810
# reverse order of locking.
1693
1812
return self._control_files.unlock()
1734
1853
def add_conflicts(self, new_conflicts):
1735
1854
conflict_set = set(self.conflicts())
1736
1855
conflict_set.update(set(list(new_conflicts)))
1737
self.set_conflicts(ConflictList(sorted(conflict_set,
1738
key=Conflict.sort_key)))
1856
self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
1857
key=_mod_conflicts.Conflict.sort_key)))
1740
1859
@needs_read_lock
1741
1860
def conflicts(self):
1743
1862
confile = self._control_files.get('conflicts')
1744
1863
except NoSuchFile:
1745
return ConflictList()
1864
return _mod_conflicts.ConflictList()
1747
1866
if confile.next() != CONFLICT_HEADER_1 + '\n':
1748
1867
raise ConflictFormatError()
1749
1868
except StopIteration:
1750
1869
raise ConflictFormatError()
1751
return ConflictList.from_stanzas(RioReader(confile))
1870
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1753
1872
def unlock(self):
1754
if self._hashcache.needs_write and self._control_files._lock_count==1:
1755
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()
1756
1879
# reverse order of locking.
1758
1881
return self._control_files.unlock()
1904
2027
_internal=True,
1906
2029
_bzrdir=a_bzrdir)
1907
wt._write_inventory(inv)
1908
wt.set_root_id(inv.root.file_id)
1909
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.
1910
2034
wt.set_parent_trees([(revision, basis_tree)])
1911
build_tree(basis_tree, wt)
2035
transform.build_tree(basis_tree, wt)
1914
2038
def __init__(self):
1986
2110
_control_files=control_files)
1987
2111
wt.lock_tree_write()
1989
wt._write_inventory(inv)
1990
wt.set_root_id(inv.root.file_id)
1991
2113
basis_tree = branch.repository.revision_tree(revision_id)
1992
if revision_id == bzrlib.revision.NULL_REVISION:
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)
2117
if revision_id == NULL_REVISION:
1993
2118
wt.set_parent_trees([])
1995
2120
wt.set_parent_trees([(revision_id, basis_tree)])
1996
build_tree(basis_tree, wt)
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()
1999
control_files.unlock()
2002
2129
def __init__(self):