Last active
January 17, 2025 07:43
-
-
Save mgsloan/f0d69f7071586f9d8dd34cc3c23f246b to your computer and use it in GitHub Desktop.
Open editor with llm proposed branch name + branch name from first line. Creates branch based on origin/main and cherry-picks commit onto it. Generated by Claude 3.5 sonnet
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
#!/usr/bin/env python3 | |
import subprocess | |
import sys | |
import tempfile | |
import os | |
import re | |
def run_git_command(command, error_message="Git command failed"): | |
""" | |
Run a git command and handle errors consistently. | |
Args: | |
command (list): Command and arguments to run | |
error_message (str): Message to include in error if command fails | |
Returns: | |
str: Command output | |
Raises: | |
subprocess.CalledProcessError: If command fails | |
""" | |
result = subprocess.run( | |
command, | |
capture_output=True, | |
text=True, | |
check=True | |
) | |
return result.stdout.strip() | |
def get_editor(): | |
"""Get the default editor from git config or environment""" | |
try: | |
return run_git_command(['git', 'config', 'core.editor']) | |
except subprocess.CalledProcessError: | |
return os.environ.get('EDITOR', 'vim') | |
def edit_branch_name(initial_content): | |
""" | |
Open the default editor with initial content and return the edited content | |
Args: | |
initial_content (str): Content to pre-fill in the editor | |
Returns: | |
str: The edited content after save | |
""" | |
editor = get_editor() | |
with tempfile.NamedTemporaryFile(mode='w+', suffix='.txt') as tf: | |
# Write initial content | |
tf.write(initial_content) | |
tf.flush() | |
# Open editor | |
subprocess.run([editor, tf.name], check=True) | |
# Read back the edited content | |
tf.seek(0) | |
return tf.read().strip() | |
def commit_message_to_branch_name(commit_message): | |
""" | |
Convert a commit message to a branch name by: | |
- Replacing any non-alphanumeric character with a hyphen | |
- Collapsing multiple hyphens into one | |
- Removing leading/trailing hyphens | |
Args: | |
commit_message (str): The commit message to convert | |
Returns: | |
str: The formatted branch name | |
Examples: | |
>>> commit_message_to_branch_name("edits_since_last") | |
'edits-since-last' | |
>>> commit_message_to_branch_name("Fix: User Auth!!") | |
'Fix-User-Auth' | |
""" | |
# Replace any non-alphanumeric character with a hyphen | |
name = re.sub(r'[^a-zA-Z0-9]+', '-', commit_message.lower()) | |
# Remove leading/trailing hyphens | |
return name.strip('-') | |
def check_and_create_branch(): | |
""" | |
Check repository status and create a new branch based on the commit message. | |
Returns: | |
tuple: (bool, str) - (success status, error message if any) | |
""" | |
try: | |
# Check git status | |
status_result = run_git_command( | |
['git', 'status', '--untracked-files=no', '--porcelain'] | |
) | |
if status_result: | |
return False, "Repository has uncommitted changes" | |
# Get the current HEAD commit SHA | |
current_sha = run_git_command(['git', 'rev-parse', 'HEAD']) | |
# Get all commits in main branch | |
main_commits = run_git_command(['git', 'rev-list', 'origin/main']).split('\n') | |
if current_sha in main_commits: | |
return False, "HEAD commit is already in main branch" | |
# Get the current commit message | |
commit_message = run_git_command(['git', 'log', '-1', '--pretty=%B']) | |
branch_name = commit_message_to_branch_name(commit_message) | |
# Generate branch name suggestion using llm | |
prompt = f"Generate a git branch name (using only alphanumeric characters and hyphens) for this commit message:\n\n{commit_message}\n\nOutput only the branch name, nothing else." | |
try: | |
suggested_name = subprocess.run( | |
['llm', 'prompt', prompt], | |
capture_output=True, | |
text=True, | |
check=True | |
).stdout.strip() | |
except subprocess.CalledProcessError as e: | |
return False, f"Failed to generate branch name using llm: {e.stderr}" | |
# Open editor for confirmation/modification | |
final_branch_name = edit_branch_name(suggested_name + "\n" + branch_name) | |
if not final_branch_name: | |
return False, "No branch name provided" | |
# Create and checkout new branch | |
run_git_command(['git', 'checkout', '-b', final_branch_name, 'origin/main']) | |
# Cherry-pick the stored commit | |
run_git_command(['git', 'cherry-pick', current_sha]) | |
return True, f"Successfully created and switched to branch '{final_branch_name}'" | |
except subprocess.CalledProcessError as e: | |
return False, f"Git command failed: {e.stderr}" | |
except Exception as e: | |
return False, f"Unexpected error: {str(e)}" | |
if __name__ == "__main__": | |
success, message = check_and_create_branch() | |
print(message) | |
sys.exit(0 if success else 1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment