/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

Update a clean branch with the dirstate improvements.

Show diffs side-by-side

added added

removed removed

Lines of Context:
41
41
 
42
42
from bzrlib.lazy_import import lazy_import
43
43
lazy_import(globals(), """
 
44
from bisect import bisect_left
44
45
import collections
45
46
from copy import deepcopy
46
47
import errno
 
48
import itertools
 
49
import operator
47
50
import stat
48
51
from time import time
49
52
import warnings
52
55
from bzrlib import (
53
56
    bzrdir,
54
57
    conflicts as _mod_conflicts,
 
58
    dirstate,
55
59
    errors,
56
60
    generate_ids,
57
61
    globbing,
59
63
    ignores,
60
64
    merge,
61
65
    osutils,
 
66
    revisiontree,
62
67
    textui,
63
68
    transform,
64
69
    urlutils,
91
96
    )
92
97
from bzrlib.trace import mutter, note
93
98
from bzrlib.transport.local import LocalTransport
94
 
import bzrlib.tree
95
99
from bzrlib.progress import DummyProgress, ProgressPhase
96
100
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
97
 
import bzrlib.revisiontree
98
101
from bzrlib.rio import RioReader, rio_file, Stanza
99
102
from bzrlib.symbol_versioning import (deprecated_passed,
100
103
        deprecated_method,
379
382
            # in the future this should return the tree for
380
383
            # 'empty:' - the implicit root empty tree.
381
384
            return self.branch.repository.revision_tree(None)
382
 
        else:
383
 
            try:
384
 
                xml = self.read_basis_inventory()
385
 
                inv = xml6.serializer_v6.read_inventory_from_string(xml)
386
 
                if inv is not None and inv.revision_id == revision_id:
387
 
                    return bzrlib.revisiontree.RevisionTree(
388
 
                        self.branch.repository, inv, revision_id)
389
 
            except (errors.NoSuchFile, errors.BadInventoryFormat):
390
 
                pass
 
385
        try:
 
386
            return self.revision_tree(revision_id)
 
387
        except errors.NoSuchRevision:
 
388
            pass
391
389
        # No cached copy available, retrieve from the repository.
392
390
        # FIXME? RBC 20060403 should we cache the inventory locally
393
391
        # at this point ?
1671
1669
            resolve(self, filenames, ignore_misses=True)
1672
1670
        return conflicts
1673
1671
 
 
1672
    def revision_tree(self, revision_id):
 
1673
        """See Tree.revision_tree.
 
1674
 
 
1675
        WorkingTree can supply revision_trees for the basis revision only
 
1676
        because there is only one cached inventory in the bzr directory.
 
1677
        """
 
1678
        if revision_id == self.last_revision():
 
1679
            try:
 
1680
                xml = self.read_basis_inventory()
 
1681
            except errors.NoSuchFile:
 
1682
                pass
 
1683
            else:
 
1684
                try:
 
1685
                    inv = xml6.serializer_v6.read_inventory_from_string(xml)
 
1686
                    # dont use the repository revision_tree api because we want
 
1687
                    # to supply the inventory.
 
1688
                    if inv.revision_id == revision_id:
 
1689
                        return revisiontree.RevisionTree(self.branch.repository,
 
1690
                            inv, revision_id)
 
1691
                except errors.BadInventoryFormat:
 
1692
                    pass
 
1693
        # raise if there was no inventory, or if we read the wrong inventory.
 
1694
        raise errors.NoSuchRevisionInTree(self, revision_id)
 
1695
 
1674
1696
    # XXX: This method should be deprecated in favour of taking in a proper
1675
1697
    # new Inventory object.
1676
1698
    @needs_tree_write_lock
1905
1927
                             file_id=self.path2id(conflicted)))
1906
1928
        return conflicts
1907
1929
 
 
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)
 
1937
        try:
 
1938
            current_disk = disk_iterator.next()
 
1939
            disk_finished = False
 
1940
        except OSError, e:
 
1941
            if e.errno != errno.ENOENT:
 
1942
                raise
 
1943
            current_disk = None
 
1944
            disk_finished = True
 
1945
        try:
 
1946
            current_inv = inventory_iterator.next()
 
1947
            inv_finished = False
 
1948
        except StopIteration:
 
1949
            current_inv = None
 
1950
            inv_finished = True
 
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
 
1957
                    # value.
 
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]
 
1962
            if inv_finished:
 
1963
                # everything is unknown
 
1964
                direction = 1
 
1965
            elif disk_finished:
 
1966
                # everything is missing
 
1967
                direction = -1
 
1968
            else:
 
1969
                direction = cmp(current_inv[0][0], current_disk[0][0])
 
1970
            if direction > 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
 
1975
                try:
 
1976
                    current_disk = disk_iterator.next()
 
1977
                except StopIteration:
 
1978
                    disk_finished = True
 
1979
            elif direction < 0:
 
1980
                # inventory is before disk - missing.
 
1981
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
 
1982
                    for relpath, basename, dkind, stat, fileid, kind in 
 
1983
                    current_inv[1]]
 
1984
                yield (current_inv[0][0], current_inv[0][1]), dirblock
 
1985
                try:
 
1986
                    current_inv = inventory_iterator.next()
 
1987
                except StopIteration:
 
1988
                    inv_finished = True
 
1989
            else:
 
1990
                # versioned present directory
 
1991
                # merge the inventory and disk data together
 
1992
                dirblock = []
 
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],
 
2002
                            inv_row[5]))
 
2003
                    elif len(path_elements[0]) == 5:
 
2004
                        # unknown disk file
 
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]))
 
2013
                    else:
 
2014
                        raise NotImplementedError('unreachable code')
 
2015
                yield current_inv[0], dirblock
 
2016
                try:
 
2017
                    current_inv = inventory_iterator.next()
 
2018
                except StopIteration:
 
2019
                    inv_finished = True
 
2020
                try:
 
2021
                    current_disk = disk_iterator.next()
 
2022
                except StopIteration:
 
2023
                    disk_finished = True
 
2024
 
 
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)
 
2030
        if top_id is None:
 
2031
            pending = []
 
2032
        else:
 
2033
            pending = [(prefix, '', _directory, None, top_id, None)]
 
2034
        while pending:
 
2035
            dirblock = []
 
2036
            currentdir = pending.pop()
 
2037
            # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
 
2038
            top_id = currentdir[4]
 
2039
            if currentdir[0]:
 
2040
                relroot = currentdir[0] + '/'
 
2041
            else:
 
2042
                relroot = ""
 
2043
            # FIXME: stash the node in pending
 
2044
            entry = inv[top_id]
 
2045
            for name, child in entry.sorted_children():
 
2046
                dirblock.append((relroot + name, name, child.kind, None,
 
2047
                    child.file_id, child.kind
 
2048
                    ))
 
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:
 
2053
                    pending.append(dir)
 
2054
 
1908
2055
 
1909
2056
class WorkingTree2(WorkingTree):
1910
2057
    """This is the Format 2 working tree.
2010
2157
            self.branch.unlock()
2011
2158
 
2012
2159
 
 
2160
class WorkingTree4(WorkingTree3):
 
2161
    """This is the Format 4 working tree.
 
2162
 
 
2163
    This differs from WorkingTree3 by:
 
2164
     - having a consolidated internal dirstate.
 
2165
 
 
2166
    This is new in bzr TODO FIXME SETMEBEFORE MERGE.
 
2167
    """
 
2168
 
 
2169
    def __init__(self, basedir,
 
2170
                 branch,
 
2171
                 _inventory=None,
 
2172
                 _control_files=None,
 
2173
                 _format=None,
 
2174
                 _bzrdir=None):
 
2175
        """Construct a WorkingTree for basedir.
 
2176
 
 
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).
 
2181
        """
 
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
 
2204
        # given path only.
 
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)
 
