194
208
(branch.base is not cross checked, because for remote branches that
195
209
would be meaningless).
211
self._format = _format
212
self.bzrdir = _bzrdir
214
# not created via open etc.
215
warn("WorkingTree() is deprecated as of bzr version 0.8. "
216
"Please use bzrdir.open_workingtree or WorkingTree.open().",
219
wt = WorkingTree.open(basedir)
220
self.branch = wt.branch
221
self.basedir = wt.basedir
222
self._control_files = wt._control_files
223
self._hashcache = wt._hashcache
224
self._set_inventory(wt._inventory)
225
self._format = wt._format
226
self.bzrdir = wt.bzrdir
197
227
from bzrlib.hashcache import HashCache
198
228
from bzrlib.trace import note, mutter
199
229
assert isinstance(basedir, basestring), \
200
230
"base directory %r is not a string" % basedir
201
231
basedir = safe_unicode(basedir)
202
mutter("openeing working tree %r", basedir)
204
branch = Branch.open(basedir)
205
assert isinstance(branch, Branch), \
206
"branch %r is not a Branch" % branch
232
mutter("opening working tree %r", basedir)
233
if deprecated_passed(branch):
235
warn("WorkingTree(..., branch=XXX) is deprecated as of bzr 0.8."
236
" Please use bzrdir.open_workingtree() or WorkingTree.open().",
242
self.branch = self.bzrdir.open_branch()
243
assert isinstance(self.branch, Branch), \
244
"branch %r is not a Branch" % self.branch
208
245
self.basedir = realpath(basedir)
209
246
# if branch is at our basedir and is a format 6 or less
210
if (isinstance(self.branch._branch_format,
211
(BzrBranchFormat4, BzrBranchFormat5, BzrBranchFormat6))
212
# might be able to share control object
213
and self.branch.base.split('/')[-2] == self.basedir.split('/')[-1]):
247
if isinstance(self._format, WorkingTreeFormat2):
248
# share control object
214
249
self._control_files = self.branch.control_files
215
250
elif _control_files is not None:
216
251
assert False, "not done yet"
217
252
# self._control_files = _control_files
254
# only ready for format 3
255
assert isinstance(self._format, WorkingTreeFormat3)
219
256
self._control_files = LockableFiles(
220
get_transport(self.basedir).clone(bzrlib.BZRDIR), 'branch-lock')
257
self.bzrdir.get_workingtree_transport(None),
258
'lock', TransportLock)
222
260
# update the whole cache up front and write to disk if anything changed;
223
261
# in the future we might want to do this more selectively
861
954
def kind(self, file_id):
862
955
return file_kind(self.id2abspath(file_id))
958
def last_revision(self):
959
"""Return the last revision id of this working tree.
961
In early branch formats this was == the branch last_revision,
962
but that cannot be relied upon - for working tree operations,
963
always use tree.last_revision().
965
return self.branch.last_revision()
864
967
def lock_read(self):
865
968
"""See Branch.lock_read, and WorkingTree.unlock."""
866
return self.branch.lock_read()
969
self.branch.lock_read()
971
return self._control_files.lock_read()
868
976
def lock_write(self):
869
977
"""See Branch.lock_write, and WorkingTree.unlock."""
870
return self.branch.lock_write()
978
self.branch.lock_write()
980
return self._control_files.lock_write()
872
985
def _basis_inventory_name(self, revision_id):
873
986
return 'basis-inventory.%s' % revision_id
875
989
def set_last_revision(self, new_revision, old_revision=None):
876
if old_revision is not None:
878
path = self._basis_inventory_name(old_revision)
879
path = self.branch.control_files._escape(path)
880
self.branch.control_files._transport.delete(path)
990
"""Change the last revision in the working tree."""
991
self._remove_old_basis(old_revision)
992
if self._change_last_revision(new_revision):
993
self._cache_basis_inventory(new_revision)
995
def _change_last_revision(self, new_revision):
996
"""Template method part of set_last_revision to perform the change."""
997
if new_revision is None:
998
self.branch.set_revision_history([])
1000
# current format is locked in with the branch
1001
revision_history = self.branch.revision_history()
1003
position = revision_history.index(new_revision)
1005
raise errors.NoSuchRevision(self.branch, new_revision)
1006
self.branch.set_revision_history(revision_history[:position + 1])
1009
def _cache_basis_inventory(self, new_revision):
1010
"""Cache new_revision as the basis inventory."""
884
1012
xml = self.branch.repository.get_inventory_xml(new_revision)
885
1013
path = self._basis_inventory_name(new_revision)
886
self.branch.control_files.put_utf8(path, xml)
1014
self._control_files.put_utf8(path, xml)
887
1015
except WeaveRevisionNotPresent:
1018
def _remove_old_basis(self, old_revision):
1019
"""Remove the old basis inventory 'old_revision'."""
1020
if old_revision is not None:
1022
path = self._basis_inventory_name(old_revision)
1023
path = self._control_files._escape(path)
1024
self._control_files._transport.delete(path)
890
1028
def read_basis_inventory(self, revision_id):
891
1029
"""Read the cached basis inventory."""
892
1030
path = self._basis_inventory_name(revision_id)
893
return self.branch.control_files.get_utf8(path).read()
1031
return self._control_files.get_utf8(path).read()
895
1033
@needs_read_lock
896
1034
def read_working_inventory(self):
897
1035
"""Read the working inventory."""
898
1036
# ElementTree does its own conversion from UTF-8, so open in
900
return bzrlib.xml5.serializer_v5.read_inventory(
1038
result = bzrlib.xml5.serializer_v5.read_inventory(
901
1039
self._control_files.get('inventory'))
1040
self._set_inventory(result)
903
1043
@needs_write_lock
904
1044
def remove(self, files, verbose=False):
1005
1142
# of a nasty hack; probably it's better to have a transaction object,
1006
1143
# which can do some finalization when it's either successfully or
1007
1144
# unsuccessfully completed. (Denys's original patch did that.)
1008
if self._hashcache.needs_write and self.branch.control_files._lock_count==1:
1145
# RBC 20060206 hookinhg into transaction will couple lock and transaction
1146
# wrongly. Hookinh into unllock on the control files object is fine though.
1148
# TODO: split this per format so there is no ugly if block
1149
if self._hashcache.needs_write and (
1150
# dedicated lock files
1151
self._control_files._lock_count==1 or
1153
(self._control_files is self.branch.control_files and
1154
self._control_files._lock_count==3)):
1009
1155
self._hashcache.write()
1010
return self.branch.unlock()
1156
# reverse order of locking.
1157
result = self._control_files.unlock()
1159
self.branch.unlock()
1165
"""Update a working tree along its branch.
1167
This will update the branch if its bound too, which means we have multiple trees involved:
1168
The new basis tree of the master.
1169
The old basis tree of the branch.
1170
The old basis tree of the working tree.
1171
The current working tree state.
1172
pathologically all three may be different, and non ancestors of each other.
1173
Conceptually we want to:
1174
Preserve the wt.basis->wt.state changes
1175
Transform the wt.basis to the new master basis.
1176
Apply a merge of the old branch basis to get any 'local' changes from it into the tree.
1177
Restore the wt.basis->wt.state changes.
1179
There isn't a single operation at the moment to do that, so we:
1180
Merge current state -> basis tree of the master w.r.t. the old tree basis.
1181
Do a 'normal' merge of the old branch basis if it is relevant.
1183
old_tip = self.branch.update()
1184
if old_tip is not None:
1185
self.add_pending_merge(old_tip)
1186
self.branch.lock_read()
1189
if self.last_revision() != self.branch.last_revision():
1190
# merge tree state up to new branch tip.
1191
basis = self.basis_tree()
1192
to_tree = self.branch.basis_tree()
1193
result += merge_inner(self.branch,
1197
self.set_last_revision(self.branch.last_revision())
1198
if old_tip and old_tip != self.last_revision():
1199
# our last revision was not the prior branch last reivison
1200
# and we have converted that last revision to a pending merge.
1201
# base is somewhere between the branch tip now
1202
# and the now pending merge
1203
from bzrlib.revision import common_ancestor
1205
base_rev_id = common_ancestor(self.branch.last_revision(),
1207
self.branch.repository)
1208
except errors.NoCommonAncestor:
1210
base_tree = self.branch.repository.revision_tree(base_rev_id)
1211
other_tree = self.branch.repository.revision_tree(old_tip)
1212
result += merge_inner(self.branch,
1218
self.branch.unlock()
1012
1220
@needs_write_lock
1013
1221
def _write_inventory(self, inv):
1018
1226
self._control_files.put('inventory', sio)
1019
1227
self._set_inventory(inv)
1020
1228
mutter('wrote working inventory')
1231
class WorkingTree3(WorkingTree):
1232
"""This is the Format 3 working tree.
1234
This differs from the base WorkingTree by:
1235
- having its own file lock
1236
- having its own last-revision property.
1240
def last_revision(self):
1241
"""See WorkingTree.last_revision."""
1243
return self._control_files.get_utf8('last-revision').read()
1247
def _change_last_revision(self, revision_id):
1248
"""See WorkingTree._change_last_revision."""
1249
if revision_id is None or revision_id == NULL_REVISION:
1251
self._control_files._transport.delete('last-revision')
1252
except errors.NoSuchFile:
1257
self.branch.revision_history().index(revision_id)
1259
raise errors.NoSuchRevision(self.branch, revision_id)
1260
self._control_files.put_utf8('last-revision', revision_id)
1023
1264
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
1024
1265
def get_conflicted_stem(path):
1025
1266
for suffix in CONFLICT_SUFFIXES:
1026
1267
if path.endswith(suffix):
1027
1268
return path[:-len(suffix)]
1270
@deprecated_function(zero_eight)
1271
def is_control_file(filename):
1272
"""See WorkingTree.is_control_filename(filename)."""
1273
## FIXME: better check
1274
filename = normpath(filename)
1275
while filename != '':
1276
head, tail = os.path.split(filename)
1277
## mutter('check %r for control file' % ((head, tail),))
1280
if filename == head:
1286
class WorkingTreeFormat(object):
1287
"""An encapsulation of the initialization and open routines for a format.
1289
Formats provide three things:
1290
* An initialization routine,
1294
Formats are placed in an dict by their format string for reference
1295
during workingtree opening. Its not required that these be instances, they
1296
can be classes themselves with class methods - it simply depends on
1297
whether state is needed for a given format or not.
1299
Once a format is deprecated, just deprecate the initialize and open
1300
methods on the format class. Do not deprecate the object, as the
1301
object will be created every time regardless.
1304
_default_format = None
1305
"""The default format used for new trees."""
1308
"""The known formats."""
1311
def find_format(klass, a_bzrdir):
1312
"""Return the format for the working tree object in a_bzrdir."""
1314
transport = a_bzrdir.get_workingtree_transport(None)
1315
format_string = transport.get("format").read()
1316
return klass._formats[format_string]
1318
raise errors.NoWorkingTree(base=transport.base)
1320
raise errors.UnknownFormatError(format_string)
1323
def get_default_format(klass):
1324
"""Return the current default format."""
1325
return klass._default_format
1327
def get_format_string(self):
1328
"""Return the ASCII format string that identifies this format."""
1329
raise NotImplementedError(self.get_format_string)
1331
def is_supported(self):
1332
"""Is this format supported?
1334
Supported formats can be initialized and opened.
1335
Unsupported formats may not support initialization or committing or
1336
some other features depending on the reason for not being supported.
1341
def register_format(klass, format):
1342
klass._formats[format.get_format_string()] = format
1345
def set_default_format(klass, format):
1346
klass._default_format = format
1349
def unregister_format(klass, format):
1350
assert klass._formats[format.get_format_string()] is format
1351
del klass._formats[format.get_format_string()]
1355
class WorkingTreeFormat2(WorkingTreeFormat):
1356
"""The second working tree format.
1358
This format modified the hash cache from the format 1 hash cache.
1361
def initialize(self, a_bzrdir, revision_id=None):
1362
"""See WorkingTreeFormat.initialize()."""
1363
if not isinstance(a_bzrdir.transport, LocalTransport):
1364
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1365
branch = a_bzrdir.open_branch()
1366
if revision_id is not None:
1369
revision_history = branch.revision_history()
1371
position = revision_history.index(revision_id)
1373
raise errors.NoSuchRevision(branch, revision_id)
1374
branch.set_revision_history(revision_history[:position + 1])
1377
revision = branch.last_revision()
1379
wt = WorkingTree(a_bzrdir.root_transport.base,
1385
wt._write_inventory(inv)
1386
wt.set_root_id(inv.root.file_id)
1387
wt.set_last_revision(revision)
1388
wt.set_pending_merges([])
1389
build_tree(wt.basis_tree(), wt)
1393
super(WorkingTreeFormat2, self).__init__()
1394
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1396
def open(self, a_bzrdir, _found=False):
1397
"""Return the WorkingTree object for a_bzrdir
1399
_found is a private parameter, do not use it. It is used to indicate
1400
if format probing has already been done.
1403
# we are being called directly and must probe.
1404
raise NotImplementedError
1405
if not isinstance(a_bzrdir.transport, LocalTransport):
1406
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1407
return WorkingTree(a_bzrdir.root_transport.base,
1413
class WorkingTreeFormat3(WorkingTreeFormat):
1414
"""The second working tree format updated to record a format marker.
1416
This format modified the hash cache from the format 1 hash cache.
1419
def get_format_string(self):
1420
"""See WorkingTreeFormat.get_format_string()."""
1421
return "Bazaar-NG Working Tree format 3"
1423
def initialize(self, a_bzrdir, revision_id=None):
1424
"""See WorkingTreeFormat.initialize().
1426
revision_id allows creating a working tree at a differnet
1427
revision than the branch is at.
1429
if not isinstance(a_bzrdir.transport, LocalTransport):
1430
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1431
transport = a_bzrdir.get_workingtree_transport(self)
1432
control_files = LockableFiles(transport, 'lock', TransportLock)
1433
control_files.put_utf8('format', self.get_format_string())
1434
branch = a_bzrdir.open_branch()
1435
if revision_id is None:
1436
revision_id = branch.last_revision()
1438
wt = WorkingTree3(a_bzrdir.root_transport.base,
1444
wt._write_inventory(inv)
1445
wt.set_root_id(inv.root.file_id)
1446
wt.set_last_revision(revision_id)
1447
wt.set_pending_merges([])
1448
build_tree(wt.basis_tree(), wt)
1452
super(WorkingTreeFormat3, self).__init__()
1453
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1455
def open(self, a_bzrdir, _found=False):
1456
"""Return the WorkingTree object for a_bzrdir
1458
_found is a private parameter, do not use it. It is used to indicate
1459
if format probing has already been done.
1462
# we are being called directly and must probe.
1463
raise NotImplementedError
1464
if not isinstance(a_bzrdir.transport, LocalTransport):
1465
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1466
return WorkingTree3(a_bzrdir.root_transport.base,
1472
return self.get_format_string()
1475
# formats which have no format string are not discoverable
1476
# and not independently creatable, so are not registered.
1477
__default_format = WorkingTreeFormat3()
1478
WorkingTreeFormat.register_format(__default_format)
1479
WorkingTreeFormat.set_default_format(__default_format)
1480
_legacy_formats = [WorkingTreeFormat2(),
1484
class WorkingTreeTestProviderAdapter(object):
1485
"""A tool to generate a suite testing multiple workingtree formats at once.
1487
This is done by copying the test once for each transport and injecting
1488
the transport_server, transport_readonly_server, and workingtree_format
1489
classes into each copy. Each copy is also given a new id() to make it
1493
def __init__(self, transport_server, transport_readonly_server, formats):
1494
self._transport_server = transport_server
1495
self._transport_readonly_server = transport_readonly_server
1496
self._formats = formats
1498
def adapt(self, test):
1499
from bzrlib.tests import TestSuite
1500
result = TestSuite()
1501
for workingtree_format, bzrdir_format in self._formats:
1502
new_test = deepcopy(test)
1503
new_test.transport_server = self._transport_server
1504
new_test.transport_readonly_server = self._transport_readonly_server
1505
new_test.bzrdir_format = bzrdir_format
1506
new_test.workingtree_format = workingtree_format
1507
def make_new_test_id():
1508
new_id = "%s(%s)" % (new_test.id(), workingtree_format.__class__.__name__)
1509
return lambda: new_id
1510
new_test.id = make_new_test_id()
1511
result.addTest(new_test)