~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: John Arbash Meinel
  • Date: 2010-02-17 17:11:16 UTC
  • mfrom: (4797.2.17 2.1)
  • mto: (4797.2.18 2.1)
  • mto: This revision was merged to the branch mainline in revision 5055.
  • Revision ID: john@arbash-meinel.com-20100217171116-h7t9223ystbnx5h8
merge bzr.2.1 in preparation for NEWS entry.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
65
65
    merge,
66
66
    revision as _mod_revision,
67
67
    revisiontree,
68
 
    textui,
69
68
    trace,
70
69
    transform,
71
70
    ui,
544
543
        else:
545
544
            parents = [last_rev]
546
545
        try:
547
 
            merges_file = self._transport.get('pending-merges')
 
546
            merges_bytes = self._transport.get_bytes('pending-merges')
548
547
        except errors.NoSuchFile:
549
548
            pass
550
549
        else:
551
 
            for l in merges_file.readlines():
 
550
            for l in osutils.split_lines(merges_bytes):
552
551
                revision_id = l.rstrip('\n')
553
552
                parents.append(revision_id)
554
553
        return parents
636
635
 
637
636
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
638
637
        file_id = self.path2id(path)
 
638
        if file_id is None:
 
639
            # For unversioned files on win32, we just assume they are not
 
640
            # executable
 
641
            return False
639
642
        return self._inventory[file_id].executable
640
643
 
641
644
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
1624
1627
                                this_tree=self,
1625
1628
                                pb=pb,
1626
1629
                                change_reporter=change_reporter)
1627
 
                    if (basis_tree.inventory.root is None and
1628
 
                        new_basis_tree.inventory.root is not None):
1629
 
                        self.set_root_id(new_basis_tree.get_root_id())
 
1630
                    basis_root_id = basis_tree.get_root_id()
 
1631
                    new_root_id = new_basis_tree.get_root_id()
 
1632
                    if basis_root_id != new_root_id:
 
1633
                        self.set_root_id(new_root_id)
1630
1634
                finally:
1631
1635
                    pb.finished()
1632
1636
                    basis_tree.unlock()
1738
1742
        r"""Check whether the filename matches an ignore pattern.
1739
1743
 
1740
1744
        Patterns containing '/' or '\' need to match the whole path;
1741
 
        others match against only the last component.
 
1745
        others match against only the last component.  Patterns starting
 
1746
        with '!' are ignore exceptions.  Exceptions take precedence
 
1747
        over regular patterns and cause the filename to not be ignored.
1742
1748
 
1743
1749
        If the file is ignored, returns the pattern which caused it to
1744
1750
        be ignored, otherwise None.  So this can simply be used as a
1745
1751
        boolean if desired."""
1746
1752
        if getattr(self, '_ignoreglobster', None) is None:
1747
 
            self._ignoreglobster = globbing.Globster(self.get_ignore_list())
 
1753
            self._ignoreglobster = globbing.ExceptionGlobster(self.get_ignore_list())
1748
1754
        return self._ignoreglobster.match(filename)
1749
1755
 
1750
1756
    def kind(self, file_id):
1841
1847
    def _reset_data(self):
1842
1848
        """Reset transient data that cannot be revalidated."""
1843
1849
        self._inventory_is_modified = False
1844
 
        result = self._deserialize(self._transport.get('inventory'))
 
1850
        f = self._transport.get('inventory')
 
1851
        try:
 
1852
            result = self._deserialize(f)
 
1853
        finally:
 
1854
            f.close()
1845
1855
        self._set_inventory(result, dirty=False)
1846
1856
 
1847
1857
    @needs_tree_write_lock
1923
1933
        # binary.
1924
1934
        if self._inventory_is_modified:
1925
1935
            raise errors.InventoryModified(self)
1926
 
        result = self._deserialize(self._transport.get('inventory'))
 
1936
        f = self._transport.get('inventory')
 
1937
        try:
 
1938
            result = self._deserialize(f)
 
1939
        finally:
 
1940
            f.close()
1927
1941
        self._set_inventory(result, dirty=False)
1928
1942
        return result
1929
1943
 
1944
1958
 
1945
1959
        new_files=set()
1946
1960
        unknown_nested_files=set()
 
1961
        if to_file is None:
 
1962
            to_file = sys.stdout
1947
1963
 
1948
1964
        def recurse_directory_to_add_files(directory):
1949
1965
            # Recurse directory and add all files
2019
2035
                        new_status = 'I'
2020
2036
                    else:
2021
2037
                        new_status = '?'
2022
 
                    textui.show_status(new_status, self.kind(fid), f,
2023
 
                                       to_file=to_file)
 
2038
                    # XXX: Really should be a more abstract reporter interface
 
2039
                    kind_ch = osutils.kind_marker(self.kind(fid))
 
2040
                    to_file.write(new_status + '       ' + f + kind_ch + '\n')
2024
2041
                # Unversion file
2025
2042
                inv_delta.append((f, None, fid, None))
2026
2043
                message = "removed %s" % (f,)
