1
# Copyright (C) 2005, 2006 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""WorkingTree4 format and implementation.
19
WorkingTree4 provides the dirstate based working tree logic.
21
To get a WorkingTree, call bzrdir.open_workingtree() or
22
WorkingTree.open(dir).
27
from bzrlib.lazy_import import lazy_import
28
lazy_import(globals(), """
29
from bisect import bisect_left
31
from copy import deepcopy
42
conflicts as _mod_conflicts,
58
from bzrlib.transport import get_transport
62
from bzrlib import symbol_versioning
63
from bzrlib.decorators import needs_read_lock, needs_write_lock
64
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
65
from bzrlib.lockable_files import LockableFiles, TransportLock
66
from bzrlib.lockdir import LockDir
67
import bzrlib.mutabletree
68
from bzrlib.mutabletree import needs_tree_write_lock
69
from bzrlib.osutils import (
81
from bzrlib.trace import mutter, note
82
from bzrlib.transport.local import LocalTransport
83
from bzrlib.progress import DummyProgress, ProgressPhase
84
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
85
from bzrlib.rio import RioReader, rio_file, Stanza
86
from bzrlib.symbol_versioning import (deprecated_passed,
94
from bzrlib.tree import Tree
95
from bzrlib.workingtree import WorkingTree3, WorkingTreeFormat3
98
class WorkingTree4(WorkingTree3):
99
"""This is the Format 4 working tree.
101
This differs from WorkingTree3 by:
102
- having a consolidated internal dirstate.
104
This is new in bzr TODO FIXME SETMEBEFORE MERGE.
107
def __init__(self, basedir,
113
"""Construct a WorkingTree for basedir.
115
If the branch is not supplied, it is opened automatically.
116
If the branch is supplied, it must be the branch for this basedir.
117
(branch.base is not cross checked, because for remote branches that
118
would be meaningless).
120
self._format = _format
121
self.bzrdir = _bzrdir
122
from bzrlib.hashcache import HashCache
123
from bzrlib.trace import note, mutter
124
assert isinstance(basedir, basestring), \
125
"base directory %r is not a string" % basedir
126
basedir = safe_unicode(basedir)
127
mutter("opening working tree %r", basedir)
128
self._branch = branch
129
assert isinstance(self.branch, bzrlib.branch.Branch), \
130
"branch %r is not a Branch" % self.branch
131
self.basedir = realpath(basedir)
132
# if branch is at our basedir and is a format 6 or less
133
# assume all other formats have their own control files.
134
assert isinstance(_control_files, LockableFiles), \
135
"_control_files must be a LockableFiles, not %r" % _control_files
136
self._control_files = _control_files
137
# update the whole cache up front and write to disk if anything changed;
138
# in the future we might want to do this more selectively
139
# two possible ways offer themselves : in self._unlock, write the cache
140
# if needed, or, when the cache sees a change, append it to the hash
141
# cache file, and have the parser take the most recent entry for a
143
cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
144
hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
146
# is this scan needed ? it makes things kinda slow.
153
if _inventory is None:
154
self._inventory_is_modified = False
155
self.read_working_inventory()
157
# the caller of __init__ has provided an inventory,
158
# we assume they know what they are doing - as its only
159
# the Format factory and creation methods that are
160
# permitted to do this.
161
self._set_inventory(_inventory, dirty=False)
163
self._parent_revisions = None
164
self._dirstate = None
166
def current_dirstate(self):
167
"""Return the current dirstate object.
169
This is not part of the tree interface and only exposed for ease of
172
:raises errors.NotWriteLocked: when not in a lock.
173
XXX: This should probably be errors.NotLocked.
175
if not self._control_files._lock_count:
176
raise errors.NotWriteLocked(self)
177
if self._dirstate is not None:
178
return self._dirstate
179
local_path = self.bzrdir.get_workingtree_transport(None
180
).local_abspath('dirstate')
181
self._dirstate = dirstate.DirState.on_file(local_path)
182
return self._dirstate
185
"""Initialize the state in this tree to be a new tree."""
186
self._parent_revisions = [NULL_REVISION]
187
self._inventory = Inventory()
191
def revision_tree(self, revision_id):
192
"""See Tree.revision_tree.
194
WorkingTree4 supplies revision_trees for any basis tree.
196
dirstate = self.current_dirstate()
197
parent_ids = dirstate.get_parent_ids()
198
if revision_id not in parent_ids:
199
raise errors.NoSuchRevisionInTree(self, revision_id)
200
return DirStateRevisionTree(dirstate, revision_id,
201
self.branch.repository)
204
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
205
"""Set the parent ids to revision_ids.
207
See also set_parent_trees. This api will try to retrieve the tree data
208
for each element of revision_ids from the trees repository. If you have
209
tree data already available, it is more efficient to use
210
set_parent_trees rather than set_parent_ids. set_parent_ids is however
211
an easier API to use.
213
:param revision_ids: The revision_ids to set as the parent ids of this
214
working tree. Any of these may be ghosts.
217
for revision_id in revision_ids:
219
revtree = self.branch.repository.revision_tree(revision_id)
220
except errors.NoSuchRevision:
222
trees.append((revision_id, revtree))
223
self.set_parent_trees(trees,
224
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
227
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
228
"""Set the parents of the working tree.
230
:param parents_list: A list of (revision_id, tree) tuples.
231
If tree is None, then that element is treated as an unreachable
232
parent tree - i.e. a ghost.
234
dirstate = self.current_dirstate()
235
if len(parents_list) > 0:
236
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
237
raise errors.GhostRevisionUnusableHere(leftmost_id)
240
# convert absent trees to the null tree, which we convert back to
242
for rev_id, tree in parents_list:
244
real_trees.append((rev_id, tree))
246
real_trees.append((rev_id,
247
self.branch.repository.revision_tree(None)))
248
ghosts.append(rev_id)
249
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
253
"""Unlock in format 4 trees needs to write the entire dirstate."""
254
if self._control_files._lock_count == 1:
255
if self._hashcache.needs_write:
256
self._hashcache.write()
257
# eventually we should do signature checking during read locks for
259
if self._control_files._lock_mode == 'w':
262
self._dirstate = None
263
# reverse order of locking.
265
return self._control_files.unlock()
270
"""Write the full dirstate to disk."""
271
self.current_dirstate().save()
274
class WorkingTreeFormat4(WorkingTreeFormat3):
275
"""The first consolidated dirstate working tree format.
278
- exists within a metadir controlling .bzr
279
- includes an explicit version marker for the workingtree control
280
files, separate from the BzrDir format
281
- modifies the hash cache format
282
- is new in bzr TODO FIXME SETBEFOREMERGE
283
- uses a LockDir to guard access to it.
286
def get_format_string(self):
287
"""See WorkingTreeFormat.get_format_string()."""
288
return "Bazaar Working Tree format 4\n"
290
def get_format_description(self):
291
"""See WorkingTreeFormat.get_format_description()."""
292
return "Working tree format 4"
294
def initialize(self, a_bzrdir, revision_id=None):
295
"""See WorkingTreeFormat.initialize().
297
revision_id allows creating a working tree at a different
298
revision than the branch is at.
300
if not isinstance(a_bzrdir.transport, LocalTransport):
301
raise errors.NotLocalUrl(a_bzrdir.transport.base)
302
transport = a_bzrdir.get_workingtree_transport(self)
303
control_files = self._open_control_files(a_bzrdir)
304
control_files.create_lock()
305
control_files.lock_write()
306
control_files.put_utf8('format', self.get_format_string())
307
branch = a_bzrdir.open_branch()
308
if revision_id is None:
309
revision_id = branch.last_revision()
311
local_path = transport.local_abspath('dirstate')
312
dirstate.DirState.initialize(local_path)
313
wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
318
_control_files=control_files)
322
wt._write_inventory(inv)
323
wt.set_root_id(inv.root.file_id)
324
wt.set_last_revision(revision_id)
325
transform.build_tree(wt.basis_tree(), wt)
327
control_files.unlock()
332
def _open(self, a_bzrdir, control_files):
333
"""Open the tree itself.
335
:param a_bzrdir: the dir for the tree.
336
:param control_files: the control files for the tree.
338
return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
339
branch=a_bzrdir.open_branch(),
342
_control_files=control_files)
345
class DirStateRevisionTree(Tree):
346
"""A revision tree pulling the inventory from a dirstate."""
348
def __init__(self, dirstate, revision_id, repository):
349
self._dirstate = dirstate
350
self._revision_id = revision_id
351
self._repository = repository
352
self._inventory = None
355
def _comparison_data(self, entry, path):
356
"""See Tree._comparison_data."""
358
return None, False, None
359
# trust the entry as RevisionTree does, but this may not be
360
# sensible: the entry might not have come from us?
361
return entry.kind, entry.executable, None
363
def _get_inventory(self):
364
if self._inventory is not None:
365
return self._inventory
366
self._generate_inventory()
367
return self._inventory
369
inventory = property(_get_inventory,
370
doc="Inventory of this Tree")
372
def _generate_inventory(self):
373
"""Create and set self.inventory from the dirstate object.
375
This is relatively expensive: we have to walk the entire dirstate.
376
Ideally we would not, and instead would """
377
assert self._locked, 'cannot generate inventory of an unlocked '\
378
'dirstate revision tree'
379
assert self._revision_id in self._dirstate.get_parent_ids(), \
380
'parent %s has disappeared from %s' % (
381
self._revision_id, self._dirstate.get_parent_ids())
383
for line in self._dirstate._iter_rows():
385
self._inventory = inv
387
def get_parent_ids(self):
388
"""The parents of a tree in the dirstate are not cached."""
389
return self._repository.get_revision(self._revision_id).parent_ids
392
"""Lock the tree for a set of operations."""
396
"""Unlock, freeing any cache memory used during the lock."""
397
# outside of a lock, the inventory is suspect: release it.
398
self._inventory = None