#!/usr/bin/env bash # Enhanced Git Workflow for Local Self-Contained Development # Provides full branching, forking, PR simulation, and merge workflows set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_ROOT" # Configuration DEFAULT_MAIN_BRANCH="master" DEFAULT_DEV_BRANCH="develop" LOCAL_FORK_PREFIX="fork" BACKUP_DIR="$PROJECT_ROOT/.git-backups" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color log() { echo -e "${GREEN}[GIT-WORKFLOW]${NC} $*" } warn() { echo -e "${YELLOW}[WARNING]${NC} $*" } error() { echo -e "${RED}[ERROR]${NC} $*" } info() { echo -e "${BLUE}[INFO]${NC} $*" } usage() { cat << EOF Enhanced Git Workflow for MEV Bot - Self-Contained Development Stack USAGE: $0 [args] BRANCH OPERATIONS: feature - Create feature branch from develop fix - Create hotfix branch from master release - Create release branch branch-list - List all branches with status branch-clean - Clean merged branches FORK & PR SIMULATION: fork-create - Create local fork simulation fork-list - List all local forks pr-create - Create PR simulation (local merge preparation) pr-review - Review changes for PR pr-merge - Merge PR with validation MERGE & REBASE: merge - Smart merge with CI validation rebase - Interactive rebase with conflict resolution sync - Sync current branch with upstream CI INTEGRATION: ci-branch - Run CI on current branch ci-pr - Run CI for PR validation pre-commit - Manual pre-commit validation pre-push - Manual pre-push validation BACKUP & RESTORE: backup - Create full git backup restore - Restore from backup checkpoint - Create named checkpoint ADVANCED WORKFLOWS: flow-init - Initialize gitflow-style workflows hotfix - Complete hotfix workflow release-finish - Complete release workflow conflict-resolve - Interactive conflict resolution EXAMPLES: $0 feature add-math-optimizations $0 fork-create experimental-trading $0 pr-create develop $0 merge feature/new-arbitrage-engine $0 ci-pr feature/fix-parser-bug EOF } # Check if we're in a git repository check_git_repo() { if ! git rev-parse --git-dir >/dev/null 2>&1; then error "Not in a git repository" exit 1 fi } # Get current branch get_current_branch() { git rev-parse --abbrev-ref HEAD } # Check if branch exists branch_exists() { git show-ref --verify --quiet "refs/heads/$1" } # Run CI pipeline for branch validation run_ci_for_branch() { local branch="$1" log "Running CI validation for branch: $branch" # Switch to branch temporarily if not current local current_branch current_branch=$(get_current_branch) if [[ "$current_branch" != "$branch" ]]; then git checkout "$branch" fi # Run appropriate CI level based on branch type if [[ "$branch" =~ ^(feature|fix)/ ]]; then log "Running development CI for $branch" make ci-dev elif [[ "$branch" =~ ^release/ ]] || [[ "$branch" == "$DEFAULT_MAIN_BRANCH" ]]; then log "Running full CI for $branch" make ci-full else log "Running quick CI for $branch" make ci-quick fi # Switch back if we changed branches if [[ "$current_branch" != "$branch" ]]; then git checkout "$current_branch" fi } # Create feature branch create_feature_branch() { local feature_name="$1" if [[ -z "$feature_name" ]]; then error "Feature name required" echo "Usage: $0 feature " exit 1 fi local branch_name="feature/$feature_name" if branch_exists "$branch_name"; then error "Branch $branch_name already exists" exit 1 fi log "Creating feature branch: $branch_name" # Ensure we're on develop and it's up to date if branch_exists "$DEFAULT_DEV_BRANCH"; then git checkout "$DEFAULT_DEV_BRANCH" git pull origin "$DEFAULT_DEV_BRANCH" 2>/dev/null || true else warn "Develop branch doesn't exist, creating from master" git checkout "$DEFAULT_MAIN_BRANCH" git checkout -b "$DEFAULT_DEV_BRANCH" fi # Create and switch to feature branch git checkout -b "$branch_name" log "✅ Feature branch '$branch_name' created and active" log "💡 Run tests with: make ci-dev" } # Create hotfix branch create_fix_branch() { local fix_name="$1" if [[ -z "$fix_name" ]]; then error "Fix name required" echo "Usage: $0 fix " exit 1 fi local branch_name="fix/$fix_name" if branch_exists "$branch_name"; then error "Branch $branch_name already exists" exit 1 fi log "Creating hotfix branch: $branch_name" # Create from master git checkout "$DEFAULT_MAIN_BRANCH" git pull origin "$DEFAULT_MAIN_BRANCH" 2>/dev/null || true git checkout -b "$branch_name" log "✅ Hotfix branch '$branch_name' created and active" log "💡 Run tests with: make ci-full" } # Create local fork simulation create_fork() { local fork_name="$1" if [[ -z "$fork_name" ]]; then error "Fork name required" echo "Usage: $0 fork-create " exit 1 fi local fork_branch="${LOCAL_FORK_PREFIX}/$fork_name" if branch_exists "$fork_branch"; then error "Fork $fork_branch already exists" exit 1 fi log "Creating local fork: $fork_branch" # Create orphan branch for fork simulation git checkout --orphan "$fork_branch" git reset --hard "$DEFAULT_MAIN_BRANCH" # Create initial commit for fork git commit --allow-empty -m "chore: initialize fork '$fork_name'" log "✅ Local fork '$fork_name' created" log "💡 This simulates a separate fork for experimental development" } # List all branches with status list_branches() { log "Repository branch overview:" echo "" echo "📋 Local Branches:" git branch -v echo "" echo "🔀 Remote Branches:" git branch -rv 2>/dev/null || echo "No remote branches" echo "" echo "🍴 Local Forks:" git branch | grep "^ $LOCAL_FORK_PREFIX/" || echo "No local forks" echo "" echo "📊 Branch Status:" local current_branch current_branch=$(get_current_branch) echo "Current: $current_branch" # Check if current branch has uncommitted changes if ! git diff --quiet || ! git diff --cached --quiet; then warn "Current branch has uncommitted changes" fi # Check if current branch is ahead/behind local upstream upstream=$(git rev-parse --abbrev-ref @{u} 2>/dev/null || echo "") if [[ -n "$upstream" ]]; then local ahead behind ahead=$(git rev-list --count "$upstream..HEAD" 2>/dev/null || echo "0") behind=$(git rev-list --count "HEAD..$upstream" 2>/dev/null || echo "0") if [[ "$ahead" -gt 0 ]]; then info "Current branch is $ahead commits ahead of $upstream" fi if [[ "$behind" -gt 0 ]]; then warn "Current branch is $behind commits behind $upstream" fi fi } # Create PR simulation (prepare for merge) create_pr() { local target_branch="$1" local current_branch current_branch=$(get_current_branch) if [[ -z "$target_branch" ]]; then error "Target branch required" echo "Usage: $0 pr-create " exit 1 fi if ! branch_exists "$target_branch"; then error "Target branch '$target_branch' does not exist" exit 1 fi log "Creating PR simulation: $current_branch → $target_branch" # Run CI validation for PR run_ci_for_branch "$current_branch" # Show diff summary echo "" log "PR Summary:" echo "From: $current_branch" echo "To: $target_branch" echo "" echo "📝 Changed files:" git diff --name-status "$target_branch..$current_branch" echo "" echo "📊 Commit summary:" git log --oneline "$target_branch..$current_branch" echo "" echo "🔍 Conflict check:" if git merge-tree "$(git merge-base "$target_branch" "$current_branch")" "$target_branch" "$current_branch" | grep -q "<<<<<<< "; then warn "Potential merge conflicts detected" echo "Run: $0 conflict-resolve" else log "No merge conflicts detected" fi echo "" log "✅ PR simulation ready" log "Next steps:" log " - Review changes: $0 pr-review" log " - Merge PR: $0 pr-merge $target_branch" } # Review PR changes review_pr() { local current_branch current_branch=$(get_current_branch) log "Reviewing changes for: $current_branch" # Show detailed diff echo "" echo "📝 Detailed changes:" git diff develop.."$current_branch" --stat echo "" read -p "View full diff? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then git diff develop.."$current_branch" fi echo "" log "Code quality checks:" # Run linting if make ci-quick; then log "✅ Code quality checks passed" else error "❌ Code quality checks failed" echo "Fix issues before merging" exit 1 fi } # Merge PR with validation merge_pr() { local target_branch="$1" local current_branch current_branch=$(get_current_branch) if [[ -z "$target_branch" ]]; then error "Target branch required" echo "Usage: $0 pr-merge " exit 1 fi log "Merging PR: $current_branch → $target_branch" # Final CI validation log "Running final CI validation..." run_ci_for_branch "$current_branch" # Create backup before merge local backup_name="before-merge-$(date +%Y%m%d-%H%M%S)" create_backup "$backup_name" # Switch to target branch and merge git checkout "$target_branch" # Update target branch git pull origin "$target_branch" 2>/dev/null || true # Perform merge if git merge --no-ff "$current_branch" -m "Merge branch '$current_branch' into $target_branch 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude "; then log "✅ Merge completed successfully" # Run post-merge CI log "Running post-merge validation..." run_ci_for_branch "$target_branch" # Optional: Delete merged branch read -p "Delete merged branch '$current_branch'? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then git branch -d "$current_branch" log "Deleted merged branch: $current_branch" fi else error "Merge failed - conflicts need resolution" echo "Run: $0 conflict-resolve" exit 1 fi } # Smart merge with CI validation smart_merge() { local source_branch="$1" local current_branch current_branch=$(get_current_branch) if [[ -z "$source_branch" ]]; then error "Source branch required" echo "Usage: $0 merge " exit 1 fi log "Smart merge: $source_branch → $current_branch" # Validate both branches run_ci_for_branch "$current_branch" run_ci_for_branch "$source_branch" # Create backup local backup_name="before-smart-merge-$(date +%Y%m%d-%H%M%S)" create_backup "$backup_name" # Perform merge if git merge "$source_branch"; then log "✅ Smart merge completed" run_ci_for_branch "$current_branch" else error "Merge conflicts detected" echo "Run: $0 conflict-resolve" exit 1 fi } # Interactive conflict resolution resolve_conflicts() { log "Interactive conflict resolution" # Check if we're in a merge if [[ ! -f .git/MERGE_HEAD ]]; then error "No merge in progress" exit 1 fi # Show conflicted files echo "" echo "📝 Conflicted files:" git diff --name-only --diff-filter=U echo "" echo "🔧 Resolution options:" echo "1) Open merge tool (if configured)" echo "2) Show conflicts in terminal" echo "3) Abort merge" read -p "Choose option (1-3): " -n 1 -r echo case $REPLY in 1) if git config merge.tool >/dev/null; then git mergetool else warn "No merge tool configured" echo "Configure with: git config merge.tool " fi ;; 2) echo "" echo "📝 Conflicts:" git diff --diff-filter=U ;; 3) git merge --abort log "Merge aborted" return ;; *) error "Invalid option" return ;; esac # After resolution echo "" read -p "Have conflicts been resolved? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then git add . git commit --no-edit log "✅ Merge completed" # Run CI after merge run_ci_for_branch "$(get_current_branch)" fi } # Create backup create_backup() { local backup_name="${1:-backup-$(date +%Y%m%d-%H%M%S)}" mkdir -p "$BACKUP_DIR" log "Creating backup: $backup_name" # Create git bundle git bundle create "$BACKUP_DIR/$backup_name.bundle" --all # Create stash backup if there are changes if ! git diff --quiet || ! git diff --cached --quiet; then git stash push -m "Backup stash: $backup_name" echo "backup-stash-$backup_name" > "$BACKUP_DIR/$backup_name.stash" fi # Save current branch info echo "$(get_current_branch)" > "$BACKUP_DIR/$backup_name.branch" log "✅ Backup created: $backup_name" log "Location: $BACKUP_DIR/" } # Restore from backup restore_backup() { local backup_name="$1" if [[ -z "$backup_name" ]]; then error "Backup name required" echo "Available backups:" ls -1 "$BACKUP_DIR"/*.bundle 2>/dev/null | sed 's/.*\///' | sed 's/\.bundle$//' || echo "No backups found" exit 1 fi local bundle_file="$BACKUP_DIR/$backup_name.bundle" if [[ ! -f "$bundle_file" ]]; then error "Backup not found: $backup_name" exit 1 fi warn "This will restore git state from backup: $backup_name" read -p "Continue? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Restore cancelled" exit 0 fi log "Restoring from backup: $backup_name" # Restore from bundle git fetch "$bundle_file" # Restore branch if saved if [[ -f "$BACKUP_DIR/$backup_name.branch" ]]; then local saved_branch saved_branch=$(cat "$BACKUP_DIR/$backup_name.branch") if branch_exists "$saved_branch"; then git checkout "$saved_branch" fi fi log "✅ Backup restored: $backup_name" } # Run CI for current branch run_ci_branch() { local current_branch current_branch=$(get_current_branch) run_ci_for_branch "$current_branch" } # Run CI for PR validation run_ci_pr() { local branch="$1" if [[ -z "$branch" ]]; then branch=$(get_current_branch) fi log "Running PR validation CI for: $branch" run_ci_for_branch "$branch" # Additional PR-specific checks log "Running PR-specific validations..." # Check commit messages log "Checking commit message format..." git log --oneline "develop..$branch" | while read -r line; do if [[ ! "$line" =~ ^[a-f0-9]+\ (feat|fix|chore|docs|style|refactor|perf|test)(\(.+\))?:\ .+ ]]; then warn "Commit message format: $line" warn "Should follow: type(scope): description" fi done log "✅ PR validation completed" } # Clean merged branches clean_branches() { log "Cleaning merged branches..." # Get merged branches (excluding master, develop, and current) local current_branch current_branch=$(get_current_branch) local merged_branches merged_branches=$(git branch --merged "$DEFAULT_MAIN_BRANCH" | grep -v "$DEFAULT_MAIN_BRANCH" | grep -v "$DEFAULT_DEV_BRANCH" | grep -v "^* $current_branch" | grep -v "^ $current_branch" | sed 's/^[ *]*//') if [[ -z "$merged_branches" ]]; then log "No merged branches to clean" return fi echo "📝 Merged branches to delete:" echo "$merged_branches" echo "" read -p "Delete these branches? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then echo "$merged_branches" | while read -r branch; do if [[ -n "$branch" ]]; then git branch -d "$branch" log "Deleted: $branch" fi done fi } # Main command dispatcher main() { check_git_repo if [[ $# -eq 0 ]]; then usage exit 0 fi local command="$1" shift case "$command" in feature) create_feature_branch "$@" ;; fix) create_fix_branch "$@" ;; fork-create) create_fork "$@" ;; fork-list) git branch | grep "^ $LOCAL_FORK_PREFIX/" || echo "No local forks" ;; pr-create) create_pr "$@" ;; pr-review) review_pr ;; pr-merge) merge_pr "$@" ;; merge) smart_merge "$@" ;; conflict-resolve) resolve_conflicts ;; branch-list) list_branches ;; branch-clean) clean_branches ;; backup) create_backup "$@" ;; restore) restore_backup "$@" ;; ci-branch) run_ci_branch ;; ci-pr) run_ci_pr "$@" ;; pre-commit) make ci-precommit ;; pre-push) make ci-dev ;; *) error "Unknown command: $command" usage exit 1 ;; esac } # Run main function main "$@"