GAI: Let AI Generate Your Git Commit Messages Locally

GAI: Let AI Generate Your Git Commit Messages Locally

# devchallenge# hf26challenge# ai# productivity
GAI: Let AI Generate Your Git Commit Messages LocallyHarshit Rai

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady. ...

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady.

What I Built

Writing commit messages is one of those small tasks developers do constantly but rarely enjoy. After debugging an issue or implementing a feature, describing the changes clearly can feel like unnecessary friction.

GAI is my attempt to automate that part of the development workflow without relying on external LLM APIs.

GAI analyzes staged Git changes, retrieves similar commits from historical commit data, and uses a locally running language model to generate a commit message that follows the repository's existing tagging conventions.

Instead of simply sending an entire Git diff to a model, GAI combines two approaches:

  • Retrieval-augmented generation (RAG): Finds historically similar changes using embeddings and FAISS.
  • Change-focused diff parsing: Extracts file paths, hunks, additions, deletions, and relevant context to make the changes easier for the generation model to understand.

I use GAI in my own development workflow to reduce the friction of writing commit messages.

The generated message is not committed automatically. I can accept it, edit it, regenerate it, or cancel the operation. Before committing, GAI also checks that the staged changes have not changed since the message was generated.

The idea is simple: spend less time writing commit messages and more time writing code.

Workflow

flowchart TD
    A([Run gai]) --> AZ[Invokes src/cli.py]
    AZ --> B[Validate Git repository]
    B --> C[Read staged Git diff]
    C --> D{Staged changes exist?}

    D -- No --> E[Display error and exit]
    D -- Yes --> F[Preserve raw diff snapshot]

    F --> G[Parse diff by file and hunk]
    G --> H["Extract added lines, <br>removed lines and context"]
    H --> I["Format structured diff <br>for generation"]

    F --> J[Generate embedding <br>from raw diff]
    J --> K[Search FAISS index]
    K --> L["Retrieve top-k <br>historical commits"]

    I --> M[Build generation prompt]
    L --> M

    M --> N["Call local Gemma model <br>through Ollama"]
    N --> O[Parse generated commit message]
    O --> P{Message valid?}

    P -- No --> Q["Handle invalid output <br>or generation error"]
    P -- Yes --> R["Display suggested <br>commit message"]

    R --> S{User choice}
    S -- Edit --> T[Enter replacement message]
    T --> U{Edited message valid?}
    U -- Yes --> R
    U -- No --> R

    S -- Regenerate --> M
    S -- Cancel --> V([Exit without committing])

    S -- Accept --> W[Read current staged diff again]
    W --> X{Raw diff unchanged?}

    X -- No --> Y[Abort commit and request rerun]
    X -- Yes --> Z[Execute git commit]
    Z --> AA{Commit successful?}
    AA -- Yes --> AB([Done])
    AA -- No --> AC[Display Git error]

Demo

GAI is a local command-line tool, so it runs directly in a developer's environment.
Here's how I use GAI on my own code changes.
1. Stage the changes

git add README.md
git diff --cached --stat
Enter fullscreen mode Exit fullscreen mode

Staged git diff from VS code UI

2. Generate a commit message

gai
Enter fullscreen mode Exit fullscreen mode

gai terminal execution -- formatted-diff

gai terminal execution -- generating the commit using gemma

GAI retrieves similar historical commits, generates a message, and lets me review it before committing.

3. Verify the commit (optional)

git log -1 --oneline
Enter fullscreen mode Exit fullscreen mode

Git log output showing the commit created using GAI's generated message

Code

GitHub repository: https://github.com/kira14102005/gai

GAI is implemented in Python and can also be invoked through its Python CLI.

python -m src.cli
Enter fullscreen mode Exit fullscreen mode

You can control the number of historical examples retrieved:

python -m src.cli --top-k 3
Enter fullscreen mode Exit fullscreen mode

The repository README contains the setup instructions, FAISS index preparation steps, and details of the current workflow.

How I Built It

The current implementation uses Ollama, FAISS, EmbeddingGemma, and Gemma4:e2b.

I started with historical commit data containing tags, messages, and diffs. The indexing pipeline generates embeddings for usable historical diffs using EmbeddingGemma and stores them in a FAISS IndexFlatL2 index. The corresponding commit metadata is stored separately so retrieved vectors can be mapped back to their original messages and tags.

When I run GAI, it reads the staged Git diff. The raw diff is used for retrieval, while a parsed and formatted representation gives the generation model a clearer view of the actual changes.

FAISS retrieves the most relevant historical commits, and their messages and tags are included alongside the formatted diff in the generation prompt. Gemma then generates a candidate message through Ollama.

The CLI handles the final review. I remain in control of whether a commit is created, and GAI verifies that the staged diff is unchanged before committing.

One challenge I'm interested in solving is how smaller local models handle large diffs. Simply increasing the context window isn't always the best solution; giving the model better-structured information may be more useful.

My planned next step is a parallel LangGraph workflow with two generation paths:

  1. A retrieval-based chain that uses historical commits, as the current implementation does.
  2. A diff-understanding chain that summarizes large changes by file and hunk before generating another candidate.

An evaluator would assess both candidates for correctness, relevance, specificity, and tag validity. A reducer would then select the stronger candidate or synthesize a better one when necessary.

These LangGraph chains, evaluator, and reducer are future improvements; they are not yet part of the current implementation.

Why Open Innovation Matters

I wanted GAI to generate commit messages that reflect how I actually write commits, rather than producing generic descriptions of code changes.

That's why I combined local inference with retrieval from historical commits.

  • Learning from existing conventions: FAISS retrieves similar historical diffs and their commit messages, giving Gemma concrete examples of the repository's tagging conventions and writing style.
  • Keeping code local: Git diffs can contain proprietary implementation details. Running both embedding and generation models through Ollama lets GAI process these changes locally without depending on an external LLM API.
  • Experimenting without being locked in: I can change the generation model, refine prompts, or experiment with retrieval strategies without redesigning the application around a particular hosted AI provider.

The interesting part isn't simply running an LLM locally. It's combining open models with a repository's own history to build a commit-message generator tailored to the way its developers work.

The Lazy Payoff

The goal is to remove a repetitive task from the development loop.

Instead of switching from coding to thinking about the wording of a commit message, I can stage my changes, run gai, review the suggestion, and continue.

The human review step is intentional. Automating the boring part shouldn't mean giving up control over the final commit.

What's Next?

I'm planning to experiment with parallel generation chains, hierarchical diff summarization, and an evaluator/reducer workflow to improve the quality of generated messages.

I'd love to hear how others approach commit-message generation, especially when working with smaller local models. What would you improve in this pipeline?

Prize Categories

  • Best Use of Gemma — GAI uses gemma4:e2b through Ollama to generate commit messages locally, combining open-weight AI with retrieval from historical Git commits.