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