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.