~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Ian Clatworthy
  • Date: 2010-02-19 03:02:07 UTC
  • mto: (4797.23.1 integration-2.1)
  • mto: This revision was merged to the branch mainline in revision 5055.
  • Revision ID: ian.clatworthy@canonical.com-20100219030207-zpbzx021zavx4sqt
What's New in 2.1 - a summary of changes since 2.0

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,
281
280
        self._control_files.break_lock()
282
281
        self.branch.break_lock()
283
282
 
 
283
    def _get_check_refs(self):
 
284
        """Return the references needed to perform a check of this tree.
 
285
        
 
286
        The default implementation returns no refs, and is only suitable for
 
287
        trees that have no local caching and can commit on ghosts at any time.
 
288
 
 
289
        :seealso: bzrlib.check for details about check_refs.
 
290
        """
 
291
        return []
 
292
 
284
293
    def requires_rich_root(self):
285
294
        return self._format.requires_rich_root
286
295
 
534
543
        else:
535
544
            parents = [last_rev]
536
545
        try:
537
 
            merges_file = self._transport.get('pending-merges')
 
546
            merges_bytes = self._transport.get_bytes('pending-merges')
538
547
        except errors.NoSuchFile:
539
548
            pass
540
549
        else:
541
 
            for l in merges_file.readlines():
 
550
            for l in osutils.split_lines(merges_bytes):
542
551
                revision_id = l.rstrip('\n')
543
552
                parents.append(revision_id)
544
553
        return parents
603
612
 
604
613
    def get_file_size(self, file_id):
605
614
        """See Tree.get_file_size"""
 
615
        # XXX: this returns the on-disk size; it should probably return the
 
616
        # canonical size
606
617
        try:
607
618
            return os.path.getsize(self.id2abspath(file_id))
608
619
        except OSError, e:
624
635
 
625
636
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
626
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
627
642
        return self._inventory[file_id].executable
628
643
 
629
644
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
739
754
            raise
740
755
        kind = _mapper(stat_result.st_mode)
741
756
        if kind == 'file':
742
 
            size = stat_result.st_size
743
 
            # try for a stat cache lookup
744
 
            executable = self._is_executable_from_path_and_stat(path, stat_result)
745
 
            return (kind, size, executable, self._sha_from_stat(
746
 
                path, stat_result))
 
757
            return self._file_content_summary(path, stat_result)
747
758
        elif kind == 'directory':
748
759
            # perhaps it looks like a plain directory, but it's really a
749
760
            # reference.
756
767
        else:
757
768
            return (kind, None, None, None)
758
769
 
 
770
    def _file_content_summary(self, path, stat_result):
 
771
        size = stat_result.st_size
 
772
        executable = self._is_executable_from_path_and_stat(path, stat_result)
 
773
        # try for a stat cache lookup
 
774
        return ('file', size, executable, self._sha_from_stat(
 
775
            path, stat_result))
 
776
 
759
777
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
760
778
        """Common ghost checking functionality from set_parent_*.
761
779
 
881
899
 
882
900
    @needs_write_lock # because merge pulls data into the branch.
883
901
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
884
 
        merge_type=None):
 
902
                          merge_type=None, force=False):
885
903
        """Merge from a branch into this working tree.
886
904
 
887
905
        :param branch: The branch to merge from.
896
914
            merger = Merger(self.branch, this_tree=self, pb=pb)
897
915
            merger.pp = ProgressPhase("Merge phase", 5, pb)
898
916
            merger.pp.next_phase()
899
 
            # check that there are no
900
 
            # local alterations
901
 
            merger.check_basis(check_clean=True, require_commits=False)
 
917
            # check that there are no local alterations
 
918
            if not force and self.has_changes():
 
919
                raise errors.UncommittedChanges(self)
902
920
            if to_revision is None:
903
921
                to_revision = _mod_revision.ensure_null(branch.last_revision())
904
922
            merger.other_rev_id = to_revision
1609
1627
                                this_tree=self,
1610
1628
                                pb=pb,
1611
1629
                                change_reporter=change_reporter)
1612
 
                    if (basis_tree.inventory.root is None and
1613
 
                        new_basis_tree.inventory.root is not None):
1614
 
                        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)
1615
1634
                finally:
1616
1635
                    pb.finished()
1617
1636
                    basis_tree.unlock()
