Created
January 10, 2025 10:30
-
-
Save jamesparsloe/68c692d293c0950503e0e0158414f98f to your computer and use it in GitHub Desktop.
Silly little script I use to sync my Obsidian notes to GitHub with a descriptive commit message
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Commit with a message generated by Claude and push. | |
""" | |
import argparse | |
import os | |
import subprocess | |
import anthropic | |
def get_git_diff() -> str: | |
"""Get the git diff of staged changes.""" | |
try: | |
# Stage all changes first | |
subprocess.check_output(["git", "add", "."]) | |
# Get diff of staged changes | |
diff = subprocess.check_output(["git", "diff", "--cached"]).decode("utf-8") | |
return diff | |
except subprocess.CalledProcessError as e: | |
print(f"Error getting git diff: {e}") | |
return "" | |
def get_changed_files() -> list[str]: | |
"""Get list of changed files.""" | |
try: | |
# Stage all changes first | |
subprocess.check_output(["git", "add", "."]) | |
# Get staged files | |
staged = ( | |
subprocess.check_output(["git", "diff", "--cached", "--name-only"]) | |
.decode("utf-8") | |
.splitlines() | |
) | |
return list(set(staged)) | |
except subprocess.CalledProcessError as e: | |
print(f"Error getting changed files: {e}") | |
return [] | |
def generate_commit_message(diff: str, files: list[str]) -> str: | |
client = anthropic.Client() | |
# Truncate diff if too long | |
if len(diff) > 4000: | |
diff = diff[:4000] + "\n...[diff truncated]" | |
prompt = f"""Based on the following git diff and changed files, write a clear and descriptive commit message. | |
The subject line should be in present tense, concise (max 50 chars), and start with a verb. Lines in the body should be wrapped at 72 characters. For changes to markdown files, give the file name and then a short description of the changes. For code, just describe the behaviour change. | |
Changed files: | |
{', '.join(files)} | |
Diff: | |
{diff} | |
Generate only the commit message, nothing else.""" | |
try: | |
message = client.messages.create( | |
model="claude-3-5-sonnet-20241022", | |
max_tokens=1000, | |
temperature=0, | |
messages=[{"role": "user", "content": prompt}], | |
) | |
return message.content[0].text | |
except Exception as e: | |
print(f"Error generating commit message: {e}") | |
return "sync" # Fallback to original message | |
def git_commit_and_push(message: str) -> tuple[bool, str]: | |
"""Execute git commit and push commands.""" | |
try: | |
# Commit with generated message | |
subprocess.check_output(["git", "commit", "-m", message]) | |
# Push changes | |
subprocess.check_output(["git", "push"]) | |
return True, "Successfully committed and pushed changes" | |
except subprocess.CalledProcessError as e: | |
return False, f"Error in git operations: {e}" | |
def main(): | |
# Set up argument parser | |
parser = argparse.ArgumentParser( | |
description="Commit and push changes with AI-generated commit message" | |
) | |
parser.add_argument( | |
"-y", "--yes", action="store_true", help="Skip confirmation prompt" | |
) | |
args = parser.parse_args() | |
# Ensure ANTHROPIC_API_KEY is set | |
if not os.getenv("ANTHROPIC_API_KEY"): | |
print("Error: ANTHROPIC_API_KEY environment variable not set") | |
return | |
# Stage all changes first | |
try: | |
subprocess.check_output(["git", "add", "."]) | |
except subprocess.CalledProcessError as e: | |
print(f"Error staging changes: {e}") | |
return | |
# Get git changes | |
diff = get_git_diff() | |
files = get_changed_files() | |
if not diff and not files: | |
print("No changes detected") | |
return | |
# Generate commit message | |
commit_message = generate_commit_message(diff, files) | |
# Show the message to user and ask for confirmation if -y not specified | |
print(f"\nProposed commit message:\n{commit_message}") | |
if not args.yes: | |
confirm = input("\nProceed with this commit message? (Y/n): ").lower() | |
should_proceed = confirm in ["y", ""] | |
else: | |
should_proceed = True | |
if should_proceed: | |
success, message = git_commit_and_push(commit_message) | |
print(message) | |
else: | |
print("Commit aborted") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment