Rev 3483: Improve build_tree performance (igc, abentley) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Sun Jun 8 01:21:30 BST 2008


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 3483
revision-id:pqm at pqm.ubuntu.com-20080608002120-r3kcq0kxq24lhnak
parent: pqm at pqm.ubuntu.com-20080606135624-1ambbf8pct52xfh8
parent: aaron at aaronbentley.com-20080607193326-eyxo0nvfowppii2c
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Sun 2008-06-08 01:21:20 +0100
message:
  Improve build_tree performance (igc, abentley)
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
  bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
  bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
    ------------------------------------------------------------
    revno: 3453.2.11
    revision-id:aaron at aaronbentley.com-20080607193326-eyxo0nvfowppii2c
    parent: aaron at aaronbentley.com-20080606191036-7uctnxi5ozdiiss8
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: fast-checkout2
    timestamp: Sat 2008-06-07 15:33:26 -0400
    message:
      Updates from review
    modified:
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
    ------------------------------------------------------------
    revno: 3453.2.10
    revision-id:aaron at aaronbentley.com-20080606191036-7uctnxi5ozdiiss8
    parent: aaron at aaronbentley.com-20080606190750-f6064uc2u1ea05dk
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: fast-checkout2
    timestamp: Fri 2008-06-06 15:10:36 -0400
    message:
      Fix long lines
    modified:
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
    ------------------------------------------------------------
    revno: 3453.2.9
    revision-id:aaron at aaronbentley.com-20080606190750-f6064uc2u1ea05dk
    parent: aaron at aaronbentley.com-20080606164046-ghbxplxuhtpcb9iz
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: fast-checkout2
    timestamp: Fri 2008-06-06 15:07:50 -0400
    message:
      Update NEWS
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
    ------------------------------------------------------------
    revno: 3453.2.8
    revision-id:aaron at aaronbentley.com-20080606164046-ghbxplxuhtpcb9iz
    parent: abentley at bruiser-20080606163615-8ceq4an8jxjcmnu1
    parent: pqm at pqm.ubuntu.com-20080606135624-1ambbf8pct52xfh8
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: fast-checkout2
    timestamp: Fri 2008-06-06 12:40:46 -0400
    message:
      Merge with bzr.dev
    added:
      bzrlib/tests/blackbox/test_alias.py test_alias.py-20080425112253-fbt0yz1c1834jriz-1
      bzrlib/tests/blackbox/test_modified.py test_modified.py-20080424085848-nwqjenan4dq2vq3w-1
      bzrlib/tests/per_repository_reference/ repository_external_-20080220025549-nnm2s80it1lvcwnc-1
      bzrlib/tests/per_repository_reference/__init__.py __init__.py-20080220025549-nnm2s80it1lvcwnc-2
      bzrlib/tests/per_repository_reference/test_add_inventory.py test_add_inventory.p-20080220025549-nnm2s80it1lvcwnc-3
      bzrlib/tests/per_repository_reference/test_add_revision.py test_add_revision.py-20080220034108-ao1u8qgakqbo5a08-1
      bzrlib/tests/per_repository_reference/test_add_signature_text.py test_add_signature_t-20080220041905-1j2g4lyz3c6h34v4-1
      bzrlib/tests/per_repository_reference/test_all_revision_ids.py test_all_revision_id-20080220041905-1j2g4lyz3c6h34v4-2
      bzrlib/tests/per_repository_reference/test_break_lock.py test_break_lock.py-20080220042825-1f48qmpnuqqp5wg2-1
      bzrlib/tests/per_repository_reference/test_check.py test_check.py-20080220044229-sxxe747gzi6q8fyv-1
    renamed:
      doc/en/user-guide/revnos.txt => doc/en/user-guide/zen.txt revnos.txt-20080111231928-pbntxea0ynh9ww1t-1
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/__init__.py             __init__.py-20050309040759-33e65acf91bbcd5d
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
      bzrlib/check.py                check.py-20050309040759-f3a679400c06bcc1
      bzrlib/commands.py             bzr.py-20050309040720-d10f4714595cf8c3
      bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
      bzrlib/counted_lock.py         counted_lock.py-20070502135927-7dk86io3ok7ctx6k-1
      bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
      bzrlib/graph.py                graph_walker.py-20070525030359-y852guab65d4wtn0-1
      bzrlib/knit.py                 knit.py-20051212171256-f056ac8f0fbe1bd9
      bzrlib/lockdir.py              lockdir.py-20060220222025-98258adf27fbdda3
      bzrlib/log.py                  log.py-20050505065812-c40ce11702fe5fb1
      bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
      bzrlib/merge_directive.py      merge_directive.py-20070228184838-ja62280spt1g7f4x-1
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/repofmt/knitrepo.py     knitrepo.py-20070206081537-pyy4a00xdas0j4pf-1
      bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
      bzrlib/revisionspec.py         revisionspec.py-20050907152633-17567659fd5c0ddb
      bzrlib/smart/client.py         client.py-20061116014825-2k6ada6xgulslami-1
      bzrlib/smart/message.py        message.py-20080222013625-ncqmh3nrxjkxab87-1
      bzrlib/symbol_versioning.py    symbol_versioning.py-20060105104851-9ecf8af605d15a80
      bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
      bzrlib/tests/blackbox/__init__.py __init__.py-20051128053524-eba30d8255e08dc3
      bzrlib/tests/blackbox/test_added.py test_added.py-20060119085008-6b8b90369d42a26c
      bzrlib/tests/blackbox/test_non_ascii.py test_non_ascii.py-20060105214030-68010be784a5d854
      bzrlib/tests/blackbox/test_uncommit.py test_uncommit.py-20051027212835-84944b63adae51be
      bzrlib/tests/blackbox/test_unknowns.py test_unknowns.py-20070905015344-74tg6s1synijo2oe-1
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
      bzrlib/tests/branch_implementations/test_update.py test_update.py-20060305010612-e68efbcbb1baa69f
      bzrlib/tests/http_server.py    httpserver.py-20061012142527-m1yxdj1xazsf8d7s-1
      bzrlib/tests/repository_implementations/__init__.py __init__.py-20060131092037-9564957a7d4a841b
      bzrlib/tests/test_branch.py    test_branch.py-20060116013032-97819aa07b8ab3b5
      bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
      bzrlib/tests/test_counted_lock.py test_counted_lock.py-20070502135927-7dk86io3ok7ctx6k-2
      bzrlib/tests/test_diff.py      testdiff.py-20050727164403-d1a3496ebb12e339
      bzrlib/tests/test_errors.py    test_errors.py-20060210110251-41aba2deddf936a8
      bzrlib/tests/test_graph.py     test_graph_walker.py-20070525030405-enq4r60hhi9xrujc-1
      bzrlib/tests/test_lockable_files.py test_lockable_files.py-20051225183927-365c7fd99591caf1
      bzrlib/tests/test_lockdir.py   test_lockdir.py-20060220222025-33d4221569a3d600
      bzrlib/tests/test_merge.py     testmerge.py-20050905070950-c1b5aa49ff911024
      bzrlib/tests/test_merge_directive.py test_merge_directive-20070228184838-ja62280spt1g7f4x-2
      bzrlib/tests/test_msgeditor.py test_msgeditor.py-20051202041359-920315ec6011ee51
      bzrlib/tests/test_revisionspec.py testrevisionnamespaces.py-20050711050225-8b4af89e6b1efe84
      bzrlib/tests/test_selftest.py  test_selftest.py-20051202044319-c110a115d8c0456a
      bzrlib/tests/test_smart_transport.py test_ssh_transport.py-20060608202016-c25gvf1ob7ypbus6-2
      bzrlib/tests/test_switch.py    test_switch.py-20071116011000-v5lnw7d2wkng9eux-2
      bzrlib/tests/test_symbol_versioning.py test_symbol_versioning.py-20060105104851-51d7722c2018d42b
      bzrlib/tests/test_transport_implementations.py test_transport_implementations.py-20051227111451-f97c5c7d5c49fce7
      bzrlib/tests/test_versionedfile.py test_versionedfile.py-20060222045249-db45c9ed14a1c2e5
      bzrlib/tests/test_workingtree_4.py test_workingtree_4.p-20070223025758-531n3tznl3zacv2o-1
      bzrlib/tests/workingtree_implementations/test_parents.py test_set_parents.py-20060807231740-yicmnlci1mj8smu1-1
      bzrlib/uncommit.py             uncommit.py-20050626215513-5ec509fa425b305c
      bzrlib/workingtree.py          workingtree.py-20050511021032-29b6ec0a681e02e3
      bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
      doc/developers/HACKING.txt     HACKING-20050805200004-2a5dc975d870f78c
      doc/developers/index.txt       index.txt-20070508041241-qznziunkg0nffhiw-1
      doc/developers/releasing.txt   releasing.txt-20080502015919-fnrcav8fwy8ccibu-1
      doc/en/user-guide/branching_a_project.txt branching_a_project.-20071122141511-0knao2lklsdsvb1q-2
      doc/en/user-guide/core_concepts.txt core_concepts.txt-20071114035000-q36a9h57ps06uvnl-2
      doc/en/user-guide/index.txt    index.txt-20060622101119-tgwtdci8z769bjb9-2
      doc/en/user-guide/partner_intro.txt partner_workflow.txt-20071122141511-0knao2lklsdsvb1q-4
      doc/en/user-guide/releasing_a_project.txt releasing_a_project.-20071121073725-0corxykv5irjal00-5
      doc/en/user-guide/svn_plugin.txt svn_plugin.txt-20080509065016-cjc90f46407vi9a0-2
      doc/en/user-guide/undoing_mistakes.txt undoing_mistakes.txt-20071121092300-8fyacngt1w98e5mp-1
      doc/en/user-guide/zen.txt      revnos.txt-20080111231928-pbntxea0ynh9ww1t-1
    ------------------------------------------------------------
    revno: 3453.2.7
    revision-id:abentley at bruiser-20080606163615-8ceq4an8jxjcmnu1
    parent: aaron at aaronbentley.com-20080606161445-b7ii90zduprtigkb
    committer: Aaron Bentley <abentley at bruiser>
    branch nick: fast-checkout2
    timestamp: Fri 2008-06-06 12:36:15 -0400
    message:
      Remove test kipple
    modified:
      bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
    ------------------------------------------------------------
    revno: 3453.2.6
    revision-id:aaron at aaronbentley.com-20080606161445-b7ii90zduprtigkb
    parent: aaron at aaronbentley.com-20080606152106-145ajhf9pw31fu7p
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: fast-checkout2
    timestamp: Fri 2008-06-06 12:14:45 -0400
    message:
      Rename mutate_tree to delta_from_tree, add comment
    modified:
      bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
      bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
    ------------------------------------------------------------
    revno: 3453.2.5
    revision-id:aaron at aaronbentley.com-20080606152106-145ajhf9pw31fu7p
    parent: aaron at aaronbentley.com-20080606134221-54emmgm6eagq1szs
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: fast-checkout2
    timestamp: Fri 2008-06-06 11:21:06 -0400
    message:
      Avoid statting tons of non-existant files when building from scratch
    modified:
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
    ------------------------------------------------------------
    revno: 3453.2.4
    revision-id:aaron at aaronbentley.com-20080606134221-54emmgm6eagq1szs
    parent: aaron at aaronbentley.com-20080606025405-l7e8qeh6spaym69x
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: fast-checkout2
    timestamp: Fri 2008-06-06 09:42:21 -0400
    message:
      Disable fast-path when conflicts are encountered
    modified:
      bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
    ------------------------------------------------------------
    revno: 3453.2.3
    revision-id:aaron at aaronbentley.com-20080606025405-l7e8qeh6spaym69x
    parent: aaron at aaronbentley.com-20080605193053-v21g2jieqihlz1oz
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: fast-checkout2
    timestamp: Thu 2008-06-05 22:54:05 -0400
    message:
      Enable using a precomputed inventory delta for build_tree.
    modified:
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
      bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
    ------------------------------------------------------------
    revno: 3453.2.2
    revision-id:aaron at aaronbentley.com-20080605193053-v21g2jieqihlz1oz
    parent: aaron at aaronbentley.com-20080602190332-6bfet6syo8wpl3jc
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: fast-checkout2
    timestamp: Thu 2008-06-05 15:30:53 -0400
    message:
      Avoid unnecessary file_id lookups
    modified:
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
    ------------------------------------------------------------
    revno: 3453.2.1
    revision-id:aaron at aaronbentley.com-20080602190332-6bfet6syo8wpl3jc
    parent: pqm at pqm.ubuntu.com-20080527013230-8qjaju10duxpy3e2
    committer: Aaron Bentley <aaron at aaronbentley.com>
    branch nick: fast-checkout2
    timestamp: Mon 2008-06-02 15:03:32 -0400
    message:
      Speed up apply_insertions using iter_entries_by_dir instead of id2path
    modified:
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
=== modified file 'NEWS'
--- a/NEWS	2008-06-06 12:02:48 +0000
+++ b/NEWS	2008-06-06 19:07:50 +0000
@@ -31,6 +31,9 @@
       ``modified`` (for null-separated unknowns, use ``ls --unknown --null``)
       (Adrian Wilkins)
 
