Claude
Skills
Sign in
Back

git-sync-manager

Included with Lifetime
$97 forever

Multi-repository git synchronization patterns for batch operations

bashbashgitsyncmulti-repobatchautomation

What this skill does


# Git Sync Manager

Patterns for synchronizing multiple Git repositories with phased operations, status tracking, and error handling. Extracted from workspace-hub's repository sync scripts.

## When to Use This Skill

✅ **Use when:**
- Managing multiple Git repositories from a central location
- Performing batch git operations (pull, commit, push)
- Need consistent sync workflows across many repos
- Automating daily/periodic repository synchronization
- Building repository management CLIs

❌ **Avoid when:**
- Single repository operations
- Complex merge/rebase workflows requiring manual intervention
- Repositories with conflicting changes that need resolution

## Core Capabilities

### 1. Repository Discovery from .gitignore

Automatically discover repositories listed in .gitignore:

```bash
#!/bin/bash
# ABOUTME: Discover repositories from .gitignore patterns
# ABOUTME: Parses directory entries and validates git repos

WORKSPACE_ROOT="${1:-.}"

# Discover repositories from .gitignore
discover_repos() {
    local repos=()

    while IFS= read -r line; do
        # Skip comments and empty lines
        [[ "$line" =~ ^[[:space:]]*# ]] && continue
        [[ -z "$line" ]] && continue

        # Extract repo name (before the /)
        local repo_name="${line%%/*}"
        [[ -z "$repo_name" ]] && continue

        # Check if directory exists and is a git repo
        if [[ -d "$WORKSPACE_ROOT/$repo_name/.git" ]]; then
            repos+=("$repo_name")
        fi
    done < <(grep -v "^[[:space:]]*#" "$WORKSPACE_ROOT/.gitignore" | grep "/" | head -100)

    printf '%s\n' "${repos[@]}"
}

# Usage
REPOS=($(discover_repos))
echo "Found ${#REPOS[@]} repositories"
```

### 2. Multi-Phase Sync Pattern

Execute git operations in ordered phases:

