garrytan / garrytan/gstack

ship: redact pre-push guard never auto-installs on Windows (HOOKS_IN_GIT_DIR path-format mismatch)

Open
#2,805 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
133k
Forks
19.9k
Avg merge
18h 46m
Merged PRs (30d)
26

Description

## Summary

On Windows (Git Bash / MSYS), the `HOOKS_IN_GIT_DIR` probe in `/ship` Step "redact pre-push guard" always evaluates to `no`. The guard is therefore never installed silently, even when the user has explicitly opted in with `gstack-config set redact_prepush_hook true`.

The user sets the flag, sees it read back as `true`, and reasonably believes the credential guard is active. It is not. It fails closed on install, which is the safe direction, but silently, so the opt-in is inert.

## Environment

- gstack v1.79.0.0
- Windows 11, Git Bash (MSYS2)
- git 2.x, repo using linked worktrees

## Root cause

`ship/SKILL.md.tmpl` lines 432 to 442 build two path strings through different mechanisms and then string-match them:

```bash
_HOOKS_DIR=$(git rev-parse --git-path hooks ...) # line 432
_GIT_COMMON=$(cd "$(git rev-parse --git-common-dir ...)" && pwd ...) # line 439
case "$_HOOKS_DIR" in
"$_GIT_DIR"/*|"$_GIT_COMMON"/*|hooks|.git/hooks) _HOOKS_IN_GIT_DIR="yes" ;;
esac
```

On Windows these three commands return three different path formats for the same directory:

| Command | Output |
|---|---|
| `git rev-parse --git-path hooks` | `C:\Users\me\proj\.git\hooks` |
| `git rev-parse --git-common-dir` | `C:/Users/me/proj/.git` |
| `cd "$(...)" && pwd` | `/c/Users/me/proj/.git` |

`_GIT_COMMON` ends up in MSYS form (`/c/...`) because `pwd` in Git Bash normalizes to it. `_HOOKS_DIR` stays in Windows form. The glob `/c/Users/me/proj/.git/*` can never match `C:\Users\me\proj\.git\hooks`, so the case falls through to `no`.

Note this is not limited to repos that set `core.hooksPath`. Any absolute value returned by `--git-path hooks`, which includes the linked-worktree case the `_GIT_COMMON` fallback was specifically added to handle, hits the same mismatch on Windows. The `hooks` and `.git/hooks` literal patterns only match the relative form, so plain non-worktree repos on Windows happen to pass.

## Reproduction

In a Git Bash shell on Windows, inside a linked worktree or any repo with an absolute `core.hooksPath`:

```bash
git rev-parse --git-path hooks
git rev-parse --git-common-dir
cd "$(git rev-parse --git-common-dir)" && pwd
```

Observed on my machine:

```
C:\Users\nblin\Projects\foresight-aero-website\.git\hooks
C:/Users/nblin/Projects/foresight-aero-website/.git
/c/Users/nblin/Projects/foresight-aero-website/.git
```

Running the Step block verbatim then prints:

```
REDACT_PREPUSH: true
HOOK_INSTALLED: no
HOOKS_IN_GIT_DIR: no
```

so `/ship` takes the "custom core.hooksPath" branch and skips the install.

## Impact

Opt-in users on Windows silently get no pre-push credential guard. `gstack-redact install-prepush-hook` itself works correctly when run by hand, and the installed hook functions: feeding it a synthetic `sk_live_...` key returns exit 3 as documented. Only the automatic install path is affected.

## Suggested fix

Normalize both sides through the same mechanism before comparing, rather than comparing raw `git rev-parse` output:

```bash
_HOOKS_DIR_RESOLVED=$(cd "$_HOOKS_DIR" 2>/dev/null && pwd || echo /nonexistent)
```

and match `$_HOOKS_DIR_RESOLVED` against `$_GIT_COMMON` and `$_GIT_DIR`. Since `cd` plus `pwd` yields MSYS form on Windows and POSIX form elsewhere, both sides then agree on all platforms.

One caveat: the hooks directory may not exist yet on a fresh clone, in which case `cd` fails. Falling back to normalizing the parent directory and appending the basename handles that.

An alternative is `git rev-parse --path-format=absolute --git-path hooks` (git 2.31+), which at least makes the git-side output format consistent, though it still returns `C:/...` rather than `/c/...` on Windows and so would need the same normalization on the other side.

Happy to send a PR if the maintainers prefer a particular approach.

🤖 Filed with [Claude Code](https://claude.com/claude-code)

Contributor guide

Open the contributing guide

Research direction

Start in ship/SKILL.md.tmpl at the “redact pre-push guard” step, especially lines 432–442, and reproduce the path outputs in a Windows Git Bash linked worktree. Normalize the hook and Git directory paths consistently, including when the hooks directory does not yet exist. Done means the step reports HOOKS_IN_GIT_DIR: yes and automatically installs the opted-in guard in the affected Windows cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
bash, git
Domain
cli, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.