+    * Faster branching (1.09x) and lightweight checkouts (1.06x) on large trees.
+      (Ian Clatworthy, Aaron Bentley)
+
   BUGFIXES:
 
     * ``bzr status`` was breaking if you merged the same revision twice.

=== modified file 'bzrlib/tests/test_transform.py'
--- a/bzrlib/tests/test_transform.py	2008-05-12 02:48:08 +0000
+++ b/bzrlib/tests/test_transform.py	2008-06-06 16:36:15 +0000
@@ -64,7 +64,7 @@
 
     def get_transform(self):
         transform = TreeTransform(self.wt)
-        #self.addCleanup(transform.finalize)
+        self.addCleanup(transform.finalize)
         return transform, transform.root
 
     def test_existing_limbo(self):
@@ -1704,6 +1704,19 @@
         self.assertEqual([], list(target.iter_changes(revision_tree)))
         self.assertTrue(source.is_executable('file1-id'))
 
+    def test_case_insensitive_build_tree_inventory(self):
+        source = self.make_branch_and_tree('source')
+        self.build_tree(['source/file', 'source/FILE'])
+        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
+        source.commit('added files')
+        # Don't try this at home, kids!
+        # Force the tree to report that it is case insensitive
+        target = self.make_branch_and_tree('target')
+        target.case_sensitive = False
+        build_tree(source.basis_tree(), target, source, delta_from_tree=True)
+        self.assertEqual('file.moved', target.id2path('lower-id'))
+        self.assertEqual('FILE', target.id2path('upper-id'))
+
 
 class MockTransform(object):
 

=== modified file 'bzrlib/transform.py'
--- a/bzrlib/transform.py	2008-05-12 02:48:08 +0000
+++ b/bzrlib/transform.py	2008-06-07 19:33:26 +0000
@@ -450,16 +450,21 @@
         del self._new_id[trans_id]
         del self._r_new_id[file_id]
 
-    def new_paths(self):
-        """Determine the paths of all new and changed files"""
+    def new_paths(self, filesystem_only=False):
+        """Determine the paths of all new and changed files.
+
+        :param filesystem_only: if True, only calculate values for files
+            that require renames or execute bit changes.
+        """
         new_ids = set()
-        fp = FinalPaths(self)
-        for id_set in (self._new_name, self._new_parent, self._new_contents,
-                       self._new_id, self._new_executability):
+        if filesystem_only:
+            id_sets = (self._needs_rename, self._new_executability)
+        else:
+            id_sets = (self._new_name, self._new_parent, self._new_contents,
+                       self._new_id, self._new_executability)
+        for id_set in id_sets:
             new_ids.update(id_set)
-        new_paths = [(fp.get_path(t), t) for t in new_ids]
-        new_paths.sort()
-        return new_paths
+        return sorted(FinalPaths(self).get_paths(new_ids))
 
     def tree_kind(self, trans_id):
         """Determine the file kind in the working tree.
@@ -856,7 +861,8 @@
     def _set_executability(self, path, entry, trans_id):
         """Set the executability of versioned files """
         new_executability = self._new_executability[trans_id]
-        entry.executable = new_executability
+        if entry is not None:
+            entry.executable = new_executability
         if supports_executable():
             abspath = self._tree.abspath(path)
             current_mode = os.stat(abspath).st_mode
@@ -1167,7 +1173,7 @@
                                    tree.case_sensitive)
         self._deletiondir = deletiondir
 
-    def apply(self, no_conflicts=False, _mover=None):
+    def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
         """Apply all changes to the inventory and filesystem.
 
         If filesystem or inventory conflicts are present, MalformedTransform
