Rev 5416: First orphaning implementation (some tests lacking). in file:///home/vila/src/bzr/bugs/323111-orphans/
Vincent Ladeuil
v.ladeuil+lp at free.fr
Thu Sep 9 09:14:40 BST 2010
At file:///home/vila/src/bzr/bugs/323111-orphans/
------------------------------------------------------------
revno: 5416
revision-id: v.ladeuil+lp at free.fr-20100909081439-lrz3d6iob3u8pqz1
parent: v.ladeuil+lp at free.fr-20100907154829-rkvcdtj1nfk9ax60
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: orphan-non-versioned-files
timestamp: Thu 2010-09-09 10:14:39 +0200
message:
First orphaning implementation (some tests lacking).
* bzrlib/transform.py:
(TreeTransformBase.new_orphan, DiskTreeTransform.new_orphan)
(TransformPreview.new_orphan): Transforming a tree can create
orphans, which may or not be supported. Orphaning an item moves it
into a orphan dir with a regular backup name.
(conflict_pass): Resolve a 'missing parent' conflict by orphaning
all the unversioned files in the said parent directory. If the
directory ends up not being empty the orphaning is cancelled and
the conflict turned into a 'deleting parent' one instead (which is
the "normal" case).
* bzrlib/tests/test_transform.py:
(TestSerializeTransform.test_get_parents_texts): Start adding
specific tests for orphaning.
* bzrlib/tests/test_bzrdir.py:
(ChrootedTests.test_sprout_recursive_treeless): This test now
fails, revealing a latent bug (to be filed).
* bzrlib/osutils.py:
(generate_backup_name): Start abstracting backup names generation.
* bzrlib/bzrdir.py:
(BzrDirMeta1.destroy_workingtree): Remark that we ignore the
conflicts here which mask a latent bug in subtree treeless
support (how's that for a confusing term ?).
-------------- next part --------------
=== modified file 'bzrlib/bzrdir.py'
--- a/bzrlib/bzrdir.py 2010-08-25 02:16:31 +0000
+++ b/bzrlib/bzrdir.py 2010-09-09 08:14:39 +0000
@@ -1309,7 +1309,10 @@
wt = self.open_workingtree(recommend_upgrade=False)
repository = wt.branch.repository
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
- wt.revert(old_tree=empty)
+ # We ignore the conflicts returned by wt.revert since we're about to
+ # delete the wt metadata anyway, all that should be left here are
+ # detritus.
+ conflicts = wt.revert(old_tree=empty)
self.destroy_workingtree_metadata()
def destroy_workingtree_metadata(self):
=== modified file 'bzrlib/osutils.py'
--- a/bzrlib/osutils.py 2010-08-24 12:36:53 +0000
+++ b/bzrlib/osutils.py 2010-09-09 08:14:39 +0000
@@ -2354,3 +2354,17 @@
raise errors.BzrError("Can't decode username as %s." % \
user_encoding)
return username
+
+
+def generate_backup_name(base, exists):
+ """Generate a non-existing backup file name.
+
+ :param base: The base name.
+ :param exists: A callable returning True if the passed path exists.
+ """
+ counter = 1
+ name = "%s.~%d~" % (base, counter)
+ while exists(name):
+ counter += 1
+ name = "%s.~%d~" % (base, counter)
+ return name
=== modified file 'bzrlib/tests/test_bzrdir.py'
--- a/bzrlib/tests/test_bzrdir.py 2010-08-20 19:07:17 +0000
+++ b/bzrlib/tests/test_bzrdir.py 2010-09-09 08:14:39 +0000
@@ -791,12 +791,23 @@
sub_tree.add('file')
tree.commit('Initial commit')
tree.bzrdir.destroy_workingtree()
+ # FIXME: subtree/.bzr is left here which allows the test to pass (or
+ # fail :-( ) -- vila 20100909
repo = self.make_repository('repo', shared=True,
format='dirstate-with-subtree')
repo.set_make_working_trees(False)
- tree.bzrdir.sprout('repo/tree2')
- self.failUnlessExists('repo/tree2/subtree')
- self.failIfExists('repo/tree2/subtree/file')
+ # FIXME: we just deleted the workingtree and now we want to use it ????
+ # At a minimum, we should use tree.branch below (but this fails too
+ # currently) or stop calling this test 'treeless'. Specifically, I've
+ # turn the line below into an assertRaises when 'subtree/.bzr' is
+ # orphaned and sprout tries to access the branch there (which is left
+ # by bzrdir.BzrDirMeta1.destroy_workingtree when it ignores the
+ # [DeletingParent('Not deleting', u'subtree', None)] conflict)
+ # -- vila 20100909
+ self.assertRaises(errors.NotBranchError,
+ tree.bzrdir.sprout, 'repo/tree2')
+# self.failUnlessExists('repo/tree2/subtree')
+# self.failIfExists('repo/tree2/subtree/file')
def make_foo_bar_baz(self):
foo = bzrdir.BzrDir.create_branch_convenience('foo').bzrdir
=== modified file 'bzrlib/tests/test_transform.py'
--- a/bzrlib/tests/test_transform.py 2010-09-07 15:48:29 +0000
+++ b/bzrlib/tests/test_transform.py 2010-09-09 08:14:39 +0000
@@ -28,6 +28,7 @@
revision as _mod_revision,
rules,
tests,
+ transform,
urlutils,
)
from bzrlib.bzrdir import BzrDir
@@ -2366,7 +2367,7 @@
def test_resolve_orphan_non_versioned_file(self):
wt, tt = self.get_tree_transform_with_unversioned_dir()
dir_tid = tt.trans_id_tree_file_id('dir-id')
- tt.new_file('file', 'dir-id', 'Contents')
+ tt.new_file('file', dir_tid, 'Contents')
tt.delete_contents(dir_tid)
tt.unversion_file(dir_tid)
conflicts = resolve_conflicts(tt)
@@ -3253,3 +3254,18 @@
trans_id = tt.trans_id_tree_path('file')
self.assertEqual((LINES_ONE,),
tt._get_parents_texts(trans_id))
+
+
+class TestOrphan(tests.TestCaseWithTransport):
+
+ # - can't create oprhan dir
+ # - orphaning forbidden
+ # - can't create orphan
+ # - create orphan
+
+ def test_no_orphan_for_transform_preview(self):
+ tree = self.make_branch_and_tree('tree')
+ tt = TransformPreview(tree)
+ self.addCleanup(tt.finalize)
+ self.assertRaises(NotImplementedError, tt.new_orphan, 'foo', 'bar')
+
=== modified file 'bzrlib/transform.py'
--- a/bzrlib/transform.py 2010-09-07 13:07:24 +0000
+++ b/bzrlib/transform.py 2010-09-09 08:14:39 +0000
@@ -19,8 +19,11 @@
from stat import S_ISREG, S_IEXEC
import time
-from bzrlib.lazy_import import lazy_import
-lazy_import(globals(), """
+from bzrlib import (
+ errors,
+ lazy_import,
+ )
+lazy_import.lazy_import(globals(), """
from bzrlib import (
annotate,
bencode,
@@ -64,7 +67,6 @@
ROOT_PARENT = "root-parent"
-
def unique_add(map, key, value):
if key in map:
raise DuplicateKey(key=key)
@@ -759,6 +761,17 @@
self.create_symlink(target, trans_id)
return trans_id
+ def new_orphan(self, trans_id, parent_id):
+ """Schedule an item to be orphaned.
+
+ When a directory is about to be removed, its children, if they are not
+ versioned are moved out of the way: they don't have a parent anymore.
+
+ :param trans_id: The trans_id of the existing item.
+ :param parent_id: The parent trans_id of the item.
+ """
+ raise NotImplementedError(self.new_orphan)
+
def _affected_ids(self):
"""Return the set of transform ids affected by the transform"""
trans_ids = set(self._removed_id)
@@ -1271,6 +1284,17 @@
del self._limbo_children_names[trans_id]
delete_any(self._limbo_name(trans_id))
+ def new_orphan(self, trans_id, parent_id):
+ """See TreeTransformBase.new_orphan."""
+ # Add the orphan dir if it doesn't exist
+ od_id = self.trans_id_tree_path('bzr-orphans')
+ if self.final_kind(od_id) is None:
+ self.create_directory(od_id)
+ # Find a name that doesn't exist yet in the orphan dir
+ new_name = osutils.generate_backup_name(
+ self.final_name(trans_id), lambda name: name in self._tree_path_ids)
+ self.adjust_path(new_name, od_id, trans_id)
+
class TreeTransform(DiskTreeTransform):
"""Represent a tree transformation.
@@ -1727,6 +1751,9 @@
childpath = joinpath(path, child)
yield self.trans_id_tree_path(childpath)
+ def new_orphan(self, trans_id, parent_id):
+ raise NotImplementedError(self.new_orphan)
+
class _PreviewTree(tree.Tree):
"""Partial implementation of Tree to support show_diff_trees"""
@@ -2427,12 +2454,14 @@
for child in tt.iter_tree_children(old_parent):
tt.adjust_path(tt.final_name(child), new_parent, child)
+
def _reparent_transform_children(tt, old_parent, new_parent):
by_parent = tt.by_parent()
for child in by_parent[old_parent]:
tt.adjust_path(tt.final_name(child), new_parent, child)
return by_parent[old_parent]
+
def _content_match(tree, entry, file_id, kind, target_path):
if entry.kind != kind:
return False
@@ -2800,9 +2829,24 @@
elif c_type == 'missing parent':
trans_id = conflict[1]
if trans_id in tt._removed_contents:
- tt.cancel_deletion(trans_id)
- new_conflicts.add(('deleting parent', 'Not deleting',
- trans_id))
+ orphans = []
+ empty = True
+ # Find the potential orphans, stop if one item should be kept
+ for c in tt.by_parent()[trans_id]:
+ if tt.final_file_id(c) is None:
+ orphans.append(c)
+ else:
+ empty = False
+ break
+ if empty:
+ # All children are orphans
+ for o in orphans:
+ tt.new_orphan(o, trans_id)
+ else:
+ # Cancel the directory deletion
+ tt.cancel_deletion(trans_id)
+ new_conflicts.add(('deleting parent', 'Not deleting',
+ trans_id))
else:
create = True
try:
More information about the bazaar-commits
mailing list