186
201
(branch.base is not cross checked, because for remote branches that
187
202
would be meaningless).
204
self._format = _format
205
self.bzrdir = _bzrdir
207
# created via open etc.
208
wt = WorkingTree.open(basedir)
209
self.branch = wt.branch
210
self.basedir = wt.basedir
211
self._control_files = wt._control_files
212
self._hashcache = wt._hashcache
213
self._set_inventory(wt._inventory)
214
self._format = wt._format
215
self.bzrdir = wt.bzrdir
189
216
from bzrlib.hashcache import HashCache
190
217
from bzrlib.trace import note, mutter
191
218
assert isinstance(basedir, basestring), \
192
219
"base directory %r is not a string" % basedir
220
basedir = safe_unicode(basedir)
221
mutter("openeing working tree %r", basedir)
193
222
if branch is None:
194
223
branch = Branch.open(basedir)
195
224
assert isinstance(branch, Branch), \
196
225
"branch %r is not a Branch" % branch
197
226
self.branch = branch
198
227
self.basedir = realpath(basedir)
228
# if branch is at our basedir and is a format 6 or less
229
if isinstance(self._format, WorkingTreeFormat2):
230
# share control object
231
self._control_files = self.branch.control_files
232
elif _control_files is not None:
233
assert False, "not done yet"
234
# self._control_files = _control_files
236
# only ready for format 3
237
assert isinstance(self._format, WorkingTreeFormat3)
238
self._control_files = LockableFiles(
239
self.bzrdir.get_workingtree_transport(None),
200
242
# update the whole cache up front and write to disk if anything changed;
201
243
# in the future we might want to do this more selectively
269
316
def abspath(self, filename):
270
317
return pathjoin(self.basedir, filename)
319
def basis_tree(self):
320
"""Return RevisionTree for the current last revision."""
321
revision_id = self.last_revision()
322
if revision_id is not None:
324
xml = self.read_basis_inventory(revision_id)
325
inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
326
return bzrlib.tree.RevisionTree(self.branch.repository, inv,
330
return self.branch.repository.revision_tree(revision_id)
333
@deprecated_method(zero_eight)
334
def create(branch, directory):
335
"""Create a workingtree for branch at directory.
337
If existing_directory already exists it must have a .bzr directory.
338
If it does not exist, it will be created.
340
This returns a new WorkingTree object for the new checkout.
342
TODO FIXME RBC 20060124 when we have checkout formats in place this
343
should accept an optional revisionid to checkout [and reject this if
344
checking out into the same dir as a pre-checkout-aware branch format.]
346
XXX: When BzrDir is present, these should be created through that
349
warn('delete WorkingTree.create', stacklevel=3)
350
transport = get_transport(directory)
351
if branch.bzrdir.root_transport.base == transport.base:
353
return branch.bzrdir.create_workingtree()
354
# different directory,
355
# create a branch reference
356
# and now a working tree.
357
raise NotImplementedError
360
@deprecated_method(zero_eight)
361
def create_standalone(directory):
362
"""Create a checkout and a branch and a repo at directory.
364
Directory must exist and be empty.
366
please use BzrDir.create_standalone_workingtree
368
return bzrdir.BzrDir.create_standalone_workingtree(directory)
272
370
def relpath(self, abs):
273
371
"""Return the local path portion from a given absolute path."""
291
389
## XXX: badly named; this is not in the store at all
292
390
return self.abspath(self.id2path(file_id))
393
def clone(self, to_bzrdir, revision_id=None, basis=None):
394
"""Duplicate this working tree into to_bzr, including all state.
396
Specifically modified files are kept as modified, but
397
ignored and unknown files are discarded.
399
If you want to make a new line of development, see bzrdir.sprout()
402
If not None, the cloned tree will have its last revision set to
403
revision, and and difference between the source trees last revision
404
and this one merged in.
407
If not None, a closer copy of a tree which may have some files in
408
common, and which file content should be preferentially copied from.
410
# assumes the target bzr dir format is compatible.
411
result = self._format.initialize(to_bzrdir)
412
self.copy_content_into(result, revision_id)
416
def copy_content_into(self, tree, revision_id=None):
417
"""Copy the current content and user files of this tree into tree."""
418
if revision_id is None:
419
transform_tree(tree, self)
421
# TODO now merge from tree.last_revision to revision
422
transform_tree(tree, self)
423
tree.set_last_revision(revision_id)
294
425
@needs_write_lock
295
def commit(self, *args, **kw):
426
def commit(self, *args, **kwargs):
296
427
from bzrlib.commit import Commit
297
Commit().commit(self.branch, *args, **kw)
428
# args for wt.commit start at message from the Commit.commit method,
429
# but with branch a kwarg now, passing in args as is results in the
430
#message being used for the branch
431
args = (DEPRECATED_PARAMETER, ) + args
432
Commit().commit(working_tree=self, *args, **kwargs)
298
433
self._set_inventory(self.read_working_inventory())
300
435
def id2abspath(self, file_id):
776
919
def kind(self, file_id):
777
920
return file_kind(self.id2abspath(file_id))
922
def last_revision(self):
923
"""Return the last revision id of this working tree.
925
In early branch formats this was == the branch last_revision,
926
but that cannot be relied upon - for working tree operations,
927
always use tree.last_revision().
929
return self.branch.last_revision()
779
931
def lock_read(self):
780
932
"""See Branch.lock_read, and WorkingTree.unlock."""
781
return self.branch.lock_read()
933
self.branch.lock_read()
935
return self._control_files.lock_read()
783
940
def lock_write(self):
784
941
"""See Branch.lock_write, and WorkingTree.unlock."""
785
return self.branch.lock_write()
942
self.branch.lock_write()
944
return self._control_files.lock_write()
787
949
def _basis_inventory_name(self, revision_id):
788
950
return 'basis-inventory.%s' % revision_id
790
952
def set_last_revision(self, new_revision, old_revision=None):
953
if old_revision is not None:
793
955
path = self._basis_inventory_name(old_revision)
794
path = self.branch._rel_controlfilename(path)
795
self.branch._transport.delete(path)
956
path = self._control_files._escape(path)
957
self._control_files._transport.delete(path)
799
xml = self.branch.get_inventory_xml(new_revision)
960
if new_revision is None:
961
self.branch.set_revision_history([])
963
# current format is locked in with the branch
964
revision_history = self.branch.revision_history()
966
position = revision_history.index(new_revision)
968
raise errors.NoSuchRevision(self.branch, new_revision)
969
self.branch.set_revision_history(revision_history[:position + 1])
971
xml = self.branch.repository.get_inventory_xml(new_revision)
800
972
path = self._basis_inventory_name(new_revision)
801
self.branch.put_controlfile(path, xml)
973
self._control_files.put_utf8(path, xml)
802
974
except WeaveRevisionNotPresent:
805
977
def read_basis_inventory(self, revision_id):
806
978
"""Read the cached basis inventory."""
807
979
path = self._basis_inventory_name(revision_id)
808
return self.branch.controlfile(path, 'r').read()
980
return self._control_files.get_utf8(path).read()
811
983
def read_working_inventory(self):
812
984
"""Read the working inventory."""
813
985
# ElementTree does its own conversion from UTF-8, so open in
815
f = self.branch.controlfile('inventory', 'rb')
816
return bzrlib.xml5.serializer_v5.read_inventory(f)
987
result = bzrlib.xml5.serializer_v5.read_inventory(
988
self._control_files.get('inventory'))
989
self._set_inventory(result)
818
992
@needs_write_lock
819
993
def remove(self, files, verbose=False):
914
1089
between multiple working trees, i.e. via shared storage, then we
915
1090
would probably want to lock both the local tree, and the branch.
917
if self._hashcache.needs_write:
1092
# FIXME: We want to write out the hashcache only when the last lock on
1093
# this working copy is released. Peeking at the lock count is a bit
1094
# of a nasty hack; probably it's better to have a transaction object,
1095
# which can do some finalization when it's either successfully or
1096
# unsuccessfully completed. (Denys's original patch did that.)
1097
# RBC 20060206 hookinhg into transaction will couple lock and transaction
1098
# wrongly. Hookinh into unllock on the control files object is fine though.
1100
# TODO: split this per format so there is no ugly if block
1101
if self._hashcache.needs_write and (
1102
self._control_files._lock_count==1 or
1103
(self._control_files is self.branch.control_files and
1104
self._control_files._lock_count==2)):
918
1105
self._hashcache.write()
919
return self.branch.unlock()
1106
# reverse order of locking.
1107
result = self._control_files.unlock()
1109
self.branch.unlock()
921
1113
@needs_write_lock
922
1114
def _write_inventory(self, inv):
923
1115
"""Write inventory as the current inventory."""
924
from cStringIO import StringIO
925
from bzrlib.atomicfile import AtomicFile
926
1116
sio = StringIO()
927
1117
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
929
f = AtomicFile(self.branch.controlfilename('inventory'))
1119
self._control_files.put('inventory', sio)
935
1120
self._set_inventory(inv)
936
1121
mutter('wrote working inventory')
941
1126
for suffix in CONFLICT_SUFFIXES:
942
1127
if path.endswith(suffix):
943
1128
return path[:-len(suffix)]
1130
def is_control_file(filename):
1131
## FIXME: better check
1132
filename = normpath(filename)
1133
while filename != '':
1134
head, tail = os.path.split(filename)
1135
## mutter('check %r for control file' % ((head, tail),))
1136
if tail == bzrlib.BZRDIR:
1138
if filename == head:
1144
class WorkingTreeFormat(object):
1145
"""An encapsulation of the initialization and open routines for a format.
1147
Formats provide three things:
1148
* An initialization routine,
1152
Formats are placed in an dict by their format string for reference
1153
during workingtree opening. Its not required that these be instances, they
1154
can be classes themselves with class methods - it simply depends on
1155
whether state is needed for a given format or not.
1157
Once a format is deprecated, just deprecate the initialize and open
1158
methods on the format class. Do not deprecate the object, as the
1159
object will be created every time regardless.
1162
_default_format = None
1163
"""The default format used for new trees."""
1166
"""The known formats."""
1169
def find_format(klass, a_bzrdir):
1170
"""Return the format for the working tree object in a_bzrdir."""
1172
transport = a_bzrdir.get_workingtree_transport(None)
1173
format_string = transport.get("format").read()
1174
return klass._formats[format_string]
1176
raise errors.NotBranchError(path=transport.base)
1178
raise errors.UnknownFormatError(format_string)
1181
def get_default_format(klass):
1182
"""Return the current default format."""
1183
return klass._default_format
1185
def get_format_string(self):
1186
"""Return the ASCII format string that identifies this format."""
1187
raise NotImplementedError(self.get_format_string)
1189
def is_supported(self):
1190
"""Is this format supported?
1192
Supported formats can be initialized and opened.
1193
Unsupported formats may not support initialization or committing or
1194
some other features depending on the reason for not being supported.
1199
def register_format(klass, format):
1200
klass._formats[format.get_format_string()] = format
1203
def set_default_format(klass, format):
1204
klass._default_format = format
1207
def unregister_format(klass, format):
1208
assert klass._formats[format.get_format_string()] is format
1209
del klass._formats[format.get_format_string()]
1213
class WorkingTreeFormat2(WorkingTreeFormat):
1214
"""The second working tree format.
1216
This format modified the hash cache from the format 1 hash cache.
1219
def initialize(self, a_bzrdir):
1220
"""See WorkingTreeFormat.initialize()."""
1221
if not isinstance(a_bzrdir.transport, LocalTransport):
1222
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1223
branch = a_bzrdir.open_branch()
1224
revision = branch.last_revision()
1225
basis_tree = branch.repository.revision_tree(revision)
1226
inv = basis_tree.inventory
1227
wt = WorkingTree(a_bzrdir.root_transport.base,
1233
wt._write_inventory(inv)
1234
wt.set_root_id(inv.root.file_id)
1235
wt.set_last_revision(revision)
1236
wt.set_pending_merges([])
1241
super(WorkingTreeFormat2, self).__init__()
1242
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1244
def open(self, a_bzrdir, _found=False):
1245
"""Return the WorkingTree object for a_bzrdir
1247
_found is a private parameter, do not use it. It is used to indicate
1248
if format probing has already been done.
1251
# we are being called directly and must probe.
1252
raise NotImplementedError
1253
if not isinstance(a_bzrdir.transport, LocalTransport):
1254
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1255
return WorkingTree(a_bzrdir.root_transport.base,
1261
class WorkingTreeFormat3(WorkingTreeFormat):
1262
"""The second working tree format updated to record a format marker.
1264
This format modified the hash cache from the format 1 hash cache.
1267
def get_format_string(self):
1268
"""See WorkingTreeFormat.get_format_string()."""
1269
return "Bazaar-NG Working Tree format 3"
1271
def initialize(self, a_bzrdir):
1272
"""See WorkingTreeFormat.initialize()."""
1273
if not isinstance(a_bzrdir.transport, LocalTransport):
1274
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1275
transport = a_bzrdir.get_workingtree_transport(self)
1276
control_files = LockableFiles(transport, 'lock')
1277
control_files.put_utf8('format', self.get_format_string())
1278
branch = a_bzrdir.open_branch()
1279
revision = branch.last_revision()
1280
basis_tree = branch.repository.revision_tree(revision)
1281
inv = basis_tree.inventory
1282
wt = WorkingTree(a_bzrdir.root_transport.base,
1288
wt._write_inventory(inv)
1289
wt.set_root_id(inv.root.file_id)
1290
wt.set_last_revision(revision)
1291
wt.set_pending_merges([])
1296
super(WorkingTreeFormat3, self).__init__()
1297
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1299
def open(self, a_bzrdir, _found=False):
1300
"""Return the WorkingTree object for a_bzrdir
1302
_found is a private parameter, do not use it. It is used to indicate
1303
if format probing has already been done.
1306
# we are being called directly and must probe.
1307
raise NotImplementedError
1308
if not isinstance(a_bzrdir.transport, LocalTransport):
1309
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1310
return WorkingTree(a_bzrdir.root_transport.base,
1316
# formats which have no format string are not discoverable
1317
# and not independently creatable, so are not registered.
1318
__default_format = WorkingTreeFormat3()
1319
WorkingTreeFormat.register_format(__default_format)
1320
WorkingTreeFormat.set_default_format(__default_format)
1321
_legacy_formats = [WorkingTreeFormat2(),
1325
class WorkingTreeTestProviderAdapter(object):
1326
"""A tool to generate a suite testing multiple workingtree formats at once.
1328
This is done by copying the test once for each transport and injecting
1329
the transport_server, transport_readonly_server, and workingtree_format
1330
classes into each copy. Each copy is also given a new id() to make it
1334
def __init__(self, transport_server, transport_readonly_server, formats):
1335
self._transport_server = transport_server
1336
self._transport_readonly_server = transport_readonly_server
1337
self._formats = formats
1339
def adapt(self, test):
1340
from bzrlib.tests import TestSuite
1341
result = TestSuite()
1342
for workingtree_format, bzrdir_format in self._formats:
1343
new_test = deepcopy(test)
1344
new_test.transport_server = self._transport_server
1345
new_test.transport_readonly_server = self._transport_readonly_server
1346
new_test.bzrdir_format = bzrdir_format
1347
new_test.workingtree_format = workingtree_format
1348
def make_new_test_id():
1349
new_id = "%s(%s)" % (new_test.id(), workingtree_format.__class__.__name__)
1350
return lambda: new_id
1351
new_test.id = make_new_test_id()
1352
result.addTest(new_test)