MCPcopy Create free account
hub / github.com/agentforce314/clawcodex / _run_git

Function _run_git

src/bridge/worktree.py:74–122  ·  view source on GitHub ↗

Run ``git `` in ``cwd``. Returns ``(returncode, stderr)``. stdout is discarded — worktree commands don't return data we need. Every failure mode — missing git binary, cwd unreadable, subprocess spawn failure, communicate timeout — is converted to a non-zero return code so call

(*args: str, cwd: str)

Source from the content-addressed store, hash-verified

72
73
74async def _run_git(*args: str, cwd: str) -> tuple[int, str]:
75 """Run ``git <args>`` in ``cwd``. Returns ``(returncode, stderr)``.
76
77 stdout is discarded — worktree commands don&#x27;t return data we need.
78 Every failure mode — missing git binary, cwd unreadable, subprocess
79 spawn failure, communicate timeout — is converted to a non-zero
80 return code so callers can fall back on a simple rc check. The
81 one thing this CAN&#x27;T do is hang: ``_GIT_TIMEOUT_S`` caps the wait
82 and we ``kill()`` the subprocess on timeout.
83 """
84 try:
85 proc = await asyncio.create_subprocess_exec(
86 'git', *args,
87 cwd=cwd,
88 stdout=asyncio.subprocess.DEVNULL,
89 stderr=asyncio.subprocess.PIPE,
90 )
91 except (OSError, ValueError) as err:
92 # FileNotFoundError (git not on PATH), PermissionError (cwd
93 # not +x), NotADirectoryError, etc. — all OSError subclasses.
94 # ValueError can come from create_subprocess_exec when args
95 # contain NULs.
96 return -1, str(err)
97 # The try/finally guarantees we kill the subprocess on every exit
98 # path: timeout, ``CancelledError`` from a parent task, or any
99 # other exception. Without it, a cancelled ``_run_git`` leaves the
100 # ``git`` process running after the daemon has shut down — see
101 # the cancellation-cleanup discussion in the Phase 12a review.
102 try:
103 try:
104 _, stderr_bytes = await asyncio.wait_for(
105 proc.communicate(), timeout=_GIT_TIMEOUT_S,
106 )
107 except asyncio.TimeoutError:
108 return -1, f'git timed out after {_GIT_TIMEOUT_S:.2f}s'
109 finally:
110 if proc.returncode is None:
111 try:
112 proc.kill()
113 except ProcessLookupError:
114 pass
115 # Drain so the transport releases its FDs. Bounded so a
116 # truly wedged process can't hang the finally block.
117 try:
118 await asyncio.wait_for(proc.wait(), timeout=1.0)
119 except (asyncio.TimeoutError, asyncio.CancelledError):
120 pass
121 rc = proc.returncode if proc.returncode is not None else -1
122 return rc, stderr_bytes.decode('utf-8', 'replace')
123
124
125async def _is_git_repo(path: str) -> bool:

Callers 3

_is_git_repoFunction · 0.70
create_agent_worktreeFunction · 0.70
remove_agent_worktreeFunction · 0.70

Calls 3

communicateMethod · 0.45
killMethod · 0.45
waitMethod · 0.45

Tested by

no test coverage detected