Răsfoiți Sursa

server: Allow cloning an empty repo.

Git clients expect this to work with a client-side warning that the
repo they're cloning is empty. All the server needs to do in this case
is send a single flush-pkt during the refs advertisement.

Change-Id: Ia08b595be78b5f1a4ab8b617aa3f8cb85cc75702
Dave Borowitz 13 ani în urmă
părinte
comite
8c9d3c8aa6
4 a modificat fișierele cu 29 adăugiri și 2 ștergeri
  1. 2 0
      NEWS
  2. 3 1
      dulwich/server.py
  3. 22 0
      dulwich/tests/compat/server_utils.py
  4. 2 1
      dulwich/tests/test_server.py

+ 2 - 0
NEWS

@@ -47,6 +47,8 @@
 
   * Don't send a pack with duplicates of the same object. (Dave Borowitz)
 
+  * Teach the server how to serve a clone of an empty repo. (Dave Borowitz)
+
  API CHANGES
 
   * write_pack no longer takes the num_objects argument and requires an object

+ 3 - 1
dulwich/server.py

@@ -332,7 +332,9 @@ class ProtocolGraphWalker(object):
         :return: a list of SHA1s requested by the client
         """
         if not heads:
-            raise GitProtocolError('No heads found')
+            # The repo is empty, so short-circuit the whole process.
+            self.proto.write_pkt_line(None)
+            return None
         values = set(heads.itervalues())
         if self.advertise_refs or not self.http_req:
             for i, (ref, sha) in enumerate(heads.iteritems()):

+ 22 - 0
dulwich/tests/compat/server_utils.py

@@ -20,10 +20,14 @@
 """Utilities for testing git server compatibility."""
 
 
+import os
 import select
+import shutil
 import socket
+import tempfile
 import threading
 
+from dulwich.repo import Repo
 from dulwich.server import (
     ReceivePackHandler,
     )
@@ -100,6 +104,24 @@ class ServerTests(object):
         self._old_repo.object_store._pack_cache = None
         self.assertReposEqual(self._old_repo, self._new_repo)
 
+    def test_clone_from_dulwich_empty(self):
+        old_repo_dir = os.path.join(tempfile.mkdtemp(), 'empty_old')
+        run_git_or_fail(['init', '--quiet', '--bare', old_repo_dir])
+        self._old_repo = Repo(old_repo_dir)
+        port = self._start_server(self._old_repo)
+
+        new_repo_base_dir = tempfile.mkdtemp()
+        try:
+            new_repo_dir = os.path.join(new_repo_base_dir, 'empty_new')
+            run_git_or_fail(['clone', self.url(port), new_repo_dir],
+                            cwd=new_repo_base_dir)
+            new_repo = Repo(new_repo_dir)
+            self.assertReposEqual(self._old_repo, new_repo)
+        finally:
+            # We don't create a Repo from new_repo_dir until after some errors
+            # may have occurred, so don't depend on tearDown to clean it up.
+            shutil.rmtree(new_repo_base_dir)
+
 
 class ShutdownServerMixIn:
     """Mixin that allows serve_forever to be shut down.

+ 2 - 1
dulwich/tests/test_server.py

@@ -268,7 +268,8 @@ class ProtocolGraphWalkerTestCase(TestCase):
         self.assertEquals((None, None), _split_proto_line('', allowed))
 
     def test_determine_wants(self):
-        self.assertRaises(GitProtocolError, self._walker.determine_wants, {})
+        self.assertEqual(None, self._walker.determine_wants({}))
+        self.assertEqual('None', self._walker.proto.get_received_line())
 
         self._walker.proto.set_output([
           'want %s multi_ack' % ONE,