Sevalla is the all-in-one PaaS for your web projects. Host and deploy your applications, databases, object storage, and static sites. Enjoy advanced deployment pipelines, a complete database studio, instant preview apps, and one-click templates. The pricing is simple: no hidden fees, no seat-based pricing, and you pay only for what you use. Get real human support from developers.

Get started now with a $50 credit at Sevalla.com.

How to automatically generate a commit message using Claude

Link –

For years, my git history contains "wip" commit messages. I don't really often use git history myself, but my colleagues do. And when they're trying to understand a change I made six months ago, "wip" tells them absolutely nothing. Might as well not have commit messages at all.

I knew I should write better commit messages, but the friction was real. Stopping to think about how to summarize my changes felt like it broke my flow. So I kept typing "wip".

Now, I have a bash function in my dotfiles that uses Claude to generate commit messages for me.

My commit function

Here's the core of it the function:

function commit() {
  commitMessage="$*"

  git add .

  if [ "$commitMessage" = "" ]; then
     diff_input=$(echo "=== Summary ===" && git diff --cached --stat && echo -e "\n=== Diff (truncated if large) ===" && git diff --cached | head -c 50000)
     commitMessage=$(echo "$diff_input" | claude -p "Write a single-line commit message for this diff. Output ONLY the message, no quotes, no explanation, no markdown.")

     git commit -m "$commitMessage"
     return
  fi

  eval "git commit -a -m '${commitMessage}'"
}

If I call commit with no arguments, it stages everything and asks Claude to generate a commit message based on the diff. If I pass in a message like commit "Fix bug in auth", it uses that instead.

What are dotfiles anyway?

Quick sidebar: "dotfiles" are configuration files that live in your home directory and typically start with a dot (like .zshrc or .bashrc). They control how your terminal, shell, and various command-line tools behave.

Many developers (myself included) keep their dotfiles in a git repository so they can sync their entire development environment across machines. If I get a new laptop, I just clone my dotfiles repo and I'm back to my familiar setup in minutes.

In my case, I have a .functions file in my dotfiles that contains custom bash functions like this commit function. My .zshrc sources that file, so these functions are available in every terminal session.

How the AI generation works

The interesting part is what gets sent to Claude. I don't just send the raw diff - that could be huge and contain too much noise. Instead, I send two things:

  1. A summary: git diff --cached --stat shows which files changed and how many lines were added/removed
  2. The actual diff: But truncated to 50,000 characters to avoid overwhelming the AI with massive diffs

Then I pass it to the claude CLI with a simple prompt: "Write a single-line commit message for this diff. Output ONLY the message, no quotes, no explanation, no markdown."

The result? Commit messages that actually describe what changed:

  • Add caching layer to user repository
  • Fix N+1 query in post index
  • Remove deprecated payment gateway integration

Instead of:

  • wip
  • wip
  • wip

Still fast, still flexible

The whole thing takes maybe 2-3 seconds. I type commit, and my changes are committed with a descriptive message.

If I want to override it with my own message, I just pass it as an argument: commit "Fix authentication bug". The function detects that I've provided a message and uses that instead of generating one. Best of both worlds.

Adding a spinner for nicer output

The function works fine as-is, but waiting 2-3 seconds staring at a blank terminal feels longer than it actually is. So I added a spinner animation to give some visual feedback.

Here's the full version with the spinner:

function commit() {
  commitMessage="$*"

  git add .

  if [ "$commitMessage" = "" ]; then
     # Start spinner in background
     {
       spinner="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
       while true; do
         for (( i=0; i<${#spinner}; i++ )); do
           printf "\r${spinner:$i:1} Generating commit message..."
           sleep 0.1
         done
       done
     } &!
     spinner_pid=$!

     # Cleanup function for interrupt
     cleanup() {
       { kill $spinner_pid; wait $spinner_pid; } 2>/dev/null
       printf "\r\033[K"
       trap - INT
       return 1
     }
     trap cleanup INT

     # Get diff and generate message
     diff_input=$(echo "=== Summary ===" && git diff --cached --stat && echo -e "\n=== Diff (truncated if large) ===" && git diff --cached | head -c 50000)
     commitMessage=$(echo "$diff_input" | claude -p "Write a single-line commit message for this diff. Output ONLY the message, no quotes, no explanation, no markdown.")

     # Stop spinner and clear line
     trap - INT
     { kill $spinner_pid; wait $spinner_pid; } 2>/dev/null
     printf "\r\033[K"

     git commit -m "$commitMessage"
     return
  fi

  eval "git commit -a -m '${commitMessage}'"
}

Now when I run commit, I see:

It's purely aesthetic, but it makes the tool feel more polished. Little details like this turn a script into something that feels good to use.

In closing

This is a small automation, but it's made me a better teammate. My commit history is now actually useful for the people who do read it. When my colleagues are tracking down when a feature was added or trying to understand why something changed, they can scan through my commits and find what they're looking for.

And I never have to type "wip" again.

If you want to use this yourself, you can find the full commit function in my dotfiles. You'll need the Claude CLI installed, but once you have that, just drop the function into your shell config and you're good to go.

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.