Skip to content

Instantly share code, notes, and snippets.

@Hammer2900
Forked from tailot/IAcommit.py
Last active May 28, 2025 07:41
Show Gist options
  • Save Hammer2900/648aed896e798e308882d9cda88d598d to your computer and use it in GitHub Desktop.
Save Hammer2900/648aed896e798e308882d9cda88d598d to your computer and use it in GitHub Desktop.
This Python script analyzes the commit history of a specified Git repository using a locally running Ollama instance and a user-selected AI model. It takes the repository path, an Ollama model name, and an optional commit limit as command-line arguments. For each selected commit, the script extracts the changes (diff) and prompts the AI to gener…
import subprocess
import ollama
import os
_OLLAMA_SYSTEM_PROMPT = 'You are an assistant that analyzes Git commits and their diffs to generate improved commit messages according to the Conventional Commits standard.'
_COMMIT_ANALYSIS_PROMPT_TEMPLATE = """Analyze the following 'git show' output, which includes the original commit message and the diff of the changes.
Your task is to generate a new, concise, and well-written commit message following Conventional Commits standards,
based ONLY ON THE CHANGES (the diff). Ignore the original commit message present in the input.
Provide only the commit message itself, without any introductory or concluding phrases.
The message should start with a type (e.g., feat, fix, docs, style, refactor, test, chore),
optionally followed by a scope in parentheses, a colon and a space, and then the description.
Example: feat(api): add user endpoint
'git show' output:
{commit_content}
New suggested commit message (based only on the diff and following Conventional Commits):"""
def analyze_git_repository_with_ai(repository_path: str, limit: int = None, model_name: str = 'llama3', specific_hash: str = None):
try:
ollama.list()
print('Ollama is accessible.')
except Exception as e:
print(f'Error: Could not communicate with Ollama. Ensure Ollama is running. Details: {e}')
return
try:
subprocess.run(['git', '--version'], check=True, capture_output=True, text=True)
except (subprocess.CalledProcessError, FileNotFoundError):
print('Error: Git command-line tool not found or not functional. Ensure Git is installed and in your PATH.')
return
abs_repo_path = os.path.abspath(repository_path)
if not os.path.isdir(abs_repo_path) or not os.path.isdir(os.path.join(abs_repo_path, '.git')):
print(f"Error: The path '{abs_repo_path}' is not a valid Git repository.")
return
original_cwd = os.getcwd()
try:
os.chdir(abs_repo_path)
print(f'Changed working directory to: {abs_repo_path}')
print(f'Using Ollama model: {model_name}')
commit_hashes_to_analyze = []
if specific_hash:
try:
subprocess.run(
['git', 'cat-file', '-e', specific_hash],
check=True,
capture_output=True,
text=True,
encoding='utf-8',
)
commit_hashes_to_analyze = [specific_hash]
print(f'Analyzing specific commit: {specific_hash}')
except subprocess.CalledProcessError:
print(
f"Error: Commit hash '{specific_hash}' does not exist or is not a valid commit object in this repository."
)
return
else:
log_command = ['git', 'log', '--pretty=format:%H']
if limit is not None and isinstance(limit, int) and limit > 0:
log_command.extend(['-n', str(limit)])
print(f'Analyzing up to the last {limit} commits.')
else:
print("Analyzing all available commits (or Git's default log limit if no limit specified).")
try:
result = subprocess.run(log_command, capture_output=True, text=True, check=True, encoding='utf-8')
commit_hashes_to_analyze = [line for line in result.stdout.strip().split('\n') if line]
if commit_hashes_to_analyze: # Process oldest of the selection first
commit_hashes_to_analyze.reverse()
except subprocess.CalledProcessError as e:
print(f"Error getting commit hashes: {e.stderr.strip() if e.stderr else 'Unknown Git error'}")
return
if not commit_hashes_to_analyze:
print('No commits found to analyze with the current criteria.')
return
total_commits = len(commit_hashes_to_analyze)
print(f'Found {total_commits} commit(s) to analyze.')
for index, commit_hash in enumerate(commit_hashes_to_analyze):
print(f'\n--- Analyzing Commit {index + 1}/{total_commits}: {commit_hash} ---')
commit_content = None
try:
diff_result = subprocess.run(
['git', 'show', commit_hash, '--patch-with-raw'],
capture_output=True,
text=True,
check=True,
encoding='utf-8',
errors='ignore',
)
commit_content = diff_result.stdout
except subprocess.CalledProcessError as e:
print(
f"Error getting diff for commit {commit_hash}: {e.stderr.strip() if e.stderr else 'Unknown Git error'}"
)
continue
if not commit_content:
print(f'Could not retrieve content/diff for commit {commit_hash}. Skipping.')
continue
prompt = _COMMIT_ANALYSIS_PROMPT_TEMPLATE.format(commit_content=commit_content)
print(f'Sending diff for commit {commit_hash} to Ollama (model: {model_name})...')
try:
response = ollama.chat(
model=model_name,
messages=[
{'role': 'system', 'content': _OLLAMA_SYSTEM_PROMPT},
{'role': 'user', 'content': prompt},
],
options={'temperature': 0.5},
)
ai_suggestion = response['message']['content'].strip()
print('\n---------------------------------- SUGGESTED MESSAGE ----------------------------------')
print(ai_suggestion)
print('-----------------------------------------------------------------------------------------')
except Exception as e:
print(f'Error during AI analysis for {commit_hash} with Ollama ({model_name}): {e}')
except OSError as e:
print(f"Error related to file system operations for '{abs_repo_path}': {e}")
finally:
if 'original_cwd' in locals() and os.getcwd() != original_cwd:
try:
os.chdir(original_cwd)
print(f'\nRestored working directory to: {original_cwd}')
except OSError as e:
print(f"Error restoring original working directory '{original_cwd}': {e}")
if __name__ == '__main__':
repo_path_to_analyze = '/dev/shm/Nim-roguelike'
commits_limit = 2
target_model = 'qwen3:14b'
target_specific_hash = None
print('Starting Git commit analysis script...')
analyze_git_repository_with_ai(
repository_path=repo_path_to_analyze,
limit=commits_limit,
model_name=target_model,
specific_hash=target_specific_hash,
)
print('Script finished.')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment