How to Generate Git Commit Messages with a Local LLM

Writing good commit messages is one of those tasks developers do constantly but often rush. A local LLM can generate meaningful, conventional-format commit messages from a git diff in under 5 seconds — fast enough to become a natural part of the commit workflow. This guide covers building a git commit message generator backed by Ollama.

Basic Commit Message from Diff

#!/usr/bin/env python3
import subprocess
import ollama

def get_staged_diff() -> str:
    stat = subprocess.run(['git','diff','--cached','--stat'],
        capture_output=True, text=True).stdout
    diff = subprocess.run(['git','diff','--cached'],
        capture_output=True, text=True).stdout
    if len(diff) > 8000:
        diff = diff[:8000] + '\n... (truncated)'
    return f'{stat}\n\n{diff}'

def generate_commit_message(diff: str) -> str:
    response = ollama.chat(
        model='llama3.2',
        messages=[{
            'role': 'user',
            'content': f"""Generate a git commit message for this diff.

Use Conventional Commits format: <type>(<scope>): <description>
Types: feat, fix, docs, style, refactor, test, chore, perf
Subject line under 72 characters.
Return only the commit message.

Diff:
{diff}"""
        }],
        options={'temperature': 0.2}
    )
    return response['message']['content'].strip()

if __name__ == '__main__':
    diff = get_staged_diff()
    if not diff.strip():
        print('No staged changes. Run: git add <files>')
        exit(1)
    print(generate_commit_message(diff))

Git Hook Integration

#!/bin/bash
# .git/hooks/prepare-commit-msg
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2

# Only run for regular commits
[ -n "$COMMIT_SOURCE" ] && exit 0
git diff --cached --quiet && exit 0

SUGGESTION=$(python3 ~/.local/bin/commit_msg.py 2>/dev/null)
if [ -n "$SUGGESTION" ]; then
    CURRENT=$(cat "$COMMIT_MSG_FILE")
    printf '# AI suggestion (edit as needed):\n%s\n\n%s' \
        "$SUGGESTION" "$CURRENT" > "$COMMIT_MSG_FILE"
fi
chmod +x .git/hooks/prepare-commit-msg

Interactive CLI

#!/usr/bin/env python3
import subprocess, sys, tempfile, os
import ollama

def main():
    diff = get_staged_diff()
    if not diff.strip():
        print('No staged changes.')
        sys.exit(1)

    print('Generating...', end='', flush=True)
    msg = generate_commit_message(diff)
    print(' done\n')
    print(f'Suggested:\n  {msg}\n')

    choice = input('[a]ccept / [e]dit / [r]egenerate / [q]uit: ').lower()
    if choice == 'a':
        subprocess.run(['git', 'commit', '-m', msg])
    elif choice == 'e':
        with tempfile.NamedTemporaryFile(suffix='.txt', mode='w', delete=False) as f:
            f.write(msg); fname = f.name
        editor = os.getenv('EDITOR', 'nano')
        os.system(f'{editor} {fname}')
        with open(fname) as f:
            edited = f.read().strip()
        os.unlink(fname)
        if edited:
            subprocess.run(['git', 'commit', '-m', edited])
    elif choice == 'r':
        subprocess.run(['git', 'commit', '-m', generate_commit_message(diff)])
    else:
        print('Aborted.')

if __name__ == '__main__':
    main()

Why AI-Generated Commit Messages

Good commit messages have three qualities: they explain what changed, why it changed, and the scope of the change. Writing a message with all three qualities for every commit takes cognitive effort that many developers skip when they are deep in a flow state or under time pressure. The result is commit histories full of “fix”, “update”, “WIP”, and “stuff” that are useless for future debugging and archaeology. A local LLM generates a first-draft message that captures the what and scope directly from the diff — the developer’s job becomes reviewing and approving rather than writing from scratch, which is both faster and produces better outcomes.

The git hook approach is particularly valuable because it integrates without any workflow change — you stage changes and type git commit as usual, and the AI suggestion appears in your editor ready to accept, edit, or replace. Zero extra steps for a meaningfully better commit history over time.

Improving the Prompt

The default prompt in this article produces good general-purpose conventional commits. For specific codebases or team conventions, customise the prompt:

SYSTEM_PROMPT = """You are a senior developer writing git commit messages.
Our conventions:
- Use Conventional Commits (feat/fix/docs/refactor/test/chore)
- Reference Jira tickets if the diff mentions them
- For database changes, always mention the table affected
- Keep subject under 72 chars
- Add body for breaking changes only"""

