Gitmoji Commit
Turn a pile of working changes into a clean sequence of commits, each styled as an emoji + conventional type + imperative subject:
β¨ feat: add avatar upload to the profile page
You plan, show the plan, then run it. Lay out the commit sequence so the user can see what's coming, then run the git add and git commit commands yourself.
Workflow
Survey everything. Don't trust what's staged. Run
git status,git diff --staged,git diff, and open untracked files. Read the substance, not filenames β atests/change may belong to a feature; a.mdfile may be docs or just a comment.Group into atomic commits. Each commit = one coherent concern a reviewer can follow on its own.
- Keep related edits together (feature + its tests + its doc note = one story).
- Split unrelated edits apart, even if staged together now.
- Separate mechanical noise (formatting, renames, generated files) from logic β give it its own commit.
- Default to file-level grouping for clean copy-paste (
git add path/a path/b). Only suggestgit add -pwhen one file mixes two concerns β and warn it's interactive. - Dependencies: bundle the manifest (
package.json,pyproject.toml,Cargo.toml) with the feature when the feature uses the dep it adds. Give independent bumps their own commit. The lockfile is always separate and last (see below).
Order sensibly. Foundational/prep first, the feature/fix that uses them next, mechanical/generated changes (formatting, lockfiles) last. Ideally the tree builds at every step.
For each commit, prepare three things: the gitmoji message (subject + a why-focused body when non-trivial), the
git addcommand(s) staging exactly that group, and a signedgit commit.Show the plan, then run it. Present the commits as an ordered sequence so the user sees what's about to happen, then execute the
git add/git commitcommands in order. If a commit fails (e.g. signing not configured, pre-commit hook rejects), stop, report what happened, and don't blindly continue the sequence.
Lockfiles go last, in their own commit
Lockfiles always get their own commit, ordered last, never mixed with source:
package-lock.json, npm-shrinkwrap.json, yarn.lock, pnpm-lock.yaml, bun.lock, bun.lockb β and other ecosystems' lockfiles (Cargo.lock, poetry.lock, composer.lock).
Why: they're large, generated, and conflict constantly. Conflicts are resolved by regenerating (npm install, etc.), not hand-merging. Isolating them keeps that regeneration off your real source changes.
Use π¦οΈ build (or β/β/β¬οΈ for add/remove/upgrade), e.g. π¦οΈ build: update package-lock.json.
Sign each commit
Add -S to every git commit so commits are signed:
git commit -S -m "β¨ feat: ..."
Check whether signing is configured before running anything: git config --get commit.gpgsign and git config --get user.signingkey. If neither is set, -S will make the commit fail β so don't run it. Instead, tell the user signing isn't set up (point to git config user.signingkey / gpg.format) and ask whether to commit without -S or hold off while they configure a key.
Message format
- Subject:
<emoji> <type>: <description>β e.g.π fix: handle null user in payment processor.- Imperative mood ("add", "fix" β not "added"). Lowercase first word, no trailing period.
- Aim ~50 chars, hard-stop ~72. One space after the emoji. Some emoji (ποΈ ποΈ π¦οΈ) carry a variation selector β copy them as-is.
- Scope (optional):
β¨ feat(auth): add SSO loginβ use when the repo has clear modules, skip when it'd be noise. - Body (optional, for non-trivial changes): blank line, then the why and context β not a restatement of the diff. Wrap ~72 chars.
- Breaking changes: π₯ with
!after the type and aBREAKING CHANGE:footer, e.g.π₯ feat!: drop support for Node 16. - Issue refs: footer like
Refs: #123/Closes: #123when a ticket is mentioned.
Emoji β type (common cases)
Pick the single emoji that fits the dominant change; the type follows. Full list of all 73 in references/gitmojis.md. When two rows fit, prefer the more specific (ποΈ over π for a hotfix; β°οΈ over π₯ for dead code).
| Intent | Emoji | Type |
|---|---|---|
| New feature | β¨ | feat |
| Bug fix | π | fix |
| Critical hotfix | ποΈ | fix |
| Simple non-critical fix | π©Ή | fix |
| Security/privacy fix | ποΈ | fix |
| Fix typos | βοΈ | fix |
| Documentation | π | docs |
| Comments in source | π‘ | docs |
| Refactor (no behavior change) | β»οΈ | refactor |
| Improve structure / format | π¨ | refactor |
| Remove code or files | π₯ | refactor |
| Remove dead code | β°οΈ | refactor |
| Move or rename | π | refactor |
| Performance | β‘οΈ | perf |
| Tests | β | test |
| Failing test (TDD) | π§ͺ | test |
| UI / style files | π | style |
| Compiler / linter warnings | π¨ | style |
| Config files | π§ | chore |
| Work in progress | π§ | chore |
| Dev scripts | π¨ | chore |
| Logs | π | chore |
| Deploy | π | chore |
| Release / version tag | π | chore |
| Begin a project | π | chore |
| Add dependency | β | build |
| Remove dependency | β | build |
| Upgrade dependencies | β¬οΈ | build |
| Downgrade dependencies | β¬οΈ | build |
| Pin dependencies | π | build |
| Lockfiles / compiled packages | π¦οΈ | build |
| Add/update CI | π· | ci |
| Fix CI build | π | ci |
| Revert | βͺοΈ | revert |
| Merge branches | π | (merge) |
| Breaking changes | π₯ | feat!/fix! |
| Types | π·οΈ | feat |
Worked example
git status shows: a new debounce on a search input (search.js) + its test (search.test.js), the lodash.debounce dep it uses in package.json, an unrelated typo fix in README.md, and a regenerated package-lock.json.
The feature uses the new dep, so the manifest rides with it β two concerns + a lockfile β three commits, lockfile last. Show this plan, then run the commands in order:
# 1 β feature, its test, and the dependency it needs (one buildable story)
git add src/search.js src/search.test.js package.json
git commit -S -m "β¨ feat(search): debounce query input" -m "Searching fired a request on every keystroke, hammering the API on fast typers. Debounce by 300ms so we only query once typing settles. Adds lodash.debounce and a test covering the timing."
# 2 β unrelated docs typo, on its own
git add README.md
git commit -S -m "βοΈ fix: correct typo in README setup steps"
# 3 β lockfile last, isolated so a merge conflict can just be regenerated
git add package-lock.json
git commit -S -m "π¦οΈ build: update package-lock.json"
Single-commit change, no body β one -m is enough:
git add report.py
git commit -S -m "β»οΈ refactor(report): simplify build with a line-total helper"
If the user wants everything as one commit, pick the emoji for the most significant change and say what you set aside.