Merge 0.4.
[jelmer/subvertpy.git] / tree.py
diff --git a/tree.py b/tree.py
index 1d3b842ae20f15cd45bcc1d5bbd2b335b5a69f0b..30edf1433b40c03e8550d0b24fbd8ca11d3f7658 100644 (file)
--- a/tree.py
+++ b/tree.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2005-2006 Jelmer Vernooij <jelmer@samba.org>
+# Copyright (C) 2005-2008 Jelmer Vernooij <jelmer@samba.org>
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -28,10 +28,8 @@ import md5
 from cStringIO import StringIO
 import urllib
 
-import svn.core, svn.wc, svn.delta
-from svn.core import Pool
-
-from bzrlib.plugins.svn import errors, properties, core
+from bzrlib.plugins.svn.delta import apply_txdelta_handler
+from bzrlib.plugins.svn import core, errors, properties, wc
 
 def parse_externals_description(base_url, val):
     """Parse an svn:externals property value.
@@ -97,101 +95,100 @@ def inventory_add_external(inv, parent_id, path, revid, ref_revnum, url):
     inv.add(ie)
 
 
-# Deal with Subversion 1.5 and the patched Subversion 1.4 (which are 
-# slightly different).
-
-if hasattr(svn.delta, 'tx_invoke_window_handler'):
-    def apply_txdelta_handler(src_stream, target_stream, pool):
-        assert hasattr(src_stream, 'read')
-        assert hasattr(target_stream, 'write')
-        window_handler, baton = svn.delta.tx_apply(src_stream, target_stream, 
-                                                   None, pool)
-
-        def wrapper(window):
-            window_handler(window, baton)
-
-        return wrapper
-else:
-    def apply_txdelta_handler(src_stream, target_stream, pool):
-        assert hasattr(src_stream, 'read')
-        assert hasattr(target_stream, 'write')
-        ret = svn.delta.svn_txdelta_apply(src_stream, target_stream, None, pool)
-
-        def wrapper(window):
-            svn.delta.invoke_txdelta_window_handler(
-                ret[1], window, ret[2])
-
-        return wrapper
-
-
 class SvnRevisionTree(RevisionTree):
     """A tree that existed in a historical Subversion revision."""
     def __init__(self, repository, revision_id):
         self._repository = repository
         self._revision_id = revision_id
-        pool = Pool()
         (self.branch_path, self.revnum, mapping) = repository.lookup_revision_id(revision_id)
         self._inventory = Inventory()
         self.id_map = repository.get_fileid_map(self.revnum, self.branch_path, 
                                                 mapping)
-        editor = TreeBuildEditor(self, pool)
+        editor = TreeBuildEditor(self)
         self.file_data = {}
         root_repos = repository.transport.get_svn_repos_root()
-        reporter = repository.transport.do_switch(
-                self.revnum, True, 
-                urlutils.join(root_repos, self.branch_path), editor, pool)
-        reporter.set_path("", 0, True, None, pool)
-        reporter.finish_report(pool)
-        pool.destroy()
+        conn = repository.transport.get_connection()
+        reporter = conn.do_switch(
+                self.revnum, "", True, 
+                urlutils.join(root_repos, self.branch_path), editor)
+        try:
+            reporter.set_path("", 0, True)
+            reporter.finish()
+        finally:
+            repository.transport.add_connection(conn)
 
     def get_file_lines(self, file_id):
         return osutils.split_lines(self.file_data[file_id])
 
 
-class TreeBuildEditor(svn.delta.Editor):
+class TreeBuildEditor:
     """Builds a tree given Subversion tree transform calls."""
-    def __init__(self, tree, pool):
+    def __init__(self, tree):
         self.tree = tree
         self.repository = tree._repository
         self.last_revnum = {}
-        self.dir_revnum = {}
-        self.dir_ignores = {}
-        self.pool = pool
 
     def set_target_revision(self, revnum):
         self.revnum = revnum
 
-    def open_root(self, revnum, baton):
+    def open_root(self, revnum):
         file_id, revision_id = self.tree.id_map[""]
         ie = self.tree._inventory.add_path("", 'directory', file_id)
         ie.revision = revision_id
         self.tree._inventory.revision_id = revision_id
-        return file_id
+        return DirectoryTreeEditor(self.tree, file_id)
 
-    def add_directory(self, path, parent_baton, copyfrom_path, copyfrom_revnum, pool):
+    def close(self):
+        pass
+
+    def abort(self):
+        pass
+
+class DirectoryTreeEditor:
+    def __init__(self, tree, file_id):
+        self.tree = tree
+        self.file_id = file_id
+
+    def add_directory(self, path, copyfrom_path=None, copyfrom_revnum=-1):
         path = path.decode("utf-8")
         file_id, revision_id = self.tree.id_map[path]
         ie = self.tree._inventory.add_path(path, 'directory', file_id)
         ie.revision = revision_id
-        return file_id
+        return DirectoryTreeEditor(self.tree, file_id)
 
-    def change_dir_prop(self, id, name, value, pool):
-        if name == properties.PROP_ENTRY_COMMITTED_REV:
-            self.dir_revnum[id] = int(value)
-        elif name == properties.PROP_IGNORE:
-            self.dir_ignores[id] = value
-        elif name in (properties.PROP_ENTRY_COMMITTED_DATE,
+    def change_prop(self, name, value):
+        if name in (properties.PROP_ENTRY_COMMITTED_DATE,
+                      properties.PROP_ENTRY_COMMITTED_REV,
                       properties.PROP_ENTRY_LAST_AUTHOR,
                       properties.PROP_ENTRY_LOCK_TOKEN,
                       properties.PROP_ENTRY_UUID,
-                      properties.PROP_EXECUTABLE):
+                      properties.PROP_EXECUTABLE,
+                      properties.PROP_IGNORE):
             pass
         elif name.startswith(properties.PROP_WC_PREFIX):
             pass
         elif name.startswith(properties.PROP_PREFIX):
             mutter('unsupported dir property %r', name)
 
-    def change_file_prop(self, id, name, value, pool):
+    def add_file(self, path, copyfrom_path=None, copyfrom_revnum=-1):
+        path = path.decode("utf-8")
+        return FileTreeEditor(self.tree, path)
+
+    def close(self):
+        pass
+
+
+class FileTreeEditor:
+    def __init__(self, tree, path):
+        self.tree = tree
+        self.path = path
+        self.is_executable = False
+        self.is_symlink = False
+        self.last_file_rev = None
+
+    def change_prop(self, name, value):
+        from mapping import SVN_PROP_BZR_PREFIX
+
         if name == properties.PROP_EXECUTABLE:
             self.is_executable = (value != None)
         elif name == properties.PROP_SPECIAL:
@@ -211,22 +208,12 @@ class TreeBuildEditor(svn.delta.Editor):
         elif name.startswith(properties.PROP_PREFIX):
             mutter('unsupported file property %r', name)
 
-    def add_file(self, path, parent_id, copyfrom_path, copyfrom_revnum, baton):
-        path = path.decode("utf-8")
-        self.is_symlink = False
-        self.is_executable = False
-        return path
-
-    def close_dir(self, id):
-        if id in self.tree._inventory and self.dir_ignores.has_key(id):
-            self.tree._inventory[id].ignores = self.dir_ignores[id]
-
-    def close_file(self, path, checksum):
-        file_id, revision_id = self.tree.id_map[path]
+    def close(self, checksum=None):
+        file_id, revision_id = self.tree.id_map[self.path]
         if self.is_symlink:
-            ie = self.tree._inventory.add_path(path, 'symlink', file_id)
+            ie = self.tree._inventory.add_path(self.path, 'symlink', file_id)
         else:
-            ie = self.tree._inventory.add_path(path, 'file', file_id)
+            ie = self.tree._inventory.add_path(self.path, 'file', file_id)
         ie.revision = revision_id
 
         if self.file_stream:
@@ -253,15 +240,9 @@ class TreeBuildEditor(svn.delta.Editor):
 
         self.file_stream = None
 
-    def close_edit(self):
-        pass
-
-    def abort_edit(self):
-        pass
-
-    def apply_textdelta(self, file_id, base_checksum):
+    def apply_textdelta(self, base_checksum=None):
         self.file_stream = StringIO()
-        return apply_txdelta_handler(StringIO(""), self.file_stream, self.pool)
+        return apply_txdelta_handler("", self.file_stream)
 
 
 class SvnBasisTree(RevisionTree):
@@ -277,10 +258,8 @@ class SvnBasisTree(RevisionTree):
         self._inventory = Inventory(root_id=None)
         self._repository = workingtree.branch.repository
 
-        def add_file_to_inv(relpath, id, revid, wc):
-            props = svn.wc.get_prop_diffs(self.workingtree.abspath(relpath).encode("utf-8"), wc)
-            if isinstance(props, list): # Subversion 1.5
-                props = props[1]
+        def add_file_to_inv(relpath, id, revid, adm):
+            (delta_props, props) = adm.get_prop_diffs(self.workingtree.abspath(relpath))
             if props.has_key(properties.PROP_SPECIAL):
                 ie = self._inventory.add_path(relpath, 'symlink', id)
                 ie.symlink_target = open(self._abspath(relpath)).read()[len("link "):]
@@ -299,14 +278,14 @@ class SvnBasisTree(RevisionTree):
 
         def find_ids(entry):
             relpath = urllib.unquote(entry.url[len(entry.repos):].strip("/"))
-            if entry.schedule in (svn.wc.schedule_normal
-                                  svn.wc.schedule_delete
-                                  svn.wc.schedule_replace):
+            if entry.schedule in (wc.SCHEDULE_NORMAL
+                                  wc.SCHEDULE_DELETE
+                                  wc.SCHEDULE_REPLACE):
                 return self.id_map[workingtree.branch.unprefix(relpath.decode("utf-8"))]
             return (None, None)
 
-        def add_dir_to_inv(relpath, wc, parent_id):
-            entries = svn.wc.entries_read(wc, False)
+        def add_dir_to_inv(relpath, adm, parent_id):
+            entries = adm.entries_read(False)
             entry = entries[""]
             (id, revid) = find_ids(entry)
             if id == None:
@@ -331,26 +310,26 @@ class SvnBasisTree(RevisionTree):
                 assert entry
                 
                 if entry.kind == core.NODE_DIR:
-                    subwc = svn.wc.adm_open3(wc
+                    subwc = wc.WorkingCopy(adm
                             self.workingtree.abspath(subrelpath), 
                                              False, 0, None)
                     try:
                         add_dir_to_inv(subrelpath, subwc, id)
                     finally:
-                        svn.wc.adm_close(subwc)
+                        subwc.close()
                 else:
                     (subid, subrevid) = find_ids(entry)
                     if subid is not None:
-                        add_file_to_inv(subrelpath, subid, subrevid, wc)
+                        add_file_to_inv(subrelpath, subid, subrevid, adm)
 
-        wc = workingtree._get_wc() 
+        adm = workingtree._get_wc() 
         try:
-            add_dir_to_inv(u"", wc, None)
+            add_dir_to_inv(u"", adm, None)
         finally:
-            svn.wc.adm_close(wc)
+            adm.close()
 
     def _abspath(self, relpath):
-        return svn.wc.get_pristine_copy_path(self.workingtree.abspath(relpath).encode("utf-8"))
+        return wc.get_pristine_copy_path(self.workingtree.abspath(relpath).encode("utf-8"))
 
     def get_file_lines(self, file_id):
         base_copy = self._abspath(self.id2path(file_id))