commit - 0078a59e44b767f958279733ef22bc7003d623e1
commit + 90a2b333aee44a800ea9beb89728e6c9b5ff336b
blob - c51ea2312778272fecff47edc8015963042d288b
blob + b63494263735a2f6758a23bbafb6950c98683faa
--- NEWS
+++ NEWS
+0.20.31 2022-01-21
+
+ * Add GitClient.clone(). (Jelmer Vernooij, #920)
+
0.20.30 2022-01-08
0.20.29 2022-01-08
blob - fc2a42b75edd1cae3f05952f180f61368145a86c
blob + 10989e70ddc37e59a0b546b680dcdbc375f5e454
--- PKG-INFO
+++ PKG-INFO
Metadata-Version: 2.1
Name: dulwich
-Version: 0.20.30
+Version: 0.20.31
Summary: Python Git Library
Home-page: https://www.dulwich.io/
Author: Jelmer Vernooij
blob - b6d263798a69926bced09fe3528edf516a10fed5
blob + e313fa9d074b29fbbc1daf5fc705e0caf99fc051
--- dulwich/__init__.py
+++ dulwich/__init__.py
"""Python implementation of the Git file formats and protocols."""
-__version__ = (0, 20, 30)
+__version__ = (0, 20, 31)
blob - ad552ab0a52daeb0ab7ce6c956929a2d28106b60
blob + 12e5b7a10f51a02ae1f1a609989c4a1996c88a3f
--- dulwich/bundle.py
+++ dulwich/bundle.py
for ref, obj_id in bundle.references.items():
f.write(b"%s %s\n" % (obj_id, ref))
f.write(b"\n")
- write_pack_data(f, len(bundle.pack_data), iter(bundle.pack_data))
+ write_pack_data(f, records=bundle.pack_data)
blob - 03e94f5d8c74c7e64b01a25526f9ac734615c965
blob + 0dc009171566b286623cc4f8411cfd1331872c21
--- dulwich/client.py
+++ dulwich/client.py
from dulwich.refs import (
read_info_refs,
ANNOTATED_TAG_SUFFIX,
+ _import_remote_refs,
)
"""
raise NotImplementedError(self.send_pack)
+
+ def clone(self, path, target_path, mkdir: bool = 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)
+ # TODO(jelmer): abstract method for get_location?
+ if isinstance(self, (LocalGitClient, SubprocessGitClient)):
+ encoded_path = path.encode('utf-8')
+ else:
+ 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.
blob - 9b76a1d0c91629092479f242c1c2409c5a3276e0
blob + 74c1a1fa59090fec5a02a427fa823fe1b1fc983c
--- dulwich/pack.py
+++ dulwich/pack.py
)
-def write_pack_data(f, num_records, records, progress=None, compression_level=-1):
+def write_pack_data(f, num_records=None, records=None, progress=None, compression_level=-1):
"""Write a new pack data file.
Args:
f: File to write to
- num_records: Number of records
+ num_records: Number of records (defaults to len(records) if None)
records: Iterator over type_num, object_id, delta_base, raw
progress: Function to report progress to
compression_level: the zlib compression level
# Write the pack
entries = {}
f = SHA1Writer(f)
+ if num_records is None:
+ num_records = len(records)
write_pack_header(f, num_records)
+ actual_num_records = 0
for i, (type_num, object_id, delta_base, raw) in enumerate(records):
if progress is not None:
progress(("writing pack data: %d/%d\r" % (i, num_records)).encode("ascii"))
type_num = OFS_DELTA
raw = (offset - base_offset, raw)
crc32 = write_pack_object(f, type_num, raw, compression_level=compression_level)
+ actual_num_records += 1
entries[object_id] = (offset, crc32)
+ if actual_num_records != num_records:
+ raise AssertionError(
+ 'actual records written differs: %d != %d' % (
+ actual_num_records, num_records))
return entries, f.write_sha()
blob - 8d4ab0eb4f9731223253ae61659977f5a5308aac
blob + 95309066ea3b3918b21a7bdac2b7a26d1ff0f0dc
--- dulwich/porcelain.py
+++ dulwich/porcelain.py
import sys
import time
from typing import (
- Dict,
Optional,
Tuple,
Union,
ZERO_SHA,
)
from dulwich.refs import (
- ANNOTATED_TAG_SUFFIX,
LOCAL_BRANCH_PREFIX,
LOCAL_TAG_PREFIX,
- strip_peeled_refs,
- RefsContainer,
+ _import_remote_refs,
)
from dulwich.repo import BaseRepo, Repo
from dulwich.server import (
checkout=None,
errstream=default_bytes_err_stream,
outstream=None,
- origin=b"origin",
+ origin="origin",
depth=None,
branch=None,
**kwargs
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):
except KeyError:
remote_name = b"origin"
return remote_name
-
-
-def _import_remote_refs(
- refs_container: RefsContainer,
- remote_name: str,
- refs: Dict[str, str],
- message: Optional[bytes] = None,
- prune: bool = False,
- prune_tags: bool = False,
-):
- stripped_refs = strip_peeled_refs(refs)
- branches = {
- n[len(LOCAL_BRANCH_PREFIX) :]: v
- for (n, v) in stripped_refs.items()
- if n.startswith(LOCAL_BRANCH_PREFIX)
- }
- refs_container.import_refs(
- b"refs/remotes/" + remote_name.encode(),
- branches,
- message=message,
- prune=prune,
- )
- tags = {
- n[len(b"refs/tags/") :]: v
- for (n, v) in stripped_refs.items()
- if n.startswith(b"refs/tags/") and not n.endswith(ANNOTATED_TAG_SUFFIX)
- }
- refs_container.import_refs(b"refs/tags", tags, message=message, prune=prune_tags)
def fetch(
blob - 9d022f44cc481abfb0c44e6923387c0d398ebe0e
blob + e82f08d5b3d2cb245370501143754fb2a0e6afca
--- dulwich/refs.py
+++ dulwich/refs.py
"""
import os
+from typing import Dict, Optional
from dulwich.errors import (
PackedRefsException,
)
+HEADREF = b"HEAD"
SYMREF = b"ref: "
LOCAL_BRANCH_PREFIX = b"refs/heads/"
LOCAL_TAG_PREFIX = b"refs/tags/"
Raises:
KeyError: if a refname is not HEAD or is otherwise not valid.
"""
- if name in (b"HEAD", b"refs/stash"):
+ if name in (HEADREF, b"refs/stash"):
return
if not name.startswith(b"refs/") or not check_ref_format(name[5:]):
raise RefFormatError(name)
def allkeys(self):
allkeys = set()
- if os.path.exists(self.refpath(b"HEAD")):
- allkeys.add(b"HEAD")
+ if os.path.exists(self.refpath(HEADREF)):
+ allkeys.add(HEADREF)
path = self.refpath(b"")
refspath = self.refpath(b"refs")
for root, unused_dirs, files in os.walk(refspath):
name = name.replace(b"/", os.fsencode(os.path.sep))
# TODO: as the 'HEAD' reference is working tree specific, it
# should actually not be a part of RefsContainer
- if name == b"HEAD":
+ if name == HEADREF:
return os.path.join(self.worktree_path, name)
else:
return os.path.join(self.path, name)
for name, sha in sorted(refs.items()):
# get_refs() includes HEAD as a special case, but we don't want to
# advertise it
- if name == b"HEAD":
+ if name == HEADREF:
continue
try:
o = store[sha]
# set refs/remotes/origin/HEAD
origin_base = b"refs/remotes/" + origin + b"/"
if origin_head and origin_head.startswith(LOCAL_BRANCH_PREFIX):
- origin_ref = origin_base + b"HEAD"
+ origin_ref = origin_base + HEADREF
target_ref = origin_base + origin_head[len(LOCAL_BRANCH_PREFIX) :]
if target_ref in refs:
refs.set_symbolic_ref(origin_ref, target_ref)
if isinstance(head, Tag):
_cls, obj = head.object
head = obj.get_object(obj).id
- del refs[b"HEAD"]
+ del refs[HEADREF]
refs.set_if_equals(
- b"HEAD", None, head, message=ref_message
+ HEADREF, None, head, message=ref_message
)
else:
# set HEAD to specific branch
try:
head = refs[head_ref]
- refs.set_symbolic_ref(b"HEAD", head_ref)
- refs.set_if_equals(
- b"HEAD", None, head, message=ref_message
- )
+ refs.set_symbolic_ref(HEADREF, head_ref)
+ refs.set_if_equals(HEADREF, None, head, message=ref_message)
except KeyError:
head = None
return head
+
+
+def _import_remote_refs(
+ refs_container: RefsContainer,
+ remote_name: str,
+ refs: Dict[str, str],
+ message: Optional[bytes] = None,
+ prune: bool = False,
+ prune_tags: bool = False,
+):
+ stripped_refs = strip_peeled_refs(refs)
+ branches = {
+ n[len(LOCAL_BRANCH_PREFIX) :]: v
+ for (n, v) in stripped_refs.items()
+ if n.startswith(LOCAL_BRANCH_PREFIX)
+ }
+ refs_container.import_refs(
+ b"refs/remotes/" + remote_name.encode(),
+ branches,
+ message=message,
+ prune=prune,
+ )
+ tags = {
+ n[len(LOCAL_TAG_PREFIX) :]: v
+ for (n, v) in stripped_refs.items()
+ if n.startswith(LOCAL_TAG_PREFIX) and not n.endswith(ANNOTATED_TAG_SUFFIX)
+ }
+ refs_container.import_refs(LOCAL_TAG_PREFIX, tags, message=message, prune=prune_tags)
blob - e0362ef44e7fc68e42b391ea3ee532c0619d0c7b
blob + 93bfe021f0127a20a677900d924f342440be9602
--- dulwich/repo.py
+++ dulwich/repo.py
origin=b"origin",
checkout=None,
branch=None,
+ depth=None,
):
"""Clone this repository.
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`
"""
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"),
blob - db5ed86a9b515476060843c85841e0be0751cc40
blob + 1717b942ad30f42fe2a9338a4e5a37c8979ffe42
--- dulwich/tests/test_client.py
+++ dulwich/tests/test_client.py
s = open_repo("a.git")
self.addCleanup(tear_down_repo, s)
self.assertEqual(s.get_refs(), c.fetch(s.path, t).refs)
+
+ def test_clone(self):
+ c = LocalGitClient()
+ s = open_repo("a.git")
+ self.addCleanup(tear_down_repo, s)
+ target = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, target)
+ result_repo = c.clone(s.path, target, mkdir=False)
+ expected = dict(s.get_refs())
+ expected[b'refs/remotes/origin/HEAD'] = expected[b'HEAD']
+ expected[b'refs/remotes/origin/master'] = expected[b'refs/heads/master']
+ self.assertEqual(expected, result_repo.get_refs())
def test_fetch_empty(self):
c = LocalGitClient()
blob - fc2a42b75edd1cae3f05952f180f61368145a86c
blob + 10989e70ddc37e59a0b546b680dcdbc375f5e454
--- dulwich.egg-info/PKG-INFO
+++ dulwich.egg-info/PKG-INFO
Metadata-Version: 2.1
Name: dulwich
-Version: 0.20.30
+Version: 0.20.31
Summary: Python Git Library
Home-page: https://www.dulwich.io/
Author: Jelmer Vernooij
blob - e0ee7431ac4e6b4f1f0ec9fe5766e68c227588b9
blob + 5d4ab14a88f6589b45ef7675a38bb8c7dc836708
--- setup.py
+++ setup.py
'For 2.7 support, please install a version prior to 0.20')
-dulwich_version_string = '0.20.30'
+dulwich_version_string = '0.20.31'
class DulwichDistribution(Distribution):