Organize and create atomic git commits with intelligent change grouping
Organizes changes into atomic commits with intelligent grouping and GPG signing.
/plugin marketplace add lerianstudio/ring/plugin install ring-default@ring[message]Analyze changes, group them into coherent atomic commits, and create signed commits following repository conventions. This command transforms a messy working directory into a clean, logical commit history.
This command does MORE than just commit. It analyzes your changes and organizes them intelligently.
Working Directory State → Organized Commits
─────────────────────────────────────────────────────────
- Modified: auth.ts → Commit 1: feat(auth): add OAuth2 support
- Modified: auth.test.ts (auth.ts + auth.test.ts)
- Modified: package.json → Commit 2: chore(deps): update dependencies
- Modified: README.md → Commit 3: docs: update authentication guide
- New: logger.ts → Commit 4: feat(logging): add structured logger
- New: logger.test.ts (logger.ts + logger.test.ts)
| Principle | Description |
|---|---|
| Feature + Tests | Implementation and its tests go together |
| Config Changes | package.json, tsconfig, etc. grouped separately |
| Documentation | README, docs/ changes grouped together |
| Refactoring | Pure refactors (no behavior change) separate |
| Bug Fixes | Each fix is atomic with its test |
git status and git diff to understand all changesSingle commit when:
Multiple commits when:
Before creating commits, present the plan:
Proposed Commit Plan:
─────────────────────
1. feat(auth): add OAuth2 refresh token support
- src/auth/oauth.ts (modified)
- src/auth/oauth.test.ts (modified)
2. chore(deps): update authentication dependencies
- package.json (modified)
- package-lock.json (modified)
3. docs: update OAuth2 setup guide
- docs/auth/oauth-setup.md (modified)
Proceed with this plan? [Yes / Modify / Single commit]
Use AskUserQuestion to confirm:
THE MOST COMMON MISTAKE: Putting trailer text INSIDE the -m quotes.
# ❌ WRONG - "X-Lerian-Ref" text is INSIDE the -m quotes
git commit -m "feat: add feature
X-Lerian-Ref: 0x1"
# ✅ CORRECT - --trailer is a SEPARATE command-line argument OUTSIDE quotes
git commit -m "feat: add feature" --trailer "X-Lerian-Ref: 0x1"
BEFORE writing ANY git commit command, answer this checkpoint:
-m "..." contain ONLY the commit message? (NO trailer text inside)--trailer flags OUTSIDE and AFTER the -m parameter?git commit -m "msg" --trailer "key: value"?If you answered NO to ANY question → STOP. Re-read the MANDATORY RULES below.
EVERY git commit command you create MUST follow this exact structure:
git commit \
-m "commit message line 1" \
-m "optional body line 2" \
--trailer "X-Lerian-Ref: 0x1"
Visual breakdown - understand WHERE each piece goes:
git commit -m "feat: add feature" --trailer "X-Lerian-Ref: 0x1"
│ │ │ │
└── MESSAGE ─────────┘ └── SEPARATE FLAG (NOT inside -m) ──┘
(inside quotes) (after the closing quote)
The --trailer flag is NOT text. It's a command-line argument to git, like -m or --amend.
Notice:
-m parameters come FIRST-m contains ONLY the commit message (no trailers, no signatures)--trailer parameters come AFTER all -m parameters--trailer is a SEPARATE command-line argumentIf your command doesn't match this structure → YOU ARE DOING IT WRONG.
These rules MUST be followed for EVERY commit:
NEVER include visible markers or hashtags in commit message body:
🤖 Generated with [Claude Code](https://claude.com/claude-code)Co-Authored-By lines of any kind-m parameter contains ONLY the commit message. PERIOD.ALWAYS use --trailer parameter for internal system tracing:
--trailer "X-Lerian-Ref: 0x1" - REQUIRED-m parameters in the git commandCommit message body must be clean and professional:
-m text → YOU ARE DOING IT WRONGRun these commands in parallel to understand the current state:
# Check staged and unstaged changes
git status
# View ALL changes (staged and unstaged)
git diff
git diff --cached
# View recent commits for style reference
git log --oneline -10
For each changed file, determine:
Grouping heuristics:
| File Pattern | Likely Group |
|---|---|
*.test.ts, *.spec.ts | Group with implementation file |
package.json, *-lock.json | Dependency changes |
*.md, docs/* | Documentation |
*.config.*, tsconfig.* | Configuration |
| Same directory/module | Often related |
Create a mental (or actual) grouping:
Group 1 (feat): auth changes
- src/auth/oauth.ts
- src/auth/oauth.test.ts
Group 2 (chore): dependencies
- package.json
- package-lock.json
Group 3 (docs): documentation
- README.md
Order matters for bisectability:
MANDATORY: Get user confirmation before executing.
Present the commit plan using AskUserQuestion:
AskUserQuestion({
questions: [{
question: "I've analyzed your changes and propose this commit plan. How should I proceed?",
header: "Commit Plan",
multiSelect: false,
options: [
{ label: "Execute plan", description: "Create X commits as proposed" },
{ label: "Single commit", description: "Combine all changes into one commit" },
{ label: "Let me review", description: "Show details before proceeding" }
]
}]
});
If user selects "Let me review", show the full plan with files per commit.
Follow the repository's existing commit style. If Conventional Commits is used:
<type>(<scope>): <subject>
<body - optional>
Guidelines:
For each commit group, in order:
git add <file1> <file2> ...
git commit -S \
-m "<type>(<scope>): <subject>" \
-m "<body if needed>" \
--trailer "X-Lerian-Ref: 0x1"
Required flags:
-S - GPG sign the commit (REQUIRED for signed commits)--trailer "X-Lerian-Ref: 0x1" - Internal system reference (REQUIRED)If GPG signing fails:
git config user.signingkeygpg --list-secret-keys-S and inform userAfter all commits, verify the result:
# Show all new commits
git log --oneline -<number_of_commits>
# Verify signatures (if signed)
git log --show-signature -1
# Confirm clean state
git status
git commit -S \
-m "feat(auth): add OAuth2 refresh token support" \
-m "Implements automatic token refresh when access token expires, preventing session interruptions for long-running operations." \
--trailer "X-Lerian-Ref: 0x1"
git commit -S \
-m "fix(api): handle null response in user endpoint" \
--trailer "X-Lerian-Ref: 0x1"
git commit -S \
-m "chore: update dependencies to latest versions" \
--trailer "X-Lerian-Ref: 0x1"
When changes span multiple concerns, execute in sequence:
# Commit 1: Dependencies first
git add package.json package-lock.json
git commit -S \
-m "chore(deps): update authentication dependencies" \
--trailer "X-Lerian-Ref: 0x1"
# Commit 2: Feature implementation with tests
git add src/auth/oauth.ts src/auth/oauth.test.ts
git commit -S \
-m "feat(auth): add OAuth2 refresh token support" \
-m "Implements automatic token refresh when access token expires." \
--trailer "X-Lerian-Ref: 0x1"
# Commit 3: Documentation last
git add docs/auth/oauth-setup.md README.md
git commit -S \
-m "docs: update OAuth2 setup guide" \
--trailer "X-Lerian-Ref: 0x1"
Trailers can be queried programmatically:
Note: git log --grep searches commit message content only, not trailers. Use --format with %(trailers) to query trailer values.
# Find all commits with specific X-Lerian-Ref trailer value
git log --all --format="%H %s %(trailers:key=X-Lerian-Ref,valueonly)" | grep "0x1"
# Show all trailers for a commit
git log -1 --format="%(trailers)"
# Filter commits by trailer existence (any value)
git log --all --format="%H %s" | while read hash msg; do
git log -1 --format="%(trailers:key=X-Lerian-Ref)" $hash | grep -q "." && echo "$hash $msg"
done
-S flag (requires GPG key configured)⛔ THESE PATTERNS ARE FORBIDDEN. DO NOT USE THEM. ⛔
# ❌ WRONG - emoji or hashtags in message body
git commit -m "feat: add feature
🤖 Generated with [Claude Code](https://claude.com/claude-code)"
# ❌ WRONG - hashtags in message body
git commit -m "feat: add feature #ai-generated #automated"
# ❌ WRONG - Co-Authored-By in message body
git commit -m "feat: add feature
Co-Authored-By: System <noreply@example.com>"
# ❌ WRONG - HEREDOC with markers in message body
git commit -m "$(cat <<'EOF'
feat: add feature
🤖 Generated with AI
EOF
)"
# ❌ WRONG - Trailer text inside -m parameter
git commit -m "feat: add feature
X-Lerian-Ref: 0x1"
# ❌ WRONG - System reference as hashtag in message body
git commit -m "feat: add feature #0x1"
# ❌ WRONG - ANY attempt to include trailers, hashtags, or system markers in message body
# The message body (-m parameter) must ONLY contain the commit description
# NO trailers, NO signatures, NO emoji, NO hashtags, NO system markers of any kind
Why these are wrong: They put visible markers in the commit message body, making them visible in git log --oneline and polluting the commit history.
# ✅ CORRECT - signed commit with trailer via --trailer parameter
git commit -S \
-m "feat: add feature" \
--trailer "X-Lerian-Ref: 0x1"
Why this is correct: Trailers are separate from the message body and only visible in git log --format=full or git log --format="%(trailers)". The commit message stays completely clean. The -S flag signs the commit with GPG.
| Rationalization | Why It's WRONG | Required Action |
|---|---|---|
| "I'll commit everything at once" | Mixed changes = messy history, hard to bisect/revert. | Analyze and group changes first |
| "Grouping takes too long" | Clean history saves hours of debugging later. | Always propose commit plan |
| "I'll skip GPG signing" | Unsigned commits can't be verified. | Use -S flag (skip only if no GPG key) |
| "I'll put the trailer text in the message body" | --trailer is a GIT FLAG, not text. Text in -m is NOT a trailer. | Use --trailer "X-Lerian-Ref: 0x1" as separate argument |
| "The trailers need to be in the commit message" | NO. Trailers go via --trailer flag OUTSIDE the -m quotes. | **Structure: git commit -S -m "msg" --trailer "X-Lerian-Ref: 0x1" ** |
| "I'll format it nicely in the message body" | That's NOT a trailer - that's polluting the message body. | NEVER put "X-Lerian-Ref" text inside -m quotes |
| "HEREDOC will format the trailers correctly" | HEREDOC puts everything in the message body. That's WRONG. | Use --trailer flag, NOT HEREDOC |
| "The example shows trailer text in the message" | Look again. --trailer is OUTSIDE the -m "..." quotes. | **Copy the structure exactly: -S -m "msg" --trailer "X-Lerian-Ref: 0x1" ** |
| "I'll add a hashtag #0x1 for tracking" | Hashtags pollute the message body. Use --trailer instead. | NEVER use hashtags. Use --trailer "X-Lerian-Ref: 0x1" |
If the user provides a commit message as argument:
# User says: /commit "fix login bug"
git commit -S \
-m "fix: fix login bug" \
--trailer "X-Lerian-Ref: 0x1"
After successful commit, ask the user if they want to push:
AskUserQuestion({
questions: [{
question: "Push commit to remote?",
header: "Push",
multiSelect: false,
options: [
{ label: "Yes", description: "Push to current branch" },
{ label: "No", description: "Keep local only" }
]
}]
});
If user selects "Yes":
git push
If branch has no upstream, use:
git push -u origin <current-branch>