Overview

The Git history is the project’s permanent record, and agents read git log for context. These rules keep it legible. Command lookups are in git-commands; read general-principles first.

Commit one logical change at a time

A reviewer must be able to revert a commit in isolation. A commit that renames a file, fixes a bug, and bumps a dependency is three commits; split it with git add -p. Land refactors before behavior changes, never mixed in. If the diff cannot be summarized in one sentence, it is too big. Small commits also give git bisect something to work with.

Write the message for the reader of git log

The subject is the contract for git log --oneline; the body explains the why the diff cannot show.

fix(parser): reject invoices with negative line items

The supplier started emitting refunds as negative amounts on the same
invoice as positive lines. Treat the whole invoice as a credit note.

Closes #482.

Keep the subject near 50 characters, leave a blank line, wrap the body near 72. No fix, WIP, or updates on main.

Use Conventional Commits for the subject

Use the Conventional Commits prefix so changelog and release tooling can parse the log: feat, fix, refactor, docs, test, and chore, with a scope for the subsystem (fix(parser):). Drop the scope only for repo-wide changes.

Rebase what is yours; merge what others pulled

Rebase a short-lived feature branch onto main before opening the PR for a linear history. Merge a long-lived integration branch (a release train, a multi-month migration), because rebasing rewrites commits other people have.

git fetch origin
git rebase origin/main

Never rewrite published commits: after a push, --amend and rebase force every collaborator to reconcile a divergent history. When you must force-push your own feature branch, use git push --force-with-lease, which refuses if the remote moved. Never force-push main. Merge options are in git-merge-strategies.

Run formatters, linters, and a secrets scanner in pre-commit hooks

Hooks catch what would fail CI before the commit lands. Manage them with the pre-commit framework so the config is portable, and wire in formatters (ruff, prettier), shellcheck, and a scanner such as gitleaks. Reserve git commit --no-verify for emergencies; habitual skipping means the hooks need fixing.

Treat .gitignore as noise control, not security

A secret committed once stays in history and in every clone. Rotate any leaked credential; do not rely on a “remove it” commit. Fail CI with gitleaks or trufflehog on key patterns, and keep secrets out of the repo from the start (dotfiles, config-files).

Squash-merge pull requests by default

Squash-merge turns a branch into one commit on main; with a Conventional Commits subject, history reads as one line per shipped change. Set it as the default in github. Use a merge commit only when the branch’s individual commits are each meaningful. For scripted Git workflows see shell; for agent-driven commits see claude-code-workflow.