Files
gstack/bin/gstack-path-spelling.sh
T
e6edff0265 v1.91.89.0 fix(windows,setup): Git Bash hook false-positive recovery, incoming-checker auto-update, setup never deletes its own checkout (Wave 9 Lane A0) (#3140)
Git Bash hands native Bun C:/ paths while gstack-hook-check looked them up as
/c/, so every TypeScript hook was refused and team-mode auto-update held every
release since 1.91.31. The checker maps Bun's paths back to the shell spelling,
the auto-updater lets the incoming release's own checker decide (installed
checker only as a fallback, hook-check-unavailable when neither verifies), and
docs/troubleshooting.md gives stuck installs one recovery command.
/gstack-upgrade and /spec guards use the --show-prefix root check, gstack-wtree
treats drive-letter --git-path answers as absolute, and setup refuses to
replace a runtime root that is or contains its own checkout, Kiro's in-place
root included (#3084), with --status suggesting the move. New sourced helper
bin/gstack-path-spelling.sh and TS twin lib/path-spelling.ts.

Co-authored-by: yuriysan <63948747+yuriysan@users.noreply.github.com>
Co-authored-by: y$un_ <3579304+szsunyuan@users.noreply.github.com>
Co-authored-by: Frederik Reventlow <267160539+dmfrede@users.noreply.github.com>
Co-authored-by: skleinbe-ny <226563731+skleinbe-ny@users.noreply.github.com>
Co-authored-by: bkermen <184220642+bkermen@users.noreply.github.com>
Co-authored-by: Drew Jankowski <3200063+andrewkjankowski@users.noreply.github.com>
2026-10-11 16:48:11 +00:00

88 lines
3.1 KiB
Bash

# shellcheck shell=bash
# gstack-path-spelling.sh — sourced helper: one spelling for paths on Windows
# shells. Git Bash (MSYS) spells a drive path /c/x, native programs (git.exe,
# bun.exe) answer C:/x or C:\x, and MSYS converts /c/x arguments to C:/x before
# a native program starts. Comparing two spellings as strings is the bug class
# behind #3088, #3100 and #3101. TS twin: lib/path-spelling.ts.
#
# gstack_path_shell PATH Git Bash's drive spelling (/c/x) of an absolute
# PATH, mount aliases such as /tmp resolved through
# their drive path; unchanged outside Windows shells
# gstack_path_native PATH the native spelling (C:/x) on Windows shells;
# unchanged elsewhere
# gstack_path_same A B true when A and B name the same file: filesystem
# identity when both exist, else equal shell
# spellings
# gstack_path_absolute PATH true when PATH is absolute in any spelling (/x,
# C:/x, C:\x): Git for Windows answers C:/x
# gstack_path_within PATH DIR
# true when the existing DIR is PATH or one of its
# ancestors, by gstack_path_same on each ancestor
# of the absolute PATH
#
# No side effects beyond the _GSTACK_PS_* variables; safe under set -u.
case "$(uname -s 2>/dev/null)" in
MINGW*|MSYS*) _GSTACK_PS_KIND=msys ;;
CYGWIN*) _GSTACK_PS_KIND=cygwin ;;
*) _GSTACK_PS_KIND=posix ;;
esac
gstack_path_shell() {
case "$_GSTACK_PS_KIND" in
posix) printf '%s\n' "$1"; return 0 ;;
cygwin) cygpath -u "$1" 2>/dev/null || printf '%s\n' "$1"; return 0 ;;
esac
_gps_p=$(cygpath -am "$1" 2>/dev/null) || _gps_p=""
[ -n "$_gps_p" ] || _gps_p=$1
_gps_p=$(printf '%s' "$_gps_p" | tr '\\' '/')
case "$_gps_p" in
[A-Za-z]:|[A-Za-z]:/*)
_gps_d=$(printf '%s' "${_gps_p%%:*}" | tr '[:upper:]' '[:lower:]')
_gps_p="/$_gps_d${_gps_p#?:}" ;;
esac
printf '%s\n' "$_gps_p"
}
gstack_path_native() {
case "$_GSTACK_PS_KIND" in
posix) printf '%s\n' "$1"; return 0 ;;
esac
_gpn_p=$(cygpath -am "$1" 2>/dev/null) || _gpn_p=""
if [ -z "$_gpn_p" ]; then
_gpn_p=$(printf '%s' "$1" | tr '\\' '/')
case "$_gpn_p" in
/[A-Za-z]|/[A-Za-z]/*)
_gpn_d=$(printf '%s' "${_gpn_p:1:1}" | tr '[:lower:]' '[:upper:]')
_gpn_p="$_gpn_d:${_gpn_p:2}"
[ -n "${_gpn_p#?:}" ] || _gpn_p="$_gpn_p/" ;;
esac
fi
printf '%s\n' "$_gpn_p"
}
gstack_path_same() {
if { [ -e "$1" ] || [ -L "$1" ]; } && { [ -e "$2" ] || [ -L "$2" ]; }; then
[ "$1" -ef "$2" ]
return
fi
[ "$(gstack_path_shell "$1")" = "$(gstack_path_shell "$2")" ]
}
gstack_path_absolute() {
case "$1" in /*|[A-Za-z]:[\\/]*) return 0 ;; esac
return 1
}
gstack_path_within() {
[ -e "$2" ] || return 1
_gpw_p=$1
while [ -n "$_gpw_p" ]; do
gstack_path_same "$_gpw_p" "$2" && return 0
case "$_gpw_p" in /|[A-Za-z]:|[A-Za-z]:/) return 1 ;; esac
_gpw_p=${_gpw_p%/}
case "$_gpw_p" in */*) _gpw_p=${_gpw_p%/*}; [ -n "$_gpw_p" ] || _gpw_p=/ ;; *) return 1 ;; esac
done
return 1
}