Infrastructure
developer tools
How to Create a Git Workflow Automation System
Automate Git branching, merging, and release workflows.
Jay Banlasan
The AI Systems Guy
Git workflow automation for branch management removes the repetitive parts of version control. I use a set of scripts that handle branch creation, commit formatting, merge workflows, and release tagging. The goal is consistency across every project without remembering conventions manually.
What You Need Before Starting
- Git installed
- Bash or Python
- A branching convention (this tutorial uses main/feature/release)
- A project repository
Step 1: Automate Branch Creation
#!/bin/bash
# git-feature.sh - Create a feature branch with conventions
BRANCH_TYPE="${1:-feature}"
DESCRIPTION="$2"
if [ -z "$DESCRIPTION" ]; then
echo "Usage: ./git-feature.sh [feature|fix|hotfix] description-here"
exit 1
fi
SLUG=$(echo "$DESCRIPTION" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd '[:alnum:]-')
DATE=$(date '+%Y%m%d')
BRANCH_NAME="${BRANCH_TYPE}/${DATE}-${SLUG}"
git checkout main
git pull origin main
git checkout -b "$BRANCH_NAME"
echo "Created branch: $BRANCH_NAME"
Step 2: Enforce Commit Message Format
#!/usr/bin/env python3
"""Git commit-msg hook for format enforcement."""
import sys
import re
VALID_TYPES = ["feat", "fix", "refactor", "docs", "test", "chore", "perf"]
def validate_commit_message(message):
pattern = rf"^({'|'.join(VALID_TYPES)}): .{{10,72}}$"
first_line = message.strip().split("\n")[0]
if not re.match(pattern, first_line):
print(f"Invalid commit message format.")
print(f"Expected: type: description (10-72 chars)")
print(f"Valid types: {', '.join(VALID_TYPES)}")
print(f"Got: {first_line}")
return False
return True
if __name__ == "__main__":
commit_msg_file = sys.argv[1]
with open(commit_msg_file) as f:
message = f.read()
if not validate_commit_message(message):
sys.exit(1)
Install it:
cp commit_msg_hook.py .git/hooks/commit-msg
chmod +x .git/hooks/commit-msg
Step 3: Build a Smart Merge Script
#!/bin/bash
# git-merge-feature.sh - Merge current branch into main with checks
CURRENT=$(git branch --show-current)
if [ "$CURRENT" = "main" ]; then
echo "Already on main. Switch to a feature branch first."
exit 1
fi
echo "Merging $CURRENT into main..."
# Run tests first
if [ -f "pytest.ini" ] || [ -f "setup.cfg" ]; then
echo "Running tests..."
python -m pytest --quiet
if [ $? -ne 0 ]; then
echo "Tests failed. Fix before merging."
exit 1
fi
fi
# Check for uncommitted changes
if [ -n "$(git status --porcelain)" ]; then
echo "Uncommitted changes detected. Commit or stash first."
exit 1
fi
git checkout main
git pull origin main
git merge "$CURRENT" --no-ff -m "merge: $CURRENT into main"
if [ $? -eq 0 ]; then
echo "Merge successful."
echo "Push with: git push origin main"
echo "Delete branch with: git branch -d $CURRENT"
else
echo "Merge conflicts detected. Resolve manually."
fi
Step 4: Automate Release Tagging
import subprocess
import json
from datetime import datetime
def get_latest_tag():
result = subprocess.run(
["git", "tag", "--sort=-v:refname"],
capture_output=True, text=True
)
tags = result.stdout.strip().split("\n")
return tags[0] if tags[0] else "v0.0.0"
def bump_version(current, bump_type="patch"):
parts = current.lstrip("v").split(".")
major, minor, patch = int(parts[0]), int(parts[1]), int(parts[2])
if bump_type == "major":
major += 1; minor = 0; patch = 0
elif bump_type == "minor":
minor += 1; patch = 0
else:
patch += 1
return f"v{major}.{minor}.{patch}"
def create_release(bump_type="patch"):
current = get_latest_tag()
new_version = bump_version(current, bump_type)
log = subprocess.run(
["git", "log", f"{current}..HEAD", "--oneline"],
capture_output=True, text=True
).stdout.strip()
tag_message = f"Release {new_version}\n\nChanges:\n{log}"
subprocess.run(["git", "tag", "-a", new_version, "-m", tag_message])
print(f"Created tag: {new_version}")
print(f"Changes since {current}:\n{log}")
print(f"\nPush with: git push origin {new_version}")
if __name__ == "__main__":
import sys
bump_type = sys.argv[1] if len(sys.argv) > 1 else "patch"
create_release(bump_type)
Step 5: Create Aliases for Speed
Add to your .gitconfig or .bashrc:
alias gf="./scripts/git-feature.sh feature"
alias gfix="./scripts/git-feature.sh fix"
alias gmerge="./scripts/git-merge-feature.sh"
alias grelease="python scripts/release.py"
Now branching is just:
gf "add user notifications"
# work, commit, commit...
gmerge
grelease patch
What to Build Next
Add changelog generation that pulls commit messages between tags and formats them into a CHANGELOG.md automatically on each release.
Related Reading
- Building a Changelog for Your Operations - tracking changes systematically
- Why Process Documentation Is the First Step - documenting your workflow conventions
- The Canary Deployment for Operations - safe deployment practices
Want this system built for your business?
Get a free assessment. We will map every system your business needs and show you the ROI.
Get Your Free Assessment