{"slug": "i-built-a-git-commit-message-generator-with-ai-here-s-what-i-learned", "title": "I Built a Git Commit Message Generator with AI (Here's What I Learned)", "summary": "A developer built an AI-powered Git commit message generator using OpenAI's GPT-4o-mini model and a hybrid technique combining classification, validation, and fallback logic. The tool enforces Conventional Commits format by first classifying the diff type, then generating a one-line message, and validating it against a regex. After experimenting with simple prompts, few-shot learning, and local models like Llama 3, the developer settled on a three-part approach that includes truncating diffs to 250 lines and retrying up to three times before falling back to a simple fix message.", "body_md": "I used to be that developer who commits with messages like \"fixed bug\" or \"updated stuff\" – and I hated myself for it. Every pull request required a frantic rewrite of commit history. So I decided to automate the process with AI. My goal: generate meaningful, conventional commit messages directly from my diff, without (too much) embarrassment.\n\nSpoiler: it wasn't as simple as slapping a prompt in front of GPT. Here's the rollercoaster I went through, the dead ends, and the surprisingly elegant solution I eventually landed on.\n\nI was working on a medium-sized project with a dozen contributors. We enforced Conventional Commits (`feat:`\n\n, `fix:`\n\n, etc.), but I kept getting lazy. My brain simply didn't want to switch from code mode to prose mode after every diff. I needed a tool that could:\n\nI didn't want a full CI pipeline – I wanted a local script I could run before `git commit`\n\n.\n\n```\ngit diff --cached | curl -X POST https://api.openai.com/v1/chat/completions -H \"Authorization: Bearer $OPENAI_KEY\" -d '{\"model\":\"gpt-4\", \"messages\": [{\"role\":\"user\", \"content\": \"Generate a conventional commit message for this diff:\"}]}'\n```\n\nSimple, right? The output was… verbose. It treated the diff like a novel and wrote a paragraph. Worse, it ignored the `conventional`\n\nprefix and just wrote random sentences. I tried adding more instructions, but then it would hallucinate features that weren't there. Total garbage.\n\nI copied prompts from a known blog post – few-shot examples with proper Conventional Commits. Worked about 60% of the time. But for small changes (typo fix) it still generated `refactor:`\n\ninstead of `fix:`\n\n. And the token cost was high because I included 10 examples every time.\n\nI ran Ollama with Llama 3. It was slow (20 seconds per message) and often wrote commit messages in the style of a 19th century novel: \"In this commit, improvements were made to the authentication module…\" Absolutely unusable for a team.\n\nAfter two weeks of frustrating iterations, I settled on a hybrid technique that combines three parts:\n\nHere's the core of my implementation in Node.js (using the OpenAI SDK – but you can swap in any API that supports chat completions):\n\n``` python\nimport { execSync } from 'child_process';\nimport OpenAI from 'openai';\n\nconst openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });\n\nfunction getDiff() {\n  const output = execSync('git diff --cached --no-color', { encoding: 'utf-8' });\n  // Truncate to first 250 lines to save tokens and keep focus\n  return output.split('\\n').slice(0, 250).join('\\n');\n}\n\nfunction validateMessage(msg) {\n  // Conventional Commit regex\n  return /^(feat|fix|chore|docs|refactor|test|style|perf)\\(?.*?\\)?: .{1,72}$/.test(msg);\n}\n\nasync function generateCommitMessage(diff) {\n  const systemPrompt = `You are a git commit message generator.\nFirst, classify the diff into one of these types: feat, fix, chore, docs, refactor, test, style, perf.\nThen write a one-line commit message in the format: type(scope): description\nScope is optional. Keep the description under 50 characters.\nDo not add extra commentary.`;\n\n  for (let attempt = 0; attempt < 3; attempt++) {\n    const response = await openai.chat.completions.create({\n      model: 'gpt-4o-mini', // cheaper and faster\n      messages: [\n        { role: 'system', content: systemPrompt },\n        { role: 'user', content: `Diff:\\n\\`\\`\\` diff\\n${diff}\\n\\`\\`\\`` }\n      ],\n      max_tokens: 100,\n      temperature: 0.2,\n    });\n\n    const message = response.choices[0].message.content.trim();\n    if (validateMessage(message)) return message;\n    console.warn(`Attempt ${attempt + 1} failed validation: ${message}`);\n  }\n  // Fallback: just concatenate type and first line of diff summary\n  return `fix: ${diff.split('\\n')[1]?.trim()?.slice(0, 50) || 'minor change'}`;\n}\n\nasync function main() {\n  const diff = getDiff();\n  if (!diff) { console.log('No staged changes'); process.exit(0); }\n  const message = await generateCommitMessage(diff);\n  console.log('Suggested commit message:');\n  console.log(message);\n}\n\nmain().catch(console.error);\n```\n\nI run this as a Git alias:\n\n```\ngit config --global alias.aimsg '!node ~/scripts/ai-commit.mjs'\n```\n\nThen `git aismsg`\n\nprints a suggestion. I copy/paste it into my actual commit. (Automated commit hooks are dangerous – I trust my brain more than an LLM for the final decision.)\n\n`fix:`\n\nin the wrong place.I sacrificed offline capability for reliability. If you're in a remote cabin without internet, my script won't help. You could swap in a local model, but expect lower accuracy.\n\nI also deliberately didn't use the `--amend`\n\nor automatic commit. Why? Because AI makes mistakes. If I auto-commit a message like \"fix: removed console.log\" when I actually changed business logic, that's a lie in history. Manual review is cheap insurance.\n\n`fix: typo`\n\nyourself. The overhead of running the script isn't worth it.I'd explore a **two-model approach**: use a small, fast model (like GPT-4o-mini) for type classification and a cheaper summarization model (t5-small) for the description. That would cut costs further and allow more offline flexibility.\n\nAlso, I'd build a simple TUI (terminal UI) that shows the diff and the suggestion side-by-side, letting me edit before committing. But that's a weekend project I keep pushing off.\n\nThis approach works for me, but everyone's workflow is different. Have you tried automating commit messages? Did you end up with a similar pipeline, or do you swear by handwritten messages? I'd love to hear how you handle this (or why you think it's a bad idea in the first place).", "url": "https://wpnews.pro/news/i-built-a-git-commit-message-generator-with-ai-here-s-what-i-learned", "canonical_source": "https://dev.to/__c1b9e06dc90a7e0a676b/i-built-a-git-commit-message-generator-with-ai-heres-what-i-learned-2534", "published_at": "2026-06-24 02:04:09+00:00", "updated_at": "2026-06-24 02:43:43.054494+00:00", "lang": "en", "topics": ["artificial-intelligence", "large-language-models", "developer-tools"], "entities": ["OpenAI", "GPT-4o-mini", "Llama 3", "Ollama", "Conventional Commits"], "alternates": {"html": "https://wpnews.pro/news/i-built-a-git-commit-message-generator-with-ai-here-s-what-i-learned", "markdown": "https://wpnews.pro/news/i-built-a-git-commit-message-generator-with-ai-here-s-what-i-learned.md", "text": "https://wpnews.pro/news/i-built-a-git-commit-message-generator-with-ai-here-s-what-i-learned.txt", "jsonld": "https://wpnews.pro/news/i-built-a-git-commit-message-generator-with-ai-here-s-what-i-learned.jsonld"}}