Rev 2401: Implement move_directory by factoring out move_one in http://bazaar.launchpad.net/%7Ebzr/bzr/dirstate

John Arbash Meinel john at arbash-meinel.com
Sun Feb 25 23:29:38 GMT 2007


At http://bazaar.launchpad.net/%7Ebzr/bzr/dirstate

------------------------------------------------------------
revno: 2401
revision-id: john at arbash-meinel.com-20070225232832-0i66e7smyz5po931
parent: robertc at robertcollins.net-20070225225034-fts7vcvfhkqwcms8
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: dirstate
timestamp: Sun 2007-02-25 17:28:32 -0600
message:
  Implement move_directory by factoring out move_one
  as a helper function, and then implementing a recursive update for
  all moved dirblocks.
modified:
  bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
  bzrlib/tests/workingtree_implementations/test_move.py test_move.py-20070225171927-mohn2vqj5fx7edc6-1
  bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
-------------- next part --------------
=== modified file 'bzrlib/dirstate.py'
--- a/bzrlib/dirstate.py	2007-02-25 22:07:48 +0000
+++ b/bzrlib/dirstate.py	2007-02-25 23:28:32 +0000
@@ -304,7 +304,7 @@
         # add it.
         #------- copied from bzrlib.inventory.make_entry
         # --- normalized_filename wants a unicode basename only, so get one.
-        dirname, basename = os.path.split(path)
+        dirname, basename = osutils.split(path)
         # we dont import normalized_filename directly because we want to be
         # able to change the implementation at runtime for tests.
         norm_name, can_access = osutils.normalized_filename(basename)
@@ -317,7 +317,7 @@
         # dirname and basename elements. This single encode and split should be
         # faster than three separate encodes.
         utf8path = (dirname + '/' + basename).strip('/').encode('utf8')
-        dirname, basename = os.path.split(utf8path)
+        dirname, basename = osutils.split(utf8path)
         assert file_id.__class__ == str, \
             "must be a utf8 file_id not %s" % (type(file_id))
         entry_key = (dirname, basename, file_id)
@@ -917,7 +917,7 @@
                 # dirblock at parse time.
                 # This is an uncommon branch to take: most dirs have children,
                 # and most code works with versioned paths.
-                parent_base, parent_name = os.path.split(key[0])
+                parent_base, parent_name = osutils.split(key[0])
                 if not self._get_block_entry_index(parent_base, parent_name, 0)[3]:
                     # some parent path has not been added - its an error to add
                     # this child
@@ -1154,7 +1154,7 @@
         if path_utf8 is not None:
             assert path_utf8.__class__ == str, 'path_utf8 is not a str: %s %s' % (type(path_utf8), path_utf8)
             # path lookups are faster
-            dirname, basename = os.path.split(path_utf8)
+            dirname, basename = osutils.split(path_utf8)
             block_index, entry_index, dir_present, file_present = \
                 self._get_block_entry_index(dirname, basename, tree_index)
             if not file_present:
@@ -1595,7 +1595,7 @@
                 # records where needed. 
                 file_id = entry.file_id
                 path_utf8 = path.encode('utf8')
-                dirname, basename = os.path.split(path_utf8)
+                dirname, basename = osutils.split(path_utf8)
                 new_entry_key = (dirname, basename, file_id)
                 # tree index consistency: All other paths for this id in this tree
                 # index must point to the correct path.
@@ -1688,7 +1688,7 @@
             if current_new:
                 # convert new into dirblock style
                 new_path_utf8 = current_new[0].encode('utf8')
-                new_dirname, new_basename = os.path.split(new_path_utf8)
+                new_dirname, new_basename = osutils.split(new_path_utf8)
                 new_id = current_new[1].file_id
                 new_entry_key = (new_dirname, new_basename, new_id)
                 current_new_minikind = \
@@ -1750,7 +1750,7 @@
                 all_remaining_keys.add(current_old[0])
             elif details[0] == 'r': # relocated
                 # record the key for the real path.
-                all_remaining_keys.add(tuple(os.path.split(details[1])) + (current_old[0][2],))
+                all_remaining_keys.add(tuple(osutils.split(details[1])) + (current_old[0][2],))
             # absent rows are not present at any path.
         last_reference = current_old[0] not in all_remaining_keys
         if last_reference:
@@ -1850,7 +1850,7 @@
                         new_entry[1].append(update_details)
                     else:
                         # we have the right key, make a pointer to it.
