Created
April 25, 2025 01:34
-
-
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'." | |
else | |
# Tag not provided, try to extract from branch name | |
local current_branch=$(git branch --show-current 2>/dev/null) # Get current branch, suppress stderr if not in a repo/branch | |
if [ -z "$current_branch" ]; then | |
echo "Error: Could not determine current git branch." >&2 | |
echo "Please provide the issue tag manually or ensure you are on a branch." >&2 | |
echo "Usage: autocommit [issue-tag]" >&2 | |
return 1 | |
fi | |
echo "Info: No issue tag provided, attempting to extract from branch '$current_branch'." | |
# Regex to find pattern like 'abc-123' after the first '/' | |
# - sed -n: suppress default output | |
# - s|.*/\([a-zA-Z]\+-[0-9]\+\).*|\1|p: | |
# - .*/ : Match anything up to the last '/' (greedy) | |
# - \([a-zA-Z]\+-[0-9]\+\) : Capture group 1: one or more letters, '-', one or more digits | |
# - .* : Match the rest of the string | |
# - \1 : Replace the whole line with the captured group | |
# - p : Print the result if substitution occurred | |
ISSUE_TAG=$(echo "$current_branch" | sed -n 's|.*/\([a-zA-Z]\+-[0-9]\+\).*|\1|p') | |
fi | |
# Store the issue tag | |
local ISSUE_TAG="$1" | |
# Get the git diff, both staged and unstaged | |
local GIT_DIFF=$(git diff head) | |
# 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="Based on the following git diff, generate a concise and descriptive commit message. | |
Start the message with a conventional commit type (fix, feat, chore, refactor, docs, style, test, build, ci, perf, revert - keep it lowercase) followed by the issue tag. | |
The format should be: '<type>: ${ISSUE_TAG} <summary line based on git diff, the first word of the summary should be lowercase>' | |
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. | |
Do not include a body UNLESS ABSOLUTELY NECESSARY for clarity. | |
Here's the git diff: | |
${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 | |
# 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: accept, Esc: cancel, y: accept, 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" | |
;; | |
*) # 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 | |
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.