@@ -1177,13 +1183,20 @@
 
         :param no_conflicts: if True, the caller guarantees there are no
             conflicts, so no check is made.
+        :param precomputed_delta: An inventory delta to use instead of
+            calculating one.
         :param _mover: Supply an alternate FileMover, for testing
         """
         if not no_conflicts:
             conflicts = self.find_conflicts()
             if len(conflicts) != 0:
                 raise MalformedTransform(conflicts=conflicts)
-        inventory_delta = []
+        if precomputed_delta is None:
+            new_inventory_delta = []
+            inventory_delta = new_inventory_delta
+        else:
+            new_inventory_delta = None
+            inventory_delta = precomputed_delta
         child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
         try:
             if _mover is None:
@@ -1192,9 +1205,10 @@
                 mover = _mover
             try:
                 child_pb.update('Apply phase', 0, 2)
-                self._apply_removals(inventory_delta, mover)
+                self._apply_removals(new_inventory_delta, mover)
                 child_pb.update('Apply phase', 1, 2)
-                modified_paths = self._apply_insertions(inventory_delta, mover)
+                modified_paths = self._apply_insertions(new_inventory_delta,
+                                                        mover)
             except:
                 mover.rollback()
                 raise
@@ -1213,6 +1227,8 @@
         That is, delete files that are to be deleted, and put any files that
         need renaming into limbo.  This must be done in strict child-to-parent
         order.
+
+        If inventory_delta is None, no inventory delta generation is performed.
         """
         tree_paths = list(self._tree_path_ids.iteritems())
         tree_paths.sort(reverse=True)