-                        pointer_path = os.path.join(*other_key[0:2])
+                        pointer_path = osutils.pathjoin(*other_key[0:2])
                         new_entry[1].append(('r', pointer_path, 0, False, ''))
             block.insert(entry_index, new_entry)
             existing_keys.add(key)
@@ -1884,7 +1884,7 @@
                         ('r', path_utf8, 0, False, '')
         # add a containing dirblock if needed.
         if new_details[0] == 'd':
-            subdir_key = (os.path.join(*key[0:2]), '', '')
+            subdir_key = (osutils.pathjoin(*key[0:2]), '', '')
             block_index, present = self._find_block_index_from_key(subdir_key)
             if not present:
                 self._dirblocks.insert(block_index, (subdir_key[0], []))

=== modified file 'bzrlib/tests/workingtree_implementations/test_move.py'
--- a/bzrlib/tests/workingtree_implementations/test_move.py	2007-02-25 20:36:34 +0000
+++ b/bzrlib/tests/workingtree_implementations/test_move.py	2007-02-25 23:28:32 +0000
@@ -319,3 +319,16 @@
         # But it shouldn't actually move anything
         self.assertFileEqual(a_text, 'a')
         self.assertFileEqual(ba_text, 'b/a')
+
+    def test_move_directory(self):
+        tree = self.make_branch_and_tree('.')
+        self.build_tree(['a/', 'a/b', 'a/c/', 'a/c/d', 'e/'])
+        tree.add(['a', 'a/b', 'a/c', 'a/c/d', 'e'],
+                 ['a-id', 'b-id', 'c-id', 'd-id', 'e-id'])
+        tree.commit('initial', rev_id='rev-1')
+        root_id = tree.get_root_id()
+
+        tree.move(['a'], 'e')
+        self.assertTreeLayout([('', root_id), ('e', 'e-id'), ('e/a', 'a-id'),
+                               ('e/a/b', 'b-id'), ('e/a/c', 'c-id'),
+                               ('e/a/c/d', 'd-id')], tree)

=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py	2007-02-25 22:07:48 +0000
+++ b/bzrlib/workingtree_4.py	2007-02-25 23:28:32 +0000
@@ -414,6 +414,7 @@
         assert not isinstance(from_paths, basestring)
         to_dir_utf8 = to_dir.encode('utf8')
         to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
+        id_index = state._get_id_index()
         # check destination directory
         # get the details for it
         to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
@@ -443,11 +444,37 @@
         else:
             update_inventory = False
 
+        rollbacks = []
+        def move_one(old_entry, from_path_utf8, minikind, executable,
+                     fingerprint, packed_stat, size,
+                     to_block, to_key, to_path_utf8):
+            state._make_absent(old_entry)
+            from_key = old_entry[0]
+            rollbacks.append(
+                lambda:state.update_minimal(from_key,
+                    minikind,
+                    executable=executable,
+                    fingerprint=fingerprint,
+                    packed_stat=packed_stat,
+                    size=size,
+                    path_utf8=from_path_utf8))
+            state.update_minimal(to_key,
+                    minikind,
+                    executable=executable,
+                    fingerprint=fingerprint,
+                    packed_stat=packed_stat,
+                    size=size,
+                    path_utf8=to_path_utf8)
+            added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
+            new_entry = to_block[1][added_entry_index]
+            rollbacks.append(lambda:state._make_absent(new_entry))
+
         # create rename entries and tuples
         for from_rel in from_paths:
             # from_rel is 'pathinroot/foo/bar'
-            from_dirname, from_tail = os.path.split(from_rel)
-            from_dirname = from_dirname.encode('utf8')
+            from_rel_utf8 = from_rel.encode('utf8')
+            from_dirname, from_tail = osutils.split(from_rel)
+            from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
             from_entry = self._get_entry(path=from_rel)
             if from_entry == (None, None):
                 raise errors.BzrMoveFailedError(from_rel,to_dir,
@@ -455,6 +482,7 @@
 
             from_id = from_entry[0][2]
             to_rel = pathjoin(to_dir, from_tail)
+            to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
             item_to_entry = self._get_entry(path=to_rel)
             if item_to_entry != (None, None):
                 raise errors.BzrMoveFailedError(from_rel, to_rel,
@@ -520,50 +548,70 @@
                         lambda: inv.rename(from_id, current_parent, from_tail))
                 # finally do the rename in the dirstate, which is a little
                 # tricky to rollback, but least likely to need it.
-                basename = from_tail.encode('utf8')
                 old_block_index, old_entry_index, dir_present, file_present = \
-                    state._get_block_entry_index(from_dirname, basename, 0)
+                    state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
                 old_block = state._dirblocks[old_block_index][1]
-                old_entry_details = old_block[old_entry_index][1]
+                old_entry = old_block[old_entry_index]
+                from_key, old_entry_details = old_entry
+                cur_details = old_entry_details[0]
                 # remove the old row
-                from_key = old_block[old_entry_index][0]
                 to_key = ((to_block[0],) + from_key[1:3])
-                # We must grab old_entry_details here, because _make_absent
-                # will mark this record as absent and destroy any info we used
-                # to have
-                minikind = old_entry_details[0][0]
-                state._make_absent(old_block[old_entry_index])
-                rollbacks.append(
-                    lambda:state.update_minimal(from_key,
-                        minikind,
-                        executable=old_entry_details[0][3],
-                        fingerprint=old_entry_details[0][1],
-                        packed_stat=old_entry_details[0][4],
-                        size=old_entry_details[0][2],
-                        path_utf8=from_rel.encode('utf8')))
-                # create new row in current block
-                state.update_minimal(to_key,
-                        minikind,
-                        executable=old_entry_details[0][3],
-                        fingerprint=old_entry_details[0][1],
-                        packed_stat=old_entry_details[0][4],
-                        size=old_entry_details[0][2],
-                        path_utf8=to_rel.encode('utf8'))
-                added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
-                new_entry = to_block[1][added_entry_index]
-                rollbacks.append(lambda:state._make_absent(new_entry))
-                if new_entry[1][0][0] == 'd':
-                    import pdb;pdb.set_trace()
-                    # if a directory, rename all the contents of child blocks
-                    # adding rollbacks as each is inserted to remove them and
-                    # restore the original
-                    # TODO: large scale slice assignment.
-                    # setup new list
-                    # save old list region
-                    # move up or down the old region
-                    # add rollback to move the region back
-                    # assign new list to new region
-                    # done
+                minikind = cur_details[0]
+                move_one(old_entry, from_path_utf8=from_rel_utf8,
+                         minikind=minikind,
+                         executable=cur_details[3],
+                         fingerprint=cur_details[1],
+                         packed_stat=cur_details[4],
+                         size=cur_details[2],
+                         to_block=to_block,
+                         to_key=to_key,
+                         to_path_utf8=to_rel_utf8)
+
+                if minikind == 'd':
+                    def update_dirblock(from_dir, to_key, to_dir_utf8):
+                        """all entries in this block need updating.
+
+                        TODO: This is pretty ugly, and doesn't support
+                        reverting, but it works.
+                        """
+                        assert from_dir != '', "renaming root not supported"
+                        from_key = (from_dir, '')
+                        from_block_idx, present = \
+                            state._find_block_index_from_key(from_key)
+                        if not present:
+                            # This is the old record, if it isn't present, then
+                            # there is theoretically nothing to update.
+                            # (Unless it isn't present because of lazy loading,
+                            # but we don't do that yet)
+                            return
+                        from_block = state._dirblocks[from_block_idx]
+                        to_block_index, to_entry_index, _, _ = \
+                            state._get_block_entry_index(to_key[0], to_key[1], 0)
+                        to_block_index = state._ensure_block(
+                            to_block_index, to_entry_index, to_dir_utf8)
+                        to_block = state._dirblocks[to_block_index]
+                        for entry in from_block[1]:
+                            assert entry[0][0] == from_dir
+                            cur_details = entry[1][0]
+                            to_key = (to_dir_utf8, entry[0][1], entry[0][2])
+                            from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
+                            to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
+                            minikind = cur_details[0]
+                            move_one(entry, from_path_utf8=from_path_utf8,
+                                     minikind=minikind,
+                                     executable=cur_details[3],
+                                     fingerprint=cur_details[1],
+                                     packed_stat=cur_details[4],
+                                     size=cur_details[2],
+                                     to_block=to_block,
+                                     to_key=to_key,
+                                     to_path_utf8=to_rel_utf8)
+                            if minikind == 'd':
+                                # We need to move all the children of this
+                                # entry
+                                update_dirblock(from_path_utf8, to_key,
+                                                to_path_utf8)
+                    update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
             except:
                 rollback_rename()
                 raise



More information about the bazaar-commits mailing list