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.
Python code
33 linesimport 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
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
- Use `git log --format=%s -n 50` to limit to the last 50 commits
- 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
More from Git + Python
- Amend Last Commit Message in Python easy
- Bisect Good Bad Automation Script in Python easy
- Build a Simple Log Graph in Python easy
- Bump Semantic Version Git Tag in Python easy
- Count Unique Contributors from Git Shortlog in Python easy
- Create a Mock GitHub Release API in Python for Testing gh CLI easy
Keep learning
Related tutorials and quizzes for this topic.