Last active
May 7, 2025 03:08
-
-
Save UncleBill/2ae97ee0ccebe868ca397a3723a1e930 to your computer and use it in GitHub Desktop.
Function to auto-generate commit messages with issue tag
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
#!/bin/bash | |
# Function to auto-generate commit messages with issue tag | |
autocommit() { | |
local ISSUE_TAG="" # Initialize variable | |
if [ -n "$1" ]; then | |
# Tag was provided as an argument | |
ISSUE_TAG="$1" | |
echo "Info: Using provided issue tag '$ISSUE_TAG'." | |
fi | |
local current_branch=$(git branch --show-current) | |
# Get the git diff, staged only | |
local GIT_DIFF=$(git diff --staged --unified=6) | |
# Check if there are staged changes | |
if [ -z "$GIT_DIFF" ]; then | |
echo "Error: No staged changes found. Please stage your changes with 'git add' first." | |
return 1 | |
fi | |
# The prompt template for generating commit messages | |
# Added a hint to keep the type lowercase as requested by the format | |
local PROMPT="You are an expert at writing Git commits. Based on the following git diff, generate a concise and descriptive commit message. | |
- Start the message with a Conventional Commits type followed by the issue tag. | |
- The format should be: '<type>(<optional scope>): <issue-tag> <description>' | |
- Focus on WHAT changes were made and WHY, not HOW they were implemented. | |
- Keep the summary line under 72 characters if possible, but prioritize clarity. | |
- Use lowercase issue tag | |
- Guess issue tag from current git branch if no one provided. | |
- Issue tag can be omitted if you can't determine it finally. | |
- Keep the body short and concise (omit it entirely if not useful) | |
- DO NOT include code block indicators | |
Current git branch: ${current_branch} | |
Issue tag: ${ISSUE_TAG} | |
Git diff (with 6 context lines): | |
${GIT_DIFF}" | |
echo "Generating..." | |
# Use smartcat to get AI-generated commit message | |
local COMMIT_MSG=$(echo "$PROMPT" | sc commit) | |
# Check if smartcat produced output | |
if [ -z "$COMMIT_MSG" ]; then | |
echo "Error: smartcat failed to generate a commit message." | |
return 1 | |
fi | |
# Remove any <thought></thought> blocks from the output (including multi-line blocks) | |
if command -v perl &> /dev/null; then | |
# Use perl for multi-line pattern matching (more reliable) | |
COMMIT_MSG=$(echo "$COMMIT_MSG" | perl -0pe 's/<thought>.*?<\/thought>//gs') | |
echo "Info: Removed <thought></thought> blocks from commit message." | |
echo $COMMIT_MSG | |
fi | |
# Show the generated commit message to the user and ask for confirmation | |
echo -e "\nGenerated commit message:\n${COMMIT_MSG}\n" | |
echo -n "Do you want to use this commit message? \n(Enter/y: accept, Esc: cancel, r: regenerate, n: cancel, e: edit): " | |
# --- Single-Key Input Handling using stty and dd --- | |
local confirmation_char | |
local confirmation_result="" # Will store 'y', 'n', or 'e' | |
# Check if running in a terminal | |
if [ -t 0 ]; then | |
# Save current terminal settings | |
local old_settings=$(stty -g) | |
# Ensure settings are restored on exit, interrupt, or terminate | |
trap 'stty "$old_settings"; echo "";' EXIT INT TERM | |
# Set terminal to not echo input (-echo), non-canonical mode (-icanon) | |
# and return after reading 1 character (min 1) | |
stty -echo -icanon min 1 | |
# Read a single byte from standard input using dd | |
# Redirect stderr to suppress dd's output like "1+0 records in" | |
confirmation_char=$(dd bs=1 count=1 2>/dev/null) | |
# Restore terminal settings immediately after reading the character | |
stty "$old_settings" | |
# Remove the trap now that settings are restored | |
trap - EXIT INT TERM | |
else | |
# Not running in a terminal, this input method won't work well. | |
# Default to cancel or handle as you see fit for non-interactive | |
echo "Warning: Not running in a terminal. Skipping interactive confirmation." >&2 | |
confirmation_result="n" # Default to cancel in non-interactive | |
confirmation_char="" # Ensure case doesn't match unexpected input | |
fi | |
# Process the input character to determine the result ('y', 'n', or 'e') | |
if [ -n "$confirmation_char" ]; then # Only process if a character was read | |
case "$confirmation_char" in | |
$'\r'|$'\n'|"") # Enter key (CR or LF) or sometimes empty string | |
confirmation_result="y" | |
;; | |
$'\033') # Escape key | |
confirmation_result="n" | |
;; | |
[Yy]) # 'y' key | |
confirmation_result="y" | |
;; | |
[Nn]) # 'n' key | |
confirmation_result="n" | |
;; | |
[Ee]) # 'e' key | |
confirmation_result="e" | |
;; | |
[Rr]) # 'r' key | |
confirmation_result="r" | |
;; | |
*) # Any other key - treat as cancel for safety | |
confirmation_result="n" | |
;; | |
esac | |
else | |
confirmation_result="y" | |
fi | |
# Note: if not in a terminal, confirmation_result is already 'n' | |
# Provide feedback by printing the action result and a newline | |
echo "$confirmation_result" | |
# A second newline here ensures the next prompt/output is clearly separated | |
echo "" | |
# --- Act on the confirmation_result --- | |
if [[ "$confirmation_result" == "y" ]]; then | |
git commit -m "$COMMIT_MSG" | |
echo "Changes committed successfully!" | |
elif [[ "$confirmation_result" == "e" ]]; then | |
# Create a temporary file with the commit message | |
# Use mktemp with a template for better security and uniqueness | |
local TEMP_FILE=$(mktemp /tmp/autocommit_edit_XXXXXX) | |
if [ ! -f "$TEMP_FILE" ]; then | |
echo "Error: Could not create temporary file for editing." >&2 | |
return 1 | |
fi | |
echo "$COMMIT_MSG" > "$TEMP_FILE" | |
# Open the file in the default editor ($EDITOR or vi) | |
local editor="${EDITOR:-vi}" | |
if ! command -v "$editor" &> /dev/null; then | |
echo "Error: Editor '$editor' not found. Cannot edit message." >&2 | |
rm "$TEMP_FILE" # Clean up temp file | |
return 1 | |
fi | |
echo "Opening editor for commit message..." | |
# Run the editor | |
"$editor" "$TEMP_FILE" | |
# Read the edited message, check if file is empty after editing | |
local EDITED_MSG | |
if [ -s "$TEMP_FILE" ]; then # -s checks if file exists and is not empty | |
EDITED_MSG=$(cat "$TEMP_FILE") | |
else | |
EDITED_MSG="" | |
fi | |
# Clean up temp file | |
rm "$TEMP_FILE" | |
# Commit with the edited message if it's not empty | |
if [ -n "$EDITED_MSG" ]; then | |
git commit -m "$EDITED_MSG" | |
echo "Changes committed successfully with edited message!" | |
else | |
echo "Commit canceled - empty commit message after editing." | |
fi | |
elif [[ "$confirmation_result" == "r" ]]; then | |
echo "Regenerating commit message..." | |
autocommit "$ISSUE_TAG" # Recursive call with the same tag | |
else # confirmation_result is "n" | |
echo "Commit canceled." | |
fi | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
NICE WORK.