From 2c9792a878b4627559a6c08e3c344ddf8bc4a03f Mon Sep 17 00:00:00 2001 From: Iliyas Jorio Date: Wed, 13 Aug 2025 12:47:01 +0200 Subject: [PATCH 1/2] Deprecate Remote.ls_remotes in favor of Remote.list_heads --- pygit2/remotes.py | 41 ++++++++++++++++++++++++++--------------- test/test_remote.py | 37 ++++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/pygit2/remotes.py b/pygit2/remotes.py index 52974646..30feeb15 100644 --- a/pygit2/remotes.py +++ b/pygit2/remotes.py @@ -55,7 +55,7 @@ class RemoteHead: """ Description of a reference advertised by a remote server, - given out on `Remote.ls_remotes` calls. + given out on `Remote.list_heads` calls. """ local: bool @@ -80,19 +80,6 @@ def __init__(self, c_struct: Any) -> None: self.name = maybe_string(c_struct.name) self.symref_target = maybe_string(c_struct.symref_target) - def __getitem__(self, item: str) -> Any: - """ - DEPRECATED: Backwards compatibility with legacy user code - that expects this object to be a dictionary with string keys. - """ - warnings.warn( - 'ls_remotes no longer returns a dict. ' - 'Update your code to read from fields instead ' - '(e.g. result["name"] --> result.name)', - DeprecationWarning, - ) - return getattr(self, item) - class PushUpdate: """ @@ -259,7 +246,7 @@ def fetch( return TransferProgress(C.git_remote_stats(self._remote)) - def ls_remotes( + def list_heads( self, callbacks: RemoteCallbacks | None = None, proxy: str | None | bool = None, @@ -294,6 +281,30 @@ def ls_remotes( return results + def ls_remotes( + self, + callbacks: RemoteCallbacks | None = None, + proxy: str | None | bool = None, + connect: bool = True, + ) -> list[dict[str, Any]]: + """ + Deprecated interface to list_heads + """ + warnings.warn('Use list_heads', DeprecationWarning) + + heads = self.list_heads(callbacks, proxy, connect) + + return [ + { + 'local': h.local, + 'oid': h.oid, + 'loid': h.loid, + 'name': h.name, + 'symref_target': h.symref_target, + } + for h in heads + ] + def prune(self, callbacks: RemoteCallbacks | None = None) -> None: """Perform a prune against this remote.""" with git_remote_callbacks(callbacks) as payload: diff --git a/test/test_remote.py b/test/test_remote.py index d13bb38b..277d490a 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -193,11 +193,11 @@ def test_remote_list(testrepo: Repository) -> None: @utils.requires_network -def test_ls_remotes(testrepo: Repository) -> None: +def test_list_heads(testrepo: Repository) -> None: assert 1 == len(testrepo.remotes) remote = testrepo.remotes[0] - refs = remote.ls_remotes() + refs = remote.list_heads() assert refs # Check that a known ref is returned. @@ -205,29 +205,36 @@ def test_ls_remotes(testrepo: Repository) -> None: @utils.requires_network -def test_ls_remotes_backwards_compatibility(testrepo: Repository) -> None: +def test_ls_remotes_deprecated(testrepo: Repository) -> None: assert 1 == len(testrepo.remotes) remote = testrepo.remotes[0] - refs = remote.ls_remotes() - ref = refs[0] - for field in ('name', 'oid', 'loid', 'local', 'symref_target'): - new_value = getattr(ref, field) - with pytest.warns(DeprecationWarning, match='no longer returns a dict'): - old_value = ref[field] - assert new_value == old_value + new_refs = remote.list_heads() + + with pytest.warns(DeprecationWarning, match='Use list_heads'): + old_refs = remote.ls_remotes() + + assert new_refs + assert old_refs + + for new, old in zip(new_refs, old_refs, strict=True): + assert new.name == old['name'] + assert new.oid == old['oid'] + assert new.loid == old['loid'] + assert new.local == old['local'] + assert new.symref_target == old['symref_target'] @utils.requires_network -def test_ls_remotes_without_implicit_connect(testrepo: Repository) -> None: +def test_list_heads_without_implicit_connect(testrepo: Repository) -> None: assert 1 == len(testrepo.remotes) remote = testrepo.remotes[0] with pytest.raises(pygit2.GitError, match='this remote has never connected'): - remote.ls_remotes(connect=False) + remote.list_heads(connect=False) remote.connect() - refs = remote.ls_remotes(connect=False) + refs = remote.list_heads(connect=False) assert refs # Check that a known ref is returned. @@ -328,7 +335,7 @@ def update_tips(self, name: str, old: pygit2.Oid, new: pygit2.Oid) -> None: @utils.requires_network -def test_ls_remotes_certificate_check() -> None: +def test_list_heads_certificate_check() -> None: url = 'https://github.com/pygit2/empty.git' class MyCallbacks(pygit2.RemoteCallbacks): @@ -350,7 +357,7 @@ def certificate_check( remote = git.remotes.create_anonymous(url) callbacks = MyCallbacks() - refs = remote.ls_remotes(callbacks=callbacks) + refs = remote.list_heads(callbacks=callbacks) # Sanity check that we indeed got some refs. assert len(refs) > 0 From d16378e01217bfef13a208f35e89f59898002acf Mon Sep 17 00:00:00 2001 From: Iliyas Jorio Date: Wed, 13 Aug 2025 17:21:23 +0200 Subject: [PATCH 2/2] Remote.ls_remotes returns backwards compatible value for loid --- pygit2/remotes.py | 2 +- test/test_remote.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pygit2/remotes.py b/pygit2/remotes.py index 30feeb15..0603a6f9 100644 --- a/pygit2/remotes.py +++ b/pygit2/remotes.py @@ -298,7 +298,7 @@ def ls_remotes( { 'local': h.local, 'oid': h.oid, - 'loid': h.loid, + 'loid': h.loid if h.local else None, 'name': h.name, 'symref_target': h.symref_target, } diff --git a/test/test_remote.py b/test/test_remote.py index 277d490a..d4c3bea6 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -220,9 +220,13 @@ def test_ls_remotes_deprecated(testrepo: Repository) -> None: for new, old in zip(new_refs, old_refs, strict=True): assert new.name == old['name'] assert new.oid == old['oid'] - assert new.loid == old['loid'] assert new.local == old['local'] assert new.symref_target == old['symref_target'] + if new.local: + assert new.loid == old['loid'] + else: + assert new.loid == pygit2.Oid(b'') + assert old['loid'] is None @utils.requires_network