A commit message is not a log entry—it is a contract between developer and future reader. Commit message generation is the process of producing high-quality, consistent, and meaningful commit messages, often automatically, at the time of a git commit. It matters most when developers are working in teams, especially across distributed workflows, where clarity and context must travel with code. It is critical during feature development, release cycles, and when reviewing pull requests, and it becomes indispensable when maintaining a codebase over time with evolving contributors.
Preparing a Commit with a Generated Message
When a developer runs git commit, the default message is empty. To improve this, a generated commit message can be created from the changes in the staging area. The most direct method uses git commit -m with a message constructed from a script or a tool.
A common pattern is to generate a message from the first line of the git diff output, often enriched with a list of changed files and a summary of staged changes.
#!/bin/bash
# Extract first line of diff as initial message
INITIAL_MESSAGE=$(git diff --staged --name-status | head -n1 | cut -c4-)
# Generate list of changed files
CHANGED_FILES=$(git diff --staged --name-status | tail -n +2 | awk '{
if ($1 == "M") printf " - Modified: %s\n", $2
else if ($1 == "A") printf " - Added: %s\n", $2
else if ($1 == "D") printf " - Deleted: %s\n", $2
else if ($1 == "R") printf " - Renamed: %s -> %s\n", $2, $3
}')
# Combine into a full message
cat << EOF
$INITIAL_MESSAGE
$CHANGED_FILES
Generated by: $(git config user.name) <$(git config user.email)>
EOF
The script is invoked with git commit -F -, where the - reads the message from standard input:
git add src/ tests/
git commit -F <(./generate-commit-message.sh)
This approach ensures that every commit includes a concise summary, a structured list of changes, and author metadata—without requiring the developer to write it manually.
Automating Message Generation via Git Hooks
To make commit message generation consistent across a team, developers can define a prepare-commit-msg hook in .git/hooks/prepare-commit-msg. This hook runs before the commit message editor starts and can modify the message file in place.
#!/bin/bash
# .git/hooks/prepare-commit-msg
# Input: $1 (commit message file), $2 (branch name), $3 (commit ID)
COMMIT_MSG_FILE="$1"
BRANCH_NAME="$2"
COMMIT_ID="$3"
# Read existing message
MESSAGE=$(cat "$COMMIT_MSG_FILE")
# Generate a header from the first line of the git log
HEADLINE=$(git log -1 --pretty=format:"%s" "$COMMIT_ID" 2>/dev/null || echo "Update")
# If message is empty, use headline as initial message
if [ -z "$MESSAGE" ]; then
echo "$HEADLINE" > "$COMMIT_MSG_FILE"
else
# Prepend headline to existing message
echo "$HEADLINE" | cat - "$COMMIT_MSG_FILE" > /tmp/msg && mv /tmp/msg "$COMMIT_MSG_FILE"
fi
# Add a footer with metadata
cat << EOF >> "$COMMIT_MSG_FILE"
---
Branch: $BRANCH_NAME
Commit: $COMMIT_ID
Generated at: $(date -Iseconds)
EOF
This hook ensures that every commit includes a headline derived from the previous commit, a consistent footer, and is ready for editing. It is particularly effective when paired with git commit -m and git commit -a.
Using Git's Built-in Message Templates
Git supports message templates via the commit.template configuration. When set, git commit reads the template from a file and uses it as the initial message.
# Set template path
git config --local commit.template .git/COMMIT_TEMPLATE
# Template file: .git/COMMIT_TEMPLATE
# Line 1: Subject (50 characters max)
# Line 2: Empty
# Lines 3+: Body
# Lines 4+: Footer (metadata)
# Example template
[Feature] Add user authentication
- Implement login and signup endpoints
- Add JWT token handling
- Update API docs
Fixes: #123 Reviewed-by: Jane Doe <[email protected]>
When a developer runs `git commit`, the message is pre-filled with this structure. The template can be generated by a script:
```bash
#!/bin/bash
# generate-template.sh
cat << EOF > .git/COMMIT_TEMPLATE
[<type>] <summary>
- <description of changes>
- <list of modifications>
<optional body>
Fixes: #<issue-number>
Reviewed-by: <name> <email>
EOF
A template can be updated with git config --local commit.template .git/COMMIT_TEMPLATE and reused across multiple commits. The template is not static—it can be version-controlled and shared across repositories.
Integrating with a CI Pipeline
When commits are generated automatically—such as from a CI system—message generation must be part of the pipeline. For example, a GitHub Actions workflow can generate a commit message based on the pipeline’s context.
# .github/workflows/generate-commit-message.yml
name: Generate Commit Message
on:
pull_request:
types: [opened, synchronize]
jobs:
generate-message:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Generate commit message
run: |
# Get PR number and title
PR_NUMBER=$(echo "${{ github.event.pull_request.number }}" | tr -d '\n')
PR_TITLE=$(echo "${{ github.event.pull_request.title }}" | tr -d '\n')
# Build message body
cat << EOF > commit-message.txt
PR $PR_NUMBER: $PR_TITLE
- Add new feature: $PR_TITLE
- Update documentation for API
- Fix issues in build pipeline
Changes:
$(git diff --name-status HEAD~1 HEAD)
Generated by GitHub Actions
EOF
# Set message as environment variable
echo "COMMIT_MESSAGE=$(cat commit-message.txt)" >> $GITHUB_ENV
- name: Commit with generated message
run: |
git config user.email "[email protected]"
git config user.name "GitHub Actions"
git add .
git commit -F commit-message.txt -m "Update from CI"
The generated message includes a PR number, title, a list of changes from git diff, and metadata. The CI system can also generate a message based on a previous commit, using git log to extract a summary.
Handling Complex Commit Scenarios
Some workflows require messages that capture more than just file changes. For instance, when multiple features are added in a single commit, the message must reflect the scope and rationale.
#!/bin/bash
# commit-with-changesets.sh
# Function: generate message from changesets
generate_message() {
local changeset_file="$1"
local base_message=""
local body=""
while IFS= read -r line; do
case "$line" in
"### "*) base_message="${line#### }" ;;
"#### "*) body="$body\n - $line" ;;
" "*) body="$body\n $line" ;;
*) body="$body\n$line" ;;
esac
done < "$changeset_file"
echo "$base_message" > commit-message.txt
echo "$body" >> commit-message.txt
# Add footer
echo "" >> commit-message.txt
echo "---" >> commit-message.txt
echo "Generated: $(date -Iseconds)" >> commit-message.txt
echo "Changeset: $changeset_file" >> commit-message.txt
}
# Use in a script
generate_message changesets.md
git commit -F commit-message.txt -m "feat: add user profile management"
The changesets.md file might look like:
### feat: add user profile management
#### Add API endpoints
- POST /api/profile
- GET /api/profile/:id
- PUT /api/profile/:id
#### Add client-side component
- ProfileEditor.vue
- ProfileService.js
#### Update database schema
- Add column: profile_picture_url
- Add index on user_id
This approach allows for rich, structured messages that can be authored in a separate file and injected into the commit.
Common Pitfalls and Silent Failures
The most common silent failure occurs when the prepare-commit-msg hook does not handle the COMMIT_EDITMSG file correctly. If the hook writes to a temporary file and forgets to move it into place, the message is not used.
Another frequent issue: using git commit -m with a message that already contains a newline at the end. The message is split into subject and body, but the body is not properly aligned.
# Wrong: message has trailing newline
git commit -m "Fix login bug
Add validation for email field"
The commit message becomes:
Fix login bug
Add validation for email field
But the body should start at the first line after the blank line. The correct version uses git commit -F - or ensures the message is trimmed.
A subtle failure arises when using git log to extract a message: if the current commit is not present in the repository (e.g., in a detached HEAD), the git log -1 command returns an empty result, leading to a blank headline.
Additionally, when generating a message from a template, the template file may be ignored if it is not in the correct path. commit.template expects a file relative to the .git directory, so git config commit.template templates/commit.txt looks for templates/commit.txt inside .git/, not in the project root.
Final Message and Review
The final commit message should be reviewed before pushing. A common workflow uses git commit --amend to refine the generated message:
git commit --amend -F <(./generate-commit-message.sh)
This allows the developer to edit the message in an editor, but with the generated content already in place. The --amend flag updates the current commit, preserving the commit ID and history.
To verify the message, use git log --oneline --no-merges --stat to see a compact summary. For a detailed view, git log --pretty=fuller includes author dates, committer dates, and footers.
When a message is generated, the reader should be able to understand not only *what* changed but *why* it changed, and *how* it fits into the broader context of the project. Commit message generation is not a one-off task—it is a continuous practice that turns code commits into readable narratives.
This page was rewritten on 11 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
Join the Newsletter
Get the latest updates and deep insights shipped directly to your inbox.