@@ -1234,7 +1250,8 @@
                             raise
                     else:
                         self.rename_count += 1
-                if trans_id in self._removed_id:
+                if (trans_id in self._removed_id
+                    and inventory_delta is not None):
                     if trans_id == self._new_root:
                         file_id = self._tree.get_root_id()
                     else:
@@ -1252,62 +1269,78 @@
         That is, create any files that need to be created, and restore from
         limbo any files that needed renaming.  This must be done in strict
         parent-to-child order.
+
+        If inventory_delta is None, no inventory delta is calculated, and
+        no list of modified paths is returned.
         """
-        new_paths = self.new_paths()
+        new_paths = self.new_paths(filesystem_only=(inventory_delta is None))
         modified_paths = []
-        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
         completed_new = []
+        new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
+                                 new_paths)
+        if inventory_delta is not None:
+            entries = self._tree.iter_entries_by_dir(
+                new_path_file_ids.values())
+            old_paths = dict((e.file_id, p) for p, e in entries)
+        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
         try:
             for num, (path, trans_id) in enumerate(new_paths):
                 new_entry = None
-                child_pb.update('adding file', num, len(new_paths))
-                if trans_id in self._new_contents or \
-                    self.path_changed(trans_id):
-                    full_path = self._tree.abspath(path)
-                    if trans_id in self._needs_rename:
+                if (num % 10) == 0:
+                    child_pb.update('adding file', num, len(new_paths))
+                full_path = self._tree.abspath(path)
+                if trans_id in self._needs_rename:
+                    try:
+                        mover.rename(self._limbo_name(trans_id), full_path)
+                    except OSError, e:
+                        # We may be renaming a dangling inventory id
+                        if e.errno != errno.ENOENT:
+                            raise
+                    else:
+                        self.rename_count += 1
+                if inventory_delta is not None:
+                    if (trans_id in self._new_contents or
+                        self.path_changed(trans_id)):
+                        if trans_id in self._new_contents:
+                            modified_paths.append(full_path)
+                            completed_new.append(trans_id)
+                    file_id = new_path_file_ids[trans_id]
+                    if file_id is not None and (trans_id in self._new_id or
+                        trans_id in self._new_name or
+                        trans_id in self._new_parent
+                        or trans_id in self._new_executability):
                         try:
-                            mover.rename(self._limbo_name(trans_id), full_path)
-                        except OSError, e:
-                            # We may be renaming a dangling inventory id
-                            if e.errno != errno.ENOENT:
-                                raise
+                            kind = self.final_kind(trans_id)
+                        except NoSuchFile:
+                            kind = self._tree.stored_kind(file_id)
+                        parent_trans_id = self.final_parent(trans_id)
+                        parent_file_id = new_path_file_ids.get(parent_trans_id)
+                        if parent_file_id is None:
+                            parent_file_id = self.final_file_id(
+                                parent_trans_id)
+                        if trans_id in self._new_reference_revision:
+                            new_entry = inventory.TreeReference(
+                                file_id,
+                                self._new_name[trans_id],
+                                self.final_file_id(self._new_parent[trans_id]),
+                                None, self._new_reference_revision[trans_id])
                         else:
-                            self.rename_count += 1
-                    if trans_id in self._new_contents:
-                        modified_paths.append(full_path)
-                        completed_new.append(trans_id)
-                file_id = self.final_file_id(trans_id)
-                if file_id is not None and (trans_id in self._new_id or
-                    trans_id in self._new_name or trans_id in self._new_parent
-                    or trans_id in self._new_executability):
-                    try:
-                        kind = self.final_kind(trans_id)
-                    except NoSuchFile:
-                        kind = self._tree.stored_kind(file_id)
-                    if trans_id in self._new_reference_revision:
-                        new_entry = inventory.TreeReference(
-                            self.final_file_id(trans_id),
-                            self._new_name[trans_id],
-                            self.final_file_id(self._new_parent[trans_id]),
-                            None, self._new_reference_revision[trans_id])
-                    else:
-                        new_entry = inventory.make_entry(kind,
-                            self.final_name(trans_id),
-                            self.final_file_id(self.final_parent(trans_id)),
-                            self.final_file_id(trans_id))
-                    try:
-                        old_path = self._tree.id2path(new_entry.file_id)
-                    except errors.NoSuchId:
-                        old_path = None
-                    inventory_delta.append((old_path, path, new_entry.file_id,
-                                            new_entry))
+                            new_entry = inventory.make_entry(kind,
+                                self.final_name(trans_id),
+                                parent_file_id, file_id)
+                        old_path = old_paths.get(new_entry.file_id)
+                        inventory_delta.append(
+                            (old_path, path, new_entry.file_id, new_entry))
 
                 if trans_id in self._new_executability:
                     self._set_executability(path, new_entry, trans_id)
         finally:
             child_pb.finished()
-        for trans_id in completed_new:
-            del self._new_contents[trans_id]
+        if inventory_delta is None:
+            self._new_contents.clear()
+        else:
+            for trans_id in completed_new:
+                del self._new_contents[trans_id]
         return modified_paths
 
 
@@ -1554,6 +1587,10 @@
             self._known_paths[trans_id] = self._determine_path(trans_id)
         return self._known_paths[trans_id]
 
+    def get_paths(self, trans_ids):
+        return [(self.get_path(t), t) for t in trans_ids]
+
+
 
 def topology_sorted_ids(tree):
     """Determine the topological order of the ids in a tree"""
@@ -1562,7 +1599,8 @@
     return file_ids
 
 
-def build_tree(tree, wt, accelerator_tree=None, hardlink=False):
+def build_tree(tree, wt, accelerator_tree=None, hardlink=False,
+               delta_from_tree=False):
     """Create working tree for a branch, using a TreeTransform.
     
     This function should be used on empty trees, having a tree root at most.