def generate_commit_message(diff: str) -> str:
    response = ollama.chat(
        model='llama3.2',
        messages=[
            {'role': 'system', 'content': SYSTEM_PROMPT},
            {'role': 'user', 'content': f'Write a commit message for:\n\n{diff}'}
        ],
        options={'temperature': 0.2}
    )
    return response['message']['content'].strip()

Handling Large Diffs

Large commits with many files produce very long diffs that exceed practical context window limits. The truncation approach in the basic script (cut at 8000 characters) works but loses information for complex commits. A better approach for large diffs: use git diff --cached --stat (file-level summary only) as the primary context, and include full diff only for the most-changed files:

def get_smart_diff(max_chars: int = 6000) -> str:
    stat = subprocess.run(['git','diff','--cached','--stat'],
        capture_output=True,text=True).stdout
    files_changed = subprocess.run(['git','diff','--cached','--name-only'],
        capture_output=True,text=True).stdout.strip().split('\n')

    # Get full diff for first 3 files only
    diffs = []
    for f in files_changed[:3]:
        d = subprocess.run(['git','diff','--cached','--',f],
            capture_output=True,text=True).stdout
        diffs.append(d[:2000])

    full = '\n'.join(diffs)
    if len(full) > max_chars:
        full = full[:max_chars] + '\n...'
    return f'File summary:\n{stat}\n\nDiff excerpts:\n{full}'

Model Choice

For commit message generation, model size matters less than for complex reasoning tasks. Llama 3.2 3B generates adequate conventional commits quickly (under 2 seconds on most hardware) — fast enough that the hook does not noticeably slow down the commit flow. Llama 3.2 7B produces better output for complex multi-file refactors where the scope and type need more inference to determine correctly. Qwen2.5-Coder 7B is worth trying — its code-specific training produces more accurate type classification (reliably choosing refactor vs feat for ambiguous changes) compared to general-purpose models of the same size. Test with 20–30 real diffs from your codebase to evaluate quality before choosing a model for daily use.

Getting Started

Install the script to ~/.local/bin/commit_msg.py, make it executable, and test with git add README.md && python3 ~/.local/bin/commit_msg.py. Install the git hook by copying the prepare-commit-msg script to .git/hooks/ and making it executable. Commit as usual — the AI suggestion appears as a comment at the top of the commit message editor. Accept it by removing the comment marker and saving, or edit it freely. The workflow friction is near zero and the commit history quality improvement is immediate and persistent.

Integrating with Your Editor

Beyond the git hook, you can integrate commit message generation directly into your editor’s git interface. VS Code’s git panel, for example, can be extended with a custom command that runs the generator script and pastes the result into the commit message box. In Neovim, a simple Lua function can call the script and insert the output into the current buffer. The interactive CLI approach from this article is editor-agnostic and works from any terminal, making it the most portable starting point — add editor-specific integrations only if the terminal workflow feels too slow for your personal rhythm.

Generating Changelogs from Commit History

Once your commit messages follow conventional commits format consistently (helped by the AI generator), you can auto-generate changelogs and release notes from the git history:

import subprocess, ollama

