Severity: medium · Category: robustness
Location: get-ivim.sh — uninstall(), lines 232-247 (the unpushed-commits branch at line 238)
What's wrong
The uninstall path tries to protect a user's local work before deleting ~/.ivim. The committed-but-unpushed check is git -C "$IVIM_DIR" log '@{u}..HEAD' --oneline 2>/dev/null. When the checked-out branch has no configured upstream (e.g. the user did git checkout -b mytweaks to keep local edits, or HEAD is detached), @{u} does not resolve: git exits 128 with fatal: no upstream configured, and 2>/dev/null swallows it so the command substitution yields an empty string. dirty therefore stays empty, the guard is skipped, and rm -rf "$IVIM_DIR" (line 247) destroys the unpushed commits the guard was written to protect. The git status --porcelain check does not catch this because the work is committed, not in the working tree. The plain-clone default branch (master) does get upstream tracking so the common case is safe, but any user who forked their config onto a local branch loses it with no warning, directly contradicting the inline intent comment 'Guard against losing uncommitted/unpushed local work.'
Evidence
Line 238: elif [ -n "$(git -C "$IVIM_DIR" log '@{u}..HEAD' --oneline 2>/dev/null)" ]; then
Reproduced: on a local branch with no upstream and a fresh commit, git log '@{u}..HEAD' --oneline prints fatal: no upstream configured for branch 'master' to stderr and rc=128 with empty stdout. With 2>/dev/null the substitution is empty, so dirty="", the if [ -n "$dirty" ] block is skipped, and execution falls through to rm -rf "$IVIM_DIR" at line 247.
Suggested fix
Treat a non-zero exit from the @{u}..HEAD check as 'cannot prove it is safe' rather than 'no unpushed work'. For example, capture status explicitly and also detect the no-upstream / detached-HEAD cases as dirty: if git -C "$IVIM_DIR" rev-parse --abbrev-ref '@{u}' >/dev/null 2>&1 fails while HEAD has commits, set dirty="local commits with no upstream". Alternatively compare against the matching remote-tracking ref for the current branch, and on any ambiguity refuse to delete (require the user to remove ~/.ivim manually).
Filed from an automated multi-agent source review (2026-05-29); finding adversarially verified at high confidence. Line numbers reflect the audit-fixes-2026-05 working tree.
Severity: medium · Category: robustness
Location:
get-ivim.sh— uninstall(), lines 232-247 (the unpushed-commits branch at line 238)What's wrong
The uninstall path tries to protect a user's local work before deleting ~/.ivim. The committed-but-unpushed check is
git -C "$IVIM_DIR" log '@{u}..HEAD' --oneline 2>/dev/null. When the checked-out branch has no configured upstream (e.g. the user didgit checkout -b mytweaksto keep local edits, or HEAD is detached),@{u}does not resolve: git exits 128 withfatal: no upstream configured, and2>/dev/nullswallows it so the command substitution yields an empty string.dirtytherefore stays empty, the guard is skipped, andrm -rf "$IVIM_DIR"(line 247) destroys the unpushed commits the guard was written to protect. Thegit status --porcelaincheck does not catch this because the work is committed, not in the working tree. The plain-clone default branch (master) does get upstream tracking so the common case is safe, but any user who forked their config onto a local branch loses it with no warning, directly contradicting the inline intent comment 'Guard against losing uncommitted/unpushed local work.'Evidence
Line 238:
elif [ -n "$(git -C "$IVIM_DIR" log '@{u}..HEAD' --oneline 2>/dev/null)" ]; thenReproduced: on a local branch with no upstream and a fresh commit,
git log '@{u}..HEAD' --onelineprintsfatal: no upstream configured for branch 'master'to stderr and rc=128 with empty stdout. With2>/dev/nullthe substitution is empty, sodirty="", theif [ -n "$dirty" ]block is skipped, and execution falls through torm -rf "$IVIM_DIR"at line 247.Suggested fix
Treat a non-zero exit from the
@{u}..HEADcheck as 'cannot prove it is safe' rather than 'no unpushed work'. For example, capture status explicitly and also detect the no-upstream / detached-HEAD cases as dirty: ifgit -C "$IVIM_DIR" rev-parse --abbrev-ref '@{u}' >/dev/null 2>&1fails while HEAD has commits, set dirty="local commits with no upstream". Alternatively compare against the matching remote-tracking ref for the current branch, and on any ambiguity refuse to delete (require the user to remove ~/.ivim manually).Filed from an automated multi-agent source review (2026-05-29); finding adversarially verified at high confidence. Line numbers reflect the
audit-fixes-2026-05working tree.