[RFC] Server-side branching
Andrew Bennetts
andrew at canonical.com
Mon Jun 4 19:22:40 BST 2007
This is very hackish (see the commit message in the bundle) at the moment, but
I think this could be useful. It makes the smart server and client handle
the repository copying part of:
bzr branch bzr+ssh://same-host/aaa bzr+ssh://same-host/bbb
entirely on the server, thus avoiding sending the whole branch over the network
twice.
This is useful for situations where you don't have (or don't want) a shared
repository on the remote side. My motivation is for Launchpad's hosting
service, which doesn't currently support shared repositories, so it would
definitely be useful there.
Thoughts?
-Andrew.
-------------- next part --------------
# Bazaar revision bundle v0.9
#
# message:
# Hackish first draft of server-side branching.
#
# This needs lots of polish (e.g. error handling), and tests, and ideally a proper
# transport reuse framework, but this code is a working proof-of-concept. You can
# do:
#
# bzr branch bzr+ssh://same-host/abc bzr+ssh://same-host/xyz
#
# And it will do the repository fetch on the server side, which should be a huge
# improvement over sending large branches across the network twice.
#
# committer: Andrew Bennetts <andrew.bennetts at canonical.com>
# date: Tue 2007-06-05 04:11:36.562000036 +1000
=== modified file bzrlib/builtins.py
--- bzrlib/builtins.py
+++ bzrlib/builtins.py
@@ -882,7 +882,12 @@
else:
name = os.path.basename(to_location) + '\n'
- to_transport = transport.get_transport(to_location)
+ from_host = br_from.bzrdir.transport.clone('/').base
+ if to_location.startswith(from_host):
+ rel_url = urlutils.relative_url(from_location, to_location).encode('ascii')
+ to_transport = br_from.bzrdir.root_transport.clone(rel_url)
+ else:
+ to_transport = transport.get_transport(to_location)
try:
to_transport.mkdir('.')
except errors.FileExists:
=== modified file bzrlib/bzrdir.py
--- bzrlib/bzrdir.py
+++ bzrlib/bzrdir.py
@@ -761,7 +761,12 @@
if revision_id is not None, then the clone operation may tune
itself to download less data.
"""
- target_transport = get_transport(url)
+ from_host = self.transport.clone('/').base
+ if url.startswith(from_host):
+ rel_url = urlutils.relative_url(self.root_transport.base, url).encode('ascii')
+ target_transport = self.root_transport.clone(rel_url)
+ else:
+ target_transport = get_transport(url)
target_transport.ensure_base()
cloning_format = self.cloning_metadir()
result = cloning_format.initialize_on_transport(target_transport)
=== modified file bzrlib/remote.py
--- bzrlib/remote.py
+++ bzrlib/remote.py
@@ -452,9 +452,25 @@
def sprout(self, to_bzrdir, revision_id=None):
# TODO: Option to control what format is created?
to_repo = to_bzrdir.create_repository()
- self._copy_repository_tarball(to_repo, revision_id)
+ if to_bzrdir.transport._medium is self.bzrdir.transport._medium:
+ to_repo.remote_fetch(self, revision_id)
+ else:
+ self._copy_repository_tarball(to_repo, revision_id)
return to_repo
+ @needs_write_lock
+ def remote_fetch(self, source_repo, revision_id):
+ note("Doing copy server-side...")
+ path = self.bzrdir._path_for_remote_call(self._client)
+ source_path = source_repo.bzrdir._path_for_remote_call(source_repo._client)
+ response = self._client.call(
+ 'Repository.fetch_to', path, self._lock_token, source_path, revision_id)
+ if response == ('ok',):
+ return
+ else:
+ # XXX: what about <= 0.16 servers that don't have this method?
+ raise errors.UnexpectedSmartServerResponse(response)
+
### These methods are just thin shims to the VFS object for now.
def revision_tree(self, revision_id):
=== modified file bzrlib/smart/repository.py
--- bzrlib/smart/repository.py
+++ bzrlib/smart/repository.py
@@ -236,3 +236,19 @@
tarball.add(dirname, '.bzr') # recursive by default
finally:
tarball.close()
+
+
+class SmartServerRepositoryFetchTo(SmartServerRepositoryRequest):
+ """Server-side repository content fetching."""
+
+ def do_repository_request(self, repository, lock_token, source_path, revision_id):
+ # XXX: should restrict source_path to be a relpath, so that this can't
+ # be used to do third-party transfers?
+ transport = self._backing_transport.clone(source_path)
+ bzrdir = BzrDir.open_from_transport(transport)
+ source_repository = bzrdir.open_repository()
+ repository.lock_write(token=lock_token)
+ repository.fetch(source_repository, revision_id)
+ repository.unlock()
+ return SuccessfulSmartServerResponse(('ok',))
+
=== modified file bzrlib/smart/request.py
--- bzrlib/smart/request.py
+++ bzrlib/smart/request.py
@@ -93,8 +93,8 @@
return other.args == self.args and other.body == self.body
def __repr__(self):
- return "<SmartServerResponse args=%r body=%r>" % (self.is_successful(),
- self.args, self.body)
+ return "<%s args=%r body=%r>" % (
+ self.__class__.__name__, self.args, self.body)
class FailedSmartServerResponse(SmartServerResponse):
@@ -302,6 +302,8 @@
'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
request_handlers.register_lazy(
'rename', 'bzrlib.smart.vfs', 'RenameRequest')
+request_handlers.register_lazy(
+ 'Repository.fetch_to', 'bzrlib.smart.repository', 'SmartServerRepositoryFetchTo')
request_handlers.register_lazy('Repository.gather_stats',
'bzrlib.smart.repository',
'SmartServerRepositoryGatherStats')
=== modified directory // last-changed:andrew.bennetts at canonical.com-200706041
... 81136-7bxsr7ulxsvm3xe1
# revision id: andrew.bennetts at canonical.com-20070604181136-7bxsr7ulxsvm3xe1
# sha1: 64d7cd6a4066dac2b43354adeac591d12ce0f1ce
# inventory sha1: dd44676f5f97ec198ac1aa0a16d307cd4e0e1129
# parent ids:
# pqm at pqm.ubuntu.com-20070602184854-kwqaduxs0b19r76n
# base id: pqm at pqm.ubuntu.com-20070602184854-kwqaduxs0b19r76n
# properties:
# branch-nick: server-side-branch
More information about the bazaar
mailing list