Procházet zdrojové kódy

Implement LocalGitClient.send_pack

diryboy před 10 roky
rodič
revize
907c88f334
4 změnil soubory, kde provedl 67 přidání a 4 odebrání
  1. 24 1
      dulwich/client.py
  2. 3 1
      dulwich/repo.py
  3. 31 1
      dulwich/tests/test_client.py
  4. 9 1
      dulwich/tests/utils.py

+ 24 - 1
dulwich/client.py

@@ -679,7 +679,30 @@ class LocalGitClient(GitClient):
         :raises UpdateRefsError: if the server supports report-status
                                  and rejects ref updates
         """
-        raise NotImplementedError(self.send_pack)
+        from dulwich.repo import Repo
+        from dulwich.protocol import ZERO_SHA
+        
+        target = Repo(path)
+        old_refs = target.get_refs()
+        new_refs = determine_wants(old_refs)
+
+        have = [sha1 for sha1 in old_refs.values() if sha1 != ZERO_SHA]
+        want = []
+        for refname in set(new_refs.keys() + old_refs.keys()):
+            old_sha1 = old_refs.get(refname, ZERO_SHA)
+            new_sha1 = new_refs.get(refname, ZERO_SHA)
+            if new_sha1 not in have and new_sha1 != ZERO_SHA:
+                want.append(new_sha1)
+        
+        if not want and old_refs == new_refs:
+            return new_refs
+
+        target.object_store.add_objects(generate_pack_contents(have, want))
+        
+        for name, sha in new_refs.iteritems():
+            target.refs[name] = sha
+
+        return new_refs
 
     def fetch(self, path, target, determine_wants=None, progress=None):
         """Fetch into a target repository.

+ 3 - 1
dulwich/repo.py

@@ -888,7 +888,7 @@ class Repo(BaseRepo):
         return cls(path)
 
     @classmethod
-    def init_bare(cls, path):
+    def init_bare(cls, path, mkdir=False):
         """Create a new bare repository.
 
         ``path`` should already exist and be an emty directory.
@@ -896,6 +896,8 @@ class Repo(BaseRepo):
         :param path: Path to create bare repository in
         :return: a `Repo` instance
         """
+        if mkdir:
+            os.mkdir(path)
         return cls._init_maybe_bare(path, True)
 
     create = init_bare

+ 31 - 1
dulwich/tests/test_client.py

@@ -53,9 +53,13 @@ from dulwich.objects import (
     Commit,
     Tree
     )
-from dulwich.repo import MemoryRepo
+from dulwich.repo import (
+    MemoryRepo,
+    Repo
+    )
 from dulwich.tests.utils import (
     open_repo,
+    init_repo,
     skipIfPY3,
     )
 
@@ -610,3 +614,29 @@ class LocalGitClientTests(TestCase):
             graph_walker=walker, pack_data=out.write)
         # Hardcoding is not ideal, but we'll fix that some other day..
         self.assertTrue(out.getvalue().startswith('PACK\x00\x00\x00\x02\x00\x00\x00\x07'))
+
+    def test_send_pack_without_changes(self):
+        local = open_repo('a.git')
+        target = open_repo('a.git')
+        self.send_and_verify("master", local, target)
+
+    def test_send_pack_with_changes(self):
+        local = open_repo('a.git')
+        target = init_repo('a.git')
+        self.send_and_verify("master", local, target)
+
+    def send_and_verify(self, branch, local, target):
+        client = LocalGitClient()
+        ref_name = "refs/heads/" + branch
+        new_refs = client.send_pack(target.path,
+                                    lambda _: { ref_name: local.refs[ref_name] },
+                                    local.object_store.generate_pack_contents)
+
+        self.assertEqual(local.refs[ref_name], new_refs[ref_name])
+
+        for name, sha in new_refs.iteritems():
+            self.assertEqual(new_refs[name], target.refs[name])
+
+        obj_local = local.get_object(new_refs[ref_name])
+        obj_target = target.get_object(new_refs[ref_name])
+        self.assertEqual(obj_local, obj_target)

+ 9 - 1
dulwich/tests/utils.py

@@ -75,13 +75,21 @@ def open_repo(name):
     shutil.copytree(repo_dir, temp_repo_dir, symlinks=True)
     return Repo(temp_repo_dir)
 
+def init_repo(name):
+    """Init an empty bare repo in a temporary directory.
+
+    Use tear_down_repo to delete any temp files created.
+
+    :param name: The name of the repository
+    :returns: An initialized Repo object that lives in a temporary directory.
+    """
+    return Repo.init_bare(os.path.join(tempfile.mkdtemp(), name), mkdir = True)
 
 def tear_down_repo(repo):
     """Tear down a test repository."""
     temp_dir = os.path.dirname(repo.path.rstrip(os.sep))
     shutil.rmtree(temp_dir)
 
-
 def make_object(cls, **attrs):
     """Make an object for testing and assign some members.