1723
1742
        r"""Check whether the filename matches an ignore pattern.
1724
1743
 
1725
1744
        Patterns containing '/' or '\' need to match the whole path;
1726
 
        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.
1727
1748
 
1728
1749
        If the file is ignored, returns the pattern which caused it to
1729
1750
        be ignored, otherwise None.  So this can simply be used as a
1730
1751
        boolean if desired."""
1731
1752
        if getattr(self, '_ignoreglobster', None) is None:
1732
 
            self._ignoreglobster = globbing.Globster(self.get_ignore_list())
 
1753
            self._ignoreglobster = globbing.ExceptionGlobster(self.get_ignore_list())
1733
1754
        return self._ignoreglobster.match(filename)
1734
1755
 
1735
1756
    def kind(self, file_id):
1826
1847
    def _reset_data(self):
1827
1848
        """Reset transient data that cannot be revalidated."""
1828
1849
        self._inventory_is_modified = False
1829
 
        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()
1830
1855
        self._set_inventory(result, dirty=False)
1831
1856
 
1832
1857
    @needs_tree_write_lock
1883
1908
            firstline = xml.split('\n', 1)[0]
1884
1909
            if (not 'revision_id="' in firstline or
1885
1910
                'format="7"' not in firstline):
1886
 
                inv = self.branch.repository.deserialise_inventory(
1887
 
                    new_revision, xml)
 
1911
                inv = self.branch.repository._serializer.read_inventory_from_string(
 
1912
                    xml, new_revision)
1888
1913
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
1889
1914
            self._write_basis_inventory(xml)
1890
1915
        except (errors.NoSuchRevision, errors.RevisionNotPresent):
1908
1933
        # binary.
1909
1934
        if self._inventory_is_modified:
1910
1935
            raise errors.InventoryModified(self)
1911
 
        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()
1912
1941
        self._set_inventory(result, dirty=False)
1913
1942
        return result
1914
1943
 
1929
1958
 
1930
1959
        new_files=set()
1931
1960
        unknown_nested_files=set()
 
1961
        if to_file is None:
 
1962
            to_file = sys.stdout
1932
1963
 
1933
1964
        def recurse_directory_to_add_files(directory):
1934
1965
            # Recurse directory and add all files
2004
2035
                        new_status = 'I'
2005
2036
                    else:
2006
2037
                        new_status = '?'
2007
 
                    textui.show_status(new_status, self.kind(fid), f,
2008
 
                                       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')
2009
2041
                # Unversion file
2010
2042
                inv_delta.append((f, None, fid, None))
2011
2043
                message = "removed %s" % (f,)
2162
2194
        """
2163
2195
        raise NotImplementedError(self.unlock)
2164
2196
 
2165
 
    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):
2166
2201
        """Update a working tree along its branch.
2167
2202
 
2168
2203
        This will update the branch if its bound too, which means we have
2186
2221
        - Merge current state -> basis tree of the master w.r.t. the old tree
2187
2222
          basis.
2188
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.
2189
2230
        """
2190
2231
        if self.branch.get_bound_location() is not None:
2191
2232
            self.lock_write()
2192
 
            update_branch = True
 
2233
            update_branch = (old_tip is self._marker)
2193
2234
        else:
2194
2235
            self.lock_tree_write()
2195
2236
            update_branch = False
2197
2238
            if update_branch:
2198
2239
                old_tip = self.branch.update(possible_transports)
2199
2240
            else:
2200
 
                old_tip = None
2201
 
            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)
2202
2244
        finally:
2203
2245
            self.unlock()
2204
2246
 
2205
2247
    @needs_tree_write_lock
2206
 
    def _update_tree(self, old_tip=None, change_reporter=None):
 
2248
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None):
2207
2249
        """Update a tree to the master branch.
2208
2250
 
2209
2251
        :param old_tip: if supplied, the previous tip revision the branch,
2224
2266
            last_rev = self.get_parent_ids()[0]
2225
2267
        except IndexError:
2226
2268
            last_rev = _mod_revision.NULL_REVISION
2227
 
        if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
2228
 
            # 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.
2229
2276
            basis = self.basis_tree()
2230
2277
            basis.lock_read()
2231
2278
            try:
2232
 
                to_tree = self.branch.basis_tree()
2233
 
                if basis.inventory.root is None:
2234
 
                    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)
2235
2284
                    self.flush()
2236
2285
                result += merge.merge_inner(
2237
2286
                                      self.branch,
2239
2288
                                      basis,
2240
2289
                                      this_tree=self,
2241
2290
                                      change_reporter=change_reporter)
 
