Skip to content

Commit d91a44c

Browse files
authored
Merge pull request #48 from kenryu42/worktree-mode
Worktree mode
2 parents a8acf4d + 96555cb commit d91a44c

46 files changed

Lines changed: 7240 additions & 424 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ log*
55
plan-*.md
66
*-plan-*.md
77
.safety-net.json
8-
.sisyphus/
98
.opencode
109
.gemini
10+
coverage
1111
.codex
1212

1313
# --- Node.js ---

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ A Claude Code plugin that acts as a safety net, catching destructive git and fil
4848
- [Advanced Features](#advanced-features)
4949
- [Strict Mode](#strict-mode)
5050
- [Paranoid Mode](#paranoid-mode)
51+
- [Worktree Mode](#worktree-mode)
5152
- [Shell Wrapper Detection](#shell-wrapper-detection)
5253
- [Interpreter One-Liner Detection](#interpreter-one-liner-detection)
5354
- [Secret Redaction](#secret-redaction)
@@ -300,6 +301,7 @@ The status line displays different emojis based on the current configuration:
300301
| Paranoid mode | `🛡️ Safety Net 👁️` | `SAFETY_NET_PARANOID=1` — all paranoid checks enabled |
301302
| Paranoid RM only | `🛡️ Safety Net 🗑️` | `SAFETY_NET_PARANOID_RM=1` — blocks `rm -rf` even within cwd |
302303
| Paranoid interpreters only | `🛡️ Safety Net 🐚` | `SAFETY_NET_PARANOID_INTERPRETERS=1` — blocks interpreter one-liners |
304+
| Worktree mode | `🛡️ Safety Net 🌳` | `SAFETY_NET_WORKTREE=1` — relax local git discards inside linked worktrees |
303305
| Strict + Paranoid | `🛡️ Safety Net 🔒👁️` | Both strict and paranoid modes enabled |
304306

305307
Multiple mode emojis are combined when multiple environment variables are set.
@@ -398,6 +400,7 @@ npx cc-safety-net explain --cwd /tmp "git status"
398400
| rm -rf /var/tmp/... | System temp directory |
399401
| rm -rf $TMPDIR/... | User's temp directory |
400402
| rm -rf ./... (within cwd) | Limited to current working directory |
403+
| git restore / checkout -- / reset --hard / clean -f (in linked worktree) | Relaxed only when `SAFETY_NET_WORKTREE=1` and cwd is a linked worktree |
401404

402405
## What Happens When Blocked
403406

@@ -631,6 +634,48 @@ Paranoid behavior:
631634
- **interpreters**: blocks interpreter one-liners like `python -c`, `node -e`, `ruby -e`,
632635
and `perl -e` (these can hide destructive commands).
633636

637+
### Worktree Mode
638+
639+
Linked git worktrees are designed as disposable, isolated workspaces — discarding
640+
changes inside one doesn't risk the main working tree. Worktree mode relaxes
641+
local-discard rules when (and only when) the command is proven to run inside a
642+
linked worktree:
643+
644+
```bash
645+
export SAFETY_NET_WORKTREE=1
646+
```
647+
648+
When enabled, these commands are allowed inside a linked worktree:
649+
650+
- `git restore <file>` and `git restore --worktree <file>`
651+
- `git checkout -- <file>`, `git checkout <ref> -- <file>`, `git checkout --force`,
652+
and ambiguous multi-positional checkout forms
653+
- `git switch --discard-changes` and `git switch -f / --force`
654+
- `git reset --hard` and `git reset --merge`
655+
- `git clean -f` (and combined short flags like `-fd`)
656+
657+
These remain blocked even in linked worktrees because they reach beyond the
658+
local working tree:
659+
660+
- `git push --force` (affects remote)
661+
- `git branch -D` (affects shared refs)
662+
- `git stash drop` / `git stash clear` (stash is shared across worktrees)
663+
- `git worktree remove --force` (could delete another worktree)
664+
665+
Detection is fail-closed and mostly filesystem-based:
666+
667+
- A linked worktree is identified by a `.git` *file* containing `gitdir:` whose
668+
resolved git directory contains a `commondir` file. Main worktrees and
669+
submodules don't satisfy this and are not relaxed.
670+
- The cwd walk uses `realpath` so symlinked paths resolve correctly.
671+
- `git -C <path>` (including chained `-C` and attached `-Cpath`) is honored;
672+
unresolved targets keep the command blocked.
673+
- Relaxation is disabled if cwd becomes unknown (e.g., after `cd`/`pushd`),
674+
if `--git-dir` / `--work-tree` is passed, or if `GIT_DIR` / `GIT_WORK_TREE`
675+
/ `GIT_COMMON_DIR` is set in the environment.
676+
- Git may be invoked from a trusted system path to inspect effective config that
677+
could make submodule operations recursive.
678+
634679
### Shell Wrapper Detection
635680

636681
The guard recursively analyzes commands wrapped in shells:

0 commit comments

Comments
 (0)