From 7092f980ed59f2a070717b06c3ef6e37900b8ff5 Mon Sep 17 00:00:00 2001 From: berce <75228263+bercedev@users.noreply.github.com> Date: Wed, 14 Oct 2026 01:07:59 +0300 Subject: [PATCH] fix(mcp): don't hand git the server's stdin in _git_head (#4440) graph_stats asks git for HEAD to report staleness. The child inherited the MCP stdio pipe, and on Windows git blocks on it: the call sat on the 3s timeout, or hung under Claude Code. prs.py already isolates stdin (#3318); these two _git_head copies were missed. --- graphify/export.py | 3 ++- graphify/watch.py | 3 ++- tests/test_git_head_stdin.py | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 tests/test_git_head_stdin.py diff --git a/graphify/export.py b/graphify/export.py index cf0b630ff..e61416ec6 100644 --- a/graphify/export.py +++ b/graphify/export.py @@ -226,7 +226,8 @@ def _git_head(cwd: "str | Path | None" = None) -> str | None: import subprocess as _sp try: r = _sp.run( - ["git", "rev-parse", "HEAD"], capture_output=True, text=True, timeout=3, + ["git", "rev-parse", "HEAD"], stdin=_sp.DEVNULL, + capture_output=True, text=True, timeout=3, cwd=str(cwd) if cwd is not None else None, ) return r.stdout.strip() if r.returncode == 0 else None diff --git a/graphify/watch.py b/graphify/watch.py index ec0a33878..cad0e8357 100644 --- a/graphify/watch.py +++ b/graphify/watch.py @@ -376,7 +376,8 @@ def _git_head(cwd: Path | str | None = None) -> str | None: import subprocess as _sp try: r = _sp.run( - ["git", "rev-parse", "HEAD"], capture_output=True, text=True, timeout=3, + ["git", "rev-parse", "HEAD"], stdin=_sp.DEVNULL, + capture_output=True, text=True, timeout=3, cwd=str(cwd) if cwd is not None else None, ) return r.stdout.strip() if r.returncode == 0 else None diff --git a/tests/test_git_head_stdin.py b/tests/test_git_head_stdin.py new file mode 100644 index 000000000..c5fcdb511 --- /dev/null +++ b/tests/test_git_head_stdin.py @@ -0,0 +1,21 @@ +"""_git_head must not hand git the caller's stdin (#4440). + +Under an MCP stdio host, stdin is the protocol pipe. On Windows a git child that +inherits it blocks, so `graph_stats` stalls on the timeout or hangs. Same class +as the prs.py isolation in #3318. +""" +import subprocess +from unittest.mock import MagicMock, patch + +import pytest + +from graphify import export, watch + + +@pytest.mark.parametrize("module", [export, watch], ids=["export", "watch"]) +def test_git_head_passes_devnull_stdin(module, tmp_path): + completed = MagicMock(returncode=0, stdout="abc123\n", stderr="") + with patch("subprocess.run", return_value=completed) as mock_run: + assert module._git_head(tmp_path) == "abc123" + _args, kwargs = mock_run.call_args + assert kwargs.get("stdin") == subprocess.DEVNULL