2291
                self.set_last_revision(revision)
2242
2292
            finally:
2243
2293
                basis.unlock()
2244
2294
            # TODO - dedup parents list with things merged by pull ?
2245
2295
            # reuse the tree we've updated to to set the basis:
2246
 
            parent_trees = [(self.branch.last_revision(), to_tree)]
 
2296
            parent_trees = [(revision, to_tree)]
2247
2297
            merges = self.get_parent_ids()[1:]
2248
2298
            # Ideally we ask the tree for the trees here, that way the working
2249
2299
            # tree can decide whether to give us the entire tree or give us a
2279
2329
            #       should be able to remove this extra flush.
2280
2330
            self.flush()
2281
2331
            graph = self.branch.repository.get_graph()
2282
 
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2283
 
                                                old_tip)
 
2332
            base_rev_id = graph.find_unique_lca(revision, old_tip)
2284
2333
            base_tree = self.branch.repository.revision_tree(base_rev_id)
2285
2334
            other_tree = self.branch.repository.revision_tree(old_tip)
2286
2335
            result += merge.merge_inner(
2536
2585
        return un_resolved, resolved
2537
2586
 
2538
2587
    @needs_read_lock
2539
 
    def _check(self):
 
2588
    def _check(self, references):
 
2589
        """Check the tree for consistency.
 
2590
 
 
2591
        :param references: A dict with keys matching the items returned by
 
2592
            self._get_check_refs(), and values from looking those keys up in
 
2593
            the repository.
 
2594
        """
2540
2595
        tree_basis = self.basis_tree()
2541
2596
        tree_basis.lock_read()
2542
2597
        try:
2543
 
            repo_basis = self.branch.repository.revision_tree(
2544
 
                self.last_revision())
 
2598
            repo_basis = references[('trees', self.last_revision())]
2545
2599
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2546
2600
                raise errors.BzrCheckError(
2547
2601
                    "Mismatched basis inventory content.")
2560
2614
        """
2561
2615
        return
2562
2616
 
2563
 
    @needs_read_lock
2564
2617
    def _get_rules_searcher(self, default_searcher):
2565
2618
        """See Tree._get_rules_searcher."""
2566
2619
        if self._rules_searcher is None:
2593
2646
        if self._inventory is None:
2594
2647
            self.read_working_inventory()
2595
2648
 
 
2649
    def _get_check_refs(self):
 
2650
        """Return the references needed to perform a check of this tree."""
 
2651
        return [('trees', self.last_revision())]
 
2652
 
2596
2653
    def lock_tree_write(self):
2597
2654
        """See WorkingTree.lock_tree_write().
2598
2655
 
2655
2712
                mode=self.bzrdir._get_file_mode())
2656
2713
            return True
2657
2714
 
 
2715
    def _get_check_refs(self):
 
2716
        """Return the references needed to perform a check of this tree."""
 
2717
        return [('trees', self.last_revision())]
 
2718
 
2658
2719
    @needs_tree_write_lock
2659
2720
    def set_conflicts(self, conflicts):
2660
2721
        self._put_rio('conflicts', conflicts.to_stanzas(),
2737
2798
        """Return the format for the working tree object in a_bzrdir."""
2738
2799
        try:
2739
2800
            transport = a_bzrdir.get_workingtree_transport(None)
2740
 
            format_string = transport.get("format").read()
 
2801
            format_string = transport.get_bytes("format")
2741
2802
            return klass._formats[format_string]
2742
2803
        except errors.NoSuchFile:
2743
2804
            raise errors.NoWorkingTree(base=transport.base)
3007
3068
        return self.get_format_string()
3008
3069
 
3009
3070
 
3010
 
__default_format = WorkingTreeFormat4()
 
3071
__default_format = WorkingTreeFormat6()
3011
3072
WorkingTreeFormat.register_format(__default_format)
3012
 
WorkingTreeFormat.register_format(WorkingTreeFormat6())
3013
3073
WorkingTreeFormat.register_format(WorkingTreeFormat5())
 
3074
WorkingTreeFormat.register_format(WorkingTreeFormat4())
3014
3075
WorkingTreeFormat.register_format(WorkingTreeFormat3())
3015
3076
WorkingTreeFormat.set_default_format(__default_format)
3016
3077
# formats which have no format string are not discoverable