Skip to content

Commit 162e2b8

Browse files
codexByron
authored andcommitted
fix(gix): omit dangling symbolic references from enumeration
`for-each-ref` omits symbolic references whose targets do not exist, while GixPython's reference iterator yields them. This made `Repo.heads` include branches that the CLI backend would omit. Resolve symbolic targets while retaining their original names. A dangling target uses the existing read-error fallback, allowing Git to handle its filtering and diagnostics. Valid symbolic aliases still use native enumeration. The regression failed for both SHA-1 and SHA-256 before the fix. All six selected enumeration, query, and reference tests now pass, as do Ruff lint and format checks.
1 parent 86e7017 commit 162e2b8

2 files changed

Lines changed: 19 additions & 1 deletion

File tree

‎git/_backend.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,12 @@ def _for_each_ref(repo: Any, args: List[str], kwargs: Dict[str, Any]) -> bytes:
196196
if any(char in prefix for char in b"*?["):
197197
raise _Unsupported("reference glob patterns")
198198
with repo.references().all() as refs:
199-
names = [ref.name() for ref in refs]
199+
names = []
200+
for ref in refs:
201+
names.append(ref.name())
202+
if ref.target().try_name() is not None:
203+
# Git omits dangling symbolic refs; defer their diagnostics to it.
204+
ref.follow_to_object()
200205
return b"".join(
201206
name + b"\n"
202207
for name in sorted(names)

‎test/test_gix_backend.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@ def test_per_command_committer_identity_uses_cli(repo, fields, monkeypatch):
157157
assert cli.call_count == 1
158158

159159

160+
def test_reference_enumeration_preserves_aliases_and_skips_dangling_refs(repo):
161+
repo.index.commit("initial", skip_hooks=True)
162+
repo.git.symbolic_ref("refs/heads/alias", "refs/heads/main")
163+
with patch.object(Git, "execute", side_effect=AssertionError("unexpected CLI call")):
164+
assert repo.git._call_process_safe("for_each_ref", "--format=%(refname)", "--", "refs/heads") == (
165+
"refs/heads/alias\nrefs/heads/main"
166+
)
167+
repo.git.symbolic_ref("refs/heads/dangling", "refs/heads/missing")
168+
with patch.object(_backend, "gix", None):
169+
expected = [ref.path for ref in repo.heads]
170+
assert [ref.path for ref in repo.heads] == expected == ["refs/heads/alias", "refs/heads/main"]
171+
172+
160173
def test_a_native_write_failure_is_not_retried(repo, monkeypatch):
161174
attempts = []
162175

0 commit comments

Comments
 (0)