commit 3e933515897cdb1e4105248fb7821340d3501990 from: Jelmer Vernooij via: GitHub date: Sun Oct 20 20:43:39 2024 UTC Refactor ref handling (#1393) commit - b1287d36edf7f54d38a0ed93021f2dc84f6db027 commit + 3e933515897cdb1e4105248fb7821340d3501990 blob - a7b9bdfc180d33bd96a94d605436532b8c5a4454 blob + a79d7dd7434b10a9409ff691c9007f3e6df37b8b --- dulwich/client.py +++ dulwich/client.py @@ -121,8 +121,20 @@ from .protocol import ( parse_capability, pkt_line, ) -from .refs import PEELED_TAG_SUFFIX, _import_remote_refs, read_info_refs +from .refs import ( + PEELED_TAG_SUFFIX, + Ref, + _import_remote_refs, + _set_default_branch, + _set_head, + _set_origin_head, + read_info_refs, + split_peeled_refs, +) from .repo import Repo + +ObjectID = bytes + # url2pathname is lazily imported url2pathname = None @@ -825,8 +837,6 @@ class GitClient: protocol_version: Optional[int] = None, ) -> Repo: """Clone a repository.""" - from .refs import _set_default_branch, _set_head, _set_origin_head - if mkdir: os.mkdir(target_path) @@ -2343,7 +2353,11 @@ class AbstractHttpGitClient(GitClient): """ raise NotImplementedError(self._http_request) - def _discover_references(self, service, base_url, protocol_version=None): + def _discover_references( + self, service, base_url, protocol_version=None + ) -> Tuple[ + Dict[Ref, ObjectID], Set[bytes], str, Dict[Ref, Ref], Dict[Ref, ObjectID] + ]: if ( protocol_version is not None and protocol_version not in GIT_PROTOCOL_VERSIONS @@ -2413,11 +2427,10 @@ class AbstractHttpGitClient(GitClient): self.protocol_version = server_protocol_version if self.protocol_version == 2: server_capabilities, resp, read, proto = begin_protocol_v2(proto) - (refs, _symrefs, _peeled) = read_pkt_refs_v2(proto.read_pkt_seq()) - return refs, server_capabilities, base_url + (refs, symrefs, peeled) = read_pkt_refs_v2(proto.read_pkt_seq()) + return refs, server_capabilities, base_url, symrefs, peeled else: - server_capabilities = None # read_pkt_refs will find them try: [pkt] = list(proto.read_pkt_seq()) except ValueError as exc: @@ -2446,14 +2459,21 @@ class AbstractHttpGitClient(GitClient): server_capabilities, resp, read, proto = begin_protocol_v2( proto ) - ( - refs, - server_capabilities, - ) = read_pkt_refs_v1(proto.read_pkt_seq()) - return refs, server_capabilities, base_url + (refs, symrefs, peeled) = read_pkt_refs_v2(proto.read_pkt_seq()) + else: + ( + refs, + server_capabilities, + ) = read_pkt_refs_v1(proto.read_pkt_seq()) + (refs, peeled) = split_peeled_refs(refs) + (symrefs, agent) = _extract_symrefs_and_agent( + server_capabilities + ) + return refs, server_capabilities, base_url, symrefs, peeled else: self.protocol_version = 0 # dumb servers only support protocol v0 - return read_info_refs(resp), set(), base_url + (refs, peeled) = split_peeled_refs(read_info_refs(resp)) + return refs, set(), base_url, {}, peeled finally: resp.close() @@ -2501,7 +2521,7 @@ class AbstractHttpGitClient(GitClient): """ url = self._get_url(path) - old_refs, server_capabilities, url = self._discover_references( + old_refs, server_capabilities, url, symrefs, peeled = self._discover_references( b"git-receive-pack", url ) ( @@ -2584,7 +2604,7 @@ class AbstractHttpGitClient(GitClient): """ url = self._get_url(path) - refs, server_capabilities, url = self._discover_references( + refs, server_capabilities, url, symrefs, peeled = self._discover_references( b"git-upload-pack", url, protocol_version ) ( @@ -2651,7 +2671,7 @@ class AbstractHttpGitClient(GitClient): def get_refs(self, path): """Retrieve the current refs from a git smart server.""" url = self._get_url(path) - refs, _, _ = self._discover_references(b"git-upload-pack", url) + refs, _, _, _, _ = self._discover_references(b"git-upload-pack", url) return refs def get_url(self, path): blob - 57887ceb6181862f405f67ff9ff4a0fe082ebc38 blob + 1fa48a9c0339688ce0ab956b788828ee83fa79e8 --- dulwich/contrib/swift.py +++ dulwich/contrib/swift.py @@ -59,7 +59,7 @@ from ..pack import ( write_pack_object, ) from ..protocol import TCP_GIT_PORT -from ..refs import InfoRefsContainer, read_info_refs, write_info_refs +from ..refs import InfoRefsContainer, read_info_refs, split_peeled_refs, write_info_refs from ..repo import OBJECTDIR, BaseRepo from ..server import Backend, TCPGitServer @@ -809,6 +809,7 @@ class SwiftInfoRefsContainer(InfoRefsContainer): if not f: return {} refs = read_info_refs(f) + (refs, peeled) = split_peeled_refs(refs) if old_ref is not None: if refs[name] != old_ref: return False blob - 1a7f1ef606ed5fb248a3af5e30f2bbd8d948fd23 blob + fd774089835ad64a4e462ada8148101679950f04 --- dulwich/refs.py +++ dulwich/refs.py @@ -585,17 +585,8 @@ class InfoRefsContainer(RefsContainer): def __init__(self, f) -> None: self._refs = {} self._peeled = {} - for line in f.readlines(): - sha, name = line.rstrip(b"\n").split(b"\t") - if name.endswith(PEELED_TAG_SUFFIX): - name = name[:-3] - if not check_ref_format(name): - raise ValueError(f"invalid ref name {name!r}") - self._peeled[name] = sha - else: - if not check_ref_format(name): - raise ValueError(f"invalid ref name {name!r}") - self._refs[name] = sha + refs = read_info_refs(f) + (self._refs, self._peeled) = split_peeled_refs(refs) def allkeys(self): return self._refs.keys() @@ -1175,6 +1166,18 @@ def strip_peeled_refs(refs): } +def split_peeled_refs(refs): + """Split peeled refs from regular refs.""" + peeled = {} + regular = {} + for ref, sha in refs.items(): + if ref.endswith(PEELED_TAG_SUFFIX): + peeled[ref[: -len(PEELED_TAG_SUFFIX)]] = sha + else: + regular[ref] = sha + return regular, peeled + + def _set_origin_head(refs, origin, origin_head): # set refs/remotes/origin/HEAD origin_base = b"refs/remotes/" + origin + b"/" blob - 83c9c59c8772b4a2471a4a55c92a4bac73eb7ef8 blob + 9d2947c8b28789d00594822677740842f18cf0b9 --- tests/test_client.py +++ tests/test_client.py @@ -1138,7 +1138,9 @@ class HttpGitClientTests(TestCase): # instantiate HttpGitClient with mocked pool manager c = HttpGitClient(base_url, pool_manager=pool_manager, config=None) # call method that detects url redirection - _, _, processed_url = c._discover_references(b"git-upload-pack", base_url) + _, _, processed_url, _, _ = c._discover_references( + b"git-upload-pack", base_url + ) # send the same request as the method above without redirection resp = c.pool_manager.request("GET", base_url + tail, redirect=False) blob - 572be9d8043de99402ee1aca3561a3c9dd412a1c blob + 361e98a3c6f04c1b340738f02b35f0a40965059f --- tests/test_refs.py +++ tests/test_refs.py @@ -38,6 +38,7 @@ from dulwich.refs import ( parse_symref_value, read_packed_refs, read_packed_refs_with_peeled, + split_peeled_refs, strip_peeled_refs, write_packed_refs, ) @@ -814,3 +815,14 @@ class StripPeeledRefsTests(TestCase): def test_strip_peeled_refs(self): # Simple check of two dicts self.assertEqual(strip_peeled_refs(self.all_refs), self.non_peeled_refs) + + def test_split_peeled_refs(self): + (regular, peeled) = split_peeled_refs(self.all_refs) + self.assertEqual(regular, self.non_peeled_refs) + self.assertEqual( + peeled, + { + b"refs/tags/2.0.0": b"0749936d0956c661ac8f8d3483774509c165f89e", + b"refs/tags/1.0.0": b"a93db4b0360cc635a2b93675010bac8d101f73f0", + }, + )