|
@@ -0,0 +1,165 @@
|
|
|
+import subprocess
|
|
|
+import os
|
|
|
+import google.generativeai as genai
|
|
|
+
|
|
|
+
|
|
|
+def get_staged_diff():
|
|
|
+ """
|
|
|
+ Retrieves the diff of staged files using git.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ str: The diff of the staged files, or None on error.
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # Use subprocess.run for better control and error handling
|
|
|
+ process = subprocess.run(
|
|
|
+ ["git", "diff", "--staged"], # Corrected: --staged is the correct option
|
|
|
+ capture_output=True,
|
|
|
+ text=True, # Ensure output is returned as text
|
|
|
+ check=True, # Raise an exception for non-zero exit codes
|
|
|
+ )
|
|
|
+ return process.stdout
|
|
|
+ except subprocess.CalledProcessError as e:
|
|
|
+ print(f"Error getting staged diff: {e}")
|
|
|
+ print(f" stderr: {e.stderr}") # Print stderr for more details
|
|
|
+ return None
|
|
|
+ except FileNotFoundError:
|
|
|
+ print(
|
|
|
+ "Error: git command not found. Please ensure Git is installed and in your PATH."
|
|
|
+ )
|
|
|
+ return None
|
|
|
+ except Exception as e:
|
|
|
+ print(f"An unexpected error occurred: {e}")
|
|
|
+ return None
|
|
|
+
|
|
|
+
|
|
|
+def generate_commit_message(diff, gemini_api_key):
|
|
|
+ """
|
|
|
+ Generates a commit message using the Gemini API, given the diff.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ diff (str): The diff of the staged files.
|
|
|
+ gemini_api_key (str): Your Gemini API key.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ str: The generated commit message, or None on error.
|
|
|
+ """
|
|
|
+ if not diff:
|
|
|
+ print("Error: No diff provided to generate commit message.")
|
|
|
+ return None
|
|
|
+
|
|
|
+ genai.configure(api_key=gemini_api_key)
|
|
|
+ model = genai.GenerativeModel("gemini-2.0-flash")
|
|
|
+
|
|
|
+ prompt = f"""
|
|
|
+ You are a helpful assistant that generates Git commit messages.
|
|
|
+ Analyze the following diff of staged files and generate a commit message adhering to standard Git conventions:
|
|
|
+
|
|
|
+ 1. **Subject Line:** Write a concise, imperative subject line summarizing the change (max 50 characters). Start with a capital letter. Do not end with a period.
|
|
|
+ 2. **Blank Line:** Leave a single blank line between the subject and the body.
|
|
|
+ 3. **Body:** Write a detailed but precise body explaining the 'what' and 'why' of the changes. Wrap lines at 72 characters. Focus on the motivation for the change and contrast its implementation with the previous behavior.
|
|
|
+
|
|
|
+ Diff:
|
|
|
+ ```diff
|
|
|
+ {diff}
|
|
|
+ ```
|
|
|
+
|
|
|
+ Generate only the commit message (subject and body).
|
|
|
+ """
|
|
|
+
|
|
|
+ try:
|
|
|
+ response = model.generate_content(prompt)
|
|
|
+ # Check for a successful response.
|
|
|
+ if response and response.text:
|
|
|
+ return response.text.strip() # Remove leading/trailing whitespace
|
|
|
+ else:
|
|
|
+ print("Error: Gemini API returned an empty or invalid response.")
|
|
|
+ return None
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ print(f"Error generating commit message with Gemini: {e}")
|
|
|
+ return None
|
|
|
+
|
|
|
+
|
|
|
+def create_commit(message):
|
|
|
+ """
|
|
|
+ Creates a git commit with the given message.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ message (str): The commit message.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ bool: True if the commit was successful, False otherwise.
|
|
|
+ """
|
|
|
+ if not message:
|
|
|
+ print("Error: No commit message provided to create commit.")
|
|
|
+ return False
|
|
|
+
|
|
|
+ try:
|
|
|
+ process = subprocess.run(
|
|
|
+ ["git", "commit", "-m", message],
|
|
|
+ check=True, # Important: Raise exception on non-zero exit
|
|
|
+ capture_output=True, # capture the output
|
|
|
+ text=True,
|
|
|
+ )
|
|
|
+ print(process.stdout) # print the output
|
|
|
+ return True
|
|
|
+ except subprocess.CalledProcessError as e:
|
|
|
+ print(f"Error creating git commit: {e}")
|
|
|
+ print(e.stderr)
|
|
|
+ return False
|
|
|
+ except FileNotFoundError:
|
|
|
+ print("Error: git command not found. Is Git installed and in your PATH?")
|
|
|
+ return False
|
|
|
+ except Exception as e:
|
|
|
+ print(f"An unexpected error occurred: {e}")
|
|
|
+ return False
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ """
|
|
|
+ Main function to orchestrate the process of:
|
|
|
+ 1. Getting the staged diff.
|
|
|
+ 2. Generating a commit message using Gemini.
|
|
|
+ 3. Creating a git commit with the generated message.
|
|
|
+ """
|
|
|
+ gemini_api_key = os.environ.get("GEMINI_API_KEY")
|
|
|
+ if not gemini_api_key:
|
|
|
+ print(
|
|
|
+ "Error: GEMINI_API_KEY environment variable not set.\n"
|
|
|
+ " Please obtain an API key from Google Cloud and set the environment variable.\n"
|
|
|
+ " For example: export GEMINI_API_KEY='YOUR_API_KEY'"
|
|
|
+ )
|
|
|
+ return
|
|
|
+
|
|
|
+ diff = get_staged_diff()
|
|
|
+ if diff is None:
|
|
|
+ print("Aborting commit due to error getting diff.")
|
|
|
+ return # Exit the script
|
|
|
+
|
|
|
+ if not diff.strip(): # check if the diff is empty
|
|
|
+ print("Aborting: No changes staged to commit.")
|
|
|
+ return
|
|
|
+
|
|
|
+ message = generate_commit_message(diff, gemini_api_key)
|
|
|
+ if message is None:
|
|
|
+ print("Aborting commit due to error generating message.")
|
|
|
+ return # Exit if message generation failed
|
|
|
+
|
|
|
+ print(f"Generated commit message:\n{message}") # Print the message for review
|
|
|
+
|
|
|
+ # Prompt the user for confirmation before committing
|
|
|
+ user_input = input(
|
|
|
+ "Do you want to create the commit with this message? (y/n): "
|
|
|
+ ).lower()
|
|
|
+ if user_input == "y":
|
|
|
+ if create_commit(message):
|
|
|
+ print("Commit created successfully.")
|
|
|
+ else:
|
|
|
+ print("Commit failed.")
|
|
|
+ else:
|
|
|
+ print("Commit aborted by user.")
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|