[ZSH] Bound background git calls to prevent zombie leaks

The check_tools_repo (git ls-remote | awk) and update_dotfiles (git fetch)
background jobs could stall on a network call and, being disowned (&!),
orphan into days-long processes holding unreaped git/awk children.

Bound both network probes with `timeout` (guarded, absent on macOS) and
disable ssh multiplexing so no daemonized mux master inherits our fds.
Also dedup the double update_dotfiles startup call.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Fabian Ising
2026-07-13 15:53:50 +02:00
co-authored by Claude Opus 4.8
parent 299bdcc3f2
commit bd8d29144f
+12 -3
View File
@@ -31,7 +31,13 @@ fi
# Per-branch, so `server` on each device fast-forwards to origin/server.
update_dotfiles() {
local repo="$HOME/dotfiles"
git -C "$repo" fetch --quiet 2>/dev/null || return
# Bound the network call so a stalled fetch can't wedge this backgrounded
# (&!) job into a days-long orphan holding unreaped children. `timeout` is
# absent on macOS, so only use it when present. Disable ssh multiplexing so
# no daemonized mux master can inherit and keep our fds open past git's exit.
local -a TO; (( $+commands[timeout] )) && TO=(timeout -k 5 15)
GIT_SSH_COMMAND='ssh -o BatchMode=yes -o ConnectTimeout=5 -o ControlMaster=no -o ControlPath=none' \
$TO git -C "$repo" fetch --quiet </dev/null 2>/dev/null || return
git -C "$repo" rev-parse --abbrev-ref '@{u}' >/dev/null 2>&1 || return # no upstream
git -C "$repo" merge-base --is-ancestor '@{u}' HEAD 2>/dev/null && return # already current
git -C "$repo" merge --ff-only --quiet '@{u}' 2>/dev/null \
@@ -41,7 +47,6 @@ update_dotfiles() {
update_dotfiles &!
if [ "$shared_config" -eq 0 ]; then
update_dotfiles 2>&1 &!
export GIT_AUTHOR_NAME="Fabian Ising"
export GIT_AUTHOR_EMAIL="f.ising@fh-muenster.de"
export GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME
@@ -58,13 +63,17 @@ fi
# Uses the first candidate that is a git checkout; covers global and per-user layouts.
check_tools_repo() {
local repo branch remote mergeref remote_sha local_sha cmd
# See update_dotfiles: bound the network probe (timeout, absent on macOS) and
# disable ssh multiplexing so a stalled ls-remote can't orphan this &! job.
local -a TO; (( $+commands[timeout] )) && TO=(timeout -k 5 10)
for repo in /opt/tools "$HOME/tools" "$HOME/tools/tools-repo"; do
local -a g=(git -c 'safe.directory=*' -C "$repo")
$g rev-parse --is-inside-work-tree >/dev/null 2>&1 || continue
branch=$($g symbolic-ref --short HEAD 2>/dev/null) || return
remote=$($g config "branch.$branch.remote" 2>/dev/null) || return # no upstream
mergeref=$($g config "branch.$branch.merge" 2>/dev/null) || return
remote_sha=$($g ls-remote "$remote" "$mergeref" 2>/dev/null | awk '{print $1}')
remote_sha=$(GIT_SSH_COMMAND='ssh -o BatchMode=yes -o ConnectTimeout=5 -o ControlMaster=no -o ControlPath=none' \
$TO $g ls-remote "$remote" "$mergeref" </dev/null 2>/dev/null | awk '{print $1}')
local_sha=$($g rev-parse HEAD 2>/dev/null)
[[ -n "$remote_sha" && -n "$local_sha" ]] || return # remote unreachable
[[ "$remote_sha" == "$local_sha" ]] && return # already current