Skip to content

Commit 65144ba

Browse files
brabojclaude
andcommitted
docs: split 5 substantial playbook recipes into standalone pages
Extract git bisect, pre-commit hook, commit-msg hook, SSH setup, and remove submodule into their own pages. Update parent pages with cross-references and add entries to the playbook index. Borderline candidates (merge conflicts, sync fork, fetch+inspect, update submodule) evaluated and kept grouped — not enough substance to stand alone. Closes #153 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c8d75a5 commit 65144ba

9 files changed

Lines changed: 289 additions & 52 deletions

File tree

chapters/07-playbook.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ For definitions, see [Glossary](09-glossary.md).
3838
|--------|-----------------|
3939
| [Remote Operations](playbook/remote-operations.md) | Push, pull, force push safely, sync forks |
4040
| [Remote Management](playbook/remote-management.md) | Add, rename, remove remotes, switch URL, SSH setup |
41+
| [SSH Setup](playbook/ssh-setup.md) | Key generation, agent, GitHub registration, troubleshooting |
4142

4243
## Project Structure
4344

4445
| Recipe | What you'll find |
4546
|--------|-----------------|
4647
| [Tagging](playbook/tagging.md) | Create, push, and delete annotated tags |
4748
| [Submodules](playbook/submodules.md) | Add, clone, update, and remove submodules |
49+
| [Remove a Submodule](playbook/remove-submodule.md) | Step-by-step cleanup — deinit, git rm, cached data |
4850
| [Subtrees](playbook/subtrees.md) | Add, pull, push, and remove subtrees |
4951

5052
## Advanced
@@ -53,5 +55,8 @@ For definitions, see [Glossary](09-glossary.md).
5355
|--------|-----------------|
5456
| [Selectors](playbook/selectors.md) | Tilde, caret, double-dot, triple-dot, reflog refs |
5557
| [Hooks](playbook/hooks.md) | Pre-commit, commit-msg, sharing hooks, bypassing |
58+
| [Pre-commit Hook](playbook/pre-commit-hook.md) | Script creation, common checks, sharing and bypassing |
59+
| [Commit-msg Hook](playbook/commit-msg-hook.md) | Message validation, Conventional Commits, examples |
5660
| [Debugging](playbook/debugging.md) | Bisect, blame, and search commit history |
61+
| [Git Bisect](playbook/git-bisect.md) | Binary search for the commit that introduced a bug |
5762
| [Configuration](playbook/configuration.md) | Identity, defaults, aliases, and diagnostics |
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
title: "Commit-msg Hook"
3+
description: "How to create a Git commit-msg hook that validates or enforces commit message conventions."
4+
section: "playbook/commit-msg-hook"
5+
order: 90
6+
---
7+
8+
## Commit-msg Hook
9+
10+
A commit-msg hook runs after you write the commit message but before
11+
the commit is finalized. It receives the path to a temporary file
12+
containing the message as its first argument (`$1`). If the script
13+
exits non-zero, the commit is aborted.
14+
15+
### Create the hook
16+
17+
```text
18+
$ cat > .git/hooks/commit-msg << 'EOF'
19+
#!/bin/sh
20+
# Enforce minimum message length
21+
if [ $(wc -c < "$1") -lt 10 ]; then
22+
echo "Error: commit message too short"
23+
exit 1
24+
fi
25+
EOF
26+
$ chmod +x .git/hooks/commit-msg
27+
```
28+
29+
### Common validations
30+
31+
- **Minimum length** — reject messages that are too terse to be
32+
meaningful.
33+
- **Conventional Commits format** — check that the message matches
34+
`type(scope): description` using a regex.
35+
- **Ticket reference** — require a Jira/Linear/GitHub issue key
36+
(e.g. `PROJ-123`) in the message.
37+
38+
### Example: enforce Conventional Commits
39+
40+
```text
41+
$ cat > .git/hooks/commit-msg << 'EOF'
42+
#!/bin/sh
43+
pattern="^(feat|fix|docs|chore|refactor|test|ci|style)(\(.+\))?: .{3,}"
44+
if ! grep -qE "$pattern" "$1"; then
45+
echo "Error: message must follow Conventional Commits format"
46+
exit 1
47+
fi
48+
EOF
49+
$ chmod +x .git/hooks/commit-msg
50+
```
51+
52+
### Sharing and bypassing
53+
54+
Same as other hooks — see the [Hooks](hooks.md) recipe for
55+
`core.hooksPath` and `--no-verify`.

chapters/recipes/debugging.md

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,8 @@ order: 80
99

1010
### Find which commit introduced a bug
1111

12-
```text
13-
$ git bisect start
14-
$ git bisect bad # current commit has the bug
15-
$ git bisect good <hash> # this older commit was fine
16-
# ... test each midpoint, mark good/bad ...
17-
$ git bisect reset # done — return to original branch
18-
```
19-
20-
### Automated bisect with a test script
21-
22-
```text
23-
$ git bisect start HEAD <good-hash>
24-
$ git bisect run ./test.sh
25-
```
12+
Use [Git Bisect](git-bisect.md) for a full walkthrough of manual and
13+
automated binary search through commit history.
2614