2207
        hc.read()
 
2208
        # is this scan needed ? it makes things kinda slow.
 
2209
        #hc.scan()
 
2210
 
 
2211
        if hc.needs_write:
 
2212
            mutter("write hc")
 
2213
            hc.write()
 
2214
 
 
2215
        if _inventory is None:
 
2216
            self._inventory_is_modified = False
 
2217
            self.read_working_inventory()
 
2218
        else:
 
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)
 
2224
        self._dirty = None
 
2225
        self._parent_revisions = None
 
2226
        self._dirstate = None
 
2227
 
 
2228
    def current_dirstate(self):
 
2229
        """Return the current dirstate object. 
 
2230
 
 
2231
        This is not part of the tree interface and only exposed for ease of
 
2232
        testing.
 
2233
 
 
2234
        :raises errors.NotWriteLocked: when not in a lock. 
 
2235
            XXX: This should probably be errors.NotLocked.
 
2236
        """
 
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
 
2245
 
 
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()
 
2250
        self._dirty = True
 
2251
 
 
2252
    @needs_read_lock
 
2253
    def revision_tree(self, revision_id):
 
2254
        """See Tree.revision_tree.
 
2255
 
 
2256
        WorkingTree4 supplies revision_trees for any basis tree.
 
2257
        """
 
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)
 
