Git Commit Hygiene skill for Codex

Atomic commits a reviewer can read in order. Codex plans a commit series before staging, keeps refactors and behaviour changes apart, and stages partial files through git apply --cached because interactive add hangs an agent shell. Messages follow the 50/72 convention with real reasons and trailers, and git rebase --exec proves every commit in the series builds and passes tests.

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- git-commit-hygiene

Installs to ~/.agents/skills/git-commit-hygiene/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $git-commit-hygiene, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Turns a messy working tree into a sequence of atomic, buildable commits with messages that explain why - reviewing git status, staging selected hunks without interactive prompts via git apply --cached, separating refactors from behaviour changes, writing subject and body to the 50/72 convention with trailers, and checking each commit with git rebase --exec. Use when asked to commit work, split changes into logical commits, clean up before a PR, or write a better commit message.

Use it when

  • Work is finished and needs committing in reviewable pieces
  • One file contains changes for more than one purpose
  • A commit message is vague or describes the diff instead of the reason

Use something else when

How to check the result

git rebase --exec with the test command passes on every commit in the series, and git status shows nothing unexpected left behind.

The mistake it prevents

Running git add -A and sweeping editor files, build output or a local .env into a commit.

~/.agents/skills/git-commit-hygiene/SKILL.md

--- name: git-commit-hygiene description: Turns a messy working tree into a sequence of atomic, buildable commits with messages that explain why - reviewing git status, staging selected hunks… license: MIT ---

Each commit does one thing, builds and passes tests on its own, and has a message a stranger can understand in a year: what changed in the subject, why in the body. A reviewer can read the series commit by commit, and git revert of any one commit is safe.

Before committing anything

  1. Commit only when the user asked for commits. Read AGENTS.md for message format (Conventional Commits, ticket prefixes, sign-off) and follow it over these defaults.
  2. Look at the whole tree, not just what you remember editing:
git status --porcelain=v1
git diff --stat
git diff --cached --stat        # anything already staged by someone else?
  1. Never git add -A or git add . blindly. Unexpected files (.env, editor swap files, build output, large binaries, credentials) must be left out and mentioned.
  2. Check for whitespace damage and conflict markers: git diff --check.

Plan the commit sequence

Group changes by intent, then order them so every prefix of the series builds:

Change kindOwn commit?Goes
Pure rename / moveYesFirst, so later diffs are readable
Mechanical reformatYes, never mixed with logicFirst, or skip if unrelated to the task
Refactor with no behaviour changeYesBefore the feature that needs it
New behaviour plus its testsYes, togetherAfter its preparatory refactors
Dependency bumpYesBefore code that relies on it
Generated files (lockfile, codegen)With the change that caused themSame commit as the source change
Unrelated fix spotted on the waySeparate commit, or a note for laterAsk the user if it is out of scope

Write the plan as a short list before staging, for example:

1. refactor(cart): extract tier_for() from compute_total   src/cart.py
2. feat(cart): add bulk pricing tiers                       src/cart.py, tests/test_cart.py
3. docs: document bulk pricing                              docs/pricing.md

Staging precisely without interactive prompts

git add -p waits for keyboard input, which a non-interactive Codex shell cannot give reliably. Stage whole files with git add <path> when a file belongs to one commit. When one file holds changes for two commits, stage a hunk subset through a patch:

git diff -- src/cart.py > "$TMPDIR/cart.full.patch"
# Copy the file header (diff --git, index, ---, +++) and only the hunks for commit 1
# into cart.part1.patch. Keep each kept hunk intact, from its @@ line to the next @@.
git apply --cached --check --recount "$TMPDIR/cart.part1.patch"
git apply --cached --recount "$TMPDIR/cart.part1.patch"
git diff --cached -- src/cart.py    # confirm exactly the intended hunks are staged
git diff -- src/cart.py             # and the rest is still unstaged

If two intents share a single hunk, regenerate the diff with less context (git diff -U1) to split it, or temporarily edit the file to the intermediate state, stage it, then restore the final version from a saved copy.

Useful undo commands while staging:

git restore --staged <path>         # unstage, keep working-tree changes
git reset --soft HEAD~1             # undo last commit, keep it staged (local only)

Writing the message

Cap bulk discount at the highest single tier

Tiers were applied cumulatively, so 100 units received 5% and then 12%
off the discounted price. Finance confirmed tiers must not stack.

Pick the single highest tier the quantity qualifies for. Totals for
orders under 10 units are unchanged.

Refs: #812
  • Subject: imperative mood (“Add”, “Fix”, “Remove”), about 50 characters, hard limit 72, no trailing period. It completes “If applied, this commit will …”.
  • Blank line, then a body wrapped at 72 columns. Explain the problem, why this approach, and any consequence a reviewer would not see in the diff (migration needed, behaviour change, performance trade-off). Skip the body only for truly self-evident commits.
  • Do not narrate the diff (“changed line 42”), do not write “various fixes”, “WIP”, “address comments”, or “update file.py”.
  • Trailers go last, one per line: Refs:, Fixes #123 (closes the issue on GitHub when merged to the default branch), Co-authored-by: Name <email>, Signed-off-by: when the repo uses DCO (git commit -s).

Pass multi-line messages without an editor:

git commit -F "$TMPDIR/msg.txt"
git commit -m "Cap bulk discount at the highest single tier" -m "Tiers were applied ..." \
  --trailer "Refs: #812"

Verify the series

Check that every commit builds and passes tests, not just the tip:

git log --oneline --reverse origin/main..HEAD
git rebase --exec "npm test --silent" origin/main   # runs after each commit; stops on failure

The rebase refuses to start with unstaged changes; add --autostash if you deliberately left files out. --exec leaves the commits unchanged when every command passes. If one fails, the rebase stops at that commit: fix it, git commit --amend, then git rebase --continue. Abort with git rebase --abort if the fix belongs elsewhere.

Final checks:

  • git status is clean or shows only files you deliberately left out (and reported).
  • git log --stat origin/main..HEAD shows no stray files.
  • Each subject reads as an imperative sentence; no “WIP” or “fixup!” left unless the user wants them squashed later (see git-history-surgery).
  • No secrets: git diff origin/main..HEAD | rg -n "(AKIA|BEGIN .*PRIVATE KEY|api[_-]?key\s*=)".

Hard rules

  • Never commit with --no-verify to get past hooks unless the user explicitly asks; fix what the hook reports.
  • Never amend or rewrite commits that are already pushed to a shared branch without the user’s confirmation; that requires a force push.
  • Never commit secrets. If one was committed, stop and tell the user: removing it from history is not enough, the secret must be rotated.
  • Do not change author identity or user.email config.

Output

After committing, report:

Created 3 commits on feature/bulk-pricing (not pushed):
  a1b2c3d refactor(cart): extract tier_for() from compute_total
  d4e5f6a feat(cart): add bulk pricing tiers
  0718293 docs: document bulk pricing
Each commit passes `npm test` (checked with rebase --exec).
Left unstaged: .env.local (local config, not part of this change).

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Git Commit Hygiene"
  short_description: "Atomic commits a reviewer can read in order"
  default_prompt: "$git-commit-hygiene Commit my changes as a few logical commits instead of one big one"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc