1 # Copyright (C) 2005-2007 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 3 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
16 """Checkouts and working trees (working copies)."""
18 import bzrlib, bzrlib.add
19 from bzrlib import urlutils
20 from bzrlib.branch import PullResult
21 from bzrlib.bzrdir import BzrDirFormat, BzrDir
22 from bzrlib.errors import (InvalidRevisionId, NotBranchError, NoSuchFile,
23 NoRepositoryPresent, BzrError, UninitializableFormat,
25 from bzrlib.inventory import Inventory, InventoryFile, InventoryLink
26 from bzrlib.lockable_files import TransportLock, LockableFiles
27 from bzrlib.lockdir import LockDir
28 from bzrlib.osutils import file_kind, fingerprint_file, supports_executable
29 from bzrlib.revision import NULL_REVISION
30 from bzrlib.trace import mutter
31 from bzrlib.revisiontree import RevisionTree
32 from bzrlib.transport.local import LocalTransport
33 from bzrlib.workingtree import WorkingTree, WorkingTreeFormat
35 from bzrlib.plugins.svn import properties
36 from bzrlib.plugins.svn.branch import SvnBranch
37 from bzrlib.plugins.svn.commit import _revision_id_to_svk_feature
38 from bzrlib.plugins.svn.convert import SvnConverter
39 from bzrlib.plugins.svn.errors import LocalCommitsUnsupported, NoSvnRepositoryPresent
40 from bzrlib.plugins.svn.mapping import (SVN_PROP_BZR_ANCESTRY, SVN_PROP_BZR_FILEIDS,
41 SVN_PROP_BZR_REVISION_ID, SVN_PROP_BZR_REVISION_INFO,
42 generate_revision_metadata)
43 from bzrlib.plugins.svn.remote import SvnRemoteAccess
44 from bzrlib.plugins.svn.repository import SvnRepository
45 from bzrlib.plugins.svn.svk import SVN_PROP_SVK_MERGE, parse_svk_features, serialize_svk_features
46 from bzrlib.plugins.svn.mapping import escape_svn_path
47 from bzrlib.plugins.svn.transport import (SvnRaTransport, bzr_to_svn_url, create_svn_client,
49 from bzrlib.plugins.svn.tree import SvnBasisTree
54 import svn.core, svn.wc
55 from svn.core import SubversionException
57 from bzrlib.plugins.svn.errors import NoCheckoutSupport
58 from bzrlib.plugins.svn.format import get_rich_root_format
60 def generate_ignore_list(ignore_map):
61 """Create a list of ignores, ordered by directory.
63 :param ignore_map: Dictionary with paths as keys, patterns as values.
64 :return: list of ignores
67 keys = ignore_map.keys()
70 ignores.append("./" + os.path.join(k.strip("/"), ignore_map[k].strip("/")))
74 class SvnWorkingTree(WorkingTree):
75 """WorkingTree implementation that uses a Subversion Working Copy for storage."""
76 def __init__(self, bzrdir, local_path, branch):
77 self._format = SvnWorkingTreeFormat()
78 self.basedir = local_path
79 assert isinstance(self.basedir, unicode)
83 self.client_ctx = create_svn_client(bzrdir.svn_url)
84 self.client_ctx.log_msg_func2 = \
85 svn.client.svn_swig_py_get_commit_log_func
88 status = svn.wc.revision_status(self.basedir, None, True, None, None)
89 self.base_revnum = status.max_rev
90 self.base_tree = SvnBasisTree(self)
91 self.base_revid = branch.generate_revision_id(self.base_revnum)
93 self.read_working_inventory()
95 self.controldir = os.path.join(self.basedir, svn.wc.get_adm_dir(),
98 os.makedirs(self.controldir)
99 os.makedirs(os.path.join(self.controldir, 'lock'))
102 control_transport = bzrdir.transport.clone(urlutils.join(
103 svn.wc.get_adm_dir(), 'bzr'))
104 self._control_files = LockableFiles(control_transport, 'lock', LockDir)
106 def get_ignore_list(self):
107 ignores = set([svn.wc.get_adm_dir()])
108 ignores.update(svn.wc.get_default_ignores(svn_config))
110 def dir_add(wc, prefix, patprefix):
111 ignorestr = svn.wc.prop_get(properties.PROP_IGNORE,
112 self.abspath(prefix).rstrip("/"), wc)
113 if ignorestr is not None:
114 for pat in ignorestr.splitlines():
115 ignores.add(urlutils.joinpath(patprefix, pat))
117 entries = svn.wc.entries_read(wc, False)
118 for entry in entries:
122 # Ignore ignores on things that aren't directories
123 if entries[entry].kind != svn.core.svn_node_dir:
126 subprefix = os.path.join(prefix, entry)
128 subwc = svn.wc.adm_open3(wc, self.abspath(subprefix), False,
131 dir_add(subwc, subprefix, urlutils.joinpath(patprefix, entry))
133 svn.wc.adm_close(subwc)
143 def is_control_filename(self, path):
144 return svn.wc.is_adm_dir(path)
146 def apply_inventory_delta(self, changes):
147 raise NotImplementedError(self.apply_inventory_delta)
149 def update(self, change_reporter=None):
150 rev = svn.core.svn_opt_revision_t()
151 rev.kind = svn.core.svn_opt_revision_head
152 svn.client.update(self.basedir, rev, True, self.client_ctx)
154 def remove(self, files, verbose=False, to_file=None):
155 # FIXME: Use to_file argument
156 # FIXME: Use verbose argument
157 assert isinstance(files, list)
158 wc = self._get_wc(write_lock=True)
161 svn.wc.delete2(self.abspath(file), wc, None, None, None)
166 self._change_fileid_mapping(None, file)
167 self.read_working_inventory()
169 def _get_wc(self, relpath="", write_lock=False):
170 return svn.wc.adm_open3(None, self.abspath(relpath).rstrip("/"),
173 def _get_rel_wc(self, relpath, write_lock=False):
174 dir = os.path.dirname(relpath)
175 file = os.path.basename(relpath)
176 return (self._get_wc(dir, write_lock), file)
178 def move(self, from_paths, to_dir=None, after=False, **kwargs):
179 # FIXME: Use after argument
181 revt = svn.core.svn_opt_revision_t()
182 revt.kind = svn.core.svn_opt_revision_working
183 for entry in from_paths:
185 to_wc = self._get_wc(to_dir, write_lock=True)
186 svn.wc.copy(self.abspath(entry), to_wc,
187 os.path.basename(entry), None, None)
189 svn.wc.adm_close(to_wc)
191 from_wc = self._get_wc(write_lock=True)
192 svn.wc.delete2(self.abspath(entry), from_wc, None, None, None)
194 svn.wc.adm_close(from_wc)
195 new_name = urlutils.join(to_dir, os.path.basename(entry))
196 self._change_fileid_mapping(self.inventory.path2id(entry), new_name)
197 self._change_fileid_mapping(None, entry)
199 self.read_working_inventory()
201 def rename_one(self, from_rel, to_rel, after=False):
204 revt = svn.core.svn_opt_revision_t()
205 revt.kind = svn.core.svn_opt_revision_unspecified
206 (to_wc, to_file) = self._get_rel_wc(to_rel, write_lock=True)
207 if os.path.dirname(from_rel) == os.path.dirname(to_rel):
208 # Prevent lock contention
211 (from_wc, _) = self._get_rel_wc(from_rel, write_lock=True)
212 from_id = self.inventory.path2id(from_rel)
214 svn.wc.copy(self.abspath(from_rel), to_wc, to_file, None, None)
215 svn.wc.delete2(self.abspath(from_rel), from_wc, None, None, None)
217 svn.wc.adm_close(to_wc)
218 self._change_fileid_mapping(None, from_rel)
219 self._change_fileid_mapping(from_id, to_rel)
220 self.read_working_inventory()
222 def path_to_file_id(self, revnum, current_revnum, path):
223 """Generate a bzr file id from a Subversion file name.
225 :param revnum: Revision number.
226 :param path: Absolute path within the Subversion repository.
227 :return: Tuple with file id and revision id.
229 assert isinstance(revnum, int) and revnum >= 0
230 assert isinstance(path, str)
232 rp = self.branch.unprefix(path)
233 entry = self.base_tree.id_map[rp]
234 assert entry[0] is not None
235 assert isinstance(entry[0], str), "fileid %r for %r is not a string" % (entry[0], path)
238 def read_working_inventory(self):
241 def add_file_to_inv(relpath, id, revid, parent_id):
242 """Add a file to the inventory."""
243 assert isinstance(relpath, unicode)
244 if os.path.islink(self.abspath(relpath)):
245 file = InventoryLink(id, os.path.basename(relpath), parent_id)
246 file.revision = revid
247 file.symlink_target = os.readlink(self.abspath(relpath))
248 file.text_sha1 = None
249 file.text_size = None
250 file.executable = False
253 file = InventoryFile(id, os.path.basename(relpath), parent_id)
254 file.revision = revid
256 data = fingerprint_file(open(self.abspath(relpath)))
257 file.text_sha1 = data['sha1']
258 file.text_size = data['size']
259 file.executable = self.is_executable(id, relpath)
262 # Ignore non-existing files
265 def find_copies(url, relpath=""):
266 wc = self._get_wc(relpath)
267 entries = svn.wc.entries_read(wc, False)
268 for entry in entries.values():
269 subrelpath = os.path.join(relpath, entry.name)
270 if entry.name == "" or entry.kind != 'directory':
271 if ((entry.copyfrom_url == url or entry.url == url) and
272 not (entry.schedule in (svn.wc.schedule_delete,
273 svn.wc.schedule_replace))):
275 self.branch.get_branch_path().strip("/"),
278 find_copies(subrelpath)
281 def find_ids(entry, rootwc):
282 relpath = urllib.unquote(entry.url[len(entry.repos):].strip("/"))
283 assert entry.schedule in (svn.wc.schedule_normal,
284 svn.wc.schedule_delete,
286 svn.wc.schedule_replace)
287 if entry.schedule == svn.wc.schedule_normal:
288 assert entry.revision >= 0
290 return self.path_to_file_id(entry.cmt_rev, entry.revision,
292 elif entry.schedule == svn.wc.schedule_delete:
294 elif (entry.schedule == svn.wc.schedule_add or
295 entry.schedule == svn.wc.schedule_replace):
296 # See if the file this file was copied from disappeared
297 # and has no other copies -> in that case, take id of other file
298 if (entry.copyfrom_url and
299 list(find_copies(entry.copyfrom_url)) == [relpath]):
300 return self.path_to_file_id(entry.copyfrom_rev,
301 entry.revision, entry.copyfrom_url[len(entry.repos):])
302 ids = self._get_new_file_ids(rootwc)
303 if ids.has_key(relpath):
304 return (ids[relpath], None)
305 # FIXME: Generate more random file ids
306 return ("NEW-" + escape_svn_path(entry.url[len(entry.repos):].strip("/")), None)
308 def add_dir_to_inv(relpath, wc, parent_id):
309 assert isinstance(relpath, unicode)
310 entries = svn.wc.entries_read(wc, False)
312 assert parent_id is None or isinstance(parent_id, str), \
313 "%r is not a string" % parent_id
314 (id, revid) = find_ids(entry, rootwc)
316 mutter('no id for %r' % entry.url)
318 assert revid is None or isinstance(revid, str), "%r is not a string" % revid
319 assert isinstance(id, str), "%r is not a string" % id
321 # First handle directory itself
322 inv.add_path(relpath.decode("utf-8"), 'directory', id, parent_id).revision = revid
324 inv.revision_id = revid
330 subrelpath = os.path.join(relpath, name.decode("utf-8"))
332 entry = entries[name]
335 if entry.kind == svn.core.svn_node_dir:
336 subwc = svn.wc.adm_open3(wc, self.abspath(subrelpath),
339 add_dir_to_inv(subrelpath, subwc, id)
341 svn.wc.adm_close(subwc)
343 (subid, subrevid) = find_ids(entry, rootwc)
345 add_file_to_inv(subrelpath, subid, subrevid, id)
347 mutter('no id for %r' % entry.url)
349 rootwc = self._get_wc()
351 add_dir_to_inv(u"", rootwc, None)
353 svn.wc.adm_close(rootwc)
355 self._set_inventory(inv, dirty=False)
358 def set_last_revision(self, revid):
359 mutter('setting last revision to %r' % revid)
360 if revid is None or revid == NULL_REVISION:
361 self.base_revid = revid
363 self.base_tree = RevisionTree(self, Inventory(), revid)
366 rev = self.branch.lookup_revision_id(revid)
367 self.base_revnum = rev
368 self.base_revid = revid
369 self.base_tree = SvnBasisTree(self)
371 # TODO: Implement more efficient version
372 newrev = self.branch.repository.get_revision(revid)
373 newrevtree = self.branch.repository.revision_tree(revid)
375 def update_settings(wc, path):
376 id = newrevtree.inventory.path2id(path)
377 mutter("Updating settings for %r" % id)
378 revnum = self.branch.lookup_revision_id(
379 newrevtree.inventory[id].revision)
381 svn.wc.process_committed2(self.abspath(path).rstrip("/"), wc,
383 svn.core.svn_time_to_cstring(newrev.timestamp),
384 newrev.committer, None, False)
386 if newrevtree.inventory[id].kind != 'directory':
389 entries = svn.wc.entries_read(wc, True)
390 for entry in entries:
394 subwc = svn.wc.adm_open3(wc, os.path.join(self.basedir, path, entry), False, 0, None)
396 update_settings(subwc, os.path.join(path, entry))
398 svn.wc.adm_close(subwc)
400 # Set proper version for all files in the wc
401 wc = self._get_wc(write_lock=True)
403 update_settings(wc, "")
406 self.base_revid = revid
408 def commit(self, message=None, message_callback=None, revprops=None,
409 timestamp=None, timezone=None, committer=None, rev_id=None,
410 allow_pointless=True, strict=False, verbose=False, local=False,
411 reporter=None, config=None, specific_files=None, author=None):
412 if author is not None:
413 revprops['author'] = author
414 # FIXME: Use allow_pointless
416 # FIXME: Use reporter
419 raise LocalCommitsUnsupported()
422 specific_files = [self.abspath(x).encode('utf8') for x in specific_files]
424 specific_files = [self.basedir.encode('utf8')]
426 if message_callback is not None:
427 def log_message_func(items, pool):
428 """ Simple log message provider for unit tests. """
429 return message_callback(self).encode("utf-8")
431 assert isinstance(message, basestring)
432 def log_message_func(items, pool):
433 """ Simple log message provider for unit tests. """
434 return message.encode("utf-8")
436 self.client_ctx.log_msg_baton2 = log_message_func
437 if rev_id is not None:
438 extra = "%d %s\n" % (self.branch.revno()+1, rev_id)
441 wc = self._get_wc(write_lock=True)
443 svn.wc.prop_set(SVN_PROP_BZR_REVISION_ID+str(self.branch.mapping.scheme),
444 self._get_bzr_revids(self._get_base_branch_props()) + extra,
446 svn.wc.prop_set(SVN_PROP_BZR_REVISION_INFO,
447 generate_revision_metadata(timestamp,
457 commit_info = svn.client.commit3(specific_files, True, False,
459 except SubversionException, (_, num):
460 if num == svn.core.SVN_ERR_FS_TXN_OUT_OF_DATE:
461 raise OutOfDateTree(self)
464 # Reset properties so the next subversion commit won't
465 # accidently set these properties.
466 wc = self._get_wc(write_lock=True)
467 base_branch_props = self._get_base_branch_props()
468 svn.wc.prop_set(SVN_PROP_BZR_REVISION_ID+str(self.branch.mapping.scheme),
469 self._get_bzr_revids(base_branch_props), self.basedir, wc)
470 svn.wc.prop_set(SVN_PROP_BZR_REVISION_INFO,
471 base_branch_props.get(SVN_PROP_BZR_REVISION_INFO, ""),
476 self.client_ctx.log_msg_baton2 = None
478 revid = self.branch.generate_revision_id(commit_info.revision)
480 self.base_revid = revid
481 self.base_revnum = commit_info.revision
482 self.base_tree = SvnBasisTree(self)
486 def smart_add(self, file_list, recurse=True, action=None, save=True):
487 assert isinstance(recurse, bool)
489 action = bzrlib.add.AddAction()
492 # no paths supplied: add the entire tree.
497 for file_path in file_list:
499 file_path = os.path.abspath(file_path)
500 f = self.relpath(file_path)
501 wc = self._get_wc(os.path.dirname(f), write_lock=True)
503 if not self.inventory.has_filename(f):
505 mutter('adding %r' % file_path)
506 svn.wc.add2(file_path, wc, None, 0, None, None, None)
507 added.append(file_path)
508 if recurse and file_kind(file_path) == 'directory':
509 # Filter out ignored files and update ignored
510 for c in os.listdir(file_path):
511 if self.is_control_filename(c):
513 c_path = os.path.join(file_path, c)
514 ignore_glob = self.is_ignored(c)
515 if ignore_glob is not None:
516 ignored.setdefault(ignore_glob, []).append(c_path)
521 cadded, cignored = self.smart_add(todo, recurse, action, save)
523 ignored.update(cignored)
524 return added, ignored
526 def add(self, files, ids=None, kinds=None):
528 if isinstance(files, str):
530 if isinstance(ids, str):
534 assert isinstance(files, list)
536 wc = self._get_wc(os.path.dirname(f), write_lock=True)
539 svn.wc.add2(os.path.join(self.basedir, f), wc, None, 0,
542 self._change_fileid_mapping(ids.next(), f, wc)
543 except SubversionException, (_, num):
544 if num == svn.core.SVN_ERR_ENTRY_EXISTS:
546 elif num == svn.core.SVN_ERR_WC_PATH_NOT_FOUND:
547 raise NoSuchFile(path=f)
551 self.read_working_inventory()
553 def basis_tree(self):
554 if self.base_revid is None or self.base_revid == NULL_REVISION:
555 return self.branch.repository.revision_tree(self.base_revid)
557 return self.base_tree
559 def pull(self, source, overwrite=False, stop_revision=None,
560 delta_reporter=None, possible_transports=None):
561 # FIXME: Use delta_reporter
562 # FIXME: Use overwrite
563 result = PullResult()
564 result.source_branch = source
565 result.master_branch = None
566 result.target_branch = self.branch
567 (result.old_revno, result.old_revid) = self.branch.last_revision_info()
568 if stop_revision is None:
569 stop_revision = self.branch.last_revision()
570 rev = svn.core.svn_opt_revision_t()
571 rev.kind = svn.core.svn_opt_revision_number
572 rev.value.number = self.branch.lookup_revision_id(stop_revision)
573 fetched = svn.client.update(self.basedir, rev, True, self.client_ctx)
574 self.base_revid = self.branch.generate_revision_id(fetched)
575 result.new_revid = self.base_revid
576 result.new_revno = self.branch.revision_id_to_revno(result.new_revid)
579 def get_file_sha1(self, file_id, path=None, stat_value=None):
581 path = self._inventory.id2path(file_id)
582 return fingerprint_file(open(self.abspath(path)))['sha1']
584 def _change_fileid_mapping(self, id, path, wc=None):
586 subwc = self._get_wc(write_lock=True)
589 new_entries = self._get_new_file_ids(subwc)
591 if new_entries.has_key(path):
592 del new_entries[path]
594 assert isinstance(id, str)
595 new_entries[path] = id
596 existing = "".join(map(lambda (path, id): "%s\t%s\n" % (path, id), new_entries.items()))
598 svn.wc.prop_set(SVN_PROP_BZR_FILEIDS, existing.encode("utf-8"), self.basedir, subwc)
600 svn.wc.adm_close(subwc)
602 def _get_base_branch_props(self):
603 return self.branch.repository.branchprop_list.get_properties(
604 self.branch.get_branch_path(self.base_revnum), self.base_revnum)
606 def _get_new_file_ids(self, wc):
607 committed = self._get_base_branch_props().get(SVN_PROP_BZR_FILEIDS, "")
608 existing = svn.wc.prop_get(SVN_PROP_BZR_FILEIDS, self.basedir, wc)
609 if existing is None or committed == existing:
611 return dict(map(lambda x: str(x).split("\t"),
612 existing.splitlines()))
614 def _get_bzr_revids(self, base_branch_props):
615 return base_branch_props.get(SVN_PROP_BZR_REVISION_ID+str(self.branch.mapping.scheme), "")
617 def _get_bzr_merges(self, base_branch_props):
618 return base_branch_props.get(SVN_PROP_BZR_ANCESTRY+str(self.branch.mapping.scheme), "")
620 def _get_svk_merges(self, base_branch_props):
621 return base_branch_props.get(SVN_PROP_SVK_MERGE, "")
623 def set_pending_merges(self, merges):
624 """See MutableTree.set_pending_merges()."""
625 wc = self._get_wc(write_lock=True)
629 bzr_merge = "\t".join(merges) + "\n"
633 svn.wc.prop_set(SVN_PROP_BZR_ANCESTRY+str(self.branch.mapping.scheme),
634 self._get_bzr_merges(self._get_base_branch_props()) + bzr_merge,
637 svk_merges = parse_svk_features(self._get_svk_merges(self._get_base_branch_props()))
642 svk_merges.add(_revision_id_to_svk_feature(merge))
643 except InvalidRevisionId:
646 svn.wc.prop_set2(SVN_PROP_SVK_MERGE,
647 serialize_svk_features(svk_merges), self.basedir,
652 def add_pending_merge(self, revid):
653 merges = self.pending_merges()
655 self.set_pending_merges(merges)
657 def pending_merges(self):
658 merged = self._get_bzr_merges(self._get_base_branch_props()).splitlines()
661 merged_data = svn.wc.prop_get(
662 SVN_PROP_BZR_ANCESTRY+str(self.branch.mapping.scheme), self.basedir, wc)
663 if merged_data is None:
666 set_merged = merged_data.splitlines()
670 assert (len(merged) == len(set_merged) or
671 len(merged)+1 == len(set_merged))
673 if len(set_merged) > len(merged):
674 return set_merged[-1].split("\t")
678 def _reset_data(self):
682 # non-implementation specific cleanup
685 # reverse order of locking.
687 return self._control_files.unlock()
691 if not supports_executable():
692 def is_executable(self, file_id, path=None):
693 inv = self.basis_tree()._inventory
695 return inv[file_id].executable
696 # Default to not executable
700 class SvnWorkingTreeFormat(WorkingTreeFormat):
701 """Subversion working copy format."""
702 def __get_matchingbzrdir(self):
703 return SvnWorkingTreeDirFormat()
705 _matchingbzrdir = property(__get_matchingbzrdir)
707 def get_format_description(self):
708 return "Subversion Working Copy"
710 def get_format_string(self):
711 return "Subversion Working Copy Format"
713 def initialize(self, a_bzrdir, revision_id=None):
714 raise NotImplementedError(self.initialize)
716 def open(self, a_bzrdir):
717 raise NotImplementedError(self.initialize)
720 class SvnCheckout(BzrDir):
721 """BzrDir implementation for Subversion checkouts (directories
722 containing a .svn subdirectory."""
723 def __init__(self, transport, format):
724 super(SvnCheckout, self).__init__(transport, format)
725 self.local_path = transport.local_abspath(".")
727 # Open related remote repository + branch
728 wc = svn.wc.adm_open3(None, self.local_path, False, 0, None)
730 self.svn_url = svn.wc.entry(self.local_path, wc, True).url
734 self.remote_transport = SvnRaTransport(self.svn_url)
735 self.remote_bzrdir = SvnRemoteAccess(self.remote_transport)
736 self.svn_root_transport = self.remote_transport.clone_root()
737 self.root_transport = self.transport = transport
739 def clone(self, path, revision_id=None, force_new_repo=False):
740 raise NotImplementedError(self.clone)
742 def open_workingtree(self, _unsupported=False, recommend_upgrade=False):
743 return SvnWorkingTree(self, self.local_path, self.open_branch())
745 def sprout(self, url, revision_id=None, force_new_repo=False,
746 recurse='down', possible_transports=None, accelerator_tree=None,
748 # FIXME: honor force_new_repo
750 result = get_rich_root_format().initialize(url)
751 repo = self._find_repository()
752 repo.clone(result, revision_id)
753 branch = self.open_branch()
754 branch.sprout(result, revision_id)
755 result.create_workingtree(hardlink=hardlink)
758 def open_repository(self):
759 raise NoRepositoryPresent(self)
761 def find_repository(self):
762 raise NoRepositoryPresent(self)
764 def _find_repository(self):
765 return SvnRepository(self, self.svn_root_transport,
766 self.remote_bzrdir.branch_path)
768 def needs_format_conversion(self, format=None):
770 format = BzrDirFormat.get_default_format()
771 return not isinstance(self._format, format.__class__)
773 def create_workingtree(self, revision_id=None, hardlink=None):
774 """See BzrDir.create_workingtree().
776 Not implemented for Subversion because having a .svn directory
777 implies having a working copy.
779 raise NotImplementedError(self.create_workingtree)
781 def create_branch(self):
782 """See BzrDir.create_branch()."""
783 raise NotImplementedError(self.create_branch)
785 def open_branch(self, unsupported=True):
786 """See BzrDir.open_branch()."""
787 repos = self._find_repository()
790 branch = SvnBranch(self.remote_transport.base, repos,
791 self.remote_bzrdir.branch_path)
792 except SubversionException, (_, num):
793 if num == svn.core.SVN_ERR_WC_NOT_DIRECTORY:
794 raise NotBranchError(path=self.base)
797 branch.bzrdir = self.remote_bzrdir