Skip to main content
Massively Parallel Procrastination

Letting the LLM draft my commit messages

My friend harper built a neat tool to let the llm draft your commit messages.

I ended up tweaking his code a bit for my own use. I wanted commit messages that were a bit more...boring. And I wasn't really happy with how the original code put together the 'why' of the commit message.

I made changes to harper's prompt to try to rein things in just a bit and also hacked up his git hook to tweak the model's "temperature" and to provide full-file context to the llm to give it a bit more to go on when writing a commit message.

I find that with this setup, I do need to edit my commit messages about 90% of the time, but that those edits are relatively minor and that I'm ending up with better, more detailed commit messages. (Also, I am generally much more productive when I'm hacking up something bad than when I'm staring at a blank screen.)

Although, having said that, when I added the first version of this post to the git repo for my blog, it generated this commit message, which I accepted unchanged:

Add blog post and update CSS for code blocks

- Added a new blog post "Letting the LLM draft my commit messages" in `content/blog/2024/2024-04-21-Letting-the-LLM-draft-my-commit-messages.md`. This post discusses the use of a tool for generating commit messages with LLM and includes modifications to the original tool for better context understanding and message generation.
- Updated `public/css/index.css` to improve the styling of `<pre>` tags. Changes include setting the font size to 0.8em, adding a background color of #dedede, enabling horizontal scrolling for overflow content, and adding padding for better readability of code blocks.

You should refer to harper's post for context about what the heck this tool does and also how to set it up.

My prepare-commit-msg script:

#!/bin/sh

# Exit if the `SKIP_LLM_GITHOOK` environment variable is set
if [ ! -z "$SKIP_LLM_GITHOOK" ]; then
  exit 0
fi

# ANSI color codes for styling the output
RED='\033[0;31m'    # Sets text to red
GREEN='\033[0;32m'  # Sets text to green
YELLOW='\033[0;33m' # Sets text to yellow
BLUE='\033[0;34m'   # Sets text to blue
NC='\033[0m'        # Resets the text color to default, no color


# Function to display a spinning animation during the LLM processing
spin_animation() {
  # Array of spinner characters for the animation
  spinner=("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏")
  # Infinite loop to keep the animation running
  while true; do
    for i in "${spinner[@]}"; do
      tput civis  # Hide the cursor to enhance the animation appearance
      tput el1    # Clear the line from the cursor to the beginning to display the spinner
      printf "\\r${YELLOW}%s${NC} Generating LLM commit message..." "$i"  # Print the spinner and message
      sleep 0.1   # Delay to control the speed of the animation
      tput cub 32 # Move the cursor back 32 columns to reset the spinner position
    done
  done
}

# Check if the commit is a merge commit based on the presence of a second argument
if [ -n "$2" ]; then
  exit 0  # Exit script if it's a merge commit, no custom message needed
fi

# Check if the `llm` command is installed
if ! command -v llm &> /dev/null; then
  echo "${RED}Error: 'llm' command is not installed. Please install it and try again.${NC}"
  exit 1
fi

# Start the spinning animation in the background
spin_animation &
spin_pid=$!  # Capture the process ID of the spinning animation

# Generate the commit message using `git diff` piped into `llm` command
# The LLM command takes a system prompt from a file as input
if ! commit_msg=$(git diff -U999999 --cached | llm -t commit-message  --option temperature 0.02 --option seed 1); then
  # Stop the spinning animation by killing its process
  kill $spin_pid
  wait $spin_pid 2>/dev/null  # Wait for the process to terminate and suppress error messages

  # Finalizing output
  tput cnorm  # Show the cursor again
  printf "\\n"  # Move the cursor to the next line

  printf "${RED}Error: 'llm' command failed to generate the commit message:\\n${commit_msg}${NC}\\n\\nManually set the commit message"
  exit 1
fi

# Stop the spinning animation by killing its process
kill $spin_pid
wait $spin_pid 2>/dev/null  # Wait for the process to terminate and suppress error messages

# Finalizing output
tput cnorm  # Show the cursor again
echo  # Move the cursor to the next line

# Display the generated commit message with color-coded headings
echo "${BLUE}=== Generated Commit Message ===${NC}"
echo "${GREEN}$commit_msg${NC}"
echo "${BLUE}=================================${NC}"
echo

# Write the generated commit message to the specified file (usually the commit message file in .git)
echo "$commit_msg" > "$1"

My prompt lives inside llm's "prompt templates" feature at ~/Library/Application Support/io.datasette.llm/templates/commit-message.yaml

model: gpt-4-turbo
system: >

    Write a concise, informative commit message for these changes:

    - Review the whole context of the diff carefully to see what effect the change would have on the rest of the code and explain that. Be specific about the effect.
    - Do not guess about intent.
    - The goal of this commit message is that someone familiar with the codebase, but not with these changes would understand why the changes were made and what was changed.
    - The first line should be a short summary of the intent of the changes
    - Remember to mention the files that were changed, and what was changed
    - Keep the summary under 50 characters
    - Use bullet points for multiple changes
    - Reference related issues or tickets, but only if you are 100% sure the ticket numbers are correct.
    - If the change is just to documentation, state that.
    - If there are no changes, or the input is blank - then return a blank string

    Think carefully about what would be most helpful to someone trying to understand the intent of this commit before you write your commit message. Your commit message will be used as an example to train other team members about the content of a good commit message.

    What you write will be passed to git commit -m "[message]"

    The output format should be:

    Summary of changes\n
    \n
    - change\n
    - change\n
    ..and so on.