mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-06 04:21:33 +00:00
2bfc78a37c
Contributor guides now match what the hook enforces so failures are obvious before push when core.hooksPath points at .githooks. - Refresh commit.prompt.md hook bullets and the canonical message checklist - Update AGENTS.md workflow paragraph for commit-msg structure plus line cap
206 lines
6.7 KiB
Markdown
206 lines
6.7 KiB
Markdown
# Git Commit Workflow for AI Assistants
|
|
|
|
## Goals
|
|
|
|
- Git history should read as a **story**: small steps, one clear intent per commit.
|
|
- **Default:** one file per commit (smallest practical unit).
|
|
- **Exception:** several files in **one** commit only when they share the **same logical entity / same story** (incomplete or misleading if split). This should be **unusual**—always prefer smaller commits when in doubt.
|
|
|
|
## When to combine files (exception)
|
|
|
|
Combine only when splitting would:
|
|
|
|
- leave the tree broken or the feature half-wired, or
|
|
- tell an incoherent story (e.g. rename without updating the only call site).
|
|
|
|
**Always** state **why** those paths belong together in one commit when proposing a multi-file commit.
|
|
|
|
## User initiation
|
|
|
|
```
|
|
❌ AI: "Should I commit these files?"
|
|
❌ AI: "Let me commit this..."
|
|
|
|
✅ Human: "Commit the changed files"
|
|
✅ Human: "Do the commits now"
|
|
```
|
|
|
|
**Rule:** Do not suggest or initiate commits. Only run this workflow after an explicit user command.
|
|
|
|
## Staging
|
|
|
|
```bash
|
|
git add path/to/file.jl
|
|
# or, only when justified:
|
|
git add path/a.jl path/b.jl
|
|
```
|
|
|
|
- Avoid `git add .` and `git add -A` unless the user explicitly wants everything (still read the full staged diff after).
|
|
- Never stage unrelated changes into the same commit.
|
|
|
|
## Optional git hooks (`.githooks/`)
|
|
|
|
After `git config core.hooksPath .githooks`:
|
|
|
|
- **pre-commit:** rejects commits with **more than two** staged files. This
|
|
supports the common “impl + test” or “snippet + verifier” pair without
|
|
allowing large batches. Split bigger changes across commits or adjust the
|
|
hook deliberately.
|
|
- **commit-msg:** enforces the **subject → blank → summary → blank → bullets**
|
|
layout, **≥1** `- ...` detail line, **no** blanks inside the bullet block, and
|
|
**≤80** characters per line. Merge commits skip checks while `.git/MERGE_HEAD`
|
|
is present.
|
|
|
|
## Read the full staged diff
|
|
|
|
```bash
|
|
git diff --staged
|
|
```
|
|
|
|
**Critical:**
|
|
|
|
- Do not pipe through `head`, `tail`, `less`, or otherwise truncate.
|
|
- Read every line; the commit message must reflect the actual diff.
|
|
|
|
**Exception:** machine-generated or binary files—review only as far as is practical.
|
|
|
|
## Approval before `git commit`
|
|
|
|
**Before** running `git commit`, present to the user:
|
|
|
|
1. **File(s)** to be committed (exact paths).
|
|
2. **Full proposed commit message** (subject + body in the format below).
|
|
3. If **more than one file:** a **short justification** why one commit is appropriate.
|
|
|
|
**Wait** for explicit approval. Only then:
|
|
|
|
```bash
|
|
git commit -F- <<'EOF'
|
|
<paste full message>
|
|
EOF
|
|
```
|
|
|
|
(or equivalent multi-line `-m` usage). Do not commit silently.
|
|
|
|
## Commit message structure
|
|
|
|
**1. Subject line (first line)** — Conventional Commits:
|
|
|
|
`type(scope): specific description from the diff`
|
|
|
|
- Types include `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, etc.
|
|
- `scope` is optional but recommended when it clarifies the area.
|
|
- Imperative mood; specific; no vague "update" / "fix things".
|
|
|
|
**2. Blank line**
|
|
|
|
**3. Summary** — one short paragraph, **one to three sentences**: what changed and why, for this commit only.
|
|
|
|
**4. Blank line** (separator before the bullet list).
|
|
|
|
**5. Body detail — `-` bullets (required when `.githooks/commit-msg` is active):**
|
|
|
|
After the separator blank line, write **at least one** `- ...` line. Scale depth
|
|
to the diff: a tiny change can use a single substantive bullet; large or
|
|
multi-file patches need **grouped, substantive bullets** (major types or
|
|
functions, behavior, wiring, migrations, caveats) so a reviewer can reconstruct
|
|
the story without re-reading every hunk.
|
|
|
|
### Hook-enforced shape (with `core.hooksPath` → `.githooks`)
|
|
|
|
The **commit-msg** hook (except during merges while `.git/MERGE_HEAD` exists)
|
|
requires, in order:
|
|
|
|
1. non-empty subject line;
|
|
2. blank line;
|
|
3. one or more summary lines (none may start like a `- ` bullet);
|
|
4. blank line;
|
|
5. one or more lines each starting with `- ` (details).
|
|
|
|
Every line must be **80 characters or fewer**. There must be **no** blank lines
|
|
inside the bullet block. Rewrap before committing.
|
|
|
|
### Example (single file)
|
|
|
|
```
|
|
refactor(basis): fold lagrange includes behind generator entrypoints
|
|
|
|
The basis tree listed seven separate lagrange_*.jl includes; they now load
|
|
through lagrange_generator.jl and lagrange_generated.jl so registration lives
|
|
in one place.
|
|
|
|
- Drop redundant includes from JuliaFEM.jl
|
|
- Note the Basis suffix convention in the include comment
|
|
```
|
|
|
|
### Example (multi-file — requires justification to user)
|
|
|
|
When proposing:
|
|
|
|
> **Files:** `src/foo.jl`, `test/foo/test_foo.jl`
|
|
> **Why one commit:** adds `compute_bar` and the regression test that locks its behavior; splitting would leave the feature untested in history.
|
|
> **Message:** …
|
|
|
|
## Cycle per commit
|
|
|
|
1. Stage the smallest set (usually one file).
|
|
2. `git diff --staged` — read all of it.
|
|
3. Propose message + paths (+ multi-file justification if needed).
|
|
4. Wait for approval.
|
|
5. Commit.
|
|
6. Repeat for remaining work.
|
|
|
|
## Common mistakes
|
|
|
|
### Vague or mismatched messages
|
|
|
|
```bash
|
|
# ❌ WRONG
|
|
git commit -m "Update documentation"
|
|
|
|
# ✅ RIGHT — subject, blank, summary, blank, "- ..." bullets (from the diff)
|
|
```
|
|
|
|
### Bundling unrelated files
|
|
|
|
```bash
|
|
# ❌ WRONG — two different stories
|
|
git add docs/CONTRIBUTING.md src/unrelated_fix.jl
|
|
|
|
# ✅ RIGHT — two commits, or ask user to split worktrees if mixed in one working tree
|
|
```
|
|
|
|
### Committing without approval
|
|
|
|
Never run `git commit` until the user has approved the proposed paths and message.
|
|
|
|
### Truncating the diff
|
|
|
|
Never use `git diff --staged | head` (etc.). Read the full diff.
|
|
|
|
## Checklist before each commit
|
|
|
|
- [ ] Did the user explicitly ask to commit?
|
|
- [ ] Is the staged set as **small** as it reasonably can be?
|
|
- [ ] If multiple files: is there a **clear single story**, and will you **justify** it to the user?
|
|
- [ ] If `.githooks` is active: are there **at most two** staged paths?
|
|
- [ ] Did I read the **full** `git diff --staged`?
|
|
- [ ] Subject line: Conventional Commits, specific, matches diff?
|
|
- [ ] Is **every** message line (subject, summary, bullets) **≤ 80 characters**?
|
|
- [ ] Shape: subject, **blank**, summary paragraph, **blank**, then **≥1** `-`
|
|
bullet (required when the commit-msg hook is enabled)?
|
|
- [ ] Bullets **scaled to the diff** (one tight bullet is fine for a one-liner
|
|
patch; large patches need rich, grouped bullets)?
|
|
- [ ] Did I **propose** the commit and wait for **approval** before `git commit`?
|
|
|
|
## Why this matters
|
|
|
|
1. **Story:** History is easy to read and bisect.
|
|
2. **Review:** Each commit is reviewable in isolation.
|
|
3. **Honesty:** Messages match what the diff actually does.
|
|
4. **Control:** The author approves each step.
|
|
|
|
---
|
|
|
|
Git history is long-lived. Prefer small, well-explained commits and explicit approval before each `git commit`.
|