25
25
At the moment every WorkingTree has its own branch. Remote
26
26
WorkingTrees aren't supported.
28
To get a WorkingTree, call Branch.working_tree():
28
To get a WorkingTree, call WorkingTree(dir[, branch])
32
# TODO: Don't allow WorkingTrees to be constructed for remote branches if
35
32
# FIXME: I don't know if writing out the cache from the destructor is really a
36
33
# good idea, because destructors are considered poor taste in Python, and it's
37
34
# not predictable when it will be written out.
43
40
# At the momenthey may alias the inventory and have old copies of it in memory.
45
42
from copy import deepcopy
43
from cStringIO import StringIO
50
from bzrlib.atomicfile import AtomicFile
50
51
from bzrlib.branch import (Branch,
57
from bzrlib.decorators import needs_read_lock, needs_write_lock
53
58
from bzrlib.errors import (BzrCheckError,
82
from bzrlib.symbol_versioning import *
75
83
from bzrlib.textui import show_status
77
85
from bzrlib.trace import mutter
86
from bzrlib.transport import get_transport
79
from bzrlib.decorators import needs_read_lock, needs_write_lock
82
90
def gen_file_id(name):
178
186
not listed in the Inventory and vice versa.
181
def __init__(self, basedir=u'.', branch=None):
189
def __init__(self, basedir='.', branch=None, _inventory=None, _control_files=None):
182
190
"""Construct a WorkingTree for basedir.
184
192
If the branch is not supplied, it is opened automatically.
190
198
from bzrlib.trace import note, mutter
191
199
assert isinstance(basedir, basestring), \
192
200
"base directory %r is not a string" % basedir
201
basedir = safe_unicode(basedir)
202
mutter("openeing working tree %r", basedir)
193
203
if branch is None:
194
204
branch = Branch.open(basedir)
195
205
assert isinstance(branch, Branch), \
196
206
"branch %r is not a Branch" % branch
197
207
self.branch = branch
198
208
self.basedir = realpath(basedir)
209
# 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]):
214
self._control_files = self.branch.control_files
215
elif _control_files is not None:
216
assert False, "not done yet"
217
# self._control_files = _control_files
219
self._control_files = LockableFiles(
220
get_transport(self.basedir).clone(bzrlib.BZRDIR), 'branch-lock')
200
222
# update the whole cache up front and write to disk if anything changed;
201
223
# in the future we might want to do this more selectively
211
233
mutter("write hc")
214
self._set_inventory(self.read_working_inventory())
236
if _inventory is None:
237
self._set_inventory(self.read_working_inventory())
239
self._set_inventory(_inventory)
216
241
def _set_inventory(self, inv):
217
242
self._inventory = inv
270
295
def abspath(self, filename):
271
296
return pathjoin(self.basedir, filename)
299
def create(branch, directory):
300
"""Create a workingtree for branch at directory.
302
If existing_directory already exists it must have a .bzr directory.
303
If it does not exist, it will be created.
305
This returns a new WorkingTree object for the new checkout.
307
TODO FIXME RBC 20060124 when we have checkout formats in place this
308
should accept an optional revisionid to checkout [and reject this if
309
checking out into the same dir as a pre-checkout-aware branch format.]
311
XXX: When BzrDir is present, these should be created through that
317
if e.errno != errno.EEXIST:
320
os.mkdir(pathjoin(directory, '.bzr'))
322
if e.errno != errno.EEXIST:
324
inv = branch.repository.revision_tree(branch.last_revision()).inventory
325
wt = WorkingTree(directory, branch, inv)
326
wt._write_inventory(inv)
327
if branch.last_revision() is not None:
328
wt.set_last_revision(branch.last_revision())
329
wt.set_pending_merges([])
334
def create_standalone(directory):
335
"""Create a checkout and a branch at directory.
337
Directory must exist and be empty.
339
XXX: When BzrDir is present, these should be created through that
342
directory = safe_unicode(directory)
343
b = Branch.create(directory)
344
return WorkingTree.create(b, directory)
273
346
def relpath(self, abs):
274
347
"""Return the local path portion from a given absolute path."""
275
348
return relpath(self.basedir, abs)
293
366
return self.abspath(self.id2path(file_id))
295
368
@needs_write_lock
296
def commit(self, *args, **kw):
369
def commit(self, *args, **kwargs):
297
370
from bzrlib.commit import Commit
298
Commit().commit(self.branch, *args, **kw)
371
# args for wt.commit start at message from the Commit.commit method,
372
# but with branch a kwarg now, passing in args as is results in the
373
#message being used for the branch
374
args = (DEPRECATED_PARAMETER, ) + args
375
Commit().commit(working_tree=self, *args, **kwargs)
299
376
self._set_inventory(self.read_working_inventory())
301
378
def id2abspath(self, file_id):
419
496
directory but not yet committed.
422
f = self.branch.control_files.get_utf8('pending-merges')
499
merges_file = self._control_files.get_utf8('pending-merges')
501
if e.errno != errno.ENOENT:
426
for l in f.readlines():
505
for l in merges_file.readlines():
427
506
p.append(l.rstrip('\n'))
430
509
@needs_write_lock
431
510
def set_pending_merges(self, rev_list):
432
self.branch.control_files.put_utf8('pending-merges', '\n'.join(rev_list))
511
self._control_files.put_utf8('pending-merges', '\n'.join(rev_list))
434
513
def get_symlink_target(self, file_id):
435
514
return os.readlink(self.id2abspath(file_id))
681
760
repository = self.branch.repository
682
761
merge_inner(self.branch,
683
762
self.branch.basis_tree(),
684
repository.revision_tree(other_revision))
763
repository.revision_tree(other_revision),
765
self.set_last_revision(self.branch.last_revision())
816
897
"""Read the working inventory."""
817
898
# ElementTree does its own conversion from UTF-8, so open in
819
f = self.branch.control_files.get('inventory')
820
return bzrlib.xml5.serializer_v5.read_inventory(f)
900
return bzrlib.xml5.serializer_v5.read_inventory(
901
self._control_files.get('inventory'))
822
903
@needs_write_lock
823
904
def remove(self, files, verbose=False):
868
949
merge_inner(self.branch, old_tree,
869
950
self, ignore_zero=True,
870
951
backup_files=backups,
871
interesting_files=filenames)
952
interesting_files=filenames,
872
954
if not len(filenames):
873
955
self.set_pending_merges([])
930
1012
@needs_write_lock
931
1013
def _write_inventory(self, inv):
932
1014
"""Write inventory as the current inventory."""
933
from cStringIO import StringIO
934
from bzrlib.atomicfile import AtomicFile
935
1015
sio = StringIO()
936
1016
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
938
f = AtomicFile(self.branch.control_files.controlfilename('inventory'))
1018
self._control_files.put('inventory', sio)
944
1019
self._set_inventory(inv)
945
1020
mutter('wrote working inventory')