@@ -1584,6 +1622,8 @@
     :param hardlink: If true, hard-link files to accelerator_tree, where
         possible.  accelerator_tree must implement abspath, i.e. be a
         working tree.
+    :param delta_from_tree: If true, build_tree may use the input Tree to
+        generate the inventory delta.
     """
     wt.lock_tree_write()
     try:
@@ -1592,7 +1632,8 @@
             if accelerator_tree is not None:
                 accelerator_tree.lock_read()
             try:
-                return _build_tree(tree, wt, accelerator_tree, hardlink)
+                return _build_tree(tree, wt, accelerator_tree, hardlink,
+                                   delta_from_tree)
             finally:
                 if accelerator_tree is not None:
                     accelerator_tree.unlock()
@@ -1602,11 +1643,14 @@
         wt.unlock()
 
 
-def _build_tree(tree, wt, accelerator_tree, hardlink):
+def _build_tree(tree, wt, accelerator_tree, hardlink, delta_from_tree):
     """See build_tree."""
     for num, _unused in enumerate(wt.all_file_ids()):
         if num > 0:  # more than just a root
             raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
+    existing_files = set()
+    for dir, files in wt.walkdirs():
+        existing_files.update(f[0] for f in files)
     file_trans_id = {}
     top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
     pp = ProgressPhase("Build phase", 2, top_pb)
@@ -1631,6 +1675,10 @@
         try:
             deferred_contents = []
             num = 0
+            if delta_from_tree:
+                precomputed_delta = []
+            else:
+                precomputed_delta = None
             for num, (tree_path, entry) in \
                 enumerate(tree.inventory.iter_entries_by_dir()):
                 pb.update("Building tree", num - len(deferred_contents),
@@ -1639,12 +1687,11 @@
                     continue
                 reparent = False
                 file_id = entry.file_id
-                target_path = wt.abspath(tree_path)
-                try:
+                if delta_from_tree:
+                    precomputed_delta.append((None, tree_path, file_id, entry))
+                if tree_path in existing_files:
+                    target_path = wt.abspath(tree_path)
                     kind = file_kind(target_path)
-                except NoSuchFile:
-                    pass
-                else:
                     if kind == "directory":
                         try:
                             bzrdir.BzrDir.open(target_path)
@@ -1670,7 +1717,7 @@
                     file_trans_id[file_id] = trans_id
                     tt.version_file(entry.file_id, trans_id)
                     executable = tree.is_executable(entry.file_id, tree_path)
-                    if executable is not None:
+                    if executable:
                         tt.set_executability(executable, trans_id)
                     deferred_contents.append((entry.file_id, trans_id))
                 else:
@@ -1689,6 +1736,8 @@
         divert_trans = set(file_trans_id[f] for f in divert)
         resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
         raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
+        if len(raw_conflicts) > 0:
+            precomputed_delta = None
         conflicts = cook_conflicts(raw_conflicts, tt)
         for conflict in conflicts:
             warning(conflict)
@@ -1696,7 +1745,8 @@
             wt.add_conflicts(conflicts)
         except errors.UnsupportedOperation:
             pass
-        result = tt.apply(no_conflicts=True)
+        result = tt.apply(no_conflicts=True,
+                          precomputed_delta=precomputed_delta)
     finally:
         tt.finalize()
         top_pb.finished()

=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py	2008-06-04 17:54:52 +0000
+++ b/bzrlib/workingtree_4.py	2008-06-06 16:40:46 +0000
@@ -1373,8 +1373,11 @@
                 if basis_root_id is not None:
                     wt._set_root_id(basis_root_id)
                     wt.flush()
+                # delta_from_tree is safe even for DirStateRevisionTrees,
+                # because wt4.apply_inventory_delta does not mutate the input
+                # inventory entries.
                 transform.build_tree(basis, wt, accelerator_tree,
-                                     hardlink=hardlink)
+                                     hardlink=hardlink, delta_from_tree=True)
             finally:
                 basis.unlock()
         finally:




More information about the bazaar-commits mailing list