Skip to content

Instantly share code, notes, and snippets.

@btbytes
Last active December 12, 2025 23:38
Show Gist options
  • Select an option

  • Save btbytes/3ee4f15fb10d130dbb01bb0c8722d68e to your computer and use it in GitHub Desktop.

Select an option

Save btbytes/3ee4f15fb10d130dbb01bb0c8722d68e to your computer and use it in GitHub Desktop.
Use OpenAI to generate git commits

A python script that when run will look at the files committed to git staging and uses a openai api key from the zsh environment and write a commit summary, and use that to commit the changes.

Uses uv to make this a standalone script.

Written using Claude.

I use this to automatically generate git commits to my blog source code.

#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "openai",
# ]
# ///
"""
AI-powered Git Commit Script
Generates commit messages using OpenAI API based on staged changes
"""
import os
import subprocess
import sys
from openai import OpenAI
def run_git_command(args):
"""Run a git command and return the output."""
try:
result = subprocess.run(
["git"] + args,
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except subprocess.CalledProcessError as e:
print(f"Error running git command: {e.stderr}")
sys.exit(1)
def get_staged_diff():
"""Get the diff of staged changes."""
diff = run_git_command(["diff", "--cached"])
if not diff:
print("No staged changes found. Please stage your changes first with 'git add'.")
sys.exit(1)
return diff
def get_staged_files():
"""Get list of staged files."""
files = run_git_command(["diff", "--cached", "--name-only"])
return files.split('\n') if files else []
def generate_commit_message(diff, files):
"""Generate commit message using OpenAI API."""
api_key = os.environ.get("OPENAI_API_KEY")
if not api_key:
print("Error: OPENAI_API_KEY not found in environment variables.")
print("Please set it in your ~/.zshrc or export it in your current session:")
print(" export OPENAI_API_KEY='your-api-key-here'")
sys.exit(1)
client = OpenAI(api_key=api_key)
# Prepare the prompt
files_list = "\n".join(files)
prompt = f"""Based on the following git diff, generate a concise and descriptive commit message.
The commit message should:
- Start with a conventional commit type (feat, fix, docs, style, refactor, test, chore)
- Be clear and specific about what changed
- Be under 72 characters for the first line
- Optionally include a blank line and more details if the change is complex
Files changed:
{files_list}
Git diff:
{diff[:4000]}
Generate only the commit message, nothing else."""
try:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "You are a helpful assistant that writes clear, concise git commit messages following conventional commit standards."},
{"role": "user", "content": prompt}
],
temperature=0.7,
max_tokens=200
)
commit_message = response.choices[0].message.content.strip()
# Remove surrounding quotes if present
commit_message = commit_message.strip('"\'')
return commit_message
except Exception as e:
print(f"Error calling OpenAI API: {e}")
sys.exit(1)
def commit_changes(message):
"""Commit the staged changes with the generated message."""
try:
subprocess.run(
["git", "commit", "-m", message],
check=True
)
print("\n✓ Changes committed successfully!")
print(f"\nCommit message:\n{message}")
except subprocess.CalledProcessError as e:
print(f"Error committing changes: {e}")
sys.exit(1)
def main():
print("Checking staged changes...")
# Get staged files and diff
staged_files = get_staged_files()
staged_diff = get_staged_diff()
print(f"\nStaged files ({len(staged_files)}):")
for file in staged_files:
print(f" - {file}")
print("\nGenerating commit message using OpenAI...")
commit_message = generate_commit_message(staged_diff, staged_files)
print(f"\nProposed commit message:")
print("-" * 60)
print(commit_message)
print("-" * 60)
# Ask for confirmation
response = input("\nProceed with this commit message? (y/n): ").lower().strip()
if response == 'y':
commit_changes(commit_message)
else:
print("\nCommit cancelled.")
sys.exit(0)
if __name__ == "__main__":
main()

Comments are disabled for this gist.