fix(vibin): stop worktree-setup scripts aborting with a silent SIGPIPE - #12
Merged
Conversation
`worktree-new.sh`, `worktree-sync.sh`, and `worktree-rm.sh` could exit 141
with no output at all, leaving the caller with a bare failure and nothing to
diagnose. In `~/workspace/labby` (35 worktrees) `worktree-sync.sh --check`
aborted 7 times out of 8.
Every one of them resolved the repo root with
ROOT=$(git worktree list --porcelain | awk '/^worktree /{print $2; exit}')
The awk `exit` closes the pipe after the first record while git is still
writing. git dies of SIGPIPE, `set -o pipefail` promotes 141 to the pipeline
status, and `set -e` aborts — even though the assignment already succeeded.
It is invisible in a small repo: git emits short porcelain output in a single
write() and never observes the closed reader. The failure needs the stream to
exceed git's stdio buffer so a second write() happens, which is why this only
ever showed up in busy checkouts. It is also a race, not a certainty, so a
one-shot test misses it.
Fixed all four sites to match once without exiting, so awk drains git's
output. The one remaining early-close (`git lfs ls-files | head -1`) is safe
because it sits inside `[[ ]]`, where a command substitution's status cannot
reach pipefail; that is now commented so a future refactor out of `[[ ]]`
does not silently reintroduce the crash.
Added a smoke-test section that builds a 25-worktree fixture with long branch
names (8362 B of porcelain, comfortably past the buffer) and runs the real
scripts in a loop. Against the pre-fix scripts it catches the abort 4/20 and
7/8 of the time; a single invocation would not have.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The failure
worktree-new.sh,worktree-sync.sh, andworktree-rm.shexit 141 with nooutput at all — no error, no partial work, nothing to diagnose. Measured in
~/workspace/labby(35 worktrees):Root cause
Every script resolved the repo root the same way:
ROOT=$(git worktree list --porcelain | awk '/^worktree /{print $2; exit}')awk's
exitcloses the pipe after the first record while git is still writing.git dies of SIGPIPE,
set -o pipefailpromotes 141 to the pipeline status, andset -eaborts the script — even though the assignment already succeeded.It is invisible in a small repo: git emits short porcelain output in one
write()and never observes the closed reader. The crash needs the stream toexceed git's stdio buffer so a second
write()happens. It is also a race,not a certainty, so a single-shot test misses it.
The fix
All four sites now match once without exiting, so awk drains git's output:
awk '/^worktree /{if(!s){print $2; s=1}}'The one remaining early-close (
git lfs ls-files | head -1) is safe because itsits inside
[[ ]], where a command substitution's status cannot reachpipefail. That is now commented so a refactor out of
[[ ]]doesn't silentlyreintroduce the crash.
Tests
New smoke-test section builds a 25-worktree fixture with long branch names
(8362 B of porcelain — asserted to exceed the buffer, since a smaller fixture
proves nothing) and runs the real scripts in a loop. Against the pre-fix
scripts it catches the abort 4/20 and 7/8 of the time.
tests/smoke.sh— all pass, including the two new checks.