Systems Library / Infrastructure / How to Create a Git Workflow Automation System
Infrastructure developer tools

How to Create a Git Workflow Automation System

Automate Git branching, merging, and release workflows.

Jay Banlasan

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

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

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

Related Systems