32
32
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
33
CONFLICT_HEADER_1 = "BZR conflict list format 1"
34
35
# TODO: Give the workingtree sole responsibility for the working inventory;
35
36
# remove the variable and references to it from the branch. This may require
49
50
from bzrlib.atomicfile import AtomicFile
50
51
from bzrlib.branch import (Branch,
53
from bzrlib.conflicts import Conflict, ConflictList, CONFLICT_SUFFIXES
52
54
import bzrlib.bzrdir as bzrdir
53
55
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
56
import bzrlib.errors as errors
55
57
from bzrlib.errors import (BzrCheckError,
58
61
WeaveRevisionNotPresent,
62
MergeModifiedFormatError)
65
MergeModifiedFormatError,
63
68
from bzrlib.inventory import InventoryEntry, Inventory
64
69
from bzrlib.lockable_files import LockableFiles, TransportLock
65
70
from bzrlib.lockdir import LockDir
85
90
from bzrlib.progress import DummyProgress, ProgressPhase
86
91
from bzrlib.revision import NULL_REVISION
87
from bzrlib.rio import RioReader, RioWriter, Stanza
92
from bzrlib.rio import RioReader, rio_file, Stanza
88
93
from bzrlib.symbol_versioning import *
89
94
from bzrlib.textui import show_status
91
from bzrlib.trace import mutter
92
96
from bzrlib.transform import build_tree
97
from bzrlib.trace import mutter, note
93
98
from bzrlib.transport import get_transport
94
99
from bzrlib.transport.local import LocalTransport
234
239
if deprecated_passed(branch):
235
240
if not _internal:
236
241
warn("WorkingTree(..., branch=XXX) is deprecated as of bzr 0.8."
237
" Please use bzrdir.open_workingtree() or WorkingTree.open().",
242
" Please use bzrdir.open_workingtree() or"
243
" WorkingTree.open().",
238
244
DeprecationWarning,
247
self._branch = branch
243
self.branch = self.bzrdir.open_branch()
249
self._branch = self.bzrdir.open_branch()
244
250
assert isinstance(self.branch, Branch), \
245
251
"branch %r is not a Branch" % self.branch
246
252
self.basedir = realpath(basedir)
277
283
self._set_inventory(_inventory)
286
fget=lambda self: self._branch,
287
doc="""The branch this WorkingTree is connected to.
289
This cannot be set - it is reflective of the actual disk structure
290
the working tree has been constructed from.
279
293
def _set_inventory(self, inv):
280
294
self._inventory = inv
281
295
self.path2id = self._inventory.path2id
350
364
revision_id = self.last_revision()
351
365
if revision_id is not None:
353
xml = self.read_basis_inventory(revision_id)
367
xml = self.read_basis_inventory()
354
368
inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
371
if inv is not None and inv.revision_id == revision_id:
355
372
return bzrlib.tree.RevisionTree(self.branch.repository, inv,
374
# FIXME? RBC 20060403 should we cache the inventory here ?
359
375
return self.branch.repository.revision_tree(revision_id)
606
622
@needs_write_lock
607
623
def set_merge_modified(self, modified_hashes):
609
my_file.write(MERGE_MODIFIED_HEADER_1 + '\n')
610
writer = RioWriter(my_file)
611
for file_id, hash in modified_hashes.iteritems():
612
s = Stanza(file_id=file_id, hash=hash)
613
writer.write_stanza(s)
615
self._control_files.put('merge-hashes', my_file)
625
for file_id, hash in modified_hashes.iteritems():
626
yield Stanza(file_id=file_id, hash=hash)
627
self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
630
def _put_rio(self, filename, stanzas, header):
631
my_file = rio_file(stanzas, header)
632
self._control_files.put(filename, my_file)
618
635
def merge_modified(self):
628
645
raise MergeModifiedFormatError()
629
646
for s in RioReader(hashfile):
630
647
file_id = s.get("file_id")
648
if file_id not in self.inventory:
631
650
hash = s.get("hash")
632
651
if hash == self.get_file_sha1(file_id):
633
652
merge_hashes[file_id] = hash
859
878
if not self.is_ignored(subp):
881
@deprecated_method(zero_eight)
862
882
def iter_conflicts(self):
883
"""List all files in the tree that have text or content conflicts.
884
DEPRECATED. Use conflicts instead."""
885
return self._iter_conflicts()
887
def _iter_conflicts(self):
863
888
conflicted = set()
864
889
for path in (s[0] for s in self.list_files()):
865
890
stem = get_conflicted_stem(path)
1024
1052
self.branch.unlock()
1027
def _basis_inventory_name(self, revision_id):
1028
return 'basis-inventory.%s' % revision_id
1055
def get_physical_lock_status(self):
1056
return self._control_files.get_physical_lock_status()
1058
def _basis_inventory_name(self):
1059
return 'basis-inventory'
1030
1061
@needs_write_lock
1031
def set_last_revision(self, new_revision, old_revision=None):
1062
def set_last_revision(self, new_revision):
1032
1063
"""Change the last revision in the working tree."""
1033
self._remove_old_basis(old_revision)
1034
1064
if self._change_last_revision(new_revision):
1035
1065
self._cache_basis_inventory(new_revision)
1037
1067
def _change_last_revision(self, new_revision):
1038
"""Template method part of set_last_revision to perform the change."""
1068
"""Template method part of set_last_revision to perform the change.
1070
This is used to allow WorkingTree3 instances to not affect branch
1071
when their last revision is set.
1039
1073
if new_revision is None:
1040
1074
self.branch.set_revision_history([])
1051
1085
def _cache_basis_inventory(self, new_revision):
1052
1086
"""Cache new_revision as the basis inventory."""
1054
xml = self.branch.repository.get_inventory_xml(new_revision)
1055
path = self._basis_inventory_name(new_revision)
1088
# this double handles the inventory - unpack and repack -
1089
# but is easier to understand. We can/should put a conditional
1090
# in here based on whether the inventory is in the latest format
1091
# - perhaps we should repack all inventories on a repository
1093
inv = self.branch.repository.get_inventory(new_revision)
1094
inv.revision_id = new_revision
1095
xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1097
path = self._basis_inventory_name()
1056
1098
self._control_files.put_utf8(path, xml)
1057
1099
except WeaveRevisionNotPresent:
1060
def _remove_old_basis(self, old_revision):
1061
"""Remove the old basis inventory 'old_revision'."""
1062
if old_revision is not None:
1064
path = self._basis_inventory_name(old_revision)
1065
path = self._control_files._escape(path)
1066
self._control_files._transport.delete(path)
1070
def read_basis_inventory(self, revision_id):
1102
def read_basis_inventory(self):
1071
1103
"""Read the cached basis inventory."""
1072
path = self._basis_inventory_name(revision_id)
1104
path = self._basis_inventory_name()
1073
1105
return self._control_files.get_utf8(path).read()
1075
1107
@needs_read_lock
1127
1159
def revert(self, filenames, old_tree=None, backups=True,
1128
1160
pb=DummyProgress()):
1129
1161
from transform import revert
1162
from conflicts import resolve
1130
1163
if old_tree is None:
1131
1164
old_tree = self.basis_tree()
1132
revert(self, old_tree, filenames, backups, pb)
1165
conflicts = revert(self, old_tree, filenames, backups, pb)
1133
1166
if not len(filenames):
1134
1167
self.set_pending_merges([])
1170
resolve(self, filenames, ignore_misses=True)
1173
# XXX: This method should be deprecated in favour of taking in a proper
1174
# new Inventory object.
1136
1175
@needs_write_lock
1137
1176
def set_inventory(self, new_inventory_list):
1138
1177
from bzrlib.inventory import (Inventory,
1269
1308
self._set_inventory(inv)
1270
1309
mutter('wrote working inventory')
1311
def set_conflicts(self, arg):
1312
raise UnsupportedOperation(self.set_conflicts, self)
1315
def conflicts(self):
1316
conflicts = ConflictList()
1317
for conflicted in self._iter_conflicts():
1320
if file_kind(self.abspath(conflicted)) != "file":
1323
if e.errno == errno.ENOENT:
1328
for suffix in ('.THIS', '.OTHER'):
1330
kind = file_kind(self.abspath(conflicted+suffix))
1332
if e.errno == errno.ENOENT:
1340
ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1341
conflicts.append(Conflict.factory(ctype, path=conflicted,
1342
file_id=self.path2id(conflicted)))
1273
1346
class WorkingTree3(WorkingTree):
1274
1347
"""This is the Format 3 working tree.
1304
1377
self._control_files.put_utf8('last-revision', revision_id)
1308
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
1381
def set_conflicts(self, conflicts):
1382
self._put_rio('conflicts', conflicts.to_stanzas(),
1386
def conflicts(self):
1388
confile = self._control_files.get('conflicts')
1390
return ConflictList()
1392
if confile.next() != CONFLICT_HEADER_1 + '\n':
1393
raise ConflictFormatError()
1394
except StopIteration:
1395
raise ConflictFormatError()
1396
return ConflictList.from_stanzas(RioReader(confile))
1309
1399
def get_conflicted_stem(path):
1310
1400
for suffix in CONFLICT_SUFFIXES:
1311
1401
if path.endswith(suffix):
1372
1462
"""Return the ASCII format string that identifies this format."""
1373
1463
raise NotImplementedError(self.get_format_string)
1465
def get_format_description(self):
1466
"""Return the short description for this format."""
1467
raise NotImplementedError(self.get_format_description)
1375
1469
def is_supported(self):
1376
1470
"""Is this format supported?
1402
1496
This format modified the hash cache from the format 1 hash cache.
1499
def get_format_description(self):
1500
"""See WorkingTreeFormat.get_format_description()."""
1501
return "Working tree format 2"
1405
1503
def initialize(self, a_bzrdir, revision_id=None):
1406
1504
"""See WorkingTreeFormat.initialize()."""
1407
1505
if not isinstance(a_bzrdir.transport, LocalTransport):
1470
1568
"""See WorkingTreeFormat.get_format_string()."""
1471
1569
return "Bazaar-NG Working Tree format 3"
1571
def get_format_description(self):
1572
"""See WorkingTreeFormat.get_format_description()."""
1573
return "Working tree format 3"
1473
1575
_lock_file_name = 'lock'
1474
1576
_lock_class = LockDir
1489
1591
transport = a_bzrdir.get_workingtree_transport(self)
1490
1592
control_files = self._open_control_files(a_bzrdir)
1491
1593
control_files.create_lock()
1594
control_files.lock_write()
1492
1595
control_files.put_utf8('format', self.get_format_string())
1493
1596
branch = a_bzrdir.open_branch()
1494
1597
if revision_id is None:
1502
1605
_bzrdir=a_bzrdir,
1503
1606
_control_files=control_files)
1504
wt._write_inventory(inv)
1505
wt.set_root_id(inv.root.file_id)
1506
wt.set_last_revision(revision_id)
1507
wt.set_pending_merges([])
1508
build_tree(wt.basis_tree(), wt)
1609
wt._write_inventory(inv)
1610
wt.set_root_id(inv.root.file_id)
1611
wt.set_last_revision(revision_id)
1612
wt.set_pending_merges([])
1613
build_tree(wt.basis_tree(), wt)
1616
control_files.unlock()
1511
1619
def __init__(self):