-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathblock-destructive-git.sh
More file actions
executable file
·75 lines (66 loc) · 3.46 KB
/
Copy pathblock-destructive-git.sh
File metadata and controls
executable file
·75 lines (66 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/bin/bash
# Block destructive git operations that undo work without user approval.
# Enforces CLAUDE.md rule: "Destructive Git Operations are FORBIDDEN without explicit user approval"
#
# Blocked commands:
# - git revert (reverting commits)
# - git checkout -- . (discarding all changes)
# - git checkout -- X (discarding file changes)
# - git restore (restoring files)
# - git clean (removing untracked files)
# - git stash (hiding work, unless immediately followed by pop)
# - git reset (moving HEAD backward — --hard already blocked by block-main-commits.sh)
#
# Exit code 2 blocks the tool and sends stderr as feedback to Claude.
# In batch-resolve mode, Claude works on its own feature branch — destructive ops are safe.
if [ "$CLAUDE_BATCH_RESOLVE" = "1" ]; then
exit 0
fi
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null)
# Strip commit/tag message content to avoid false positives on words like "revert" in messages.
# The command may be multi-line (heredoc). For git commit/tag, only scan the first line
# up to -m to avoid matching keywords inside the commit message body.
if echo "$CMD" | head -1 | grep -qE '\bgit\s+(commit|tag)\b'; then
CMD_CLEAN=$(echo "$CMD" | head -1 | sed -E 's/-m[[:space:]].*//')
else
CMD_CLEAN="$CMD"
fi
# Block: git revert
if echo "$CMD_CLEAN" | grep -qE '\bgit\s+revert\b'; then
echo "BLOCKED: git revert is forbidden without explicit user approval. If a test fails, FIX the code — do not revert. If you cannot fix it, STOP and ask the user." >&2
exit 2
fi
# Block: git checkout -- (discarding changes)
# Matches: git checkout -- ., git checkout -- file.txt, git checkout .
# Does NOT block: git checkout branch-name, git checkout -b new-branch
if echo "$CMD_CLEAN" | grep -qE '\bgit\s+checkout\s+--\s'; then
echo "BLOCKED: git checkout -- (discard changes) is forbidden without explicit user approval. Fix the code instead of discarding it." >&2
exit 2
fi
# Also block: git checkout . (without --)
if echo "$CMD_CLEAN" | grep -qE '\bgit\s+checkout\s+\.\s*$'; then
echo "BLOCKED: git checkout . (discard all changes) is forbidden without explicit user approval. Fix the code instead of discarding it." >&2
exit 2
fi
# Block: git restore (any form that discards work)
# Matches: git restore ., git restore --staged, git restore file.txt
if echo "$CMD_CLEAN" | grep -qE '\bgit\s+restore\b'; then
echo "BLOCKED: git restore is forbidden without explicit user approval. Fix the code instead of restoring previous versions." >&2
exit 2
fi
# Block: git clean
if echo "$CMD_CLEAN" | grep -qE '\bgit\s+clean\b'; then
echo "BLOCKED: git clean is forbidden without explicit user approval. Do not remove untracked files without asking." >&2
exit 2
fi
# Block: git stash (but not git stash pop/apply which restore work)
if echo "$CMD_CLEAN" | grep -qE '\bgit\s+stash\b' && ! echo "$CMD_CLEAN" | grep -qE '\bgit\s+stash\s+(pop|apply|show|list)\b'; then
echo "BLOCKED: git stash is forbidden without explicit user approval. Do not hide work in progress — if you need to set aside changes, ask the user first." >&2
exit 2
fi
# Block: git reset (any form — --hard already caught by block-main-commits.sh, but catch --soft/--mixed too)
if echo "$CMD_CLEAN" | grep -qE '\bgit\s+reset\b'; then
echo "BLOCKED: git reset is forbidden without explicit user approval. Do not move HEAD backward — create new commits instead." >&2
exit 2
fi