2177
2194
        """
2178
2195
        raise NotImplementedError(self.unlock)
2179
2196
 
2180
 
    def update(self, change_reporter=None, possible_transports=None):
 
2197
    _marker = object()
 
2198
 
 
2199
    def update(self, change_reporter=None, possible_transports=None,
 
2200
               revision=None, old_tip=_marker):
2181
2201
        """Update a working tree along its branch.
2182
2202
 
2183
2203
        This will update the branch if its bound too, which means we have
2201
2221
        - Merge current state -> basis tree of the master w.r.t. the old tree
2202
2222
          basis.
2203
2223
        - Do a 'normal' merge of the old branch basis if it is relevant.
 
2224
 
 
2225
        :param revision: The target revision to update to. Must be in the
 
2226
            revision history.
 
2227
        :param old_tip: If branch.update() has already been run, the value it
 
2228
            returned (old tip of the branch or None). _marker is used
 
2229
            otherwise.
2204
2230
        """
2205
2231
        if self.branch.get_bound_location() is not None:
2206
2232
            self.lock_write()
2207
 
            update_branch = True
 
2233
            update_branch = (old_tip is self._marker)
2208
2234
        else:
2209
2235
            self.lock_tree_write()
2210
2236
            update_branch = False
2212
2238
            if update_branch:
2213
2239
                old_tip = self.branch.update(possible_transports)
2214
2240
            else:
2215
 
                old_tip = None
2216
 
            return self._update_tree(old_tip, change_reporter)
 
2241
                if old_tip is self._marker:
 
2242
                    old_tip = None
 
2243
            return self._update_tree(old_tip, change_reporter, revision)
2217
2244
        finally:
2218
2245
            self.unlock()
2219
2246
 
2220
2247
    @needs_tree_write_lock
2221
 
    def _update_tree(self, old_tip=None, change_reporter=None):
 
2248
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None):
2222
2249
        """Update a tree to the master branch.
2223
2250
 
2224
2251
        :param old_tip: if supplied, the previous tip revision the branch,
2239
2266
            last_rev = self.get_parent_ids()[0]
2240
2267
        except IndexError:
2241
2268
            last_rev = _mod_revision.NULL_REVISION
2242
 
        if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
2243
 
            # merge tree state up to new branch tip.
 
2269
        if revision is None:
 
2270
            revision = self.branch.last_revision()
 
2271
        else:
 
2272
            if revision not in self.branch.revision_history():
 
2273
                raise errors.NoSuchRevision(self.branch, revision)
 
2274
        if last_rev != _mod_revision.ensure_null(revision):
 
2275
            # merge tree state up to specified revision.
2244
2276
            basis = self.basis_tree()
2245
2277
            basis.lock_read()
2246
2278
            try:
2247
 
                to_tree = self.branch.basis_tree()
2248
 
                if basis.inventory.root is None:
2249
 
                    self.set_root_id(to_tree.get_root_id())
 
2279
                to_tree = self.branch.repository.revision_tree(revision)
 
2280
                to_root_id = to_tree.get_root_id()
 
2281
                if (basis.inventory.root is None
 
2282
                    or basis.inventory.root.file_id != to_root_id):
 
2283
                    self.set_root_id(to_root_id)
2250
2284
                    self.flush()
2251
2285
                result += merge.merge_inner(
2252
2286
                                      self.branch,
2254
2288
                                      basis,
2255
2289
                                      this_tree=self,
2256
2290
                                      change_reporter=change_reporter)
 
2291
                self.set_last_revision(revision)
2257
2292
            finally:
2258
2293
                basis.unlock()
2259
2294
            # TODO - dedup parents list with things merged by pull ?
2260
2295
            # reuse the tree we've updated to to set the basis:
2261
 
            parent_trees = [(self.branch.last_revision(), to_tree)]
 
2296
            parent_trees = [(revision, to_tree)]
2262
2297
            merges = self.get_parent_ids()[1:]
2263
2298
            # Ideally we ask the tree for the trees here, that way the working
2264
2299
            # tree can decide whether to give us the entire tree or give us a
2294
2329
            #       should be able to remove this extra flush.
2295
2330
            self.flush()
2296
2331
            graph = self.branch.repository.get_graph()
2297
 
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2298
 
                                                old_tip)
 
2332
            base_rev_id = graph.find_unique_lca(revision, old_tip)
2299
2333
            base_tree = self.branch.repository.revision_tree(base_rev_id)
2300
2334
            other_tree = self.branch.repository.revision_tree(old_tip)
2301
2335
            result += merge.merge_inner(
2580
2614
        """
2581
2615
        return
2582
2616
 
2583
 
    @needs_read_lock
2584
2617
    def _get_rules_searcher(self, default_searcher):
2585
2618
        """See Tree._get_rules_searcher."""
2586
2619
        if self._rules_searcher is None:
2765
2798
        """Return the format for the working tree object in a_bzrdir."""
2766
2799
        try:
2767
2800
            transport = a_bzrdir.get_workingtree_transport(None)
2768
 
            format_string = transport.get("format").read()
 
2801
            format_string = transport.get_bytes("format")
2769
2802
            return klass._formats[format_string]
2770
2803
        except errors.NoSuchFile:
2771
2804
            raise errors.NoWorkingTree(base=transport.base)