1905
1927
file_id=self.path2id(conflicted)))
1906
1928
return conflicts
1930
def walkdirs(self, prefix=""):
1931
disk_top = self.abspath(prefix)
1932
if disk_top.endswith('/'):
1933
disk_top = disk_top[:-1]
1934
top_strip_len = len(disk_top) + 1
1935
inventory_iterator = self._walkdirs(prefix)
1936
disk_iterator = osutils.walkdirs(disk_top, prefix)
1938
current_disk = disk_iterator.next()
1939
disk_finished = False
1941
if e.errno != errno.ENOENT:
1944
disk_finished = True
1946
current_inv = inventory_iterator.next()
1947
inv_finished = False
1948
except StopIteration:
1951
while not inv_finished or not disk_finished:
1952
if not disk_finished:
1953
# strip out .bzr dirs
1954
if current_disk[0][1][top_strip_len:] == '':
1955
# osutils.walkdirs can be made nicer -
1956
# yield the path-from-prefix rather than the pathjoined
1958
bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
1959
if current_disk[1][bzrdir_loc][0] == '.bzr':
1960
# we dont yield the contents of, or, .bzr itself.
1961
del current_disk[1][bzrdir_loc]
1963
# everything is unknown
1966
# everything is missing
1969
direction = cmp(current_inv[0][0], current_disk[0][0])
1971
# disk is before inventory - unknown
1972
dirblock = [(relpath, basename, kind, stat, None, None) for
1973
relpath, basename, kind, stat, top_path in current_disk[1]]
1974
yield (current_disk[0][0], None), dirblock
1976
current_disk = disk_iterator.next()
1977
except StopIteration:
1978
disk_finished = True
1980
# inventory is before disk - missing.
1981
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
1982
for relpath, basename, dkind, stat, fileid, kind in
1984
yield (current_inv[0][0], current_inv[0][1]), dirblock
1986
current_inv = inventory_iterator.next()
1987
except StopIteration:
1990
# versioned present directory
1991
# merge the inventory and disk data together
1993
for relpath, subiterator in itertools.groupby(sorted(
1994
current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
1995
path_elements = list(subiterator)
1996
if len(path_elements) == 2:
1997
inv_row, disk_row = path_elements
1998
# versioned, present file
1999
dirblock.append((inv_row[0],
2000
inv_row[1], disk_row[2],
2001
disk_row[3], inv_row[4],
2003
elif len(path_elements[0]) == 5:
2005
dirblock.append((path_elements[0][0],
2006
path_elements[0][1], path_elements[0][2],
2007
path_elements[0][3], None, None))
2008
elif len(path_elements[0]) == 6:
2009
# versioned, absent file.
2010
dirblock.append((path_elements[0][0],
2011
path_elements[0][1], 'unknown', None,
2012
path_elements[0][4], path_elements[0][5]))
2014
raise NotImplementedError('unreachable code')
2015
yield current_inv[0], dirblock
2017
current_inv = inventory_iterator.next()
2018
except StopIteration:
2021
current_disk = disk_iterator.next()
2022
except StopIteration:
2023
disk_finished = True
2025
def _walkdirs(self, prefix=""):
2026
_directory = 'directory'
2027
# get the root in the inventory
2028
inv = self.inventory
2029
top_id = inv.path2id(prefix)
2033
pending = [(prefix, '', _directory, None, top_id, None)]
2036
currentdir = pending.pop()
2037
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2038
top_id = currentdir[4]
2040
relroot = currentdir[0] + '/'
2043
# FIXME: stash the node in pending
2045
for name, child in entry.sorted_children():
2046
dirblock.append((relroot + name, name, child.kind, None,
2047
child.file_id, child.kind
2049
yield (currentdir[0], entry.file_id), dirblock
2050
# push the user specified dirs from dirblock
2051
for dir in reversed(dirblock):
2052
if dir[2] == _directory:
1909
2056
class WorkingTree2(WorkingTree):
1910
2057
"""This is the Format 2 working tree.
2010
2157
self.branch.unlock()
2160
class WorkingTree4(WorkingTree3):
2161
"""This is the Format 4 working tree.
2163
This differs from WorkingTree3 by:
2164
- having a consolidated internal dirstate.
2166
This is new in bzr TODO FIXME SETMEBEFORE MERGE.
2169
def __init__(self, basedir,
2172
_control_files=None,
2175
"""Construct a WorkingTree for basedir.
2177
If the branch is not supplied, it is opened automatically.
2178
If the branch is supplied, it must be the branch for this basedir.
2179
(branch.base is not cross checked, because for remote branches that
2180
would be meaningless).
2182
self._format = _format
2183
self.bzrdir = _bzrdir
2184
from bzrlib.hashcache import HashCache
2185
from bzrlib.trace import note, mutter
2186
assert isinstance(basedir, basestring), \
2187
"base directory %r is not a string" % basedir
2188
basedir = safe_unicode(basedir)
2189
mutter("opening working tree %r", basedir)
2190
self._branch = branch
2191
assert isinstance(self.branch, bzrlib.branch.Branch), \
2192
"branch %r is not a Branch" % self.branch
2193
self.basedir = realpath(basedir)
2194
# if branch is at our basedir and is a format 6 or less
2195
# assume all other formats have their own control files.
2196
assert isinstance(_control_files, LockableFiles), \
2197
"_control_files must be a LockableFiles, not %r" % _control_files
2198
self._control_files = _control_files
2199
# update the whole cache up front and write to disk if anything changed;
2200
# in the future we might want to do this more selectively
2201
# two possible ways offer themselves : in self._unlock, write the cache
2202
# if needed, or, when the cache sees a change, append it to the hash
2203
# cache file, and have the parser take the most recent entry for a
2205
cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
2206
hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
2208
# is this scan needed ? it makes things kinda slow.
2215
if _inventory is None:
2216
self._inventory_is_modified = False
2217
self.read_working_inventory()
2219
# the caller of __init__ has provided an inventory,
2220
# we assume they know what they are doing - as its only
2221
# the Format factory and creation methods that are
2222
# permitted to do this.
2223
self._set_inventory(_inventory, dirty=False)
2225
self._parent_revisions = None
2226
self._dirstate = None
2228
def current_dirstate(self):
2229
"""Return the current dirstate object.
2231
This is not part of the tree interface and only exposed for ease of
2234
:raises errors.NotWriteLocked: when not in a lock.
2235
XXX: This should probably be errors.NotLocked.
2237
if not self._control_files._lock_count:
2238
raise errors.NotWriteLocked(self)
2239
if self._dirstate is not None:
2240
return self._dirstate
2241
local_path = self.bzrdir.get_workingtree_transport(None
2242
).local_abspath('dirstate')
2243
self._dirstate = dirstate.DirState.on_file(local_path)
2244
return self._dirstate
2246
def _new_tree(self):
2247
"""Initialize the state in this tree to be a new tree."""
2248
self._parent_revisions = [NULL_REVISION]
2249
self._inventory = Inventory()
2253
def revision_tree(self, revision_id):
2254
"""See Tree.revision_tree.
2256
WorkingTree4 supplies revision_trees for any basis tree.
2258
dirstate = self.current_dirstate()
2259
parent_ids = dirstate.get_parent_ids()
2260
if revision_id not in parent_ids:
2261
raise errors.NoSuchRevisionInTree(self, revision_id)
2262
raise NotImplementedError(self.revision_tree)
2265
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
2266
"""Set the parent ids to revision_ids.
2268
See also set_parent_trees. This api will try to retrieve the tree data
2269
for each element of revision_ids from the trees repository. If you have
2270
tree data already available, it is more efficient to use
2271
set_parent_trees rather than set_parent_ids. set_parent_ids is however
2272
an easier API to use.
2274
:param revision_ids: The revision_ids to set as the parent ids of this
2275
working tree. Any of these may be ghosts.
2278
for revision_id in revision_ids:
2280
revtree = self.branch.repository.revision_tree(revision_id)
2281
except errors.NoSuchRevision:
2283
trees.append((revision_id, revtree))
2284
self.set_parent_trees(trees,
2285
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
2288
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
2289
"""Set the parents of the working tree.
2291
:param parents_list: A list of (revision_id, tree) tuples.
2292
If tree is None, then that element is treated as an unreachable
2293
parent tree - i.e. a ghost.
2295
dirstate = self.current_dirstate()
2296
if len(parents_list) > 0:
2297
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
2298
raise errors.GhostRevisionUnusableHere(leftmost_id)
2301
# convert absent trees to the null tree, which we convert back to
2302
# missing on access.
2303
for rev_id, tree in parents_list:
2304
if tree is not None:
2305
real_trees.append((rev_id, tree))
2307
real_trees.append((rev_id,
2308
self.branch.repository.revision_tree(None)))
2309
ghosts.append(rev_id)
2310
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
2314
"""Unlock in format 4 trees needs to write the entire dirstate."""
2315
if self._control_files._lock_count == 1:
2316
if self._hashcache.needs_write:
2317
self._hashcache.write()
2318
# eventually we should do signature checking during read locks for
2320
if self._control_files._lock_mode == 'w':
2323
self._dirstate = None
2324
# reverse order of locking.
2326
return self._control_files.unlock()
2328
self.branch.unlock()
2331
"""Write the full dirstate to disk."""
2332
self.current_dirstate().save()
2013
2335
def get_conflicted_stem(path):
2014
2336
for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
2015
2337
if path.endswith(suffix):
2291
2613
return self.get_format_string()
2294
# formats which have no format string are not discoverable
2295
# and not independently creatable, so are not registered.
2616
class WorkingTreeFormat4(WorkingTreeFormat3):
2617
"""The first consolidated dirstate working tree format.
2620
- exists within a metadir controlling .bzr
2621
- includes an explicit version marker for the workingtree control
2622
files, separate from the BzrDir format
2623
- modifies the hash cache format
2624
- is new in bzr TODO FIXME SETBEFOREMERGE
2625
- uses a LockDir to guard access to it.
2628
def get_format_string(self):
2629
"""See WorkingTreeFormat.get_format_string()."""
2630
return "Bazaar Working Tree format 4\n"
2632
def get_format_description(self):
2633
"""See WorkingTreeFormat.get_format_description()."""
2634
return "Working tree format 4"
2636
def initialize(self, a_bzrdir, revision_id=None):
2637
"""See WorkingTreeFormat.initialize().
2639
revision_id allows creating a working tree at a different
2640
revision than the branch is at.
2642
if not isinstance(a_bzrdir.transport, LocalTransport):
2643
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2644
transport = a_bzrdir.get_workingtree_transport(self)
2645
control_files = self._open_control_files(a_bzrdir)
2646
control_files.create_lock()
2647
control_files.lock_write()
2648
control_files.put_utf8('format', self.get_format_string())
2649
branch = a_bzrdir.open_branch()
2650
if revision_id is None:
2651
revision_id = branch.last_revision()
2653
local_path = transport.local_abspath('dirstate')
2654
dirstate.DirState.initialize(local_path)
2655
wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
2660
_control_files=control_files)
2664
wt._write_inventory(inv)
2665
wt.set_root_id(inv.root.file_id)
2666
wt.set_last_revision(revision_id)
2667
build_tree(wt.basis_tree(), wt)
2669
control_files.unlock()
2674
def _open(self, a_bzrdir, control_files):
2675
"""Open the tree itself.
2677
:param a_bzrdir: the dir for the tree.
2678
:param control_files: the control files for the tree.
2680
return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
2681
branch=a_bzrdir.open_branch(),
2684
_control_files=control_files)
2296
2687
__default_format = WorkingTreeFormat3()
2297
2688
WorkingTreeFormat.register_format(__default_format)
2689
WorkingTreeFormat.register_format(WorkingTreeFormat4())
2298
2690
WorkingTreeFormat.set_default_format(__default_format)
2691
# formats which have no format string are not discoverable
2692
# and not independently creatable, so are not registered.
2299
2693
_legacy_formats = [WorkingTreeFormat2(),