Skip to content

Instantly share code, notes, and snippets.

@UncleBill
Last active May 7, 2025 03:08
Show Gist options
  • Save UncleBill/2ae97ee0ccebe868ca397a3723a1e930 to your computer and use it in GitHub Desktop.
Save UncleBill/2ae97ee0ccebe868ca397a3723a1e930 to your computer and use it in GitHub Desktop.
Function to auto-generate commit messages with issue tag
#!/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
}
@nicejade
Copy link

NICE WORK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment