Automate git commit messages with a simple script (that r/git hated)
I like to automate as much work as possible. Here's a simple shell / bash script I'm using to save time by automatically creating git commit messages.
This script's output quality is good and I use it daily.
However, as people on r/git replied loudly, these generated git messages tend to describe what has been done, not why.
They didn't like my opinion, but I'm sticking to it.
My opinion:
-> Requiring a git commit message to provide the intention is an outdated practice.
It's better to write most git commit messages automatically. These commit messages essentially act as search targets.
Then, on the rare occasion I need to understand why someone made a specific historical commit, I can search for it and read the actual code. Reading the actual code is much more efficient way to understand intent than reading an attached git commit message.
3 steps to get this to work:
- Sign up for Openrouter (A unified API for LLM models)
- Add your
OPENROUTER_API_KEY
to your environment variables (or paste it directly into the script below, replacing$OPENROUTER_API_KEY
) - Add the below script to your ~/.bash_aliases or ~/.bashrc or equivalent
Refresh your terminal and you're good to go.
You can now generate AI commit messages automatically by typing gca
into your terminal.
Video guide for the below:
The code:
function gca() {local commit_message="$1"local push_remote="$2"# Check if Git is installedif ! command -v git &> /dev/null; thenecho "Git is not installed. Please install Git and try again."return 1fi# Check if jq is installedif ! command -v jq &> /dev/null; thenecho "jq is not installed. Please install jq and try again."return 1fi# Get the current Git repositorylocal repo=$(git rev-parse --show-toplevel 2> /dev/null)if [ -z "$repo" ]; thenecho "Not a Git repository. Please navigate to a Git repository and try again."return 1fi# Get the diff of the modified files (staged and unstaged)local diff=$(git diff HEAD)if [ -z "$diff" ]; thenecho "No changes to commit."return 0fi# Escape the diff for JSONlocal escaped_diff=$(echo "$diff" | jq -Rs .)if [ $? -ne 0 ]; thenecho "Failed to escape diff content"return 1filocal system_message="You are a master programmer who generates concise and informative Git commit messages. Explain why we have made the changes. If I remove a TODO, mention if the commit completed the task. Write a "local user_message=$(printf "Write a concise Git commit message for the following changes: Diff: %s. Only output the commit message, no other text. Use plain language to explain why the changes were made. You must exclude any formatting characters such as \`\`\` from your output. The first character of your output must be a capital letter. Write in highly succinct sentences. Never use the word 'Refactor'." "$escaped_diff")# Construct the JSON payload using jqlocal json_data=$(jq -n \--arg sm "$system_message" \--arg um "$user_message" \'{"model": "google/gemini-2.0-flash-lite-001","messages": [{"role": "system", "content": $sm},{"role": "user", "content": $um}]}')if [ $? -ne 0 ]; thenecho "Failed to construct JSON payload"return 1fi# Make the API calllocal api_response=$(curl --silent --show-error "https://openrouter.ai/api/v1/chat/completions" \-X POST \-H "Content-Type: application/json" \-H "Authorization: Bearer $OPENROUTER_API_KEY" \-d "$json_data")# Check if the API call was successfulif [ $? -ne 0 ]; thenecho "Error calling the API. Please check your internet connection and try again."return 1fiecho "API Response: $api_response"echo "\n\n"# Extract the commit message using jqlocal commit_message=$(jq -r ".choices[0].message.content" <<< "$api_response")# Check if the commit message is empty or nullif [ -z "$commit_message" ]; thenecho "Failed to generate a commit message."echo "API response: $api_response"return 1fi# Stage and commit all files (staged and unstaged)echo "Message: $commit_message"git add --allgit commit -am "$commit_message"# Push to remote if specified[ -n "$push_remote" ] && git push origin HEADreturn 0}alias gca=gca
The model is set to Gemini 2 Flash Lite. This model is:
- smart enough to create useful commit messages
- cheap and fast enough not to interrupt your git flow