def generate_changelog(since_tag: str = 'HEAD~20') -> str:
    log = subprocess.run(
        ['git', 'log', f'{since_tag}..HEAD', '--oneline', '--no-merges'],
        capture_output=True, text=True
    ).stdout

    if not log.strip():
        return 'No commits found.'

    response = ollama.chat(
        model='llama3.2',
        messages=[{
            'role': 'user',
            'content': f'"""Generate a user-facing changelog from these git commits.
Group by: Features, Bug Fixes, Other Changes.
Use plain language (not git jargon).

Commits:
{log}"""
'
        }],
        options={'temperature': 0.3}
    )
    return response['message']['content']

# Generate changelog since last tag
print(generate_changelog('v1.2.0'))

The Broader Developer Productivity Use Case

Commit message generation is one of several developer productivity tasks where local LLMs add daily value with minimal setup. The same pattern — read developer context (diff, code, error), generate a first draft, let the developer review and approve — applies to PR descriptions, code review comments, docstring generation, and test case naming. Each of these individually saves a few minutes per day; together they compound into a meaningful reduction in the cognitive overhead of the mechanics of software development, leaving more mental capacity for the actual hard problems. The local LLM approach is particularly well-suited to this category of tasks because speed (sub-5-second responses) and privacy (no code sent externally) matter more than absolute quality — a good-enough draft that appears instantly and stays on your machine is worth more in practice than a perfect draft that takes 30 seconds and sends your code to a cloud service.

Teams and Consistency

The biggest organisational benefit of AI commit message generation is not individual time savings but team consistency. When every developer uses the same generator with the same conventional commits prompt, the project’s commit history becomes uniform — consistent type prefixes, consistent scope conventions, consistent description style. This uniformity makes automated changelog generation reliable, makes git log --oneline readable at a glance, and makes searching the history for specific change types (git log --grep="^fix") actually useful. The investment in setting up the generator once (installing the script and hook) pays dividends across every future commit from every developer who adopts it, compounding in value as the project’s git history grows.

Privacy and Code Security

Sending git diffs to cloud AI APIs creates a potential code exfiltration path — your proprietary code, internal API structures, database schemas, and business logic all appear in diffs. Many organisations have policies against sending source code to external services, making cloud-based AI code tools non-compliant by default. Local LLMs via Ollama process the diff entirely on your machine — no code leaves your development environment. This makes Ollama-based developer tools the safe-by-default option for teams working on proprietary or sensitive codebases, and removes the approval processes and policy discussions that cloud AI code tools often require before adoption. The same privacy argument applies to all the developer productivity patterns in this article series: local inference lets you use AI assistance for code tasks without the compliance overhead of external data transmission.

Getting the Most Value

Three practices maximise the value of AI-generated commit messages over time. First, always review the suggestion rather than accepting blindly — the AI gets the type and scope right most of the time but occasionally misclassifies a refactor as a feat or vice versa, and a quick review catches this. Second, customise the system prompt for your team’s conventions — adding project-specific scope names, Jira ticket patterns, or changelog formatting rules produces output that fits your workflow rather than generic conventional commits. Third, share the hook with your team — the consistency benefit scales with adoption, and a one-minute setup per developer produces a significantly more useful git history that everyone benefits from. The commit message generator is one of the local AI developer tools with the best return on investment: minimal setup, daily value, and compounding benefits as the project history grows.

Beyond Commits: PR Descriptions and Review Comments

The same diff-reading pattern extends to pull request descriptions and code review comments. For PR descriptions, gather all commits since the branch diverged (git log main..HEAD --oneline) and the full diff (git diff main...HEAD), then prompt for a structured PR description covering what changed, why, and how to test it. For code review, pass a specific file diff and ask the model to identify potential issues, suggest improvements, and flag any obvious bugs. Both tasks follow the same privacy-safe, local-processing model — your proprietary code never leaves your machine, and the suggestions arrive in seconds rather than waiting for a cloud API. Used together with the commit message generator, these patterns turn Ollama into a personal coding assistant embedded in your git workflow, augmenting the quality of every commit, every PR, and every review without any cloud dependency or per-request cost.

A Note on Quality Expectations

AI-generated commit messages are consistently good for straightforward changes — adding a feature, fixing a bug, updating documentation — and occasionally imprecise for complex refactors that span many files with subtle interdependencies. The model sees the diff but not the reasoning behind the change, which means it sometimes picks the wrong Conventional Commits type or misidentifies the scope for deeply architectural changes. This is not a failure case — it is the expected boundary of what the tool handles well. The interactive CLI’s edit and regenerate options exist precisely for these cases. The right mental model is a fast first draft that is right 80% of the time and needs editing 20% of the time — still faster and better than starting from a blank commit message box every single time.

Installing and Sharing the Tool

Install the commit_msg.py script to ~/.local/bin/commit_msg.py and add the git hook to each repository you want to use it in. For team-wide adoption, commit the hook script to a scripts/hooks/ directory in your repository and add a setup step to your onboarding documentation: cp scripts/hooks/prepare-commit-msg .git/hooks/ && chmod +x .git/hooks/prepare-commit-msg. Git hooks are not committed with the repository by default (they live in .git/hooks/), so each developer needs to run the setup step once. Tools like pre-commit or Husky can automate hook installation if your team already uses them. The one-time setup cost per developer is under two minutes, and the ongoing benefit is a consistently higher-quality commit history for the life of the project — and since the hook runs silently unless there are staged changes, it adds zero overhead on the days you do not commit — and as your team grows the consistency benefits compound, making the project history increasingly useful as a reference and audit trail.

The commit message generator is one of those tools that feels like a small convenience when you first install it and becomes quietly indispensable after a week. Once your muscle memory shifts from typing a vague message to reviewing an AI-generated one, the idea of going back to writing from scratch on every commit feels unnecessary — the same way autocomplete felt optional until it was not. The setup cost is under five minutes; the benefit compounds for every commit you make from that point forward.

With the git hook in place and the script installed, your commit workflow becomes: stage changes, type git commit, review the AI suggestion in your editor, save. That four-step process — replacing the previous two-step process of staging and typing a message from scratch — produces consistently better messages with negligible overhead, and scales the investment in good commit hygiene across your entire team without requiring discipline from any individual developer.

Leave a Comment