Creating Better Git Commit Messages with ChatGPT and Ruby: A Practical Solution

Creating Better Git Commit Messages with ChatGPT and Ruby: A Practical Solution

Have you ever stared at the Git commit message prompt, struggling to create the perfect description of your changes? I certainly have. Writing Git commit messages can be tedious and time-consuming, but it is essential for maintaining a clear and organized codebase. A well-crafted commit message can make it easier for your team to understand your changes and track down bugs.

In this blog post, I'll share the Ruby script I came up with to make this process easier by using ChatGPT to automatically generate Git commit messages.

⚠️
Before we dive into the script, I want to emphasize the importance of using this tool appropriately. The script sends your staged changes to the OpenAI API, meaning your code will be sent to OpenAI's servers. Therefore, confirming that the codebase you are working on is appropriate to upload to the ChatGPT servers is crucial. Please review OpenAI's Terms of Use and the policies of the codebase you are working with to ensure you are fully compliant. Using this on personal scripts you plan to open source is one thing; using it on your employer's proprietary codebase is quite another.

Now, let's explore the script. The script requires Ruby 2.6 or higher, Bundler, and git. It also requires an OpenAI API key, which you can obtain by signing up for an OpenAI account. Once you have your API key, set it in the OPENAI_API_KEY environment variable.

The script uses the TTY-Prompt gem to display prompts in the console and the Clipboard gem to interact with the clipboard. It also uses the Colorize gem to add colors to the console output and the Ruby-OpenAI gem to access the OpenAI API. The use of bundler/inline allows the script to be run on its own without having to install the gems separately.

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'clipboard', require: true # A gem for interacting with the clipboard
  gem 'colorize', require: true # A gem for adding colors to the console output
  gem 'ruby-openai', require: false # A gem for accessing the OpenAI API
  gem 'pry', require: true # A gem for debugging
  gem 'tty-prompt', require: true # A gem for displaying prompts in the console
end

require 'json'
require 'openai'
require 'ostruct'
require 'time'

prompt = TTY::Prompt.new

OPENAI_API_KEY = ENV['OPENAI_API_KEY']
if OPENAI_API_KEY.nil?
  puts 'Please set OPENAI_API_KEY environment variable'.red
  exit 1
end

OpenAI.configure do |config|
  config.access_token = OPENAI_API_KEY
end

The script starts by checking if changes have been staged; it will exit with an error message if nothing has been staged. Next, the script sends the changes and a prompt to the ChatGPT API to generate a commit message based on the changes. The prompt includes guidelines for the commit message, such as using the past tense, including a subject line and a body, and wrapping lines at 72 characters.

It also times how long it takes for OpenAI to respond. Everybody likes metrics, right?

# Check if changes have been staged
staged_content = `git --no-pager diff --staged --unified=1`
if staged_content.empty?
  puts 'No changes have been staged. Please stage changes before running this script.'.red
  exit 1
end

# Start timer
start_time = Time.now

# Initialize OpenAI API client
puts "\n--------------------------------------------------------------------------------".white
print 'Initializing OpenAI API client...'.white
client = OpenAI::Client.new
print '✓'.green

# Form the prompt for ChatGPT
question = <<~QUESTION
  I need you to create a commit message for me based on these guidelines:

  - Use the past tense for the commit message.
  - Include a subject line and a body with a list of more details.
  - The subject line should be followed by a blank line
  - The subject line should be a single line that is 50 characters or less.
  - If the changes only include 1 file, then the subject line should include the file name.
  - The body should use bullets if appropriate.
  - The lines in the body should wrap at 72 characters
  - Add a blank line followed by "Commit message created with help from ChatGPT." to the end of the body
  - Don't output anything except the commit message contents so I can easily copy and paste it.

  Here are the differences for the commit:
  ```#{staged_content}```
QUESTION

# Send request to OpenAI API
print "\nSubmitting request...".white
response = client.chat(
  parameters: {
    # The name of the OpenAI model to use
    model: 'gpt-3.5-turbo',
    # The prompt to send to the model
    messages: [{ role: 'user', content: question }],
    # Controls the randomness of the response.
    # Lower values will result in more predictable responses.
    # Range: [0, 1]
    temperature: 0.25
  }
)

# Check for errors in the response
if response['error']
  print "✗\n".red
  puts "OpenAI API Error: #{response['error']}".red
  exit 1
end
print "✓\n".green
Note the error handling logic here. The only error I've encountered so far has been when I submitted a larger diff. According to the API docs, the current limit for the gpt-3.5-turbo-0301 model is 4096 tokens.

Once ChatGPT generates the commit message, the script copies the message to the clipboard and prompts the user on how to continue: either submit the commit with the generated message, edit the message before submitting, or exit without committing. The script handles the user's input and proceeds accordingly.

# Extract the generated commit message from the response
message = response['choices'][0]['message']['content']
Clipboard.copy(message) # Copy the generated commit message to the clipboard

# End timer and display summary
end_time = Time.now
elapsed_time = end_time - start_time
time_message = "\nTime to get message from ChatGPT: #{elapsed_time.round(2)} seconds"
puts time_message.yellow
puts "\nThe commit message has been copied to your clipboard and is displayed below".magenta
puts "\nCommit message generated by ChatGPT:\n".white
puts message.cyan
puts "\n--------------------------------------------------------------------------------".white

begin
  # Prompt the user for how to proceed
  user_input = prompt.select("\nWhat would you like to do?") do |menu|
    menu.enum '.'

    menu.choice 'Submit commit with this message', 1
    menu.choice 'Edit message before committing', 2
    menu.choice 'Exit without committing', 3
  end
rescue SystemExit, Interrupt
  # Gracefully handle exceptions like Ctrl-C or Ctrl-D
  puts "\nExiting without committing...".yellow
  exit 1
end

# Escape double quotes in the commit message
escaped_message = message.gsub('"', '\"')

# Process the user's input
case user_input
when 1
  puts "\nSubmitting commit...".white
  system("git commit -m \"#{escaped_message}\"")
when 2
  puts "\nOpening editor...".white
  system("git commit -e -m \"#{escaped_message}\"")
when 3
  puts "\nExiting without committing...".yellow
  exit 1
end

Here's an example of the script in use:

And here's a link to the asciinema recording: https://asciinema.org/a/579883

You can view the full script and my other helper scripts on my GitHub: https://github.com/ryderstorm/helper_scripts

💸
A note on the cost of using the API for ChatGPT with this script. At the time of writing, the pricing for the gpt-3.5-turbo model is "$0.002 / 1K tokens". You can check out the pricing details on OpenAI's website.

This Ruby script can save time and help you create better commit messages. By automating the process of generating commit messages, you can focus on writing code instead of crafting messages. However, it's critical to use this tool appropriately and ensure that the codebase you are working on is appropriate to upload to the ChatGPT servers.

Creating this script was an excellent opportunity for me to explore the practical usefulness of ChatGPT and similar AI technologies as they relate to a developer's workflow. The script has greatly benefited my development flow and improved my codebase, allowing me to quickly generate high-quality commit messages with just a few keystrokes. I hope it can do the same for you!

💡
I pushed a small but significant update to the script on 2023/05/24: https://github.com/ryderstorm/helper_scripts/pull/1