1 # Copyright (C) 2005-2006 Jelmer Vernooij <jelmer@samba.org>
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 from binascii import hexlify
18 from bzrlib.bzrdir import BzrDirFormat
19 from bzrlib.errors import NotBranchError, NoSuchFile
20 from bzrlib.inventory import (Inventory, InventoryDirectory, InventoryFile,
22 from bzrlib.lockable_files import TransportLock, LockableFiles
23 from bzrlib.lockdir import LockDir
24 import bzrlib.osutils as osutils
25 from bzrlib.progress import DummyProgress
26 from bzrlib.revision import NULL_REVISION
27 from bzrlib.trace import mutter
28 from bzrlib.revisiontree import RevisionTree
32 from cStringIO import StringIO
34 import svn.core, svn.wc, svn.delta, svn.ra
35 from svn.core import SubversionException, Pool
37 def apply_txdelta_handler(src_stream, target_stream, pool):
38 assert hasattr(src_stream, 'read')
39 assert hasattr(target_stream, 'write')
40 ret = svn.delta.svn_txdelta_apply(
47 svn.delta.invoke_txdelta_window_handler(
48 ret[1], window, ret[2])
52 class SvnRevisionTree(RevisionTree):
53 def __init__(self, repository, revision_id, inventory=None):
54 self._repository = repository
55 self._revision_id = revision_id
57 if revision_id == NULL_REVISION:
58 self._inventory = Inventory(ROOT_ID)
59 self._inventory.revision_id = NULL_REVISION
61 (self.branch_path, self.revnum) = repository.parse_revision_id(revision_id)
62 self._inventory = Inventory(ROOT_ID)
63 self._inventory.revision_id = revision_id
64 self.id_map = repository.get_fileid_map(self.revnum, self.branch_path)
65 self.editor = TreeBuildEditor(self, pool)
68 editor, baton = svn.delta.make_editor(self.editor, pool)
70 root_repos = repository.transport.get_repos_root()
71 reporter, reporter_baton = repository.transport.do_switch(
72 self.revnum, "", True,
73 os.path.join(root_repos, self.branch_path), editor, baton, pool)
75 svn.ra.reporter2_invoke_set_path(reporter, reporter_baton, "", 0, True, None, pool)
77 svn.ra.reporter2_invoke_finish_report(reporter, reporter_baton, pool)
81 def get_file_lines(self, file_id):
82 return osutils.split_lines(self.file_data[file_id])
85 class TreeBuildEditor(svn.delta.Editor):
86 def __init__(self, tree, pool):
88 self.repository = tree._repository
94 def set_target_revision(self, revnum):
97 def open_root(self, revnum, baton):
100 def relpath(self, path):
101 bp, rp = self.tree._repository.scheme.unprefix(path)
102 if bp == self.tree.branch_path:
106 def add_directory(self, path, parent_baton, copyfrom_path, copyfrom_revnum, pool):
107 file_id, revision_id = self.tree.id_map[path]
108 ie = self.tree._inventory.add_path(path, 'directory', file_id)
110 ie.revision = revision_id
113 def change_dir_prop(self, id, name, value, pool):
114 from repository import (SVN_PROP_BZR_MERGE, SVN_PROP_SVK_MERGE,
115 SVN_PROP_BZR_REVPROP_PREFIX)
117 if name == svn.core.SVN_PROP_ENTRY_COMMITTED_REV:
118 self.dir_revnum[id] = int(value)
119 elif name == svn.core.SVN_PROP_IGNORE:
120 self.dir_ignores[id] = value
121 elif name == SVN_PROP_BZR_MERGE or name == SVN_PROP_SVK_MERGE:
123 mutter('%r set on non-root dir!' % SVN_PROP_BZR_MERGE)
125 elif name in (svn.core.SVN_PROP_ENTRY_COMMITTED_DATE,
126 svn.core.SVN_PROP_ENTRY_LAST_AUTHOR,
127 svn.core.SVN_PROP_ENTRY_LOCK_TOKEN,
128 svn.core.SVN_PROP_ENTRY_UUID,
129 svn.core.SVN_PROP_EXECUTABLE):
131 elif name.startswith(svn.core.SVN_PROP_WC_PREFIX):
133 elif name.startswith(SVN_PROP_BZR_REVPROP_PREFIX):
136 mutter('unsupported dir property %r' % name)
138 def change_file_prop(self, id, name, value, pool):
139 if name == svn.core.SVN_PROP_EXECUTABLE:
140 self.is_executable = (value != None)
141 elif name == svn.core.SVN_PROP_SPECIAL:
142 self.is_symlink = (value != None)
143 elif name == svn.core.SVN_PROP_ENTRY_COMMITTED_REV:
144 self.last_file_rev = int(value)
145 elif name in (svn.core.SVN_PROP_ENTRY_COMMITTED_DATE,
146 svn.core.SVN_PROP_ENTRY_LAST_AUTHOR,
147 svn.core.SVN_PROP_ENTRY_LOCK_TOKEN,
148 svn.core.SVN_PROP_ENTRY_UUID,
149 svn.core.SVN_PROP_MIME_TYPE):
151 elif name.startswith(svn.core.SVN_PROP_WC_PREFIX):
154 mutter('unsupported file property %r' % name)
156 def add_file(self, path, parent_id, copyfrom_path, copyfrom_revnum, baton):
157 self.is_symlink = False
158 self.is_executable = False
161 def close_dir(self, id):
162 if id in self.tree._inventory and self.dir_ignores.has_key(id):
163 self.tree._inventory[id].ignores = self.dir_ignores[id]
165 def close_file(self, path, checksum):
166 file_id, revision_id = self.tree.id_map[path]
169 ie = self.tree._inventory.add_path(path, 'symlink', file_id)
171 ie = self.tree._inventory.add_path(path, 'file', file_id)
172 ie.revision = revision_id
175 self.file_stream.seek(0)
176 file_data = self.file_stream.read()
180 actual_checksum = md5.new(file_data).hexdigest()
181 assert(checksum is None or checksum == actual_checksum,
182 "checksum mismatch: %r != %r" % (checksum, actual_checksum))
185 ie.symlink_target = file_data[len("link "):]
190 ie.text_sha1 = osutils.sha_string(file_data)
191 ie.text_size = len(file_data)
192 self.tree.file_data[file_id] = file_data
193 ie.executable = self.is_executable
195 self.file_stream = None
197 def close_edit(self):
200 def abort_edit(self):
203 def apply_textdelta(self, file_id, base_checksum):
204 self.file_stream = StringIO()
205 return apply_txdelta_handler(StringIO(""), self.file_stream, self.pool)
208 class SvnBasisTree(SvnRevisionTree):
209 """Optimized version of SvnRevisionTree."""
210 def __init__(self, workingtree, revid):
211 super(SvnBasisTree, self).__init__(workingtree.branch.repository,
213 self.workingtree = workingtree
215 def get_file_lines(self, file_id):
216 path = self.id2path(file_id)
217 base_copy = svn.wc.get_pristine_copy_path(self.workingtree.abspath(path))
218 return osutils.split_lines(open(base_copy).read())