2715
### See who last changed each line
2816

chapters/recipes/git-bisect.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
title: "Git Bisect"
3+
description: "How to use git bisect to find the exact commit that introduced a bug, with manual and automated workflows."
4+
section: "playbook/git-bisect"
5+
order: 88
6+
---
7+
8+
## Git Bisect
9+
10+
Bisect performs a binary search through your commit history to find
11+
which commit introduced a bug. Instead of checking each commit
12+
one-by-one, it halves the search space at every step.
13+
14+
### Manual bisect
15+
16+
```text
17+
$ git bisect start
18+
$ git bisect bad # current commit has the bug
19+
$ git bisect good <hash> # this older commit was fine
20+
```
21+
22+
Git checks out a midpoint commit. Test it, then mark it:
23+
24+
```text
25+
$ git bisect good # midpoint is clean
26+
$ git bisect bad # midpoint has the bug
27+
```
28+
29+
Repeat until Git identifies the first bad commit, then clean up:
30+
31+
```text
32+
$ git bisect reset # return to original branch
33+
```
34+
35+
### Automated bisect with a test script
36+
37+
If you have a script that exits 0 for good and non-zero for bad,
38+
bisect can run unattended:
39+
40+
```text
41+
$ git bisect start HEAD <good-hash>
42+
$ git bisect run ./test.sh
43+
```
44+
45+
Git runs the script at each midpoint and reports the first bad
46+
commit when done.
47+
48+
### Tips
49+
50+
- The good and bad commits do not have to be on the same branch —
51+
bisect works across the entire reachable history.
52+
- If a midpoint commit cannot be tested (e.g. broken build), use
53+
`git bisect skip` to move past it.
54+
- Use `git bisect log` to replay or share a bisect session.

chapters/recipes/hooks.md

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,13 @@ order: 83
99

1010
### Create a pre-commit hook
1111

12-
```text
13-
$ cat > .git/hooks/pre-commit << 'EOF'
14-
#!/bin/sh
15-
# Reject commits that contain TODO
16-
if git diff --cached --quiet -S "TODO"; then
17-
exit 0
18-
fi
19-
echo "Error: commit contains TODO"
20-
exit 1
21-
EOF
22-
$ chmod +x .git/hooks/pre-commit
23-
```
24-
25-
The hook runs before every commit. A non-zero exit code aborts the
26-
commit.
12+
See [Pre-commit Hook](pre-commit-hook.md) for a full walkthrough —
13+
script creation, common checks, and sharing with the team.
2714

2815
### Create a commit-msg hook
2916

30-
```text
31-
$ cat > .git/hooks/commit-msg << 'EOF'
32-
#!/bin/sh
33-
# Enforce minimum message length
34-
if [ $(wc -c < "$1") -lt 10 ]; then
35-
echo "Error: commit message too short"
36-
exit 1
37-
fi
38-
EOF
39-
$ chmod +x .git/hooks/commit-msg
40-
```
41-
42-
Receives the commit message file as `$1`. Useful for enforcing
43-
message conventions.
17+
See [Commit-msg Hook](commit-msg-hook.md) for a full walkthrough —
18+
message validation, Conventional Commits enforcement, and examples.
4419

4520
### Share hooks with the team
4621

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
title: "Pre-commit Hook"
3+
description: "How to create a Git pre-commit hook that validates staged changes before every commit."
4+
section: "playbook/pre-commit-hook"
5+
order: 89
6+
---
7+
8+
## Pre-commit Hook
9+
10+
A pre-commit hook runs automatically before every commit. If the
11+
script exits with a non-zero code, the commit is aborted. Use it to
12+
catch problems early — linting errors, TODO markers, large files,
13+
or secrets.
14+
15+
### Create the hook
16+
17+
```text
18+
$ cat > .git/hooks/pre-commit << 'EOF'
19+
#!/bin/sh
20+
# Reject commits that contain TODO
21+
if git diff --cached --quiet -S "TODO"; then
22+
exit 0
23+
fi
24+
echo "Error: commit contains TODO"
25+
exit 1
26+
EOF
27+
$ chmod +x .git/hooks/pre-commit
28+
```
29+
30+
### How it works
31+
32+
1. You run `git commit`.
33+
2. Git executes `.git/hooks/pre-commit` before opening the message
34+
editor.
35+
3. If the script exits 0, the commit proceeds. Any other exit code
36+
aborts it.
37+
38+
### Common checks
39+
40+
- **Lint staged files** — run your linter on `git diff --cached --name-only`
41+
output.
42+
- **Detect secrets** — search for API keys, tokens, or passwords in
43+
the diff.
44+
- **Enforce file size limits** — reject files above a threshold.
45+
46+
### Sharing pre-commit hooks
47+
48+
Hooks in `.git/hooks/` are local and not committed. To share them
49+
with the team, see the [Hooks](hooks.md) recipe for the
50+
`core.hooksPath` approach.
51+
52+
### Bypassing
53+
54+
```text
55+
$ git commit --no-verify -m "WIP: skip hooks"
56+
```
57+
58+
Use sparingly — hooks exist for a reason.

