Skip to content

Commit eef288e

Browse files
blhsingclaude
andcommitted
test: distinguish real git binary from pythongit shim in interop tests
CI installs the package, which adds a 'git' console script that shadows the real git on PATH. Interop tests doing subprocess.run(['git', ...]) were therefore invoking pythongit-against-itself, not pythongit-against- real-git. Fix: tests now call conftest.real_git() which walks PATH and known absolute paths, identifying real git by its '--version' output starting with 'git version ' (pythongit's prints 'pygit version '). Skips interop assertions when no real git is found. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e983b19 commit eef288e

7 files changed

Lines changed: 96 additions & 20 deletions

File tree

tests/conftest.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from __future__ import annotations
33

44
import os
5+
import shutil
6+
import subprocess
57
import sys
68
import tempfile
79
from pathlib import Path
@@ -12,6 +14,50 @@
1214
sys.path.insert(0, str(ROOT))
1315

1416

17+
def _is_real_git(path: str) -> bool:
18+
"""A real git binary identifies itself as 'git version X.Y.Z'. Pythongit's
19+
`git` shim prints 'pygit version ...' for --version. Use this to
20+
distinguish them when pythongit is installed into the same venv."""
21+
try:
22+
r = subprocess.run([path, "--version"], capture_output=True, text=True, timeout=5)
23+
except (OSError, subprocess.TimeoutExpired):
24+
return False
25+
if r.returncode != 0:
26+
return False
27+
out = (r.stdout or "") + (r.stderr or "")
28+
# Real git prints 'git version 2.x.y'. Reject pythongit's 'pygit version ...'.
29+
return out.startswith("git version ") and "pygit" not in out
30+
31+
32+
def real_git() -> str | None:
33+
"""Return path of a real git binary, or None if not available.
34+
35+
Walks PATH and tries every `git` it finds (in order), returning the first
36+
that identifies as real git. Falls back to known absolute paths.
37+
"""
38+
seen: set[str] = set()
39+
for d in os.environ.get("PATH", "").split(os.pathsep):
40+
if not d:
41+
continue
42+
for name in ("git", "git.exe"):
43+
cand = os.path.join(d, name)
44+
if cand in seen:
45+
continue
46+
seen.add(cand)
47+
if os.path.isfile(cand) and _is_real_git(cand):
48+
return cand
49+
# Fallbacks
50+
for cand in (
51+
"/usr/bin/git", "/usr/local/bin/git",
52+
"/opt/homebrew/bin/git", "/Library/Developer/CommandLineTools/usr/bin/git",
53+
r"C:\Program Files\Git\bin\git.exe",
54+
r"C:\Program Files\Git\cmd\git.exe",
55+
):
56+
if cand not in seen and os.path.isfile(cand) and _is_real_git(cand):
57+
return cand
58+
return None
59+
60+
1561
@pytest.fixture
1662
def tmprepo(tmp_path: Path):
1763
"""Initialize a fresh repository and chdir into it. Yields (Repository, Path)."""

tests/test_phase2.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@
1414
from pythongit import cli # noqa: E402
1515

1616

17+
def _git():
18+
from conftest import real_git
19+
return real_git()
20+
21+
22+
def git_available() -> bool:
23+
return _git() is not None
24+
25+
1726
def run(*args: str) -> int:
1827
return cli.main(list(args))
1928

@@ -73,16 +82,16 @@ def check(cond, label):
7382
run("branch", "cp")
7483
run("checkout", "cp")
7584
# reset cp to C3
76-
c3 = subprocess.run(["git", "rev-parse", "HEAD^1"], cwd=tmp, capture_output=True, text=True).stdout.strip() if shutil.which("git") else None
85+
c3 = subprocess.run([_git(), "rev-parse", "HEAD^1"], cwd=tmp, capture_output=True, text=True).stdout.strip() if git_available() else None
7786
if c3:
7887
run("reset", "--hard", c3)
79-
feat_tip = subprocess.run(["git", "rev-parse", "feat"], cwd=tmp, capture_output=True, text=True).stdout.strip()
88+
feat_tip = subprocess.run([_git(), "rev-parse", "feat"], cwd=tmp, capture_output=True, text=True).stdout.strip()
8089
rc = run("cherry-pick", feat_tip)
8190
check(rc == 0, "cherry-pick clean")
8291

8392
# revert
8493
rc = run("log", "--oneline", "-n", "1")
85-
head = subprocess.run(["git", "rev-parse", "HEAD"], cwd=tmp, capture_output=True, text=True).stdout.strip() if shutil.which("git") else None
94+
head = subprocess.run([_git(), "rev-parse", "HEAD"], cwd=tmp, capture_output=True, text=True).stdout.strip() if git_available() else None
8695
if head:
8796
rc = run("revert", head)
8897
check(rc == 0, "revert HEAD succeeds")

tests/test_phase4.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@
1616
from pythongit import cli # noqa: E402
1717

1818

19+
def _git():
20+
from conftest import real_git
21+
return real_git()
22+
23+
24+
def git_available() -> bool:
25+
return _git() is not None
26+
27+
1928
def run(*args: str) -> int:
2029
return cli.main(list(args))
2130

@@ -55,8 +64,8 @@ def check(cond, label):
5564
check(rc == 0, "verify-pack")
5665

5766
# real git can read our pack
58-
if shutil.which("git"):
59-
r = subprocess.run(["git", "verify-pack", "-v", str(packs[0])], cwd=tmp,
67+
if git_available():
68+
r = subprocess.run([_git(), "verify-pack", "-v", str(packs[0])], cwd=tmp,
6069
capture_output=True, text=True)
6170
check(r.returncode == 0, f"real git verify-pack passes (stderr={r.stderr.strip()[:120]})")
6271

tests/test_smoke.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@ def run(*args: str) -> int:
2525

2626

2727
def git_available() -> bool:
28-
return shutil.which("git") is not None
28+
from conftest import real_git
29+
return real_git() is not None
30+
31+
32+
def _git():
33+
from conftest import real_git
34+
return real_git()
2935

3036

3137
def main() -> int:
@@ -65,13 +71,13 @@ def check(cond: bool, label: str) -> None:
6571

6672
# write-tree round-trip via real git, if available
6773
if git_available():
68-
r = subprocess.run(["git", "log", "--oneline"], cwd=tmp, capture_output=True, text=True)
74+
r = subprocess.run([_git(), "log", "--oneline"], cwd=tmp, capture_output=True, text=True)
6975
check(r.returncode == 0 and r.stdout.strip() != "", f"git log reads our commit: {r.stdout.strip()!r}")
7076

71-
r = subprocess.run(["git", "fsck", "--no-dangling"], cwd=tmp, capture_output=True, text=True)
77+
r = subprocess.run([_git(), "fsck", "--no-dangling"], cwd=tmp, capture_output=True, text=True)
7278
check(r.returncode == 0, f"git fsck passes (stderr={r.stderr.strip()!r})")
7379

74-
r = subprocess.run(["git", "cat-file", "-p", "HEAD^{tree}"], cwd=tmp, capture_output=True, text=True)
80+
r = subprocess.run([_git(), "cat-file", "-p", "HEAD^{tree}"], cwd=tmp, capture_output=True, text=True)
7581
ok = r.returncode == 0 and "hello.txt" in r.stdout
7682
check(ok, f"git can read tree (rc={r.returncode} stdout={r.stdout!r} stderr={r.stderr!r})")
7783

@@ -90,7 +96,7 @@ def check(cond: bool, label: str) -> None:
9096
run("commit", "-m", "feature commit")
9197

9298
if git_available():
93-
r = subprocess.run(["git", "log", "--all", "--oneline"], cwd=tmp, capture_output=True, text=True)
99+
r = subprocess.run([_git(), "log", "--all", "--oneline"], cwd=tmp, capture_output=True, text=True)
94100
lines = [l for l in r.stdout.splitlines() if l.strip()]
95101
check(len(lines) == 3,
96102
f"git sees 3 commits across branches (got {len(lines)}; rc={r.returncode} stdout={r.stdout!r} stderr={r.stderr!r})")

tests/unit_index.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,18 @@ def test_upsert_replaces_same_path_same_stage():
8484

8585
def test_real_git_reads_our_index_file(tmprepo):
8686
"""Real git ls-files should read pythongit's index correctly."""
87-
import shutil, subprocess
88-
if not shutil.which("git"):
89-
return # skip silently if git not installed
87+
import subprocess
88+
from conftest import real_git
89+
gitbin = real_git()
90+
if not gitbin:
91+
return # skip silently if real git not installed
9092
repo, _ = tmprepo
9193
from pythongit import objects as objs
9294
blob_sha = objs.write_object(repo, "blob", b"hi\n")
9395
idx = Index()
9496
idx.entries.append(IndexEntry(mode=REG_MODE, sha=blob_sha, path="hi.txt"))
9597
write_index(repo, idx)
96-
r = subprocess.run(["git", "ls-files", "--stage"], cwd=repo.path,
98+
r = subprocess.run([gitbin, "ls-files", "--stage"], cwd=repo.path,
9799
capture_output=True, text=True)
98100
assert r.returncode == 0
99101
assert "hi.txt" in r.stdout

tests/unit_integration.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,17 @@ def test_pack_objects_and_unpack(tmprepo):
182182

183183

184184
def test_commit_graph_binary_passes_real_git(tmprepo):
185-
import shutil, subprocess
186-
if not shutil.which("git"):
185+
import subprocess
186+
from conftest import real_git
187+
gitbin = real_git()
188+
if not gitbin:
187189
return
188190
from tests.conftest import commit_one
189191
repo, _ = tmprepo
190192
for i in range(5):
191193
commit_one(repo, "a", f"v{i}\n", f"c{i}")
192194
assert cli_run("commit-graph", "write") == 0
193-
r = subprocess.run(["git", "commit-graph", "verify"], cwd=repo.path,
195+
r = subprocess.run([gitbin, "commit-graph", "verify"], cwd=repo.path,
194196
capture_output=True, text=True)
195197
assert r.returncode == 0, r.stderr
196198

tests/unit_pack.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,10 @@ def test_build_pack_then_read_back(tmprepo):
7171

7272

7373
def test_real_git_verifies_our_pack(tmprepo):
74-
import shutil, subprocess, hashlib
75-
if not shutil.which("git"):
74+
import subprocess, hashlib
75+
from conftest import real_git
76+
gitbin = real_git()
77+
if not gitbin:
7678
return
7779
repo, _ = tmprepo
7880
from pythongit import objects as objs
@@ -93,7 +95,7 @@ def test_real_git_verifies_our_pack(tmprepo):
9395
i.write_bytes(idx_bytes)
9496
# Git verify-pack must succeed — output format varies across git versions
9597
# so we only assert on the exit code, not on the summary text.
96-
r = subprocess.run(["git", "verify-pack", "-v", str(p)], capture_output=True, text=True)
98+
r = subprocess.run([gitbin, "verify-pack", "-v", str(p)], capture_output=True, text=True)
9799
assert r.returncode == 0, f"stderr={r.stderr!r} stdout={r.stdout!r}"
98100
# Every blob sha we wrote must appear in the verify output.
99101
for sha in blobs:

0 commit comments

Comments
 (0)