浏览代码

Initial work on fix for cloning of remote repositories in porcelain. Fixes #920.

Jelmer Vernooij 3 年之前
父节点
当前提交
24a9d1fafc
共有 3 个文件被更改,包括 81 次插入11 次删除
  1. 65 0
      dulwich/client.py
  2. 13 10
      dulwich/porcelain.py
  3. 3 1
      dulwich/repo.py

+ 65 - 0
dulwich/client.py

@@ -110,6 +110,7 @@ from dulwich.pack import (
 from dulwich.refs import (
     read_info_refs,
     ANNOTATED_TAG_SUFFIX,
+    _import_remote_refs,
 )
 
 
@@ -495,6 +496,70 @@ class GitClient(object):
         """
         raise NotImplementedError(self.send_pack)
 
+    def clone(self, path, target_path, mkdir=True, bare=False, origin="origin",
+              checkout=None, branch=None, depth=None):
+        """Clone a repository."""
+        from .refs import _set_origin_head, _set_default_branch, _set_head
+        from .repo import Repo
+
+        if mkdir:
+            os.mkdir(target_path)
+
+        try:
+            target = None
+            if not bare:
+                target = Repo.init(target_path)
+                if checkout is None:
+                    checkout = True
+            else:
+                if checkout:
+                    raise ValueError("checkout and bare are incompatible")
+                target = Repo.init_bare(target_path)
+
+            encoded_path = self.get_url(path).encode('utf-8')
+
+            target_config = target.get_config()
+            target_config.set((b"remote", origin.encode('utf-8')), b"url", encoded_path)
+            target_config.set(
+                (b"remote", origin.encode('utf-8')),
+                b"fetch",
+                b"+refs/heads/*:refs/remotes/" + origin.encode('utf-8') + b"/*",
+            )
+            target_config.write_to_path()
+
+            ref_message = b"clone: from " + encoded_path
+            result = self.fetch(path, target, depth=depth)
+            _import_remote_refs(
+                target.refs, origin, result.refs, message=ref_message)
+
+            origin_head = result.symrefs.get(b"HEAD")
+            origin_sha = result.refs.get(b'HEAD')
+            if origin_sha and not origin_head:
+                # set detached HEAD
+                target.refs[b"HEAD"] = origin_sha
+
+            _set_origin_head(target.refs, origin.encode('utf-8'), origin_head)
+            head_ref = _set_default_branch(
+                target.refs, origin.encode('utf-8'), origin_head, branch, ref_message
+            )
+
+            # Update target head
+            if head_ref:
+                head = _set_head(target.refs, head_ref, ref_message)
+            else:
+                head = None
+
+            if checkout and head is not None:
+                target.reset_index()
+        except BaseException:
+            if target is not None:
+                target.close()
+            if mkdir:
+                import shutil
+                shutil.rmtree(target_path)
+            raise
+        return target
+
     def fetch(self, path, target, determine_wants=None, progress=None, depth=None):
         """Fetch into a target repository.
 

+ 13 - 10
dulwich/porcelain.py

@@ -402,7 +402,7 @@ def clone(
     checkout=None,
     errstream=default_bytes_err_stream,
     outstream=None,
-    origin=b"origin",
+    origin="origin",
     depth=None,
     branch=None,
     **kwargs
@@ -442,15 +442,18 @@ def clone(
 
     mkdir = not os.path.exists(target)
 
-    with open_repo_closing(source) as r:
-        return r.clone(
-            target,
-            mkdir=mkdir,
-            bare=bare,
-            origin=origin,
-            checkout=checkout,
-            branch=branch,
-        )
+    (client, path) = get_transport_and_path(source)
+
+    return client.clone(
+        path,
+        target,
+        mkdir=mkdir,
+        bare=bare,
+        origin=origin,
+        checkout=checkout,
+        branch=branch,
+        depth=depth,
+    )
 
 
 def add(repo=".", paths=None):

+ 3 - 1
dulwich/repo.py

@@ -1389,6 +1389,7 @@ class Repo(BaseRepo):
         origin=b"origin",
         checkout=None,
         branch=None,
+        depth=None,
     ):
         """Clone this repository.
 
@@ -1401,6 +1402,7 @@ class Repo(BaseRepo):
             cloned from this repository
           branch: Optional branch or tag to be used as HEAD in the new repository
             instead of this repository's HEAD.
+          depth: Depth at which to fetch
         Returns: Created repository as `Repo`
         """
 
@@ -1432,7 +1434,7 @@ class Repo(BaseRepo):
             target_config.write_to_path()
 
             ref_message = b"clone: from " + encoded_path
-            self.fetch(target)
+            self.fetch(target, depth=depth)
             target.refs.import_refs(
                 b"refs/remotes/" + origin,
                 self.refs.as_dict(b"refs/heads"),