[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