Rev 4385: (abentley) Extract BaseTreeTransform disk code into DiskTreeTransform. in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Thu May 28 09:53:48 BST 2009
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 4385
revision-id: pqm at pqm.ubuntu.com-20090528085340-bfw8729wfm9kmfmd
parent: pqm at pqm.ubuntu.com-20090527193433-rw9zh1l73zjc9knb
parent: aaron at aaronbentley.com-20090528074653-3amj8yr8m5c3yz6o
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Thu 2009-05-28 09:53:40 +0100
message:
(abentley) Extract BaseTreeTransform disk code into DiskTreeTransform.
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/transform.py transform.py-20060105172343-dd99e54394d91687
------------------------------------------------------------
revno: 4354.5.4
revision-id: aaron at aaronbentley.com-20090528074653-3amj8yr8m5c3yz6o
parent: aaron at aaronbentley.com-20090528073625-vzt9289civ2fcwo6
committer: Aaron Bentley <aaron at aaronbentley.com>
branch nick: disk-transform
timestamp: Thu 2009-05-28 09:46:53 +0200
message:
Update docs
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/transform.py transform.py-20060105172343-dd99e54394d91687
------------------------------------------------------------
revno: 4354.5.3
revision-id: aaron at aaronbentley.com-20090528073625-vzt9289civ2fcwo6
parent: aaron at aaronbentley.com-20090515210050-d1sp93gylf05scaf
parent: pqm at pqm.ubuntu.com-20090527193433-rw9zh1l73zjc9knb
committer: Aaron Bentley <aaron at aaronbentley.com>
branch nick: disk-transform
timestamp: Thu 2009-05-28 09:36:25 +0200
message:
Merge bzr.dev into disk-transform.
added:
bzrlib/_rio_py.py _rio_py.py-20090514104624-ied3d39oju8anmfz-1
bzrlib/_rio_pyx.pyx _rio_pyx.pyx-20090514104636-8203jcqvfny56yrd-1
bzrlib/tests/test__rio.py test__rio.py-20090514191748-cy74k8yj46gzoeq6-1
modified:
.bzrignore bzrignore-20050311232317-81f7b71efa2db11a
NEWS NEWS-20050323055033-4e00b5db738777ff
bzr bzr.py-20050313053754-5485f144c7006fa6
bzrlib/__init__.py __init__.py-20050309040759-33e65acf91bbcd5d
bzrlib/branch.py branch.py-20050309040759-e4baf4e0d046576e
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/chk_map.py chk_map.py-20081001014447-ue6kkuhofvdecvxa-1
bzrlib/commands.py bzr.py-20050309040720-d10f4714595cf8c3
bzrlib/errors.py errors.py-20050309040759-20512168c4e14fbd
bzrlib/foreign.py foreign.py-20081112170002-olsxmandkk8qyfuq-1
bzrlib/graph.py graph_walker.py-20070525030359-y852guab65d4wtn0-1
bzrlib/groupcompress.py groupcompress.py-20080705181503-ccbxd6xuy1bdnrpu-8
bzrlib/help_topics/en/eol.txt eol.txt-20090327060429-todzdjmqt3bpv5r8-3
bzrlib/knit.py knit.py-20051212171256-f056ac8f0fbe1bd9
bzrlib/osutils.py osutils.py-20050309040759-eeaff12fbf77ac86
bzrlib/repofmt/groupcompress_repo.py repofmt.py-20080715094215-wp1qfvoo7093c8qr-1
bzrlib/revisiontree.py revisiontree.py-20060724012533-bg8xyryhxd0o0i0h-1
bzrlib/rio.py rio.py-20051128032247-770b120b34dfff60
bzrlib/smart/server.py server.py-20061110062051-chzu10y32vx8gvur-1
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
bzrlib/tests/blackbox/test_commit.py test_commit.py-20060212094538-ae88fc861d969db0
bzrlib/tests/blackbox/test_dpush.py test_dpush.py-20090108125928-st1td6le59g0vyv2-1
bzrlib/tests/blackbox/test_log.py test_log.py-20060112090212-78f6ea560c868e24
bzrlib/tests/blackbox/test_send.py test_bundle.py-20060616222707-c21c8b7ea5ef57b1
bzrlib/tests/blackbox/test_serve.py test_serve.py-20060913064329-8t2pvmsikl4s3xhl-1
bzrlib/tests/blackbox/test_switch.py test_switch.py-20071122111948-0c5en6uz92bwl76h-1
bzrlib/tests/branch_implementations/test_push.py test_push.py-20070130153159-fhfap8uoifevg30j-1
bzrlib/tests/per_repository/test_fetch.py test_fetch.py-20070814052151-5cxha9slx4c93uog-1
bzrlib/tests/test_chk_map.py test_chk_map.py-20081001014447-ue6kkuhofvdecvxa-2
bzrlib/tests/test_foreign.py test_foreign.py-20081125004048-ywb901edgp9lluxo-1
bzrlib/tests/test_http.py testhttp.py-20051018020158-b2eef6e867c514d9
bzrlib/tests/test_osutils.py test_osutils.py-20051201224856-e48ee24c12182989
bzrlib/tests/test_pack_repository.py test_pack_repository-20080801043947-eaw0e6h2gu75kwmy-1
bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
bzrlib/transform.py transform.py-20060105172343-dd99e54394d91687
bzrlib/transport/__init__.py transport.py-20050711165921-4978aa7ce1285ad5
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
bzrlib/win32utils.py win32console.py-20051021033308-123c6c929d04973d
bzrlib/workingtree.py workingtree.py-20050511021032-29b6ec0a681e02e3
bzrlib/workingtree_4.py workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
bzrlib/xml_serializer.py xml.py-20050309040759-57d51586fdec365d
doc/en/user-guide/svn_plugin.txt svn_plugin.txt-20080509065016-cjc90f46407vi9a0-2
setup.py setup.py-20050314065409-02f8a0a6e3f9bc70
------------------------------------------------------------
revno: 4354.5.2
revision-id: aaron at aaronbentley.com-20090515210050-d1sp93gylf05scaf
parent: aaron at aaronbentley.com-20090515203115-45vb6dp9lm5m6k9l
committer: Aaron Bentley <aaron at aaronbentley.com>
branch nick: disk-transform
timestamp: Fri 2009-05-15 17:00:50 -0400
message:
Move WorkingTree-specific code out of BaseTreeTransform
modified:
bzrlib/transform.py transform.py-20060105172343-dd99e54394d91687
------------------------------------------------------------
revno: 4354.5.1
revision-id: aaron at aaronbentley.com-20090515203115-45vb6dp9lm5m6k9l
parent: pqm at pqm.ubuntu.com-20090512090530-v9355ohetp61ltc1
committer: Aaron Bentley <aaron at aaronbentley.com>
branch nick: disk-transform
timestamp: Fri 2009-05-15 16:31:15 -0400
message:
Split out a DiskTreeTransform class that manages Limbo.
modified:
bzrlib/transform.py transform.py-20060105172343-dd99e54394d91687
=== modified file 'NEWS'
--- a/NEWS 2009-05-23 04:55:52 +0000
+++ b/NEWS 2009-05-28 07:46:53 +0000
@@ -34,6 +34,10 @@
* Added osutils.parent_directories(). (Ian Clatworthy)
+* TreeTransformBase no longer assumes that limbo is provided via disk.
+ DiskTreeTransform now provides disk functionality. (Aaron Bentley)
+
+
Internals
*********
=== modified file 'bzrlib/transform.py'
--- a/bzrlib/transform.py 2009-05-23 04:55:52 +0000
+++ b/bzrlib/transform.py 2009-05-28 07:46:53 +0000
@@ -76,24 +76,20 @@
class TreeTransformBase(object):
- """The base class for TreeTransform and TreeTransformBase"""
+ """The base class for TreeTransform and its kin."""
- def __init__(self, tree, limbodir, pb=DummyProgress(),
+ def __init__(self, tree, pb=DummyProgress(),
case_sensitive=True):
"""Constructor.
:param tree: The tree that will be transformed, but not necessarily
the output tree.
- :param limbodir: A directory where new files can be stored until
- they are installed in their proper places
:param pb: A ProgressBar indicating how much progress is being made
:param case_sensitive: If True, the target of the transform is
case sensitive, not just case preserving.
"""
object.__init__(self)
self._tree = tree
- self._limbodir = limbodir
- self._deletiondir = None
self._id_number = 0
# mapping of trans_id -> new basename
self._new_name = {}
@@ -101,15 +97,6 @@
self._new_parent = {}
# mapping of trans_id with new contents -> new file_kind
self._new_contents = {}
- # A mapping of transform ids to their limbo filename
- self._limbo_files = {}
- # A mapping of transform ids to a set of the transform ids of children
- # that their limbo directory has
- self._limbo_children = {}
- # Map transform ids to maps of child filename to child transform id
- self._limbo_children_names = {}
- # List of transform ids that need to be renamed from limbo into place
- self._needs_rename = set()
# Set of trans_ids whose contents will be removed
self._removed_contents = set()
# Mapping of trans_id -> new execute-bit value
@@ -128,10 +115,6 @@
self._tree_path_ids = {}
# Mapping trans_id -> path in old tree
self._tree_id_paths = {}
- # Cache of realpath results, to speed up canonical_path
- self._realpaths = {}
- # Cache of relpath results, to speed up canonical_path
- self._relpaths = {}
# The trans_id that will be used as the tree root
root_id = tree.get_root_id()
if root_id is not None:
@@ -147,42 +130,22 @@
# A counter of how many files have been renamed
self.rename_count = 0
+ def finalize(self):
+ """Release the working tree lock, if held.
+
+ This is required if apply has not been invoked, but can be invoked
+ even after apply.
+ """
+ if self._tree is None:
+ return
+ self._tree.unlock()
+ self._tree = None
+
def __get_root(self):
return self._new_root
root = property(__get_root)
- def finalize(self):
- """Release the working tree lock, if held, clean up limbo dir.
-
- This is required if apply has not been invoked, but can be invoked
- even after apply.
- """
- if self._tree is None:
- return
- try:
- entries = [(self._limbo_name(t), t, k) for t, k in
- self._new_contents.iteritems()]
- entries.sort(reverse=True)
- for path, trans_id, kind in entries:
- if kind == "directory":
- os.rmdir(path)
- else:
- os.unlink(path)
- try:
- os.rmdir(self._limbodir)
- except OSError:
- # We don't especially care *why* the dir is immortal.
- raise ImmortalLimbo(self._limbodir)
- try:
- if self._deletiondir is not None:
- os.rmdir(self._deletiondir)
- except OSError:
- raise errors.ImmortalPendingDeletion(self._deletiondir)
- finally:
- self._tree.unlock()
- self._tree = None
-
def _assign_id(self):
"""Produce a new tranform id"""
new_id = "new-%s" % self._id_number
@@ -200,37 +163,12 @@
"""Change the path that is assigned to a transaction id."""
if trans_id == self._new_root:
raise CantMoveRoot
- previous_parent = self._new_parent.get(trans_id)
- previous_name = self._new_name.get(trans_id)
self._new_name[trans_id] = name
self._new_parent[trans_id] = parent
if parent == ROOT_PARENT:
if self._new_root is not None:
raise ValueError("Cannot have multiple roots.")
self._new_root = trans_id
- if (trans_id in self._limbo_files and
- trans_id not in self._needs_rename):
- self._rename_in_limbo([trans_id])
- self._limbo_children[previous_parent].remove(trans_id)
- del self._limbo_children_names[previous_parent][previous_name]
-
- def _rename_in_limbo(self, trans_ids):
- """Fix limbo names so that the right final path is produced.
-
- This means we outsmarted ourselves-- we tried to avoid renaming
- these files later by creating them with their final names in their
- final parents. But now the previous name or parent is no longer
- suitable, so we have to rename them.
-
- Even for trans_ids that have no new contents, we must remove their
- entries from _limbo_files, because they are now stale.
- """
- for trans_id in trans_ids:
- old_path = self._limbo_files.pop(trans_id)
- if trans_id not in self._new_contents:
- continue
- new_path = self._limbo_name(trans_id)
- os.rename(old_path, new_path)
def adjust_root_path(self, name, parent):
"""Emulate moving the root by moving all children, instead.
@@ -298,25 +236,6 @@
else:
return self.trans_id_tree_file_id(file_id)
- def canonical_path(self, path):
- """Get the canonical tree-relative path"""
- # don't follow final symlinks
- abs = self._tree.abspath(path)
- if abs in self._relpaths:
- return self._relpaths[abs]
- dirname, basename = os.path.split(abs)
- if dirname not in self._realpaths:
- self._realpaths[dirname] = os.path.realpath(dirname)
- dirname = self._realpaths[dirname]
- abs = pathjoin(dirname, basename)
- if dirname in self._relpaths:
- relpath = pathjoin(self._relpaths[dirname], basename)
- relpath = relpath.rstrip('/\\')
- else:
- relpath = self._tree.relpath(abs)
- self._relpaths[abs] = relpath
- return relpath
-
def trans_id_tree_path(self, path):
"""Determine (and maybe set) the transaction ID for a tree path."""
path = self.canonical_path(path)
@@ -332,113 +251,6 @@
return ROOT_PARENT
return self.trans_id_tree_path(os.path.dirname(path))
- def create_file(self, contents, trans_id, mode_id=None):
- """Schedule creation of a new file.
-
- See also new_file.
-
- Contents is an iterator of strings, all of which will be written
- to the target destination.
-
- New file takes the permissions of any existing file with that id,
- unless mode_id is specified.
- """
- name = self._limbo_name(trans_id)
- f = open(name, 'wb')
- try:
- try:
- unique_add(self._new_contents, trans_id, 'file')
- except:
- # Clean up the file, it never got registered so
- # TreeTransform.finalize() won't clean it up.
- f.close()
- os.unlink(name)
- raise
-
- f.writelines(contents)
- finally:
- f.close()
- self._set_mode(trans_id, mode_id, S_ISREG)
-
- def _set_mode(self, trans_id, mode_id, typefunc):
- """Set the mode of new file contents.
- The mode_id is the existing file to get the mode from (often the same
- as trans_id). The operation is only performed if there's a mode match
- according to typefunc.
- """
- if mode_id is None:
- mode_id = trans_id
- try:
- old_path = self._tree_id_paths[mode_id]
- except KeyError:
- return
- try:
- mode = os.stat(self._tree.abspath(old_path)).st_mode
- except OSError, e:
- if e.errno in (errno.ENOENT, errno.ENOTDIR):
- # Either old_path doesn't exist, or the parent of the
- # target is not a directory (but will be one eventually)
- # Either way, we know it doesn't exist *right now*
- # See also bug #248448
- return
- else:
- raise
- if typefunc(mode):
- os.chmod(self._limbo_name(trans_id), mode)
-
- def create_hardlink(self, path, trans_id):
- """Schedule creation of a hard link"""
- name = self._limbo_name(trans_id)
- try:
- os.link(path, name)
- except OSError, e:
- if e.errno != errno.EPERM:
- raise
- raise errors.HardLinkNotSupported(path)
- try:
- unique_add(self._new_contents, trans_id, 'file')
- except:
- # Clean up the file, it never got registered so
- # TreeTransform.finalize() won't clean it up.
- os.unlink(name)
- raise
-
- def create_directory(self, trans_id):
- """Schedule creation of a new directory.
-
- See also new_directory.
- """
- os.mkdir(self._limbo_name(trans_id))
- unique_add(self._new_contents, trans_id, 'directory')
-
- def create_symlink(self, target, trans_id):
- """Schedule creation of a new symbolic link.
-
- target is a bytestring.
- See also new_symlink.
- """
- if has_symlinks():
- os.symlink(target, self._limbo_name(trans_id))
- unique_add(self._new_contents, trans_id, 'symlink')
- else:
- try:
- path = FinalPaths(self).get_path(trans_id)
- except KeyError:
- path = None
- raise UnableCreateSymlink(path=path)
-
- def cancel_creation(self, trans_id):
- """Cancel the creation of new file contents."""
- del self._new_contents[trans_id]
- children = self._limbo_children.get(trans_id)
- # if this is a limbo directory with children, move them before removing
- # the directory
- if children is not None:
- self._rename_in_limbo(children)
- del self._limbo_children[trans_id]
- del self._limbo_children_names[trans_id]
- delete_any(self._limbo_name(trans_id))
-
def delete_contents(self, trans_id):
"""Schedule the contents of a path entry for deletion"""
self.tree_kind(trans_id)
@@ -518,22 +330,6 @@
new_ids.update(changed_kind)
return sorted(FinalPaths(self).get_paths(new_ids))
- def tree_kind(self, trans_id):
- """Determine the file kind in the working tree.
-
- Raises NoSuchFile if the file does not exist
- """
- path = self._tree_id_paths.get(trans_id)
- if path is None:
- raise NoSuchFile(None)
- try:
- return file_kind(self._tree.abspath(path))
- except OSError, e:
- if e.errno != errno.ENOENT:
- raise
- else:
- raise NoSuchFile(path)
-
def final_kind(self, trans_id):
"""Determine the final file kind, after any changes applied.
@@ -667,26 +463,6 @@
# ensure that all children are registered with the transaction
list(self.iter_tree_children(parent_id))
- def iter_tree_children(self, parent_id):
- """Iterate through the entry's tree children, if any"""
- try:
- path = self._tree_id_paths[parent_id]
- except KeyError:
- return
- try:
- children = os.listdir(self._tree.abspath(path))
- except OSError, e:
- if not (osutils._is_error_enotdir(e)
- or e.errno in (errno.ENOENT, errno.ESRCH)):
- raise
- return
-
- for child in children:
- childpath = joinpath(path, child)
- if self._tree.is_control_filename(childpath):
- continue
- yield self.trans_id_tree_path(childpath)
-
def has_named_child(self, by_parent, parent_id, name):
try:
children = by_parent[parent_id]
@@ -867,50 +643,6 @@
return True
return False
- def _limbo_name(self, trans_id):
- """Generate the limbo name of a file"""
- limbo_name = self._limbo_files.get(trans_id)
- if limbo_name is not None:
- return limbo_name
- parent = self._new_parent.get(trans_id)
- # if the parent directory is already in limbo (e.g. when building a
- # tree), choose a limbo name inside the parent, to reduce further
- # renames.
- use_direct_path = False
- if self._new_contents.get(parent) == 'directory':
- filename = self._new_name.get(trans_id)
- if filename is not None:
- if parent not in self._limbo_children:
- self._limbo_children[parent] = set()
- self._limbo_children_names[parent] = {}
- use_direct_path = True
- # the direct path can only be used if no other file has
- # already taken this pathname, i.e. if the name is unused, or
- # if it is already associated with this trans_id.
- elif self._case_sensitive_target:
- if (self._limbo_children_names[parent].get(filename)
- in (trans_id, None)):
- use_direct_path = True
- else:
- for l_filename, l_trans_id in\
- self._limbo_children_names[parent].iteritems():
- if l_trans_id == trans_id:
- continue
- if l_filename.lower() == filename.lower():
- break
- else:
- use_direct_path = True
-
- if use_direct_path:
- limbo_name = pathjoin(self._limbo_files[parent], filename)
- self._limbo_children[parent].add(trans_id)
- self._limbo_children_names[parent][filename] = trans_id
- else:
- limbo_name = pathjoin(self._limbodir, trans_id)
- self._needs_rename.add(trans_id)
- self._limbo_files[trans_id] = limbo_name
- return limbo_name
-
def _set_executability(self, path, trans_id):
"""Set the executability of versioned files """
if supports_executable():
@@ -1176,21 +908,17 @@
(('attribs',),))
for trans_id, kind in self._new_contents.items():
if kind == 'file':
- cur_file = open(self._limbo_name(trans_id), 'rb')
- try:
- lines = osutils.chunks_to_lines(cur_file.readlines())
- finally:
- cur_file.close()
+ lines = osutils.chunks_to_lines(
+ self._read_file_chunks(trans_id))
parents = self._get_parents_lines(trans_id)
mpdiff = multiparent.MultiParent.from_lines(lines, parents)
content = ''.join(mpdiff.to_patch())
if kind == 'directory':
content = ''
if kind == 'symlink':
- content = os.readlink(self._limbo_name(trans_id))
+ content = self._read_symlink_target(trans_id)
yield serializer.bytes_record(content, ((trans_id, kind),))
-
def deserialize(self, records):
"""Deserialize a stored TreeTransform.
@@ -1227,7 +955,228 @@
self.create_symlink(content.decode('utf-8'), trans_id)
-class TreeTransform(TreeTransformBase):
+class DiskTreeTransform(TreeTransformBase):
+ """Tree transform storing its contents on disk."""
+
+ def __init__(self, tree, limbodir, pb=DummyProgress(),
+ case_sensitive=True):
+ """Constructor.
+ :param tree: The tree that will be transformed, but not necessarily
+ the output tree.
+ :param limbodir: A directory where new files can be stored until
+ they are installed in their proper places
+ :param pb: A ProgressBar indicating how much progress is being made
+ :param case_sensitive: If True, the target of the transform is
+ case sensitive, not just case preserving.
+ """
+ TreeTransformBase.__init__(self, tree, pb, case_sensitive)
+ self._limbodir = limbodir
+ self._deletiondir = None
+ # A mapping of transform ids to their limbo filename
+ self._limbo_files = {}
+ # A mapping of transform ids to a set of the transform ids of children
+ # that their limbo directory has
+ self._limbo_children = {}
+ # Map transform ids to maps of child filename to child transform id
+ self._limbo_children_names = {}
+ # List of transform ids that need to be renamed from limbo into place
+ self._needs_rename = set()
+
+ def finalize(self):
+ """Release the working tree lock, if held, clean up limbo dir.
+
+ This is required if apply has not been invoked, but can be invoked
+ even after apply.
+ """
+ if self._tree is None:
+ return
+ try:
+ entries = [(self._limbo_name(t), t, k) for t, k in
+ self._new_contents.iteritems()]
+ entries.sort(reverse=True)
+ for path, trans_id, kind in entries:
+ if kind == "directory":
+ os.rmdir(path)
+ else:
+ os.unlink(path)
+ try:
+ os.rmdir(self._limbodir)
+ except OSError:
+ # We don't especially care *why* the dir is immortal.
+ raise ImmortalLimbo(self._limbodir)
+ try:
+ if self._deletiondir is not None:
+ os.rmdir(self._deletiondir)
+ except OSError:
+ raise errors.ImmortalPendingDeletion(self._deletiondir)
+ finally:
+ TreeTransformBase.finalize(self)
+
+ def _limbo_name(self, trans_id):
+ """Generate the limbo name of a file"""
+ limbo_name = self._limbo_files.get(trans_id)
+ if limbo_name is not None:
+ return limbo_name
+ parent = self._new_parent.get(trans_id)
+ # if the parent directory is already in limbo (e.g. when building a
+ # tree), choose a limbo name inside the parent, to reduce further
+ # renames.
+ use_direct_path = False
+ if self._new_contents.get(parent) == 'directory':
+ filename = self._new_name.get(trans_id)
+ if filename is not None:
+ if parent not in self._limbo_children:
+ self._limbo_children[parent] = set()
+ self._limbo_children_names[parent] = {}
+ use_direct_path = True
+ # the direct path can only be used if no other file has
+ # already taken this pathname, i.e. if the name is unused, or
+ # if it is already associated with this trans_id.
+ elif self._case_sensitive_target:
+ if (self._limbo_children_names[parent].get(filename)
+ in (trans_id, None)):
+ use_direct_path = True
+ else:
+ for l_filename, l_trans_id in\
+ self._limbo_children_names[parent].iteritems():
+ if l_trans_id == trans_id:
+ continue
+ if l_filename.lower() == filename.lower():
+ break
+ else:
+ use_direct_path = True
+
+ if use_direct_path:
+ limbo_name = pathjoin(self._limbo_files[parent], filename)
+ self._limbo_children[parent].add(trans_id)
+ self._limbo_children_names[parent][filename] = trans_id
+ else:
+ limbo_name = pathjoin(self._limbodir, trans_id)
+ self._needs_rename.add(trans_id)
+ self._limbo_files[trans_id] = limbo_name
+ return limbo_name
+
+ def adjust_path(self, name, parent, trans_id):
+ previous_parent = self._new_parent.get(trans_id)
+ previous_name = self._new_name.get(trans_id)
+ TreeTransformBase.adjust_path(self, name, parent, trans_id)
+ if (trans_id in self._limbo_files and
+ trans_id not in self._needs_rename):
+ self._rename_in_limbo([trans_id])
+ self._limbo_children[previous_parent].remove(trans_id)
+ del self._limbo_children_names[previous_parent][previous_name]
+
+ def _rename_in_limbo(self, trans_ids):
+ """Fix limbo names so that the right final path is produced.
+
+ This means we outsmarted ourselves-- we tried to avoid renaming
+ these files later by creating them with their final names in their
+ final parents. But now the previous name or parent is no longer
+ suitable, so we have to rename them.
+
+ Even for trans_ids that have no new contents, we must remove their
+ entries from _limbo_files, because they are now stale.
+ """
+ for trans_id in trans_ids:
+ old_path = self._limbo_files.pop(trans_id)
+ if trans_id not in self._new_contents:
+ continue
+ new_path = self._limbo_name(trans_id)
+ os.rename(old_path, new_path)
+
+ def create_file(self, contents, trans_id, mode_id=None):
+ """Schedule creation of a new file.
+
+ See also new_file.
+
+ Contents is an iterator of strings, all of which will be written
+ to the target destination.
+
+ New file takes the permissions of any existing file with that id,
+ unless mode_id is specified.
+ """
+ name = self._limbo_name(trans_id)
+ f = open(name, 'wb')
+ try:
+ try:
+ unique_add(self._new_contents, trans_id, 'file')
+ except:
+ # Clean up the file, it never got registered so
+ # TreeTransform.finalize() won't clean it up.
+ f.close()
+ os.unlink(name)
+ raise
+
+ f.writelines(contents)
+ finally:
+ f.close()
+ self._set_mode(trans_id, mode_id, S_ISREG)
+
+ def _read_file_chunks(self, trans_id):
+ cur_file = open(self._limbo_name(trans_id), 'rb')
+ try:
+ return cur_file.readlines()
+ finally:
+ cur_file.close()
+
+ def _read_symlink_target(self, trans_id):
+ return os.readlink(self._limbo_name(trans_id))
+
+ def create_hardlink(self, path, trans_id):
+ """Schedule creation of a hard link"""
+ name = self._limbo_name(trans_id)
+ try:
+ os.link(path, name)
+ except OSError, e:
+ if e.errno != errno.EPERM:
+ raise
+ raise errors.HardLinkNotSupported(path)
+ try:
+ unique_add(self._new_contents, trans_id, 'file')
+ except:
+ # Clean up the file, it never got registered so
+ # TreeTransform.finalize() won't clean it up.
+ os.unlink(name)
+ raise
+
+ def create_directory(self, trans_id):
+ """Schedule creation of a new directory.
+
+ See also new_directory.
+ """
+ os.mkdir(self._limbo_name(trans_id))
+ unique_add(self._new_contents, trans_id, 'directory')
+
+ def create_symlink(self, target, trans_id):
+ """Schedule creation of a new symbolic link.
+
+ target is a bytestring.
+ See also new_symlink.
+ """
+ if has_symlinks():
+ os.symlink(target, self._limbo_name(trans_id))
+ unique_add(self._new_contents, trans_id, 'symlink')
+ else:
+ try:
+ path = FinalPaths(self).get_path(trans_id)
+ except KeyError:
+ path = None
+ raise UnableCreateSymlink(path=path)
+
+ def cancel_creation(self, trans_id):
+ """Cancel the creation of new file contents."""
+ del self._new_contents[trans_id]
+ children = self._limbo_children.get(trans_id)
+ # if this is a limbo directory with children, move them before removing
+ # the directory
+ if children is not None:
+ self._rename_in_limbo(children)
+ del self._limbo_children[trans_id]
+ del self._limbo_children_names[trans_id]
+ delete_any(self._limbo_name(trans_id))
+
+
+class TreeTransform(DiskTreeTransform):
"""Represent a tree transformation.
This object is designed to support incremental generation of the transform,
@@ -1319,10 +1268,96 @@
tree.unlock()
raise
- TreeTransformBase.__init__(self, tree, limbodir, pb,
+ # Cache of realpath results, to speed up canonical_path
+ self._realpaths = {}
+ # Cache of relpath results, to speed up canonical_path
+ self._relpaths = {}
+ DiskTreeTransform.__init__(self, tree, limbodir, pb,
tree.case_sensitive)
self._deletiondir = deletiondir
+ def canonical_path(self, path):
+ """Get the canonical tree-relative path"""
+ # don't follow final symlinks
+ abs = self._tree.abspath(path)
+ if abs in self._relpaths:
+ return self._relpaths[abs]
+ dirname, basename = os.path.split(abs)
+ if dirname not in self._realpaths:
+ self._realpaths[dirname] = os.path.realpath(dirname)
+ dirname = self._realpaths[dirname]
+ abs = pathjoin(dirname, basename)
+ if dirname in self._relpaths:
+ relpath = pathjoin(self._relpaths[dirname], basename)
+ relpath = relpath.rstrip('/\\')
+ else:
+ relpath = self._tree.relpath(abs)
+ self._relpaths[abs] = relpath
+ return relpath
+
+ def tree_kind(self, trans_id):
+ """Determine the file kind in the working tree.
+
+ Raises NoSuchFile if the file does not exist
+ """
+ path = self._tree_id_paths.get(trans_id)
+ if path is None:
+ raise NoSuchFile(None)
+ try:
+ return file_kind(self._tree.abspath(path))
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ raise
+ else:
+ raise NoSuchFile(path)
+
+ def _set_mode(self, trans_id, mode_id, typefunc):
+ """Set the mode of new file contents.
+ The mode_id is the existing file to get the mode from (often the same
+ as trans_id). The operation is only performed if there's a mode match
+ according to typefunc.
+ """
+ if mode_id is None:
+ mode_id = trans_id
+ try:
+ old_path = self._tree_id_paths[mode_id]
+ except KeyError:
+ return
+ try:
+ mode = os.stat(self._tree.abspath(old_path)).st_mode
+ except OSError, e:
+ if e.errno in (errno.ENOENT, errno.ENOTDIR):
+ # Either old_path doesn't exist, or the parent of the
+ # target is not a directory (but will be one eventually)
+ # Either way, we know it doesn't exist *right now*
+ # See also bug #248448
+ return
+ else:
+ raise
+ if typefunc(mode):
+ os.chmod(self._limbo_name(trans_id), mode)
+
+ def iter_tree_children(self, parent_id):
+ """Iterate through the entry's tree children, if any"""
+ try:
+ path = self._tree_id_paths[parent_id]
+ except KeyError:
+ return
+ try:
+ children = os.listdir(self._tree.abspath(path))
+ except OSError, e:
+ if not (osutils._is_error_enotdir(e)
+ or e.errno in (errno.ENOENT, errno.ESRCH)):
+ raise
+ return
+
+ for child in children:
+ childpath = joinpath(path, child)
+ if self._tree.is_control_filename(childpath):
+ continue
+ yield self.trans_id_tree_path(childpath)
+
+
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
"""Apply all changes to the inventory and filesystem.
@@ -1505,7 +1540,7 @@
return modified_paths
-class TransformPreview(TreeTransformBase):
+class TransformPreview(DiskTreeTransform):
"""A TreeTransform for generating preview trees.
Unlike TreeTransform, this version works when the input tree is a
@@ -1516,7 +1551,7 @@
def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
tree.lock_read()
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
- TreeTransformBase.__init__(self, tree, limbodir, pb, case_sensitive)
+ DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
def canonical_path(self, path):
return path
More information about the bazaar-commits
mailing list