How to Auto-Suggest a SemVer Bump From Git Commit Messages in Python

This code scans Git commit messages (recent or sample) and suggests the next Semantic Versioning bump type — major, minor, patch, or none.

Easy Python 3.9+ Aug 9, 2026 Git + Python 12 views 0 copies

Python code

33 lines
Python 3.9+
import re
import subprocess
from pathlib import Path


def get_commit_messages(path="."):
    """Read commit messages from a repo or use sample messages."""
    if (Path(path) / ".git").exists():
        out = subprocess.run(
            ["git", "-C", path, "log", "--pretty=%s"], capture_output=True, text=True
        )
        return out.stdout.strip().splitlines()
    return [
        "feat: add new widget",
        "fix(parser): handle empty input",
        "docs: update readme",
        "feat!: breaking API change",
        "chore: bump deps",
        "fix: resolve crash",
    ]


def analyze_bump(messages=None):
    """Return next semver bump suggestion based on commit messages."""
    messages = messages or get_commit_messages()
    major = any(re.search(r"^(feat|fix)!\s*:", m) for m in messages)
    minor = any(re.match(r"^feat(\s*\([^)]+\))?\s*:", m) for m in messages)
    patch = any(re.match(r"^fix(\s*\([^)]+\))?\s*:", m) for m in messages)
    return "major" if major else "minor" if minor else "patch" if patch else "none"


if __name__ == "__main__":
    print(f"Suggested bump: {analyze_bump()}")

Output

stdout
Suggested bump: minor

How it works

The script uses git log --pretty=%s to get the subject line of each commit. It then applies simple regex checks: any feat!: or fix!: marks a major bump, any feat: (optionally with a scope in parentheses) marks a minor, and any fix: marks a patch. The order of checks matters — major wins over minor, minor over patch. For repositories without a .git folder, it falls back to sample messages so you can test the logic anywhere. The code relies only on the standard library (re, subprocess, and pathlib), so no extra packages are needed.

Common mistakes

  • Forgetting the optional scope in the regex for `feat` and `fix`, so messages like `feat(parser):` are missed
  • Checking for `fix` or `feat` with a `!` before the scope (e.g., `feat!(parser):`) — the regex should catch `!` after the scope
  • Not stripping the output of `git log` before splitting lines, which can leave a trailing empty string
  • Assuming the repo's default branch is always `HEAD` — you may want to pass a specific branch to `git log`

Variations

  1. Use `git log --format=%s -n 50` to limit to the last 50 commits
  2. Incorporate dependent checks for `revert:` and `perf:` to suggest a patch bump

Real-world use cases

  • A CI pipeline runs this script after each push to automatically propose the next version number for a package or release.
  • A release manager uses it to quickly decide if a PR collection needs a major, minor, or patch version bump before tagging.
  • An internal tool triggers a version update workflow in a monorepo by reading conventional commits across multiple subpackages.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Git + Python

Related tutorials and quizzes for this topic.