-# Copyright (C) 2005-2006 Jelmer Vernooij <jelmer@samba.org>
+# Copyright (C) 2005-2007 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
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import bzrlib
-from bzrlib.decorators import needs_write_lock
from bzrlib.inventory import Inventory, ROOT_ID
import bzrlib.osutils as osutils
-from bzrlib.progress import ProgressBar
from bzrlib.revision import Revision
from bzrlib.repository import InterRepository
from bzrlib.trace import mutter
+import bzrlib.ui as ui
from copy import copy
from cStringIO import StringIO
from svn.core import SubversionException, Pool
import svn.core, svn.ra
+from fileids import generate_file_id
from repository import (SvnRepository, SVN_PROP_BZR_MERGE, SVN_PROP_SVK_MERGE,
SVN_PROP_BZR_REVPROP_PREFIX, SvnRepositoryFormat)
from tree import apply_txdelta_handler
map(s.update, strings)
return s.hexdigest()
+
class RevisionBuildEditor(svn.delta.Editor):
- def __init__(self, source, target, branch_path, revnum, prev_inventory, revid, svn_revprops, id_map, parent_branch):
+ def __init__(self, source, target, branch_path, prev_inventory, revid,
+ svn_revprops, id_map):
self.branch_path = branch_path
self.old_inventory = prev_inventory
self.inventory = copy(prev_inventory)
self.revid = revid
- self.revnum = revnum
self.id_map = id_map
- self.parent_branch = parent_branch
self.source = source
self.target = target
self.transact = target.get_transaction()
self.weave_store = target.weave_store
-
self.dir_baserev = {}
-
self._parent_ids = None
self._revprops = {}
self._svn_revprops = svn_revprops
-
self.pool = Pool()
def _get_revision(self, revid):
self.inventory.revision_id = self.revid
return ROOT_ID
- def relpath(self, path):
- return path.strip("/")
+ def _get_existing_id(self, parent_id, path):
+ if self.id_map.has_key(path):
+ return self.id_map[path]
+ return self._get_old_id(parent_id, path)
- def _get_existing_id(self, parent_id, old_path):
+ def _get_old_id(self, parent_id, old_path):
return self.old_inventory[parent_id].children[os.path.basename(old_path)].file_id
- def delete_entry(self, path, revnum, parent_baton, pool):
- del self.inventory[self._get_existing_id(parent_baton, path)]
+ def _get_new_id(self, parent_id, new_path):
+ if self.id_map.has_key(new_path):
+ return self.id_map[new_path]
+ return generate_file_id(self.revid, new_path)
+
+ def delete_entry(self, path, revnum, parent_id, pool):
+ path = path.decode("utf-8")
+ del self.inventory[self._get_old_id(parent_id, path)]
def close_directory(self, id):
if id != ROOT_ID:
if not file_weave.has_version(self.revid):
file_weave.add_lines(self.revid, self.dir_baserev[id], [])
- def add_directory(self, path, parent_baton, copyfrom_path, copyfrom_revnum, pool):
- file_id = self.id_map[path]
+ def add_directory(self, path, parent_id, copyfrom_path, copyfrom_revnum, pool):
+ path = path.decode("utf-8")
+ file_id = self._get_new_id(parent_id, path)
self.dir_baserev[file_id] = []
ie = self.inventory.add_path(path, 'directory', file_id)
return file_id
- def open_directory(self, path, parent_baton, base_revnum, pool):
+ def open_directory(self, path, parent_id, base_revnum, pool):
assert base_revnum >= 0
- base_file_id = self._get_existing_id(parent_baton, path)
+ base_file_id = self._get_old_id(parent_id, path)
base_revid = self.old_inventory[base_file_id].revision
- if self.id_map.has_key(path):
- file_id = self.id_map[path]
- else:
- file_id = base_file_id
+ file_id = self._get_existing_id(parent_id, path)
if file_id == base_file_id:
self.dir_baserev[file_id] = [base_revid]
ie = self.inventory[file_id]
def change_file_prop(self, id, name, value, pool):
if name == svn.core.SVN_PROP_EXECUTABLE:
- # Strange, you'd expect executable to match svn.core.SVN_PROP_EXECUTABLE_VALUE, but that's not how SVN behaves.
+ # You'd expect executable to match
+ # svn.core.SVN_PROP_EXECUTABLE_VALUE, but that's not
+ # how SVN behaves. It appears to consider the presence
+ # of the property sufficient to mark it executable.
self.is_executable = (value != None)
elif (name == svn.core.SVN_PROP_SPECIAL):
self.is_symlink = (value != None)
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 = None
self.file_data = ""
self.file_parents = []
self.file_stream = None
- self.file_id = self.id_map[path]
+ self.file_id = self._get_new_id(parent_id, path)
return path
def open_file(self, path, parent_id, base_revnum, pool):
- base_file_id = self._get_existing_id(parent_id, path)
+ base_file_id = self._get_old_id(parent_id, path)
base_revid = self.old_inventory[base_file_id].revision
- if self.id_map.has_key(path):
- self.file_id = self.id_map[path]
- else:
- self.file_id = base_file_id
+ self.file_id = self._get_existing_id(parent_id, path)
self.is_executable = None
self.is_symlink = (self.inventory[base_file_id].kind == 'symlink')
file_weave = self.weave_store.get_weave_or_empty(base_file_id, self.transact)
"""Svn to any repository actions."""
_matching_repo_format = SvnRepositoryFormat()
- """The format to test with."""
- @needs_write_lock
- def copy_content(self, revision_id=None, basis=None, pb=ProgressBar()):
- """See InterRepository.copy_content."""
- # Dictionary with paths as keys, revnums as values
+ @staticmethod
+ def _get_repo_format_to_test():
+ return None
- # Loop over all the revnums until revision_id
- # (or youngest_revnum) and call self.target.add_revision()
- # or self.target.add_inventory() each time
- if revision_id is None:
- path = None
- until_revnum = self.source._latest_revnum
- else:
- (path, until_revnum) = self.source.parse_revision_id(revision_id)
+ def _find_all(self):
+ needed = []
+ parents = {}
+ for (branch, revnum) in self.source.follow_history(
+ self.source._latest_revnum):
+ revid = self.source.generate_revision_id(revnum, branch)
+ parents[revid] = self.source._mainline_revision_parent(branch, revnum)
- repos_root = self.source.transport.get_repos_root()
-
+ if not self.target.has_revision(revid):
+ needed.append(revid)
+ return (needed, parents)
+
+ def _find_until(self, revision_id):
needed = []
parents = {}
+ (path, until_revnum) = self.source.parse_revision_id(revision_id)
+
prev_revid = None
- if path is None:
- it = self.source.follow_history(until_revnum)
- else:
- it = self.source.follow_branch_history(path, until_revnum)
- for (branch, changes, revnum) in it:
+ for (branch, revnum) in self.source.follow_branch(path,
+ until_revnum):
revid = self.source.generate_revision_id(revnum, branch)
if prev_revid is not None:
prev_revid = revid
if not self.target.has_revision(revid):
- needed.append((branch, revnum, revid, changes))
+ needed.append(revid)
parents[prev_revid] = None
+ return (needed, parents)
- num = 0
- needed.reverse()
- prev_revid = None
- transport = self.source.transport
- for (branch, revnum, revid, changes) in needed:
- if pb is not None:
- pb.update('copying revision', num+1, len(needed)+1)
- num += 1
-
- parent_revid = parents[revid]
-
- if parent_revid is not None:
- (parent_branch, parent_revnum) = self.source.parse_revision_id(parent_revid)
- else:
- parent_revnum = 0
- parent_branch = None
-
- if parent_revid is None:
- parent_inv = Inventory(ROOT_ID)
- elif prev_revid != parent_revid:
- parent_inv = self.target.get_inventory(parent_revid)
- else:
- parent_inv = prev_inv
-
- id_map = self.source.transform_fileid_map(self.source.uuid,
- revnum, branch, changes)
-
- editor = RevisionBuildEditor(self.source, self.target, branch,
- revnum, parent_inv, revid,
- self.source._log.get_revision_info(revnum),
- id_map, parent_branch)
-
- pool = Pool()
- edit, edit_baton = svn.delta.make_editor(editor, pool)
+ def copy_content(self, revision_id=None, basis=None, pb=None):
+ """See InterRepository.copy_content."""
+ # Dictionary with paths as keys, revnums as values
- if parent_branch is None:
- transport.reparent(repos_root)
- else:
- transport.reparent("%s/%s" % (repos_root, parent_branch))
- if parent_branch != branch:
- switch_url = "%s/%s" % (repos_root, branch)
- reporter, reporter_baton = transport.do_switch(
- revnum, "", True,
- switch_url, edit, edit_baton, pool)
+ # Loop over all the revnums until revision_id
+ # (or youngest_revnum) and call self.target.add_revision()
+ # or self.target.add_inventory() each time
+ needed = []
+ parents = {}
+ self.target.lock_read()
+ try:
+ if revision_id is None:
+ (needed, parents) = self._find_all()
else:
- reporter, reporter_baton = transport.do_update(
- revnum, "", True, edit, edit_baton, pool)
+ (needed, parents) = self._find_until(revision_id)
+ finally:
+ self.target.unlock()
- # Report status of existing paths
- svn.ra.reporter2_invoke_set_path(reporter, reporter_baton,
- "", parent_revnum, False, None, pool)
+ if len(needed) == 0:
+ # Nothing to fetch
+ return
- transport.lock()
- svn.ra.reporter2_invoke_finish_report(reporter, reporter_baton, pool)
- transport.unlock()
-
- prev_inv = editor.inventory
- prev_revid = revid
-
- pool.destroy()
-
- if pb is not None:
- pb.clear()
+ repos_root = self.source.transport.get_repos_root()
+ needed.reverse()
+ prev_revid = None
+ transport = self.source.transport
+ self.target.lock_write()
+ if pb is None:
+ pb = ui.ui_factory.nested_progress_bar()
+ nested_pb = pb
+ else:
+ nested_pb = None
+ num = 0
+ try:
+ for revid in needed:
+ (branch, revnum) = self.source.parse_revision_id(revid)
+ pb.update('copying revision', num, len(needed))
+
+ parent_revid = parents[revid]
+
+ if parent_revid is None:
+ parent_inv = Inventory()
+ elif prev_revid != parent_revid:
+ parent_inv = self.target.get_inventory(parent_revid)
+ else:
+ parent_inv = prev_inv
+
+ changes = self.source._log.get_revision_paths(revnum, branch)
+ renames = self.source.revision_fileid_renames(revid)
+ id_map = self.source.transform_fileid_map(self.source.uuid,
+ revnum, branch, changes, renames)
+
+ editor = RevisionBuildEditor(self.source, self.target, branch,
+ parent_inv, revid,
+ self.source._log.get_revision_info(revnum),
+ id_map)
+
+ pool = Pool()
+ edit, edit_baton = svn.delta.make_editor(editor, pool)
+
+ if parent_revid is None:
+ transport.reparent("%s/%s" % (repos_root, branch))
+ reporter, reporter_baton = transport.do_update(
+ revnum, "", True, edit, edit_baton, pool)
+
+ # Report status of existing paths
+ svn.ra.reporter2_invoke_set_path(reporter, reporter_baton,
+ "", revnum, True, None, pool)
+ else:
+ (parent_branch, parent_revnum) = self.source.parse_revision_id(parent_revid)
+ transport.reparent("%s/%s" % (repos_root, parent_branch))
+
+ if parent_branch != branch:
+ switch_url = "%s/%s" % (repos_root, branch)
+ reporter, reporter_baton = transport.do_switch(
+ revnum, "", True,
+ switch_url, edit, edit_baton, pool)
+ else:
+ reporter, reporter_baton = transport.do_update(
+ revnum, "", True, edit, edit_baton, pool)
+
+ # Report status of existing paths
+ svn.ra.reporter2_invoke_set_path(reporter, reporter_baton,
+ "", parent_revnum, False, None, pool)
+
+ transport.lock()
+ svn.ra.reporter2_invoke_finish_report(reporter, reporter_baton, pool)
+ transport.unlock()
+
+ prev_inv = editor.inventory
+ prev_revid = revid
+ pool.destroy()
+ num += 1
+ finally:
+ self.target.unlock()
+ if nested_pb is not None:
+ nested_pb.finished()
self.source.transport.reparent(repos_root)
- @needs_write_lock
- def fetch(self, revision_id=None, pb=ProgressBar()):
+ def fetch(self, revision_id=None, pb=None):
"""Fetch revisions. """
self.copy_content(revision_id=revision_id, pb=pb)
def is_compatible(source, target):
"""Be compatible with SvnRepository."""
# FIXME: Also check target uses VersionedFile
- mutter('test %r' % source)
return isinstance(source, SvnRepository)
-
-