How to write a commit message, what to update alongside it, and what must never appear in either
Scanned 9/3/2026
Install to Claude Code
npx -y skills add istota-project/istota --skill commit --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Commit?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/istota-project-commit)More formats (shields.io, HTML) on the badges page.
---
name: commit
triggers: [commit, commit message, changelog, git commit, staging]
description: How to write a commit message, what to update alongside it, and what must never appear in either
---
# Commit
Git history is the development log. The commit body is where the narrative lives, so it has to be worth reading later and it has to be safe to publish.
## Staging
**Stage the specific files you changed. Never `git add -A`, never `git add .`.**
```bash
git status --short # look at this first, every time
git add path/to/file.py path/to/test_file.py
git status --short # confirm what is staged
```
Run these from the repository you are working in. Inside the `developer` workflow that is `$WORK_DIR`, the worktree it created; on its own it is wherever the change lives.
A worktree holds more than your change. A copied `.env`, a stray fixture, a scratch script, an editor backup, a downloaded sample — all of them are untracked, all of them are caught by `-A`, and a repository that is public or may become public keeps them forever. Read the `git status --short` output before staging and again after; anything you did not mean to include gets left out, and anything you deliberately left unstaged gets named in your report.
## Message format
**No LLM or AI attribution. Ever.** No `Generated by`, no `Co-Authored-By: Claude`, no tool name in the trailer.
The single exception is `$DEVELOPER_AUTHOR_CREDIT`. When that variable is set, append its contents after a blank line at the end of every commit message. When it is unset, the message ends with the body and carries no trailer at all. Nothing else is ever appended.
**Match the repository's existing subject convention** before writing anything:
```bash
git log --format=%s -20
```
Some repositories prefix an area (`auth: reject expired tokens`), some write a plain imperative sentence (`Reject expired tokens on refresh`). Follow whichever the log shows. Where a repository is mixed or has no history, use the plain imperative.
Subject under 72 characters, specific about what changed. Then a blank line, then the body.
The body carries what the diff cannot say for itself: why this was done, the mechanism behind the bug, what was tried and rejected, measurements, constraints that forced the shape of the fix. Never list changed files — `git show --stat` covers that. Never bullet out what the diff already shows.
```
Return 404 instead of 500 for encoded-hash paths like /%23notes/
Slugs arrive URL-decoded from the router, so /%23notes/ became slug
"#notes", and interpolating it raw into fetch() truncated the URL at
the fragment. The resulting non-JSON 404 page blew up res.json() and
surfaced as a 500.
Rejected stripping the fragment: the same truncation could corrupt
upstream queries on direct data-route hits, dropping every param
after the hash, so slugs are now validated and encoded instead.
```
One commit per coherent change, each with its own body, rather than one lump at the end. Where a body corrects or supersedes an earlier decision, name the commit it replaces by sha — a pushed message cannot be edited, so the correction has to live in the newer one.
Writing the message with `$DEVELOPER_AUTHOR_CREDIT` handled:
```bash
CREDIT="${DEVELOPER_AUTHOR_CREDIT:+
$DEVELOPER_AUTHOR_CREDIT}"
git commit -m "auth: reject expired refresh tokens
Tokens past their exp were accepted because the check compared
against issued-at rather than expiry. Adds the comparison and a
regression test covering the boundary second.${CREDIT}"
```
## What lands alongside the commit
Check for each of these; do not assume either applies.
### CHANGELOG.md
Only if the repository root already has one — **never create one**. Only user-facing changes earn an entry; a refactor, a test-only change, or dev tooling gets nothing.
Entries go under `## [Unreleased]` at the top, following Keep a Changelog: groups are Added, Changed, Deprecated, Removed, Fixed, Security, omitting the empty ones. ISO dates, link references at the bottom. Cut a versioned section only when actually releasing.
Write the entry for the user, not the maintainer — what was broken or missing from their side and what now works, in two or three sentences. No file paths, env vars, function names or thresholds. Lead with the symptom they would have noticed.
### The issue tracker
An issue left open after its fix ships is worse than no tracker, because the next reader cannot tell what is still real.
Read one or two already-closed entries and match their convention exactly — status marker, resolution heading, date format, placement. Do not invent a format or an issue ID.
- **Close only what is genuinely done.** Partial work stays open with a note saying what landed and what remains. Several issues touched: resolve each on its own merits. No associated issue: skip, and do not go hunting for something to mark closed.
- **A remote close is public.** Closing a GitLab or GitHub issue is an outbound action against a shared system. Say which issue you would close and let the user make the call, unless the task text already asked for it.
- A tracker **inside** the repository is staged with everything else and the scrub rules below cover it. A tracker **outside** the repository is just saved — do not commit it, do not push it, and do not name its path in the commit message or in any in-repo document.
## No sensitive or private data
**Absolute. Never put personal or production detail into anything committed.** Not once, not "just this example", not truncated, not obfuscated. Committed artifacts use generic placeholders only.
This covers everything the commit touches: the CHANGELOG, in-repo trackers, `README`/`CLAUDE.md`/`AGENTS.md`, code comments and docstrings, default and example config, log-message examples, and **tests, fixtures, snapshots and sample data**. It covers the commit message doubly so, because a body has room to name a real host or paste a real payload in a way a short subject never did.
Treat every repository as public even while it is private — visibility changes, and history is hard to scrub.
| Instead of | Write |
|---|---|
| a real host, internal DNS, private git host | `server.example.com`, `<host>`, "the deployment server" |
| a real repo URL | "the private repo", "the upstream repo" |
| a path leaking an org or user (a real mount root, `/home/<name>/…`) | `/srv/app/<app>`, "the user data dir", "the mounted workspace" |
| an internal product, sibling repo, team or company | "an upstream service", "the sibling repo" |
| a real user, display name, email, account or OAuth client ID | `alice`, `bob`, `alice@example.com`, `<user>` |
| a real IP or internal service port | RFC 5737 (`192.0.2.0/24`) |
| a real place, merchant or contact | "Coffee shop on Main St", "Acme Corp", "Jane Doe" |
**Personal content** pulled from the user's own life — emails, message bodies, contacts, calendar entries, location pings, financial transactions, memory notes, family or friend names, health data — gets fabricated, never copy-pasted from real input.
**Secrets** never appear. Tokens, keys, passwords, session cookies: not truncated, not rotated, not expired. Name the variable, never the value. This includes anything reachable from the environment — never echo a credential into a file you are about to stage.
**Tests are committed too**, and a test reproducing a real bug is the easiest place to leak, because the fastest way to write it is to paste the input that broke. Rewrite the fixture with placeholder values of the same shape: same field lengths, encodings, edge characters, record counts. If the bug depends on a value you cannot generalize, describe the shape in a comment and construct it in code.
When in doubt, generalize. What bug, what fix, what architecture decision is almost always enough without organization-specific identifiers. Fix drafted text before the commit step, not after — git history retains the original.
## Before you commit
- `git status --short` — is anything staged that you did not mean to stage?
- Does any staged line contain a real host, path, name, address or token?
- Does the subject match the convention in `git log --format=%s -20`?
- Does the body say why, not what?
- Is `$DEVELOPER_AUTHOR_CREDIT` appended if set, and nothing else?
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!