```bash
#!/bin/bash
# ABOUTME: Multi-phase git synchronization
# ABOUTME: Executes pull → commit → push in controlled phases

set -e

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

# Counters
SUCCESS=0
FAILED=0
SKIPPED=0

# ─────────────────────────────────────────────────────────────────
# Phase 1: Pull
# ─────────────────────────────────────────────────────────────────

phase_pull() {
    local repos=("$@")

    echo -e "${CYAN}PHASE 1: Pulling latest changes${NC}"
    echo -e "${CYAN}─────────────────────────────────${NC}"

    for repo in "${repos[@]}"; do
        if [[ -d "$repo/.git" ]]; then
            echo -n "→ Pulling $repo... "

            local branch
            branch=$(cd "$repo" && git rev-parse --abbrev-ref HEAD 2>/dev/null)

            if (cd "$repo" && git pull origin "$branch" 2>&1 | grep -qE "Already up.to" ); then
                echo -e "${GREEN}✓ (up to date)${NC}"
                ((SUCCESS++))
            elif (cd "$repo" && git pull origin "$branch" &>/dev/null); then
                echo -e "${GREEN}✓ (updated)${NC}"
                ((SUCCESS++))
            else
                echo -e "${YELLOW}⚠ (offline or error)${NC}"
                ((SKIPPED++))
            fi
        fi
    done
}

# ─────────────────────────────────────────────────────────────────
# Phase 2: Commit
# ─────────────────────────────────────────────────────────────────

phase_commit() {
    local repos=("$@")
    local message="${COMMIT_MESSAGE:-chore: Batch synchronization}"

    echo -e "\n${CYAN}PHASE 2: Staging and committing changes${NC}"
    echo -e "${CYAN}───────────────────────────────────────${NC}"

    for repo in "${repos[@]}"; do
        if [[ -d "$repo/.git" ]]; then
            # Check for changes (staged or unstaged)
            local has_changes=false

            if ! (cd "$repo" && git diff --quiet 2>/dev/null); then
                has_changes=true
            fi
            if ! (cd "$repo" && git diff --cached --quiet 2>/dev/null); then
                has_changes=true
            fi

            if [[ "$has_changes" == "true" ]]; then
                echo -n "→ Committing $repo... "

                if (cd "$repo" && git add -A && git commit -m "$message" --no-verify 2>/dev/null); then
                    echo -e "${GREEN}✓${NC}"
                    ((SUCCESS++))
                else
                    echo -e "${YELLOW}⊘ (commit failed)${NC}"
                    ((SKIPPED++))
                fi
            else
                echo -e "${YELLOW}⊘ $repo: no changes${NC}"
                ((SKIPPED++))
            fi
        fi
    done
}

# ─────────────────────────────────────────────────────────────────
# Phase 3: Push
# ─────────────────────────────────────────────────────────────────

phase_push() {
    local repos=("$@")

    echo -e "\n${CYAN}PHASE 3: Pushing to remote${NC}"
    echo -e "${CYAN}──────────────────────────${NC}"

    for repo in "${repos[@]}"; do
        if [[ -d "$repo/.git" ]]; then
            local branch
            branch=$(cd "$repo" && git rev-parse --abbrev-ref HEAD 2>/dev/null)

            echo -n "→ Pushing $repo ($branch)... "

            if (cd "$repo" && git push origin "$branch" 2>&1 | grep -qE "Everything up-to-date|up to date"); then
                echo -e "${GREEN}✓ (up to date)${NC}"
                ((SUCCESS++))
            elif (cd "$repo" && git push origin "$branch" &>/dev/null); then
                echo -e "${GREEN}✓ (pushed)${NC}"
                ((SUCCESS++))
            else
                echo -e "${RED}✗${NC}"
                ((FAILED++))
            fi
        fi
    done
}

# ─────────────────────────────────────────────────────────────────
# Full Sync
# ─────────────────────────────────────────────────────────────────

full_sync() {
    local repos=("$@")

    phase_pull "${repos[@]}"
    phase_commit "${repos[@]}"
    phase_push "${repos[@]}"

    # Summary
    echo ""
    echo -e "${CYAN}═══════════════════════════════════════${NC}"
    echo -e "${CYAN}Summary${NC}"
    echo -e "${CYAN}═══════════════════════════════════════${NC}"
    echo -e "Total Repos:    ${#repos[@]}"
    echo -e "Successful:     ${GREEN}${SUCCESS}${NC}"
    echo -e "Skipped:        ${YELLOW}${SKIPPED}${NC}"
    echo -e "Failed:         ${RED}${FAILED}${NC}"
}
```

### 3. Repository Status Check

Get detailed status for multiple repositories:

```bash
#!/bin/bash
# ABOUTME: Check git status across multiple repositories
# ABOUTME: Reports uncommitted, unpushed, and behind-remote states

check_repo_status() {
    local repo="$1"

    if [[ ! -d "$repo/.git" ]]; then
        echo "not_git"
        return
    fi

    cd "$repo" || return

    # Check for uncommitted changes
    if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
        echo "uncommitted"
        return
    fi

    # Check for unpushed commits
    local branch
    branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
    local ahead
    ahead=$(git rev-list --count "origin/$branch..HEAD" 2>/dev/null || echo "0")

    if [[ "$ahead" -gt 0 ]]; then
        echo "unpushed"
        return
    fi

    # Check if behind remote
    git fetch origin "$branch" --quiet 2>/dev/null
    local behind
    behind=$(git rev-list --count "HEAD..origin/$branch" 2>/dev/null || echo "0")

    if [[ "$behind" -gt 0 ]]; then
        echo "behind"
        return
    fi

    echo "clean"
}

# Batch status check
batch_status() {
    local repos=("$@")

    printf "%-30s %-15s %s\n" "Repository" "Status" "Branch"
    printf "%s\n" "────────────────────────────────────────────────────────"

    for repo in "${repos[@]}"; do
        local status
        status=$(check_repo_status "$repo")

        local branch="N/A"
        if [[ -d "$repo/.git" ]]; then
            branch=$(cd "$repo" && git rev-parse --abbrev-ref HEAD 2>/dev/null)
        fi

        local color=""
        case "$status" in
            clean)      color="${GREEN}" ;;
            uncommitted) color="${YELLOW}" ;;
            unpushed)   color="${CYAN}" ;;
            behind)     color="${RED}" ;;
            *)          color="${NC}" ;;
        esac

        printf "%-30s ${color}

Related in bash