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
68
import bzrlib.branch
58
from bzrlib.conflicts import Conflict, ConflictList, CONFLICT_SUFFIXES
69
from bzrlib.transport import get_transport
73
from bzrlib import symbol_versioning
59
74
from bzrlib.decorators import needs_read_lock, needs_write_lock
60
75
from bzrlib.errors import (BzrCheckError,
67
82
MergeModifiedFormatError,
68
83
UnsupportedOperation,
70
from bzrlib.inventory import InventoryEntry, Inventory
85
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
71
86
from bzrlib.lockable_files import LockableFiles, TransportLock
72
87
from bzrlib.lockdir import LockDir
73
from bzrlib.merge import merge_inner, transform_tree
74
88
import bzrlib.mutabletree
75
89
from bzrlib.mutabletree import needs_tree_write_lock
76
90
from bzrlib.osutils import (
102
from bzrlib.trace import mutter, note
103
from bzrlib.transport.local import LocalTransport
93
105
from bzrlib.progress import DummyProgress, ProgressPhase
94
106
from bzrlib.revision import NULL_REVISION
95
107
import bzrlib.revisiontree
100
112
DEPRECATED_PARAMETER,
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
113
# the regex removes any weird characters; we don't escape them
114
# but rather just pull them out
115
_gen_file_id_re = re.compile(r'[^\w.]')
116
_gen_id_suffix = None
120
def _next_id_suffix():
121
"""Create a new file id suffix that is reasonably unique.
123
On the first call we combine the current time with 64 bits of randomness
124
to give a highly probably globally unique number. Then each call in the same
125
process adds 1 to a serial number we append to that unique value.
127
# XXX TODO: change bzrlib.add.smart_add to call workingtree.add() rather
128
# than having to move the id randomness out of the inner loop like this.
129
# XXX TODO: for the global randomness this uses we should add the thread-id
130
# before the serial #.
131
global _gen_id_suffix, _gen_id_serial
132
if _gen_id_suffix is None:
133
_gen_id_suffix = "-%s-%s-" % (compact_date(time()), rand_chars(16))
135
return _gen_id_suffix + str(_gen_id_serial)
119
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
120
CONFLICT_HEADER_1 = "BZR conflict list format 1"
123
@deprecated_function(zero_thirteen)
138
124
def gen_file_id(name):
139
125
"""Return new file id for the basename 'name'.
141
The uniqueness is supplied from _next_id_suffix.
127
Use bzrlib.generate_ids.gen_file_id() instead
143
# The real randomness is in the _next_id_suffix, the
144
# rest of the identifier is just to be nice.
146
# 1) Remove non-ascii word characters to keep the ids portable
147
# 2) squash to lowercase, so the file id doesn't have to
148
# be escaped (case insensitive filesystems would bork for ids
149
# that only differred in case without escaping).
150
# 3) truncate the filename to 20 chars. Long filenames also bork on some
152
# 4) Removing starting '.' characters to prevent the file ids from
153
# being considered hidden.
154
ascii_word_only = _gen_file_id_re.sub('', name.lower())
155
short_no_dots = ascii_word_only.lstrip('.')[:20]
156
return short_no_dots + _next_id_suffix()
129
return generate_ids.gen_file_id(name)
132
@deprecated_function(zero_thirteen)
159
133
def gen_root_id():
160
"""Return a new tree-root file id."""
161
return gen_file_id('TREE_ROOT')
134
"""Return a new tree-root file id.
136
This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
138
return generate_ids.gen_root_id()
164
141
class TreeEntry(object):
253
230
self.basedir = wt.basedir
254
231
self._control_files = wt._control_files
255
232
self._hashcache = wt._hashcache
256
self._set_inventory(wt._inventory)
233
self._set_inventory(wt._inventory, dirty=False)
257
234
self._format = wt._format
258
235
self.bzrdir = wt.bzrdir
259
236
from bzrlib.hashcache import HashCache
303
280
if _inventory is None:
304
self._set_inventory(self.read_working_inventory())
281
self._inventory_is_modified = False
282
self.read_working_inventory()
306
self._set_inventory(_inventory)
284
# the caller of __init__ has provided an inventory,
285
# we assume they know what they are doing - as its only
286
# the Format factory and creation methods that are
287
# permitted to do this.
288
self._set_inventory(_inventory, dirty=False)
308
290
branch = property(
309
291
fget=lambda self: self._branch,
324
306
self._control_files.break_lock()
325
307
self.branch.break_lock()
327
def _set_inventory(self, inv):
309
def _set_inventory(self, inv, dirty):
310
"""Set the internal cached inventory.
312
:param inv: The inventory to set.
313
:param dirty: A boolean indicating whether the inventory is the same
314
logical inventory as whats on disk. If True the inventory is not
315
the same and should be written to disk or data will be lost, if
316
False then the inventory is the same as that on disk and any
317
serialisation would be unneeded overhead.
328
319
assert inv.root is not None
329
320
self._inventory = inv
321
self._inventory_is_modified = dirty
332
324
def open(path=None, _unsupported=False):
401
393
xml = self.read_basis_inventory()
402
inv = bzrlib.xml6.serializer_v6.read_inventory_from_string(xml)
394
inv = xml6.serializer_v6.read_inventory_from_string(xml)
403
395
if inv is not None and inv.revision_id == revision_id:
404
return bzrlib.tree.RevisionTree(self.branch.repository,
396
return bzrlib.revisiontree.RevisionTree(
397
self.branch.repository, inv, revision_id)
406
398
except (NoSuchFile, errors.BadInventoryFormat):
408
400
# No cached copy available, retrieve from the repository.
463
455
The path may be absolute or relative. If its a relative path it is
464
456
interpreted relative to the python current working directory.
466
return relpath(self.basedir, path)
458
return osutils.relpath(self.basedir, path)
468
460
def has_filename(self, filename):
469
461
return osutils.lexists(self.abspath(filename))
497
489
parents.append(l.rstrip('\n'))
500
493
def get_root_id(self):
501
494
"""Return the id of this trees root"""
502
inv = self.read_working_inventory()
503
return inv.root.file_id
495
return self._inventory.root.file_id
505
497
def _get_store_filename(self, file_id):
506
498
## XXX: badly named; this is not in the store at all
533
525
def copy_content_into(self, tree, revision_id=None):
534
526
"""Copy the current content and user files of this tree into tree."""
527
tree.set_root_id(self.get_root_id())
535
528
if revision_id is None:
536
transform_tree(tree, self)
529
merge.transform_tree(tree, self)
538
531
# TODO now merge from tree.last_revision to revision (to preserve
539
532
# user local changes)
540
transform_tree(tree, self)
533
merge.transform_tree(tree, self)
541
534
tree.set_parent_ids([revision_id])
543
536
def id2abspath(self, file_id):
676
669
return self.get_parent_ids()[1:]
671
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
672
"""Common ghost checking functionality from set_parent_*.
674
This checks that the left hand-parent exists if there are any
677
if len(revision_ids) > 0:
678
leftmost_id = revision_ids[0]
679
if (not allow_leftmost_as_ghost and not
680
self.branch.repository.has_revision(leftmost_id)):
681
raise errors.GhostRevisionUnusableHere(leftmost_id)
683
def _set_merges_from_parent_ids(self, parent_ids):
684
merges = parent_ids[1:]
685
self._control_files.put_utf8('pending-merges', '\n'.join(merges))
678
687
@needs_tree_write_lock
679
688
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
680
689
"""Set the parent ids to revision_ids.
688
697
:param revision_ids: The revision_ids to set as the parent ids of this
689
698
working tree. Any of these may be ghosts.
700
self._check_parents_for_ghosts(revision_ids,
701
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
691
703
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)
704
self.set_last_revision(revision_ids[0])
698
706
self.set_last_revision(None)
699
merges = revision_ids[1:]
700
self._control_files.put_utf8('pending-merges', '\n'.join(merges))
708
self._set_merges_from_parent_ids(revision_ids)
702
710
@needs_tree_write_lock
703
711
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
704
712
"""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],
713
parent_ids = [rev for (rev, tree) in parents_list]
715
self._check_parents_for_ghosts(parent_ids,
708
716
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
718
if len(parent_ids) == 0:
719
leftmost_parent_id = None
720
leftmost_parent_tree = None
722
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
724
if self._change_last_revision(leftmost_parent_id):
725
if leftmost_parent_tree is None:
726
# If we don't have a tree, fall back to reading the
727
# parent tree from the repository.
728
self._cache_basis_inventory(leftmost_parent_id)
730
inv = leftmost_parent_tree.inventory
731
xml = self._create_basis_xml_from_inventory(
732
leftmost_parent_id, inv)
733
self._write_basis_inventory(xml)
734
self._set_merges_from_parent_ids(parent_ids)
710
736
@needs_tree_write_lock
711
737
def set_pending_merges(self, rev_list):
712
738
parents = self.get_parent_ids()
794
820
def mkdir(self, path, file_id=None):
795
821
"""See MutableTree.mkdir()."""
796
822
if file_id is None:
797
file_id = gen_file_id(os.path.basename(path))
823
file_id = generate_ids.gen_file_id(os.path.basename(path))
798
824
os.mkdir(self.abspath(path))
799
825
self.add(path, file_id, 'directory')
813
def list_files(self):
840
"""Write the in memory inventory to disk."""
841
# TODO: Maybe this should only write on dirty ?
842
if self._control_files._lock_mode != 'w':
843
raise errors.NotWriteLocked(self)
845
xml5.serializer_v5.write_inventory(self._inventory, sio)
847
self._control_files.put('inventory', sio)
848
self._inventory_is_modified = False
850
def list_files(self, include_root=False):
814
851
"""Recursively list all files as (path, class, kind, id, entry).
816
853
Lists, but does not descend into unversioned directories.
821
858
Skips the control directory.
823
860
inv = self._inventory
861
if include_root is True:
862
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
824
863
# Convert these into local objects to save lookup times
825
864
pathjoin = osutils.pathjoin
826
865
file_kind = osutils.file_kind
969
1008
# create a file in this interval and then the rename might be
970
1009
# left half-done. But we should have caught most problems.
971
1010
orig_inv = deepcopy(self.inventory)
1011
original_modified = self._inventory_is_modified
1014
self._inventory_is_modified = True
973
1015
for f in from_paths:
974
1016
name_tail = splitpath(f)[-1]
975
1017
dest_path = pathjoin(to_name, name_tail)
976
1018
result.append((f, dest_path))
977
1019
inv.rename(inv.path2id(f), to_dir_id, name_tail)
979
rename(self.abspath(f), self.abspath(dest_path))
1021
osutils.rename(self.abspath(f), self.abspath(dest_path))
980
1022
except OSError, e:
981
1023
raise BzrError("failed to rename %r to %r: %s" %
982
(f, dest_path, e[1]),
983
["rename rolled back"])
1024
(f, dest_path, e[1]))
985
1026
# restore the inventory on error
986
self._set_inventory(orig_inv)
1027
self._set_inventory(orig_inv, dirty=original_modified)
988
1029
self._write_inventory(inv)
1028
1069
from_abs = self.abspath(from_rel)
1029
1070
to_abs = self.abspath(to_rel)
1031
rename(from_abs, to_abs)
1072
osutils.rename(from_abs, to_abs)
1032
1073
except OSError, e:
1033
1074
inv.rename(file_id, from_parent, from_name)
1034
1075
raise BzrError("failed to rename %r to %r: %s"
1035
% (from_abs, to_abs, e[1]),
1036
["rename rolled back"])
1076
% (from_abs, to_abs, e[1]))
1037
1077
self._write_inventory(inv)
1039
1079
@needs_read_lock
1406
1450
self.branch.set_revision_history([new_revision])
1453
def _write_basis_inventory(self, xml):
1454
"""Write the basis inventory XML to the basis-inventory file"""
1455
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1456
path = self._basis_inventory_name()
1458
self._control_files.put(path, sio)
1460
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1461
"""Create the text that will be saved in basis-inventory"""
1462
inventory.revision_id = revision_id
1463
return xml6.serializer_v6.write_inventory_to_string(inventory)
1409
1465
def _cache_basis_inventory(self, new_revision):
1410
1466
"""Cache new_revision as the basis inventory."""
1411
1467
# TODO: this should allow the ready-to-use inventory to be passed in,
1428
1484
'format="6"' not in firstline):
1429
1485
inv = self.branch.repository.deserialise_inventory(
1430
1486
new_revision, xml)
1431
inv.revision_id = new_revision
1432
xml = bzrlib.xml6.serializer_v6.write_inventory_to_string(inv)
1433
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1434
path = self._basis_inventory_name()
1436
self._control_files.put(path, sio)
1487
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1488
self._write_basis_inventory(xml)
1437
1489
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1445
1497
@needs_read_lock
1446
1498
def read_working_inventory(self):
1447
"""Read the working inventory."""
1499
"""Read the working inventory.
1501
:raises errors.InventoryModified: read_working_inventory will fail
1502
when the current in memory inventory has been modified.
1504
# conceptually this should be an implementation detail of the tree.
1505
# XXX: Deprecate this.
1448
1506
# ElementTree does its own conversion from UTF-8, so open in
1450
result = bzrlib.xml5.serializer_v5.read_inventory(
1508
if self._inventory_is_modified:
1509
raise errors.InventoryModified(self)
1510
result = xml5.serializer_v5.read_inventory(
1451
1511
self._control_files.get('inventory'))
1452
self._set_inventory(result)
1512
self._set_inventory(result, dirty=False)
1455
1515
@needs_tree_write_lock
1495
1556
@needs_tree_write_lock
1496
1557
def revert(self, filenames, old_tree=None, backups=True,
1497
1558
pb=DummyProgress()):
1498
from transform import revert
1499
from conflicts import resolve
1559
from bzrlib.conflicts import resolve
1500
1560
if old_tree is None:
1501
1561
old_tree = self.basis_tree()
1502
conflicts = revert(self, old_tree, filenames, backups, pb)
1562
conflicts = transform.revert(self, old_tree, filenames, backups, pb)
1503
1563
if not len(filenames):
1504
1564
self.set_parent_ids(self.get_parent_ids()[:1])
1535
1595
@needs_tree_write_lock
1536
1596
def set_root_id(self, file_id):
1537
1597
"""Set the root id for this tree."""
1538
inv = self.read_working_inventory()
1600
symbol_versioning.warn(symbol_versioning.zero_twelve
1601
% 'WorkingTree.set_root_id with fileid=None',
1605
inv = self._inventory
1539
1606
orig_root_id = inv.root.file_id
1607
# TODO: it might be nice to exit early if there was nothing
1608
# to do, saving us from trigger a sync on unlock.
1609
self._inventory_is_modified = True
1610
# we preserve the root inventory entry object, but
1611
# unlinkit from the byid index
1540
1612
del inv._byid[inv.root.file_id]
1541
1613
inv.root.file_id = file_id
1614
# and link it into the index with the new changed id.
1542
1615
inv._byid[inv.root.file_id] = inv.root
1616
# and finally update all children to reference the new id.
1617
# XXX: this should be safe to just look at the root.children
1618
# list, not the WHOLE INVENTORY.
1543
1619
for fid in inv:
1544
1620
entry = inv[fid]
1545
1621
if entry.parent_id == orig_root_id:
1546
1622
entry.parent_id = inv.root.file_id
1547
self._write_inventory(inv)
1549
1624
def unlock(self):
1550
1625
"""See Branch.unlock.
1558
1633
raise NotImplementedError(self.unlock)
1561
1635
def update(self):
1562
1636
"""Update a working tree along its branch.
1564
This will update the branch if its bound too, which means we have multiple trees involved:
1565
The new basis tree of the master.
1566
The old basis tree of the branch.
1567
The old basis tree of the working tree.
1568
The current working tree state.
1569
pathologically all three may be different, and non ancestors of each other.
1570
Conceptually we want to:
1571
Preserve the wt.basis->wt.state changes
1572
Transform the wt.basis to the new master basis.
1573
Apply a merge of the old branch basis to get any 'local' changes from it into the tree.
1574
Restore the wt.basis->wt.state changes.
1638
This will update the branch if its bound too, which means we have
1639
multiple trees involved:
1641
- The new basis tree of the master.
1642
- The old basis tree of the branch.
1643
- The old basis tree of the working tree.
1644
- The current working tree state.
1646
Pathologically, all three may be different, and non-ancestors of each
1647
other. Conceptually we want to:
1649
- Preserve the wt.basis->wt.state changes
1650
- Transform the wt.basis to the new master basis.
1651
- Apply a merge of the old branch basis to get any 'local' changes from
1653
- Restore the wt.basis->wt.state changes.
1576
1655
There isn't a single operation at the moment to do that, so we:
1577
Merge current state -> basis tree of the master w.r.t. the old tree basis.
1578
Do a 'normal' merge of the old branch basis if it is relevant.
1580
old_tip = self.branch.update()
1656
- Merge current state -> basis tree of the master w.r.t. the old tree
1658
- Do a 'normal' merge of the old branch basis if it is relevant.
1660
if self.branch.get_master_branch() is not None:
1662
update_branch = True
1664
self.lock_tree_write()
1665
update_branch = False
1668
old_tip = self.branch.update()
1671
return self._update_tree(old_tip)
1675
@needs_tree_write_lock
1676
def _update_tree(self, old_tip=None):
1677
"""Update a tree to the master branch.
1679
:param old_tip: if supplied, the previous tip revision the branch,
1680
before it was changed to the master branch's tip.
1581
1682
# here if old_tip is not None, it is the old tip of the branch before
1582
1683
# it was updated from the master branch. This should become a pending
1583
1684
# merge in the working tree to preserve the user existing work. we
1597
1698
# merge tree state up to new branch tip.
1598
1699
basis = self.basis_tree()
1599
1700
to_tree = self.branch.basis_tree()
1600
result += merge_inner(self.branch,
1701
if basis.inventory.root is None:
1702
self.set_root_id(to_tree.inventory.root.file_id)
1703
result += merge.merge_inner(
1603
1707
this_tree=self)
1647
1752
@needs_tree_write_lock
1648
1753
def _write_inventory(self, inv):
1649
1754
"""Write inventory as the current inventory."""
1651
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
1653
self._control_files.put('inventory', sio)
1654
self._set_inventory(inv)
1655
mutter('wrote working inventory')
1755
self._set_inventory(inv, dirty=True)
1657
1758
def set_conflicts(self, arg):
1658
1759
raise UnsupportedOperation(self.set_conflicts, self)
1710
1812
def unlock(self):
1711
1813
# we share control files:
1712
if self._hashcache.needs_write and self._control_files._lock_count==3:
1713
self._hashcache.write()
1814
if self._control_files._lock_count == 3:
1815
# _inventory_is_modified is always False during a read lock.
1816
if self._inventory_is_modified:
1818
if self._hashcache.needs_write:
1819
self._hashcache.write()
1714
1820
# reverse order of locking.
1716
1822
return self._control_files.unlock()
1757
1863
def add_conflicts(self, new_conflicts):
1758
1864
conflict_set = set(self.conflicts())
1759
1865
conflict_set.update(set(list(new_conflicts)))
1760
self.set_conflicts(ConflictList(sorted(conflict_set,
1761
key=Conflict.sort_key)))
1866
self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
1867
key=_mod_conflicts.Conflict.sort_key)))
1763
1869
@needs_read_lock
1764
1870
def conflicts(self):
1766
1872
confile = self._control_files.get('conflicts')
1767
1873
except NoSuchFile:
1768
return ConflictList()
1874
return _mod_conflicts.ConflictList()
1770
1876
if confile.next() != CONFLICT_HEADER_1 + '\n':
1771
1877
raise ConflictFormatError()
1772
1878
except StopIteration:
1773
1879
raise ConflictFormatError()
1774
return ConflictList.from_stanzas(RioReader(confile))
1880
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1776
1882
def unlock(self):
1777
if self._hashcache.needs_write and self._control_files._lock_count==1:
1778
self._hashcache.write()
1883
if self._control_files._lock_count == 1:
1884
# _inventory_is_modified is always False during a read lock.
1885
if self._inventory_is_modified:
1887
if self._hashcache.needs_write:
1888
self._hashcache.write()
1779
1889
# reverse order of locking.
1781
1891
return self._control_files.unlock()
1927
2037
_internal=True,
1929
2039
_bzrdir=a_bzrdir)
1930
wt._write_inventory(inv)
1931
wt.set_root_id(inv.root.file_id)
1932
2040
basis_tree = branch.repository.revision_tree(revision)
2041
if basis_tree.inventory.root is not None:
2042
wt.set_root_id(basis_tree.inventory.root.file_id)
2043
# set the parent list and cache the basis tree.
1933
2044
wt.set_parent_trees([(revision, basis_tree)])
1934
build_tree(basis_tree, wt)
2045
transform.build_tree(basis_tree, wt)
1937
2048
def __init__(self):
1999
2110
branch = a_bzrdir.open_branch()
2000
2111
if revision_id is None:
2001
2112
revision_id = branch.last_revision()
2113
# WorkingTree3 can handle an inventory which has a unique root id.
2114
# as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2115
# those trees. And because there isn't a format bump inbetween, we
2116
# are maintaining compatibility with older clients.
2117
# inv = Inventory(root_id=gen_root_id())
2003
2119
wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2009
2125
_control_files=control_files)
2010
2126
wt.lock_tree_write()
2012
wt._write_inventory(inv)
2013
wt.set_root_id(inv.root.file_id)
2014
2128
basis_tree = branch.repository.revision_tree(revision_id)
2015
if revision_id == bzrlib.revision.NULL_REVISION:
2129
# only set an explicit root id if there is one to set.
2130
if basis_tree.inventory.root is not None:
2131
wt.set_root_id(basis_tree.inventory.root.file_id)
2132
if revision_id == NULL_REVISION:
2016
2133
wt.set_parent_trees([])
2018
2135
wt.set_parent_trees([(revision_id, basis_tree)])
2019
build_tree(basis_tree, wt)
2136
transform.build_tree(basis_tree, wt)
2138
# Unlock in this order so that the unlock-triggers-flush in
2139
# WorkingTree is given a chance to fire.
2140
control_files.unlock()
2022
control_files.unlock()
2025
2144
def __init__(self):