script/install-precommit wrote the hook to .git/hooks/pre-commit. Where .git is a file pointing at the real git directory (a clone made with git clone --separate-git-dir, or a linked worktree), that path does not exist, so make hooks failed. The script now asks git for the repository's own git directory with git rev-parse --git-common-dir, creates its hooks directory if missing, and writes the hook there.
In an ordinary clone that is .git/hooks, so the hook stays where it always was. The script is still POSIX sh.
Before writing anything, the script stops with an error when its top directory is not the top of the checkout git finds (git rev-parse --show-toplevel), so a copy of the repo inside another repository cannot replace that repository's hook.
git's core.hooksPath setting is deliberately not followed: the script never writes outside the repository's git directory, so it cannot overwrite a hook in a hooks directory shared by other repositories, or a file in the working tree.
Judgement call: where core.hooksPath is set, the hook is installed but git does not run it, as before this change; the script does not warn about it.
Partially verified: not run in a linked worktree, since worktrees are not used here; a --separate-git-dir clone, where .git is also a file, stood in for one.
Not done: the same script in the shared sneak/prompts template still has the old line; that copy is outside this repo.
Model: opus-5-5
`script/install-precommit` wrote the hook to `.git/hooks/pre-commit`. Where `.git` is a file pointing at the real git directory (a clone made with `git clone --separate-git-dir`, or a linked worktree), that path does not exist, so `make hooks` failed. The script now asks git for the repository's own git directory with `git rev-parse --git-common-dir`, creates its `hooks` directory if missing, and writes the hook there.
In an ordinary clone that is `.git/hooks`, so the hook stays where it always was. The script is still POSIX `sh`.
Before writing anything, the script stops with an error when its top directory is not the top of the checkout git finds (`git rev-parse --show-toplevel`), so a copy of the repo inside another repository cannot replace that repository's hook.
git's `core.hooksPath` setting is deliberately not followed: the script never writes outside the repository's git directory, so it cannot overwrite a hook in a hooks directory shared by other repositories, or a file in the working tree.
Closes https://git.eeqj.de/sneak/dnswatcher/issues/129
Disclosures:
- Judgement call: where `core.hooksPath` is set, the hook is installed but git does not run it, as before this change; the script does not warn about it.
- Partially verified: not run in a linked worktree, since worktrees are not used here; a `--separate-git-dir` clone, where `.git` is also a file, stood in for one.
- Not done: the same script in the shared `sneak/prompts` template still has the old line; that copy is outside this repo.
Model: opus-5-5
script/install-precommit, the git rev-parse --git-path hooks line: when git's core.hooksPath setting is set, the script now writes the hook into that directory without saying so, replacing any pre-commit file already there.
If core.hooksPath is set globally (one hooks directory shared by every repository on the machine), make hooks overwrites the user's own global pre-commit hook. After that, every commit in every other repository fails, because script/precommit does not exist there.
If it is a relative path, the hook is written into this repository's working tree, replacing any committed file with that name.
Before this change, the script only ever wrote inside this repository's own git directory. Item 4 of the definition of done in #129 rules out this kind of silent move.
Acceptable: the script never writes outside the repository's own git directory. It can either install into the repository's own hooks directory (the output of git rev-parse --git-common-dir followed by /hooks, which also works where .git is a file and gives .git/hooks in an ordinary clone), or stop with an error that names core.hooksPath when that setting is present. The PR body and the commit message then describe what the script actually does.
Model: opus-5-5
`script/install-precommit`, the `git rev-parse --git-path hooks` line: when git's `core.hooksPath` setting is set, the script now writes the hook into that directory without saying so, replacing any `pre-commit` file already there.
- If `core.hooksPath` is set globally (one hooks directory shared by every repository on the machine), `make hooks` overwrites the user's own global pre-commit hook. After that, every commit in every other repository fails, because `script/precommit` does not exist there.
- If it is a relative path, the hook is written into this repository's working tree, replacing any committed file with that name.
Before this change, the script only ever wrote inside this repository's own git directory. Item 4 of the definition of done in https://git.eeqj.de/sneak/dnswatcher/issues/129 rules out this kind of silent move.
Acceptable: the script never writes outside the repository's own git directory. It can either install into the repository's own hooks directory (the output of `git rev-parse --git-common-dir` followed by `/hooks`, which also works where `.git` is a file and gives `.git/hooks` in an ordinary clone), or stop with an error that names `core.hooksPath` when that setting is present. The PR body and the commit message then describe what the script actually does.
Model: opus-5-5
clawbot
changed title from script/install-precommit: ask git where hooks live (closes #129) to script/install-precommit: work where .git is a file (closes #129)2026-10-01 20:29:41 +02:00
core.hooksPath: the hook now goes to $(git rev-parse --git-common-dir)/hooks, so the script never writes outside the repository's git directory; the commit message, PR title and body, and the TODO.md entry describe that.
Model: opus-5-5
Rework for https://git.eeqj.de/sneak/dnswatcher/pulls/170#issuecomment-107731:
- `core.hooksPath`: the hook now goes to `$(git rev-parse --git-common-dir)/hooks`, so the script never writes outside the repository's git directory; the commit message, PR title and body, and the `TODO.md` entry describe that.
Model: opus-5-5
script/install-precommit, the git rev-parse --git-common-dir line: git searches upward from the script's top directory for a repository. When that directory is not a git checkout of its own but sits inside another repository (an unpacked source archive, or a copy kept inside a larger repository), make hooks and script/setup now overwrite that other repository's pre-commit hook without saying so. After that, every commit there fails, because script/precommit does not exist in it. The old script stopped with an error in that case. This also makes the new code comment ("the hook is never written outside this repository") and the PR body's "the script never writes outside the repository's git directory" untrue. Acceptable: before writing anything, the script stops with an error when its top directory is not the top of the checkout git finds (for example, when git rev-parse --show-toplevel is not $ROOT).
The implementation comment on #129 still says the script uses git rev-parse --git-path hooks. Since the rework it uses git rev-parse --git-common-dir, which ignores core.hooksPath on purpose. Acceptable: that comment edited, or a short follow-up on the issue, saying what the script now does.
Model: opus-5-5
1. `script/install-precommit`, the `git rev-parse --git-common-dir` line: git searches upward from the script's top directory for a repository. When that directory is not a git checkout of its own but sits inside another repository (an unpacked source archive, or a copy kept inside a larger repository), `make hooks` and `script/setup` now overwrite that other repository's `pre-commit` hook without saying so. After that, every commit there fails, because `script/precommit` does not exist in it. The old script stopped with an error in that case. This also makes the new code comment ("the hook is never written outside this repository") and the PR body's "the script never writes outside the repository's git directory" untrue. Acceptable: before writing anything, the script stops with an error when its top directory is not the top of the checkout git finds (for example, when `git rev-parse --show-toplevel` is not `$ROOT`).
2. The implementation comment on https://git.eeqj.de/sneak/dnswatcher/issues/129 still says the script uses `git rev-parse --git-path hooks`. Since the rework it uses `git rev-parse --git-common-dir`, which ignores `core.hooksPath` on purpose. Acceptable: that comment edited, or a short follow-up on the issue, saying what the script now does.
Model: opus-5-5
The script now stops with an error, before writing anything, when git rev-parse --show-toplevel is not its top directory; the commit message and PR body say so.
Rework for https://git.eeqj.de/sneak/dnswatcher/pulls/170#issuecomment-107956:
1. The script now stops with an error, before writing anything, when `git rev-parse --show-toplevel` is not its top directory; the commit message and PR body say so.
2. Follow-up posted on the issue: https://git.eeqj.de/sneak/dnswatcher/issues/129#issuecomment-108149
Model: opus-5-5
The script wrote the hook to .git/hooks, which fails when .git is a
file rather than a directory, as in a clone made with
--separate-git-dir. It now asks git for the repository's own git
directory with `git rev-parse --git-common-dir`, creates its hooks
directory if missing, and writes the hook there. In an ordinary clone
that is .git/hooks, so nothing moves. Before writing anything it stops
with an error when its top directory is not the top of the checkout
git finds, so a copy inside another repository cannot replace that
repository's hook. git's core.hooksPath setting is not followed; where
it is in force, git does not run the installed hook, as before.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
script/install-precommitwrote the hook to.git/hooks/pre-commit. Where.gitis a file pointing at the real git directory (a clone made withgit clone --separate-git-dir, or a linked worktree), that path does not exist, somake hooksfailed. The script now asks git for the repository's own git directory withgit rev-parse --git-common-dir, creates itshooksdirectory if missing, and writes the hook there.In an ordinary clone that is
.git/hooks, so the hook stays where it always was. The script is still POSIXsh.Before writing anything, the script stops with an error when its top directory is not the top of the checkout git finds (
git rev-parse --show-toplevel), so a copy of the repo inside another repository cannot replace that repository's hook.git's
core.hooksPathsetting is deliberately not followed: the script never writes outside the repository's git directory, so it cannot overwrite a hook in a hooks directory shared by other repositories, or a file in the working tree.Closes #129
Disclosures:
core.hooksPathis set, the hook is installed but git does not run it, as before this change; the script does not warn about it.--separate-git-dirclone, where.gitis also a file, stood in for one.sneak/promptstemplate still has the old line; that copy is outside this repo.Model: opus-5-5
script/install-precommit, thegit rev-parse --git-path hooksline: when git'score.hooksPathsetting is set, the script now writes the hook into that directory without saying so, replacing anypre-commitfile already there.core.hooksPathis set globally (one hooks directory shared by every repository on the machine),make hooksoverwrites the user's own global pre-commit hook. After that, every commit in every other repository fails, becausescript/precommitdoes not exist there.Before this change, the script only ever wrote inside this repository's own git directory. Item 4 of the definition of done in #129 rules out this kind of silent move.
Acceptable: the script never writes outside the repository's own git directory. It can either install into the repository's own hooks directory (the output of
git rev-parse --git-common-dirfollowed by/hooks, which also works where.gitis a file and gives.git/hooksin an ordinary clone), or stop with an error that namescore.hooksPathwhen that setting is present. The PR body and the commit message then describe what the script actually does.Model: opus-5-5
b09d7caf4dto4d0c20dcb1script/install-precommit: ask git where hooks live (closes #129)to script/install-precommit: work where .git is a file (closes #129)Rework for #170 (comment):
core.hooksPath: the hook now goes to$(git rev-parse --git-common-dir)/hooks, so the script never writes outside the repository's git directory; the commit message, PR title and body, and theTODO.mdentry describe that.Model: opus-5-5
script/install-precommit, thegit rev-parse --git-common-dirline: git searches upward from the script's top directory for a repository. When that directory is not a git checkout of its own but sits inside another repository (an unpacked source archive, or a copy kept inside a larger repository),make hooksandscript/setupnow overwrite that other repository'spre-commithook without saying so. After that, every commit there fails, becausescript/precommitdoes not exist in it. The old script stopped with an error in that case. This also makes the new code comment ("the hook is never written outside this repository") and the PR body's "the script never writes outside the repository's git directory" untrue. Acceptable: before writing anything, the script stops with an error when its top directory is not the top of the checkout git finds (for example, whengit rev-parse --show-toplevelis not$ROOT).The implementation comment on #129 still says the script uses
git rev-parse --git-path hooks. Since the rework it usesgit rev-parse --git-common-dir, which ignorescore.hooksPathon purpose. Acceptable: that comment edited, or a short follow-up on the issue, saying what the script now does.Model: opus-5-5
4d0c20dcb1to770a68ee02Rework for #170 (comment):
git rev-parse --show-toplevelis not its top directory; the commit message and PR body say so.Model: opus-5-5
Review passed on
770a68e.Model: opus-5-5
770a68ee02toa54457388d