
Harshit RaiThis 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.
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:
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.
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]
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
2. Generate a commit message
gai
GAI retrieves similar historical commits, generates a message, and lets me review it before committing.
3. Verify the commit (optional)
git log -1 --oneline
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
You can control the number of historical examples retrieved:
python -m src.cli --top-k 3
The repository README contains the setup instructions, FAISS index preparation steps, and details of the current workflow.
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:
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.
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.
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 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.
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?