A Python library and CLI tool to manage Git tags following Semantic Versioning (SemVer) and Gitflow branching model.
It provides utilities to create, increment, and validate development, release candidate (RC), patch, and production tags.
π Live Documentation
- β Retrieve tags: Get the latest development, RC, patch, or production tags
- β
Increment pre-release tags: Automatically increment
dev,rc,patchtags - β Initialize workflows: Create new RC or patch tags from existing tags
- β Promote to production: Convert RC/Patch tags into production versions
- β
Enforce versioning rules:
- Prevents RC creation if a production tag exists
- Prevents patch creation if no production tag exists
- Validates semantic versioning format
- β Command-line interface: Easy-to-use CLI for automation and CI/CD
- Installation
- Quick Start
- Building Distribution
- Usage
- Tagging Conventions
- Gitflow Diagram
- Example Workflow
- CLI Command Reference
- Tests
- Troubleshooting
- Contributing
# Clone the repository
git clone https://github.com/0jas/vcm.git
cd vcm
# Install dependencies
pip install -r requirements.txt
# Install the package
pip install -e .# Install from wheel
pip install dist/vcm-*.whl
# Or install from PyPI (when published)
pip install vcm- Python 3.7+
- GitPython >= 3.1.0
- semver >= 2.13.0
from src.vcm.vcm import *
# Initialize with repository path
manager = VersionControlManager("/path/to/your/repo")
# Get current development tag
current_tag = manager.get_current_tag(prerelease_tag="dev")
print(f"Current dev tag: {current_tag}")
# Increment development tag
new_tag = manager.increment_prerelease_tag()
print(f"New dev tag: {new_tag}")
# Initialize release candidate
rc_tag = manager.init_new_rc()
print(f"Created RC: {rc_tag}")
# Promote to production
prod_tag = manager.create_prod_tag(rc_tag)
print(f"Production tag: {prod_tag}")# Get current development tag
vcm get-current-tag
# Get current production tag
vcm get-current-tag --production
# Increment development tag
vcm increment-prerelease --tag 1.0.0-dev.1
# Initialize release candidate
vcm init-rc
# Create production from RC
vcm create-prod --tag 1.0.0-rc.1To create distribution packages for installation via pip:
# Build both wheel (.whl) and source distribution (.tar.gz)
python setup.py bdist_wheel sdistThis command creates:
- Wheel distribution (
.whl) β Binary package indist/directory - Source distribution (
.tar.gz) β Source package indist/directory
After running the build command, you'll find:
dist/
βββ vcm-<version>-py3-none-any.whl # Wheel distribution
βββ vcm-<version>.tar.gz # Source distribution
build/ # Temporary build files
βββ ...
vcm.egg-info/ # Package metadata
βββ ...
Option 1: Install from Wheel
# Install the wheel package
pip install dist/vcm-<version>-py3-none-any.whl
# Or use wildcard
pip install dist/*.whlOption 2: Install from Source Distribution
# Install from source tarball
pip install dist/vcm-<version>.tar.gzOption 3: Install in Development Mode
# Install in editable/development mode
pip install -e .To clean up build directories:
# Remove build artifacts (Linux/macOS)
rm -rf build/ dist/ *.egg-info
# Or on Windows
rmdir /s /q build dist
del /s /q *.egg-infoEnsure you have the required build tools:
# Install build dependencies
pip install setuptools wheel twine
# Verify setup.py configuration
python setup.py checkUpdate version in src/vcm/__init__.py before building:
# src/vcm/__version__.py
__version__ = '1.0.0'
setup(
name='vcm',
version=get_version(),
...
)from src.vcm.vcm import VersionControlManager
# Default: current directory
manager = VersionControlManager()
# Specify repository path
manager = VersionControlManager("/path/to/repo")# Get current development tag
dev_tag = manager.get_current_tag(prerelease_tag="dev")
print(f"Current dev: {dev_tag}") # Output: 1.0.0-dev.5
# Get current RC tag
rc_tag = manager.get_current_tag(prerelease_tag="rc")
print(f"Current RC: {rc_tag}") # Output: 1.0.0-rc.2
# Get current production tag
prod_tag = manager.get_current_tag(production=True)
print(f"Current production: {prod_tag}") # Output: 1.0.0# Increment development tag
new_dev = manager.increment_prerelease_tag()
print(new_dev) # Output: 1.1.0-dev.1
# Increment specific tag
new_dev = manager.increment_prerelease_tag(tag="1.0.0-dev.5")
print(new_dev) # Output: 1.0.0-dev.6
# Major version bump
new_major = manager.increment_prerelease_tag(
tag="1.0.0-dev.5",
major_bump=True
)
print(new_major) # Output: 2.0.0-dev.1
# Custom prerelease identifier
alpha_tag = manager.increment_prerelease_tag(
tag="1.0.0-alpha.1",
prerelease_tag="alpha"
)
print(alpha_tag) # Output: 1.0.0-alpha.2# Create first RC from latest dev tag
rc_tag = manager.init_new_rc(prerelease_tag="dev")
print(rc_tag) # Output: 1.0.0-rc.1
# Get current RC for a version
current_rc = manager.get_current_rc_patch("1.0.0", prerelease_tag="rc")
print(current_rc) # Output: 1.0.0-rc.1
# Increment RC
next_rc = manager.increment_rc_patch("1.0.0", prerelease_tag="rc")
print(next_rc) # Output: 1.0.0-rc.2# Create first patch from latest production tag
patch_tag = manager.init_new_patch()
print(patch_tag) # Output: 1.0.0-patch.1
# Get current patch for a version
current_patch = manager.get_current_rc_patch("1.0.0", prerelease_tag="patch")
print(current_patch) # Output: 1.0.0-patch.1
# Increment patch
next_patch = manager.increment_rc_patch("1.0.0", prerelease_tag="patch")
print(next_patch) # Output: 1.0.0-patch.2# Promote RC to production
prod_tag = manager.create_prod_tag("1.0.0-rc.3")
print(prod_tag) # Output: 1.0.0
# Promote patch to production (increments patch version)
prod_patch = manager.create_prod_tag("1.0.0-patch.2")
print(prod_patch) # Output: 1.0.1# Check if specific tag exists
exists = manager.find_tag("1.0.0-dev.5")
print(exists) # Output: True or False
# Find tags matching pattern
pattern = r"^1\.0\.0-dev\.\d+$"
matching_tag = manager.find_tag_with_pattern(pattern)
print(matching_tag) # Output: 1.0.0-dev.9 (highest matching)from src.vcm.exceptions import InvalidTagCreation
try:
# This will fail if production tag already exists
manager.increment_rc_patch("1.0.0", prerelease_tag="rc")
except InvalidTagCreation as e:
print(f"Error: {e}")
try:
# This will fail if tag format is invalid
manager.increment_prerelease_tag(tag="invalid-tag")
except ValueError as e:
print(f"Invalid tag format: {e}")# Get current development tag
vcm get-current-tag
# Output: 1.0.0-dev.5
# Get current production tag
vcm get-current-tag --production
# Output: 1.0.0
# Get current tag with detailed output
vcm get-current-tag --detailed
# Output: Latest dev prerelease tag found: 1.0.0-dev.5
# Get current RC tag
vcm get-current-tag --prerelease-tag rc
# Output: 1.0.0-rc.2# Increment from current development tag
vcm increment-prerelease
# Output: 1.1.0-dev.1
# Increment specific tag
vcm increment-prerelease --tag 1.0.0-dev.5
# Output: 1.0.0-dev.6
# Major version bump
vcm increment-prerelease --tag 1.0.0-dev.5 --major-bump
# Output: 2.0.0-dev.1
# Custom prerelease identifier
vcm increment-prerelease --tag 1.0.0-alpha.1 --prerelease-tag alpha
# Output: 1.0.0-alpha.2
# Detailed output
vcm increment-prerelease --tag 1.0.0-dev.5 --detailed
# Output: Created prerelease tag: 1.0.0-dev.6# Initialize release candidate from dev
vcm init-rc
# Output: 1.0.0-rc.1
# Initialize with custom dev tag
vcm init-rc --prerelease-tag dev
# Output: 1.0.0-rc.1
# Detailed output
vcm init-rc --detailed
# Output: Created initial RC tag: 1.0.0-rc.1# Initialize patch from production
vcm init-patch
# Output: 1.0.0-patch.1
# Detailed output
vcm init-patch --detailed
# Output: Created initial patch tag: 1.0.0-patch.1# Get current RC for version
vcm get-current-rc-patch 1.0.0
# Output: 1.0.0-rc.2
# Get current patch for version
vcm get-current-rc-patch 1.0.0 --prerelease-tag patch
# Output: 1.0.0-patch.1# Increment RC
vcm increment-rc-patch 1.0.0
# Output: 1.0.0-rc.3
# Increment patch
vcm increment-rc-patch 1.0.0 --prerelease-tag patch
# Output: 1.0.0-patch.2
# Detailed output
vcm increment-rc-patch 1.0.0 --detailed
# Output: Created incremented rc tag: 1.0.0-rc.3# Create production from RC
vcm create-prod 1.0.0-rc.3
# Output: 1.0.0
# Create production from patch
vcm create-prod 1.0.0-patch.2
# Output: 1.0.1
# Detailed output
vcm create-prod 1.0.0-rc.3 --detailed
# Output: Created production tag: 1.0.0# Use custom repository path
vcm get-current-tag --repo-path /path/to/repo
# All commands support --repo-path
vcm increment-prerelease --repo-path /path/to/repo --tag 1.0.0-dev.1
vcm init-rc --repo-path /path/to/repo
vcm create-prod 1.0.0-rc.3 --repo-path /path/to/repoThis tool follows Semantic Versioning 2.0.0 (https://semver.org/):
| Tag Type | Format | Example | Description |
|---|---|---|---|
| Development | X.Y.Z-dev.N |
1.0.0-dev.5 |
Development iterations |
| Release Candidate | X.Y.Z-rc.N |
1.0.0-rc.2 |
Pre-release testing versions |
| Patch | X.Y.Z-patch.N |
1.0.0-patch.1 |
Hotfix development iterations |
| Production | X.Y.Z |
1.0.0 |
Stable production releases |
- X (Major): Incompatible API changes
- Y (Minor): Backwards-compatible functionality
- Z (Patch): Backwards-compatible bug fixes
- N (Prerelease number): Iteration number
- Development tags increment the prerelease number:
1.0.0-dev.1β1.0.0-dev.2 - Minor bumps occur when RC exists:
1.0.0-dev.5β1.1.0-dev.1(if1.0.0-rc.1exists) - Major bumps must be explicit: Use
--major-bumpflag - RC tags always start at
.1:1.0.0-rc.1 - Patch tags increment patch version on promotion:
1.0.0-patch.2β1.0.1 - Production tags have no prerelease suffix:
1.0.0
The project follows the Gitflow branching model:
master (production)
|
|---- hotfix/patch branches
|
|---- release branches
|
develop (integration)
|
|---- feature branches
masterβ Production-ready code, tagged with production versionsdevelopβ Main integration branch for featuresfeature/*β New features, branched fromdeveloprelease/*β Release preparation, branched fromdevelophotfix/*orpatch/*β Production fixes, branched frommaster
# 1. Start development
vcm increment-prerelease
# Output: 0.1.0-dev.1
# 2. Continue development iterations
vcm increment-prerelease --tag 0.1.0-dev.1
# Output: 0.1.0-dev.2
vcm increment-prerelease --tag 0.1.0-dev.2
# Output: 0.1.0-dev.3
# 3. Feature complete - create release candidate
vcm init-rc
# Output: 0.1.0-rc.1
# 4. Testing finds bugs - increment RC
vcm increment-rc-patch --tag 0.1.0
# Output: 0.1.0-rc.2
vcm increment-rc-patch --tag 0.1.0
# Output: 0.1.0-rc.3
# 5. Testing complete - promote to production
vcm create-prod --tag 0.1.0-rc.3
# Output: 0.1.0
# 6. Continue development for next version
vcm increment-prerelease
# Output: 0.2.0-dev.1 (automatically bumps minor version)
# 7. Production bug found - create hotfix
vcm init-patch
# Output: 0.1.0-patch.1
# 8. Test hotfix
vcm increment-rc-patch --tag 0.1.0 --prerelease-tag patch
# Output: 0.1.0-patch.2
# 9. Deploy hotfix to production
vcm create-prod --tag 0.1.0-patch.2
# Output: 0.1.1 (increments patch version)Get the current semantic version tag (dev/rc/patch/production).
Usage:
vcm get-current-tag [OPTIONS]Options:
--repo-path PATH- Path to Git repository (default: current directory)--prerelease-tag TAG- Prerelease identifier (default:dev)--production- Return latest production tag-d, --detailed- Show descriptive message
Examples:
vcm get-current-tag
vcm get-current-tag --production
vcm get-current-tag --prerelease-tag rc --detailed
vcm get-current-tag --repo-path /path/to/repoCreate and increment a prerelease tag.
Usage:
vcm increment-prerelease [OPTIONS]Options:
--repo-path PATH- Path to Git repository (default: current directory)--tag TAG- Base tag to increment from (optional)--prerelease-tag TAG- Prerelease identifier (default:dev)--major-bump- Perform major version bump instead of minor-d, --detailed- Show descriptive message
Examples:
vcm increment-prerelease
vcm increment-prerelease --tag 1.0.0-dev.1
vcm increment-prerelease --tag 1.0.0-dev.5 --major-bump
vcm increment-prerelease --prerelease-tag alpha --detailedInitialize a new release candidate from development tag.
Usage:
vcm init-rc [OPTIONS]Options:
--repo-path PATH- Path to Git repository (default: current directory)--prerelease-tag TAG- Development prerelease identifier (default:dev)-d, --detailed- Show descriptive message
Examples:
vcm init-rc
vcm init-rc --prerelease-tag dev --detailed
vcm init-rc --repo-path /path/to/repoInitialize a new patch prerelease from production tag.
Usage:
vcm init-patch [OPTIONS]Options:
--repo-path PATH- Path to Git repository (default: current directory)-d, --detailed- Show descriptive message
Examples:
vcm init-patch
vcm init-patch --detailed
vcm init-patch --repo-path /path/to/repoGet the current highest RC or patch tag for a version.
Usage:
vcm get-current-rc-patch --tag TAG [OPTIONS]Options:
--tag TAG- Base production version tag (e.g.,1.0.0)--repo-path PATH- Path to Git repository (default: current directory)--prerelease-tag TAG- Prerelease type:rcorpatch(default:rc)
Examples:
vcm get-current-rc-patch --tag 1.0.0
vcm get-current-rc-patch --tag 1.0.0 --prerelease-tag patch
vcm get-current-rc-patch --tag 1.0.0 --repo-path /path/to/repoIncrement a release candidate or patch prerelease tag.
Usage:
vcm increment-rc-patch --tag TAG [OPTIONS]Options:
--tag TAG- Base production version tag (e.g.,1.0.0)--repo-path PATH- Path to Git repository (default: current directory)--prerelease-tag TAG- Prerelease type:rcorpatch(default:rc)-d, --detailed- Show descriptive message
Examples:
vcm increment-rc-patch --tag 1.0.0
vcm increment-rc-patch --tag 1.0.0 --prerelease-tag patch
vcm increment-rc-patch --tag 1.0.0 --detailedCreate a production tag from RC or patch prerelease.
Usage:
vcm create-prod TAG [OPTIONS]Options:
--tag TAG- RC or patch prerelease tag to promote (e.g.,1.0.0-rc.3)--repo-path PATH- Path to Git repository (default: current directory)-d, --detailed- Show descriptive message
Examples:
vcm create-prod --tag 1.0.0-rc.3
vcm create-prod --tag 1.0.0-patch.2
vcm create-prod --tag 1.0.0-rc.3 --detailed# Run all tests
python -m pytest -v
# Run specific test
python -m pytest tests/test_vcm.py::test_current_tag -vSolution:
pip install wheelSolution: Ensure all dependencies are listed in setup.py:
install_requires=[
'GitPython>=3.1.0',
'semver>=2.13.0',
]Solution: Check entry_points in setup.py:
entry_points={
'console_scripts': [
'vcm=vcm.cli:main', # Format: command=module:function
],
}Reinstall the package:
pip uninstall vcm
pip install -e .Solution: Use a virtual environment:
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -e .Solution:
python setup.py check Error: Not a valid Git repository.
Solution:
# Initialize Git repository
cd /path/to/repo
git init
# Or specify correct path
vcm get-current-tag --repo-path /correct/pathError: "Production version available. Cannot create Release Candidate version!"
Explanation: Cannot create RC tags after production version exists.
Solution: This is expected behavior. Use patch workflow for production fixes:
vcm init-patch
vcm increment-rc-patch 1.0.0 --prerelease-tag patch
vcm create-prod 1.0.0-patch.1Error: Tag doesn't match semantic versioning pattern.
Solution: Use correct tag format:
- Development:
X.Y.Z-dev.N(e.g.,1.0.0-dev.1) - RC:
X.Y.Z-rc.N(e.g.,1.0.0-rc.1) - Patch:
X.Y.Z-patch.N(e.g.,1.0.0-patch.1) - Production:
X.Y.Z(e.g.,1.0.0)
Error: "No matching tags found in repository"
Solution: Create initial tag:
vcm increment-prerelease
# Creates: 0.1.0-dev.1Error: Cannot create tags in repository.
Solution:
# Check Git configuration
git config user.name
git config user.email
# Set if missing
git config user.name "Your Name"
git config user.email "your.email@example.com"
# Check repository permissions
ls -la .git/Solution:
# Reinstall package
pip install -e .
# Or add to PATH (if installed globally)
export PATH=$PATH:~/.local/bin
# On Windows
set PATH=%PATH%;%APPDATA%\Python\ScriptsSolution:
# Uninstall and reinstall
pip uninstall vcm
pip install -e .
# Clear Python cache
find . -type d -name __pycache__ -exec rm -r {} +Solution: Check parser configuration in cli.py:
parser = argparse.ArgumentParser(
description="Git semantic versioning operations",
formatter_class=argparse.RawDescriptionHelpFormatter,
...
)Solution:
# Install in development mode
pip install -e .
# Or add src to PYTHONPATH
export PYTHONPATH="${PYTHONPATH}:${PWD}/src"Solution: Clean test artifacts:
# Remove test repository
rm -rf test_dir/
# Rerun tests
python -m pytest -vSolution: Use correct import paths:
# Correct
from src.vcm.vcm import VersionControlManager
from src.vcm.cli import handle_get_current_tagWe welcome contributions! Please follow these guidelines:
-
Fork the repository
git clone https://github.com/0jas/vcm.git cd vcm -
Create a virtual environment
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
Install in development mode
pip install -e ".[dev]" -
Create a feature branch
git checkout -b feature/your-feature-name
-
Make your changes
- Write clear, documented code
- Follow existing code style
- Add tests for new features
-
Run tests
python -m pytest -v
-
Format code
black src/ tests/ flake8 src/ tests/
-
Commit your changes
git add . git commit -m "feat: add new feature"
-
Push and create Pull Request
git push origin feature/your-feature-name
Follow Conventional Commits:
feat:New featurefix:Bug fixdocs:Documentation changestest:Test additions or changesrefactor:Code refactoringchore:Maintenance tasks
Examples:
feat: add support for custom prerelease identifiers
fix: handle empty repository gracefully
docs: update API reference for increment_prerelease_tag
test: add comprehensive CLI tests
- Follow PEP 8
- Use type hints where appropriate
- Write comprehensive docstrings
- Keep functions focused and small
- Add comments for complex logic
- All new features must include tests
- Maintain >90% code coverage
- Tests must pass before merging
- Update README.md for user-facing changes
- Add examples for new features
- Keep documentation clear and concise
- Documentation: https://0jas.github.io/vcm/
- Issues: GitHub Issues
- Discussions: GitHub Discussions
