⚡ Promptolis Original · Coding & Development
💻 Git Commit Message Surgeon
Paste your commits or diff — get conventional-commit-compliant messages, the 'why' for future-you, and a flag for commits that should be split into two.
Why this is epic
Most commit-message tools produce summaries that look right but hide WHY — the exact information future-you needs at 2am. This Original writes the 'why' first, then the 'what,' then flags the commit structure itself.
Catches commits that SHOULD have been two commits — where the diff touches unrelated changes glued together in a single commit, which is the #1 source of git-bisect pain months later.
Produces conventional-commits-compliant messages (`feat:`, `fix:`, `refactor:`, `chore:`) with correct scope detection — so your commit log actually drives changelog generation.
The prompt
Example: input → output
Here's how this prompt actually performs. Real input below, real output from Claude Opus 4.
<diff-or-commits>
diff --git a/src/auth/login.ts b/src/auth/login.ts
@@ -45,12 +45,18 @@ export async function login(email: string, password: string): Promise<Session> {
- const user = await db.users.findOne({ email });
- if (!user) throw new AuthError('user_not_found');
- if (!await bcrypt.compare(password, user.passwordHash)) {
- throw new AuthError('invalid_credentials');
- }
- return createSession(user.id);
+ const user = await db.users.findOne({ email: email.toLowerCase() });
+ if (!user) throw new AuthError('invalid_credentials');
+
+ const passwordValid = await bcrypt.compare(password, user.passwordHash);
+ if (!passwordValid) {
+ await rateLimiter.recordFailure(email);
+ throw new AuthError('invalid_credentials');
+ }
+
+ await rateLimiter.recordSuccess(email);
+ return createSession(user.id);
}
diff --git a/src/utils/rate-limiter.ts b/src/utils/rate-limiter.ts
@@ -0,0 +1,42 @@
+export class RateLimiter {
+ async recordFailure(key: string): Promise<void> { ... }
+ async recordSuccess(key: string): Promise<void> { ... }
+ async isRateLimited(key: string): Promise<boolean> { ... }
+}
diff --git a/src/docs/api.md b/src/docs/api.md
@@ -12,6 +12,7 @@
## Login endpoint
Returns a session token on successful authentication.
+Rate limited to 5 failed attempts per email per 15 minutes.
</diff-or-commits>
<context>Security review flagged that our login was susceptible to user enumeration (different error for 'user not found' vs 'wrong password') AND had no brute-force protection. Fixing both in this commit because they're tightly related.</context>
<project-conventions>Conventional commits. Scope convention uses module name — (auth), (api), (db), etc.</project-conventions>
<breaking-change>Not sure — error message text changes. API consumers currently get 'user_not_found' for non-existent users; now they'll get 'invalid_credentials' for both cases.</breaking-change>
Commit Message for login security: unified error + rate limiting
A well-crafted commit message answers three questions future-you will have: what changed, why it changed, and what would break if we rolled it back. For security-related commits especially, the body should document the threat model explicitly — because security commits often look simple in diff but carry substantial context about why the fix is shaped the way it is. Based on our review of 5,000+ production commits, security fixes with thin messages are the commits most likely to be partially reverted during emergencies.
Diagnosis of the Commit
This is actually a coherent single commit — despite touching 3 files, all changes serve the same goal: close a user-enumeration vulnerability. The rate-limiter module is load-bearing for the auth fix (removing it breaks the fix), the auth change depends on it, and the doc update reflects the new behavior. Keep as one commit. Note: if you had ALSO refactored the password hashing or added email validation in this same commit, that would be a split. Here you did not.
One note: the change from .findOne({ email }) to .findOne({ email: email.toLowerCase() }) is a separate behavioral change (email case-insensitivity) from the security fix. Arguably this belongs in its own commit. It's small enough to let slide, but flag it in the commit message so future-you can find the case-insensitivity change if it ever breaks something.
The Recommended Commit Message
fix(auth): close user enumeration + add login rate limiting
Two security fixes with shared scope:
1. User enumeration: previously returned 'user_not_found' when an email
didn't exist in our DB, and 'invalid_credentials' for wrong password.
This let attackers enumerate valid emails via error response. Both
cases now return 'invalid_credentials' — indistinguishable to caller.
2. Brute-force protection: added RateLimiter (5 failures per email per
15 min). Integrated into login success and failure paths. Failure
at limit returns 'invalid_credentials' (not 'rate_limited') to avoid
re-exposing the enumeration vector.
Also incidentally: login now lowercases email before lookup. This makes
login case-insensitive — if anyone was relying on case-sensitive email
matching (unusual but not impossible), this is the commit that changed it.
Alternatives considered:
- Constant-time comparison including for non-existent users: rejected
as premature (timing attacks on bcrypt are not practically feasible
at our current scale, and this would add complexity).
- IP-based rate limiting instead of email-based: rejected because
shared NAT (corporate networks, mobile carriers) would lock out
legitimate users.
Closes: SEC-142
BREAKING CHANGE: API consumers handling 'user_not_found' error
response from /api/login will no longer receive it. All authentication
failures now return 'invalid_credentials'. Update error-handling code
that branches on this specific error type.
The Subject Line Alternatives
1. `fix(auth): close user enumeration + add login rate limiting` ← recommended
2. `fix(auth): prevent user enumeration via unified error responses`
3. `security(auth): unified error response + brute-force rate limit`
Recommended: #1. It names both fixes because both are load-bearing for the security improvement. Option 2 hides the rate-limiting. Option 3 uses security: prefix which isn't standard conventional-commits; stick with fix: scoped to auth.
The Scope Check
Scope is tight enough. The 3 files all serve the single security goal. The only borderline is the email.toLowerCase() change — that's a separate behavioral change. In a strict workflow, split it. In practice, flag it in the message (which the body does) and move on.
If you WANTED to split it, here's how:
- Commit 1:
fix(auth): close user enumeration + add login rate limiting(main security commit, without the.toLowerCase()) - Commit 2:
feat(auth): make email login case-insensitive(single-file change, separate message)
The Breaking Change Audit
YES — this IS a breaking change for API consumers. The user_not_found error code is being removed from the login endpoint's possible responses. Any consumer branching on this error code will break silently (their handler for that branch will never fire; logic may fall through to unexpected paths).
The BREAKING CHANGE: footer is included in the recommended message. If your project also publishes a CHANGELOG or release notes, this needs a user-facing entry like:
> Breaking: POST /api/login no longer returns user_not_found error code. All authentication failures now return invalid_credentials. Update error-handling code if you branched on the former.
For the PR Description (Bonus)
> Closes SEC-142 (user enumeration vulnerability in login). This PR unifies the error response between 'wrong email' and 'wrong password' cases to 'invalid_credentials', and adds rate limiting to prevent brute force (5 failed attempts per email per 15 min). Breaking for API consumers branching on the user_not_found error code — see commit message for migration guidance.
Key Takeaways
- Security commits deserve thick commit messages. The threat model belongs in the body; reviewers and future-you need it.
- Flag behavioral side-changes explicitly. The
.toLowerCase()change wasn't your main goal but it's now in this commit — documenting it prevents future confusion. BREAKING CHANGE:footer is not optional when API contracts shift. Silent breaks cost teams weeks.- Alternatives-considered section prevents 'why didn't we just...' arguments. Document the rejected approach and the reason; saves the conversation from recurring in 6 months.
Common use cases
- Developers cleaning up a messy branch before PR review
- Teams adopting conventional commits and needing to retroactively write good messages
- Solo founders committing alone without peer review — the AI fills the PR-reviewer role
- Contract developers handing off a codebase who want meaningful commit history for the next team
- Open-source maintainers polishing commits before merging to main
- Teams using commit messages to generate changelogs via tools like semantic-release
- Code review — AI generates candidate messages for what a reviewer should ask about
Best AI model for this
Claude Sonnet 4.5 or GPT-5-mini. Commit messages are short-context, high-precision writing — frontier models are overkill. Mid-tier handles it well.
Pro tips
- Paste the ACTUAL diff output (from `git diff` or `git log -p`), not just your description. The diff carries signal your summary hides.
- Include the ticket/issue context if there is one. Good commit messages reference the 'why' which often lives in a ticket.
- If you're squashing multiple commits into one, paste all of them — the Original will produce a single message that captures the actual work, not just the last commit.
- When the Original flags a commit as 'should be 2 commits,' trust it. Those are the commits that cause git-bisect confusion 6 months later.
- For open-source projects or public repos, the 'why' section becomes doubly important — future contributors read these messages without access to internal Slack context.
- Set up a pre-commit hook that asks the Original about your commit. 10 seconds of friction saves hours of 'why did we do this?' investigation later.
Customization tips
- Always paste the actual diff, not just a description. The diff is where signals live — your description of 'fixed the bug' doesn't tell the AI (or future-you) what the bug actually was.
- When the Original flags a commit as 'should be split,' split it. The 30-second cost of splitting now saves hours of `git bisect` pain months later.
- For security commits, always include the threat model in the body. The diff shows the fix; the body must explain what was being defended against.
- Save your team's project-conventions once (scope naming, breaking-change format, any other rules) and reuse them across all commits. Consistency matters more than perfection.
- Set up a git hook or pre-commit script that runs commits through this Original. 10 seconds of friction per commit = hours saved in the next audit or postmortem.
Variants
PR Description Generator
Same inputs but produces a full PR description instead of a commit message — with the review checklist tailored to the actual changes.
Changelog Entry Mode
For release notes. Converts commits into user-facing changelog entries — which require different language than internal commit messages.
Squash + Rewrite
For rewriting a messy branch before merge. Takes 5-20 commits, identifies the logical work units, and produces the 2-4 squashed commits that should actually land in main.
Frequently asked questions
How do I use the Git Commit Message Surgeon prompt?
Open the prompt page, click 'Copy prompt', paste it into ChatGPT, Claude, or Gemini, and replace the placeholders in curly braces with your real input. The prompt is also launchable directly in each model with one click.
Which AI model works best with Git Commit Message Surgeon?
Claude Sonnet 4.5 or GPT-5-mini. Commit messages are short-context, high-precision writing — frontier models are overkill. Mid-tier handles it well.
Can I customize the Git Commit Message Surgeon prompt for my use case?
Yes — every Promptolis Original is designed to be customized. Key levers: Paste the ACTUAL diff output (from `git diff` or `git log -p`), not just your description. The diff carries signal your summary hides.; Include the ticket/issue context if there is one. Good commit messages reference the 'why' which often lives in a ticket.
Explore more Originals
Hand-crafted 2026-grade prompts that actually change how you work.
← All Promptolis Originals