chapters/recipes/remote-management.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,6 @@ $ git remote set-url origin git@github.com:<user>/<repo>.git
4646

4747
### Set up SSH authentication
4848

49-
```text
50-
$ ssh-keygen -t ed25519 -C "you@example.com"
51-
$ eval "$(ssh-agent -s)"
52-
$ ssh-add ~/.ssh/id_ed25519
53-
$ ssh -T git@github.com # verify connection
54-
```
55-
56-
Add the public key (`~/.ssh/id_ed25519.pub`) to your GitHub account
57-
under Settings > SSH Keys.
49+
See [SSH Setup](ssh-setup.md) for a full walkthrough — key
50+
generation, agent configuration, GitHub registration, and
51+
troubleshooting.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
title: "Remove a Submodule"
3+
description: "Step-by-step guide to fully removing a Git submodule — deinit, git rm, and cleaning up cached module data."
4+
section: "playbook/remove-submodule"
5+
order: 92
6+
---
7+
8+
## Remove a Submodule
9+
10+
Removing a submodule is not a single command — it requires three
11+
cleanup steps to fully clear the submodule from your repository.
12+
13+
### Steps
14+
15+
```text
16+
$ git submodule deinit <path> # 1. unregister from .git/config
17+
$ git rm <path> # 2. remove from working tree and index
18+
$ rm -rf .git/modules/<path> # 3. delete cached clone
19+
$ git commit -m "Remove submodule"
20+
```
21+
22+
### What each step does
23+
24+
1. **`deinit`** — removes the submodule entry from `.git/config` and
25+
clears the working directory at `<path>`. The submodule's URL
26+
remains in `.gitmodules` until the next step removes it.
27+
2. **`git rm`** — removes the submodule entry from `.gitmodules` and
28+
from the index (staging area). Also deletes the working directory
29+
if `deinit` did not already.
30+
3. **`rm -rf .git/modules/<path>`** — deletes the cached bare clone
31+
that Git keeps under `.git/modules/`. Without this step, re-adding
32+
a submodule at the same path can use stale data.
33+
34+
### Common gotchas
35+
36+
- **Forgetting step 3** — the cached clone stays behind. If you later
37+
add a submodule at the same path, Git may reuse the old checkout
38+
and you get confusing state.
39+
- **Uncommitted changes in the submodule**`deinit` will refuse to
40+
run. Use `--force` if you are certain the changes are not needed.
41+
- **Nested submodules** — if the submodule itself contains submodules,
42+
you need to deinit recursively or clean up `.git/modules/` manually.

chapters/recipes/ssh-setup.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
title: "SSH Setup"
3+
description: "How to generate an SSH key, add it to the SSH agent, register it with GitHub, and switch a remote from HTTPS to SSH."
4+
section: "playbook/ssh-setup"
5+
order: 91
6+
---
7+
8+
## SSH Setup
9+
10+
SSH authentication lets you push and pull without entering your
11+
password every time. It uses a key pair — a private key on your
12+
machine and a public key registered with your Git host.
13+
14+
### 1. Generate a key
15+
16+
```text
17+
$ ssh-keygen -t ed25519 -C "you@example.com"
18+
```
19+
20+
Accept the default file location (`~/.ssh/id_ed25519`). Set a
21+
passphrase for extra security, or press Enter to skip.
22+
23+
### 2. Start the SSH agent and add the key
24+
25+
```text
26+
$ eval "$(ssh-agent -s)"
27+
$ ssh-add ~/.ssh/id_ed25519
28+
```
29+
30+
On macOS, add `--apple-use-keychain` to persist the key across
31+
reboots.
32+
33+
### 3. Register the public key with GitHub
34+
35+
Copy the public key to your clipboard:
36+
37+
```text
38+
$ cat ~/.ssh/id_ed25519.pub
39+
```
40+
41+
Then go to **GitHub > Settings > SSH and GPG Keys > New SSH Key**,
42+
paste the key, and save.
43+
44+
### 4. Verify the connection
45+
46+
```text
47+
$ ssh -T git@github.com
48+
```
49+
50+
You should see a message like "Hi username! You've successfully
51+
authenticated."
52+
53+
### 5. Switch an existing remote from HTTPS to SSH
54+
55+
```text
56+
$ git remote set-url origin git@github.com:<user>/<repo>.git
57+
```
58+
59+
### Troubleshooting
60+
61+
- **Permission denied (publickey)** — the agent does not have the
62+
key loaded. Run `ssh-add` again.
63+
- **Wrong key used** — if you have multiple keys, create an
64+
`~/.ssh/config` entry to map the host to the correct key file.
65+
- **Firewall blocks port 22** — use SSH over HTTPS port:
66+
`ssh -T -p 443 git@ssh.github.com`.

0 commit comments

Comments
 (0)