2263
 
 
2264
    @needs_write_lock
 
2265
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
 
2266
        """Set the parent ids to revision_ids.
 
2267
        
 
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.
 
2273
 
 
2274
        :param revision_ids: The revision_ids to set as the parent ids of this
 
2275
            working tree. Any of these may be ghosts.
 
2276
        """
 
2277
        trees = []
 
2278
        for revision_id in revision_ids:
 
2279
            try:
 
2280
                revtree = self.branch.repository.revision_tree(revision_id)
 
2281
            except errors.NoSuchRevision:
 
2282
                revtree = None
 
2283
            trees.append((revision_id, revtree))
 
2284
        self.set_parent_trees(trees,
 
2285
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
2286
 
 
2287
    @needs_write_lock
 
2288
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
 
2289
        """Set the parents of the working tree.
 
2290
 
 
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.
 
2294
        """
 
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)
 
2299
        real_trees = []
 
2300
        ghosts = []
 
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))
 
2306
            else:
 
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)
 
2311
        self._dirty = True
 
2312
 
 
2313
    def unlock(self):
 
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
 
2319
            # dirstate updates.
 
2320
            if self._control_files._lock_mode == 'w':
 
2321
                if self._dirty:
 
2322
                    self.flush()
 
2323
            self._dirstate = None
 
2324
        # reverse order of locking.
 
2325
        try:
 
2326
            return self._control_files.unlock()
 
2327
        finally:
 
2328
            self.branch.unlock()
 
2329
 
 
2330
    def flush(self):
 
2331
        """Write the full dirstate to disk."""
 
2332
        self.current_dirstate().save()
 
2333
        
 
2334
 
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()
2292
2614
 
2293
2615
 
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.
 
2618
 
 
2619
    This 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.
 
2626
    """
 
2627
 
 
2628
    def get_format_string(self):
 
2629
        """See WorkingTreeFormat.get_format_string()."""
 
2630
        return "Bazaar Working Tree format 4\n"
 
2631
 
 
2632
    def get_format_description(self):
 
2633
        """See WorkingTreeFormat.get_format_description()."""
 
2634
        return "Working tree format 4"
 
2635
 
 
2636
    def initialize(self, a_bzrdir, revision_id=None):
 
2637
        """See WorkingTreeFormat.initialize().
 
2638
        
 
2639
        revision_id allows creating a working tree at a different
 
2640
        revision than the branch is at.
 
2641
        """
 
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()
 
2652
        inv = Inventory()
 
2653
        local_path = transport.local_abspath('dirstate')
 
2654
        dirstate.DirState.initialize(local_path)
 
2655
        wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
 
2656
                         branch,
 
2657
                         inv,
 
2658
                         _format=self,
 
2659
                         _bzrdir=a_bzrdir,
 
2660
                         _control_files=control_files)
 
2661
        wt._new_tree()
 
2662
        wt.lock_write()
 
2663
        try:
 
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)
 
2668
        finally:
 
2669
            control_files.unlock()
 
2670
            wt.unlock()
 
2671
        return wt
 
2672
 
 
2673
 
 
2674
    def _open(self, a_bzrdir, control_files):
 
2675
        """Open the tree itself.
 
2676
        
 
2677
        :param a_bzrdir: the dir for the tree.
 
2678
        :param control_files: the control files for the tree.
 
2679
        """
 
2680
        return WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
 
2681
                           branch=a_bzrdir.open_branch(),
 
2682
                           _format=self,
 
2683
                           _bzrdir=a_bzrdir,
 
2684
                           _control_files=control_files)
 
2685
 
 
2686
 
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(),
2300
2694
                   ]
2301
2695