Skip to content

Commit e935a2d

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 4c88830 commit e935a2d

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
@@ -192,7 +192,12 @@ def _for_each_ref(repo: Any, args: List[str], kwargs: Dict[str, Any]) -> bytes:
192192
if any(char in prefix for char in b"*?["):
193193
raise _Unsupported("reference glob patterns")
194194
with repo.references().all() as refs:
195-
names = [ref.name() for ref in refs]
195+
names = []
196+
for ref in refs:
197+
names.append(ref.name())
198+
if ref.target().try_name() is not None:
199+
# Git omits dangling symbolic refs; defer their diagnostics to it.
200+
ref.follow_to_object()
196201
return b"".join(
197202
name + b"\n"
198203
for name in sorted(names)

‎test/test_gix_backend.py‎

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

158158

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

0 commit comments

Comments
 (0)