Git Commit Generator
Analyze git changes and generate a precise, standards-compliant commit message in both Chinese and English.
Before / After
| Aspect | Without This Skill | With This Skill |
|---|---|---|
| Quality | Vague messages like "update code" or "fix bug" | Conventional Commits with correct type, scope, and bilingual summary |
| Speed | 2-5 min writing + translating | Instant, ready to paste |
| Consistency | Varies wildly by mood and fatigue | Always follows the same professional format |
| Context for Team | "what does this even do?" | Message explains the what and why in two languages |
| Bisect / Changelog | Unusable for automation | Machine-parseable, feeds directly into changelogs and semantic versioning |
Workflow
Step 1: Gather the Diff
Run the bundled scanner script first — it produces structured JSON that removes guesswork:
bash <skill-path>/scripts/analyze_diff.sh
The script outputs a JSON block with:
staged_files— files in the index (ready to commit)unstaged_files— working tree changes not yet stageddiff_stats— per-file additions/deletions and total line countsnew_files,deleted_files,renamed_files— detected file-level operationsbranch— current branch name (useful for scope inference)
If you can't run the script for some reason, gather equivalent info with:
git diff --cached # Actual code changes (staged) — required for classification
git diff --cached --stat # Summary statistics
git diff --stat # Unstaged summary
git status --short # File-level overview
git branch --show-current # Current branch for scope inference
Why this matters: Reading the raw diff is unreliable for classification. Structured stats let you see the shape of the change — one file with 3 lines added is a fix; ten files with 200 lines added across a new directory is a feature.
Step 2: Classify the Change Type
Based on the diff content, pick the single most descriptive type:
| Type | When to Use | Signal in Diff |
|---|---|---|
feat |
New feature or functionality | New files, new functions/classes, new API endpoints, new CLI flags |
fix |
Bug fix | Diff touches error-handling code, corrects logic, adds missing null checks |
docs |
Documentation only | Changes are confined to .md, .rst, comments, or docstrings |
style |
Formatting, whitespace, semicolons | No logic change — only indentation, line breaks, quote style |
refactor |
Code restructuring without feature/fix | Moves code between files, renames symbols, extracts functions — no behavior change |
perf |
Performance improvement | Optimizes loops, adds caching, reduces allocations |
test |
Test-only changes | Changes are in test/, tests/, __tests__/, spec/ directories |
chore |
Maintenance tasks | Updates dependencies, CI config, .gitignore, build scripts |
ci |
CI/CD pipeline changes | Modifies .github/workflows/, Jenkinsfile, .gitlab-ci.yml |
build |
Build system or external dependencies | Changes to Makefile, Cargo.toml, package.json deps, CMakeLists.txt |
revert |
Reverting a previous commit | git revert output, diff is the inverse of a prior commit |
Why this matters: The type is the first thing teammates see in git log --oneline. A fix that's labeled feat wastes time during bisecting. A refactor labeled chore hides important restructuring from code review.
If the change spans multiple types (e.g., a feature that also refactors helpers), pick the dominant intent — what you'd tell a colleague this commit is "about."
Step 3: Determine the Scope
The scope is optional but recommended. Derive it from the affected area:
- Single file: use the file basename without extension (e.g.,
auth,parser,router) - Single directory: use the directory name (e.g.,
api,components,utils) - Multiple related files: use the shared module/concept name
- Cross-cutting change: omit scope or use
*
Read the scope table in references/conventional_commits.md for edge cases and project-specific conventions.
Why this matters: Scope turns feat: add validation into feat(api): add request validation. The latter tells someone skimming the log exactly where the change happened without opening the commit.
Step 4: Write the Commit Message
Generate the message in this exact format:
<type>(<scope>): <English description>
<中文描述>
<body or footer if needed>
Rules for the English description:
- Use imperative mood ("add" not "added", "fix" not "fixed")
- First letter lowercase (after the colon)
- Keep it under 72 characters total for the first line
- Be specific — "fix null pointer in auth middleware" not "fix bug"
Rules for the Chinese description:
- Natural, fluent Chinese — not a machine-translation word-for-word
- Match the English intent, not the English words
- Keep it concise (one line, under ~40 characters)
When to add a body/footer:
- Breaking change: Add
BREAKING CHANGE:footer explaining what breaks - Non-obvious context: Add a 1-2 sentence body explaining why this change was made
- Linked issue: Add
Closes #123orRefs #456if mentioned in the diff or branch name
Step 5: Present and Offer to Commit
Present the final message in a code block for easy copying:
```
feat(auth): add JWT refresh token rotation
添加JWT刷新令牌轮换机制
```
Then ask: "Commit with this message?"
If the user says yes, run:
git commit -m "<the full message>"
If there are no staged changes, ask whether to stage all changes first (git add -A) or let the user pick specific files.
Change Classification Guide
When the diff is ambiguous, use this decision tree:
- Does the diff contain a new file that exposes a previously-nonexistent capability? →
feat - Does the diff correct behavior that was observably wrong? →
fix - Does the diff only touch docs, formatting, tests, or deps? → use the corresponding type from the table
- Does the diff restructure code without changing external behavior? →
refactor - Does the diff improve speed or resource usage? →
perf - Everything else →
chore
Output Format
Present exactly this structure to the user:
## Generated Commit Message
**Type:** feat
**Scope:** auth
**Files changed:** 3 (+45, -12)
---
feat(auth): add JWT refresh token rotation
添加JWT刷新令牌轮换机制
---
Commit this? [y/n]
The "Files changed" summary gives the user a quick sanity check — they should recognize what they changed before approving.
Definition of Done
-
analyze_diff.sh(or equivalent commands) has been run and diff has been inspected - Change type matches the dominant intent of the diff, not a guess
- Scope is derived from actual file paths in the diff
- English description uses imperative mood, is under 72 chars, starts lowercase
- Chinese description is natural and matches the English intent
- Breaking changes are explicitly called out with
BREAKING CHANGE:footer - User has reviewed the proposed message before committing
- If user approved,
git commithas been executed successfully
Rules
- Read the actual diff before generating. Never propose a message from file names alone — the content of the change is what determines the type.
- One commit, one purpose. If the diff does three unrelated things, tell the user and suggest splitting into separate commits. Don't cram everything into a vague
choremessage. - Don't invent context. If the diff doesn't explain why a change was made, don't fabricate a reason. Stick to describing what changed.
- Respect the user's language. Present the commit message in the same language the user spoke to you. If they asked in Chinese, include the Chinese description; if English-only, provide only English.
- Check staging area first.
git diff --cachedtakes priority. If nothing is staged, usegit diff(unstaged changes). If both are empty, tell the user there's nothing to commit. - Handle the edge case of a dirty working tree. If changes are unstaged and the user wants to commit, ask before staging — never
git add -Awithout confirmation. - No destructive git operations. Never
git reset,git stash, or modify the working tree beyond the user's explicit request.
