Skip to content

Latest commit

 

History

History
898 lines (855 loc) · 30.1 KB

File metadata and controls

898 lines (855 loc) · 30.1 KB

gitmanager

config.mk

# git_manager version
VERSION = :0.8.2

# Customize below to fit your system

# paths
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man

Makefile

# gitmanager Manage git repos
# Copyright (C) 2019-2021 Nan0Scho1ar (Christopher Mackinga)
# See LICENSE file for copyright and license details.

include config.mk

SRC = gitmanager

install: all
	mkdir -p ${DESTDIR}${PREFIX}/bin
	cp -f ${SRC} ${DESTDIR}${PREFIX}/bin/gitmanager
	chmod 755 ${DESTDIR}${PREFIX}/bin/gitmanager
	mkdir -p ${DESTDIR}${MANPREFIX}/man1
	sed "s/VERSION/${VERSION}/g" < gitmanager.1 > ${DESTDIR}${MANPREFIX}/man1/gitmanager.1
	chmod 644 ${DESTDIR}${MANPREFIX}/man1/gitmanager.1

uninstall:
	rm -f ${DESTDIR}${PREFIX}/bin/gitmanager\
		${DESTDIR}${MANPREFIX}/man1/gitmanager.1

.PHONY: all install uninstall

File Header

#!/bin/bash
# gitmanager: Manage multiple git repositories and branches
# Copyright (C) 2019-2021 Nan0Scho1ar (Christopher Mackinga)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

Setup

options=("Search for git repos" "Check for changes" "List changes" "Pull and push" "Pull and push auto" "Add all changes + commit + push" "Clean old branches" "Merge origin/master into branches" "List branches" "Compare master" "Compare remote" "Quit")

if [ -t 1 ] && command -v tput > /dev/null; then
    # see if it supports colors
    ncolors=$(tput colors)
    if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
        bold="$(tput bold       || echo)"
        blink="$(tput blink     || echo)"
        reset="$(tput sgr0      || echo)"
        black="$(tput setaf 0   || echo)"
        red="$(tput setaf 1     || echo)"
        green="$(tput setaf 2   || echo)"
        yellow="$(tput setaf 3  || echo)"
        blue="$(tput setaf 4    || echo)"
        magenta="$(tput setaf 5 || echo)"
        cyan="$(tput setaf 6    || echo)"
        white="$(tput setaf 7   || echo)"
    fi
fi

repos=""
repo_auto=false
branch_auto=false

if [ -z $XDG_CONFIG_HOME ]; then
    cfg_path="$HOME/.config/gitmanager"
else
    cfg_path="$XDG_CONFIG_HOME/gitmanager"
fi
mkdir -p "$cfg_path"

Helpers

show_menu()

# Displays the option menu (if no args provided to program)
show_menu() {
    select option in "${options[@]}"; do
        echo $option
        break
    done
}

set_repos()

# Sets repos variable using cache files
set_repos() { repos=$(cat ${cfg_path}/repos.cache | grep -v -f ${cfg_path}/repos.exclude); }

using_repos()

# Sets repos and prints them to the user
using_repos() { set_repos && echo "Using repos:${cyan}$(cat ${cfg_path}/repos.cache | grep -vf ${cfg_path}/repos.exclude)${reset}"; }

Colour

colour_pwd()

# pwd but coloured
colour_pwd() { echo ${cyan}$(pwd)${reset}; }

colour_path

# path bub coloured
colour_path() { echo ${cyan}${path}${reset}; }

Git funcs

git_branch()

# Used to make all calls to branch use the same formatting.
git_branch() { git branch; }

git_commit()

# Used to make all calls to commit use the same formatting.
git_commit() { read -p "Please enter commit message: " && git commit -m "$REPLY"; }

git_push()

# Used to make all calls to push use the same formatting.
git_push() { echo "${magenta}Pushing...${reset}" && git push && echo; }

git_pull()

# Used to make all calls to pull use the same formatting.
git_pull() { echo "${magenta}Pulling...${reset}" && git pull; }

git_merge()

# Used to make all calls to merge use the same formatting.
git_merge() { echo "${magenta}Merging...${reset}" && git merge $1; }

git_merge_abort()

# Used to make all calls to merge use the same formatting.
git_merge_abort() { echo "${magenta}Aborting Merge...${reset}" && git merge --abort; }

git_rebase()

# Used to make all calls to rebase use the same formatting.
git_rebase() { echo "${magenta}Rebasing...${reset}" && git rebase $1; }

git_checkout()

# Used to make all calls to checkout use the same formatting.
git_checkout() { git checkout $1 2>&1 1>/dev/null | sed -E "s/(.+')(.+)('.*)/\1${blue}\2${reset}\3/"; }

git_fetch()

# Fetches a git repo
git_fetch() {
    cd $1
    if [ -z $2 ]; then
        echo -e "${magenta}Fetching...${reset} $(colour_pwd)"
        git fetch origin --prune 2>&1 >/dev/null;
    else
        tput cup $2 0
        echo -e "${magenta}Fetching...${reset} $(colour_pwd)"
        git fetch origin --prune 2>&1 >/dev/null;
        tput cup $2 0
        echo -e "${magenta}Fetching...${reset} $(colour_pwd)    ${magenta}Done${reset}"
    fi
}

git_fetch_all_repos()

# Fetches all repos
git_fetch_all_repos() {
    echo -e "\e[?1049h"
    sleep 0.05
    line_num=0
    for path in $repos; do
        git_fetch "$path/.." $line_num &
        line_num=$((line_num+1))
        sleep 0.01
    done
    wait
    echo -e "\e[?1049l"
    tput cuu 1
}

git_status()

# Calls git status with ability to use cached value
git_status() {
    git status
}

git_get_branches()

# Checks out master and updates list of branches
git_get_branches() {
    git_checkout master
    branches=$(git for-each-ref --format='%(refname)' refs/heads/ | sed "s|refs/heads/||")
    echo -e "\nFound branches:\n$(echo ${blue}${branches}${reset} | tr " " "\n")\n"
}

git_summary()

# Provides a one line summary on the current repo/branch
git_summary() {
    tree_is_clean && branch_up_to_date "CACHED" && echo "$(colour_pwd): ${green}No changes found${reset}" && return 0
    tree_is_clean "CACHED" && echo "$(colour_pwd): ${yellow}Out of sync${reset}" && return 1
    has_conflicts "CACHED" && echo "$(colour_pwd): ${red}Merge conflict detected${reset}" && return 2
    echo "$(colour_pwd): ${red}Changes detected${reset}" && return 3
}

git_state()

# Provides a one line summary on the current repo/branch
git_state() {
    tree_is_clean && branch_up_to_date "CACHED" && return 0
    tree_is_clean "CACHED" && return 1
    has_conflicts "CACHED" && return 2
    return 3
}

Checks

tree_is_clean()

# Checks if the current branch working tree is clean. arg1 can be used to toggle CACHED
tree_is_clean() { git_status $1 | grep -q "nothing to commit, working tree clean" && return 0 || return 1; }

check_should_skip_repo()

# Checks if a repo should be skipped
check_should_skip_repo() {
    [[ "$repo_auto" == true ]] || ask "Update project $(colour_path)" || return 1
    git_fetch "$path/.."
    ask_if_skip_dirty && return 0 || echo; return 1
}

branch_up_to_date()

# Checks if the current branch is up to date. arg1 can be used to toggle CACHED
branch_up_to_date() { git_status $1 | grep -q "Your branch is up to date with " && return 0 || return 1; }

has_conflicts()

# Checks if the current branch has conflicts. arg1 can be used to toggle CACHED
has_conflicts() { git_status $1 | grep -Eq "both added|both modified" && return 0 || return 1; }

get_ahead_behind()

# Checks how far ahead/behind a branch is
get_ahead_behind() {
    git_upstream_status_delta=$(git rev-list --left-right ${1}...${2} -- 2>/dev/null)
    AHEAD=$(echo $git_upstream_status_delta | grep -c '^<');
    BEHIND=$(echo $git_upstream_status_delta | grep -c '^>');
    [[ "$AHEAD" == 0 ]] && ahead="${green}$AHEAD${reset}" || ahead="${red}$AHEAD${reset}"
    [[ "$BEHIND" == 0 ]] && behind="${green}$BEHIND${reset}" || behind="${red}$BEHIND${reset}"
}

Ask

ask()

# Promts the user to answer a yes/no question.
# Returns after a single char is entered without hitting return.
ask() {
    read -p "${1} ${yellow}y/n${reset} " -n 1 -r
    echo
    [[ $REPLY =~ ^[Yy]$ ]] && return 0 || return 1
}

ask_if_push()

# Asks the user if they want to push then pushes and shows status.
ask_if_push() {
    git_checkout $branch | grep -q "but the upstream is gone." && echo -e "Remote has been deleted. Pushing will recreate it.\n"
    ask "Push" && git_push && git_status && echo
}

ask_if_skip_repo()

# Prompts the user to skip if the current branch is dirty
ask_if_skip_dirty() {
    tree_is_clean && return 0
    git_status
    ask "Working tree is not clean, would you like to skip this project (y to skip, n to recheck)" && return 1
    ask_if_skip_dirty
}

ask_if_skip_dirty_merge()

# Prompts the user to skip if the current branch is dirty or revert the merge and continue
ask_if_skip_dirty_merge() {
    tree_is_clean && return 0
    git_status
    echo    "${red}Auto merge failed, conflicts found${reset}."
    echo    "Would you like to skip remaining branches in this project or revert the merge?"
    read -p "(y to skip, n to recheck, r to revert merge and continue) ${yellow}y/n/r${reset} " -n 1 -r
    echo -e "\n\n"
    [[ $REPLY =~ ^[Yy]$ ]] && return 1
    [[ $REPLY =~ ^[Rr]$ ]] && git_merge_abort
    ask_if_skip_dirty_merge
}

Cmds

find()_repos

# Finds repos on the system
find_repos() {
    cd $1
    find ~+ -name .git -type d -prune 2> /dev/null  | grep -v -f ${cfg_path}/repos.cache >> ${cfg_path}/repos.cache
    echo -e "Updated repos.cache\n"

    if [[ ! -e ${cfg_path}/repos.cache ]]; then
        echo "Cannot find repos.cache. Exiting..."
        read -p "Press enter to continue"
        exit 1
    fi
    echo "Found the following repos (in repos.cache)"
    cat ${cfg_path}/repos.cache
    echo
    if [[ ! -e ${cfg_path}/repos.exclude ]]; then
        echo "Cannot find repos.exclude"
        touch ${cfg_path}/repos.exclude
        echo -e "Created repos.exclude\nCopy the path of any unwanted repos in the above output to a new line of this file.\n"
        read -p "Press enter to continue once you have completed this step"
    fi
    echo "Excluding the following repos (in repos.exclude)"
    cat ${cfg_path}/repos.exclude
    echo -e "\nFinal list"
    cat ${cfg_path}/repos.cache | grep -vf ${cfg_path}/repos.exclude
    repos=$(cat ${cfg_path}/repos.cache | grep -v -f ${cfg_path}/repos.exclude)
}

repos_summary()

# Displays a brief summary of all repos
repos_summary() {
    git_fetch_all_repos
    for path in $repos; do
        cd "$path/..";
        git_summary;
    done | column -t -c 1 -s ":";
}

repos_summary_compact()

# Displays a brief summary of all repos
repos_summary_compact() {
    git_fetch_all_repos
    total_repos=0
    clean=0
    out_of_sync=0
    merge_conflicts=0
    changes_detected=0
    for path in $repos; do
        cd "$path/..";
        git_state
        exit_code=$?
        total_repos=$((total_repos+1))
        case $exit_code in
            0) clean=$((clean+1)) ;;
            1) out_of_sync=$((out_of_sync+1)) ;;
            2) merge_conflicts=$((merge_conflicts+1)) ;;
            3) changes_detected=$((changes_detected+1)) ;;
        esac
    done
    echo "Total Repos: $total_repos"
    echo "Clean: $green$clean$reset | Out Of Sync: $yellow$out_of_sync$reset | Merge Conflicts: $red$merge_conflicts$reset | Changes Detected: $red$changes_detected$reset"
}

repos_status()

# Displays the status of all repos
repos_status() {
    for path in $repos; do
        cd "$path/.."
        git_summary
        git_status
        echo -e '\n'
    done
}

compare_branches()

# Compares all the branches of all the repos to a specific branch
compare_branches() {
    [ -z "$1" ] && continue;
    for path in $repos; do
        cd "$path/.."
        header=$(echo -e "----------------------------------------~-----< $(pwd | xargs basename) >-----~----------------------------------------")
        footer=$(echo -e "________________________________________~______________________~________________________________________")
        git for-each-ref --format="%(refname:short) %(upstream:short)" refs/heads | \
        while read local remote; do
            [[ $1 == "origin/master" ]] && remote="origin/master"
            get_ahead_behind $local $remote
            echo -e "${local}~(ahead ${ahead}) | (behind ${behind})~$remote\n"
        done | echo -e "${header}\n$(cat)\n${footer}"
    done | cat | column -t -c 1 -s "~"
    echo
}

commit_and_push()

# Commit changes and push in all branches
commit_and_push() {
    for path in $repos; do
        cd "$path/.."
        git_summary && continue
        [[ $? == 1 ]] && continue
        git_status
        # prompt for diff unless NODIFF specified
        [[ $1 != "NODIFF" ]] && ask "Show diff" && echo -e "$(git diff --color=always)\n"

        if ask "Stage all changes + commit"; then
            git add -A
            git_status
            git_commit
            git_status
            ask_if_push
        fi
        echo -e "$(git_status)\n\n"
    done
}

rebase_branches()

# Rebase all branches
rebase_branches() {
    for path in $repos; do
        check_should_skip_repo && git_get_branches || continue
        for branch in $branches; do
            git_checkout
            if [[ $2 != "AUTOREBASE" ]]; then
                ask "Rebase this branch on $1" || echo -e "Skipping...\n"; continue
            fi
            git_rebase
            ask_if_skip_dirty_merge || continue
            ask_if_push
        done
    done
}

merge_into_branches()

# Merge a branch into all branches
merge_into_branches() {
    for path in $repos; do
        check_should_skip_repo && git_get_branches || continue
        for branch in $branches; do
            git_checkout
            if [[ $1 != "AUTOMERGE" ]]; then
                ask "Merge origin/master into this branch" || echo "Skipping..."; continue
            fi
            git_merge
            ask_if_skip_dirty_merge || continue
            ask_if_push
        done
    done
}

clean_branches()

# Clean old branches already merged into master
clean_branches() {
    for path in $repos; do
       check_should_skip_repo && git_get_branches || continue
       for branch in $branches; do
           get_ahead_behind "$branch" "origin/master"
           printf "$branch (ahead $ahead) | (behind $behind) origin/master\n"
           if [[ "$AHEAD" == 0 ]] && ask "This branch is ${green}0${reset} commits ahead of origin/master. Would you like to delete it"; then
               echo -e "\n$(git branch -D $branch)\n"
           else
               echo -e "\n\n"
           fi
       done
    done
}

pull()

# pull all branches
pull() {
    for path in $repos; do
        check_should_skip_repo || continue
        git_get_branches
        for branch in $branches; do
            git_checkout $branch
            [[ "$branch_auto" == true ]] || ask "pull" || continue
            git_pull
        done
    done
}

push_pull()

# push pull all branches
push_pull() {
    for path in $repos; do
        check_should_skip_repo || continue
        git_get_branches
        for branch in $branches; do
            git_checkout $branch
            [[ "$branch_auto" == true ]] || ask "pull+push" || continue
            git_pull
            ask_if_skip_dirty_merge || continue
            git_push
        done
    done
}

list_branches()

# List all branches in all repos
list_branches() {
    for path in $repos; do
        echo "Branches in $(colour_path)"
        cd ${path}/..
        git_branch
    done
}

everything()

# Bring everything up to date
everything() {
    for path in $repos; do
        [[ "$repo_auto" == true ]] || ask "Update project $(colour_path)" || continue
        git_fetch "$path/.."
        git_get_branches
        for branch in $branches; do
        git_summary && continue
        ret="$?"
        # If out of sync then pull push
        if [[ "$ret" == 1 ]]; then
            [[ "$branch_auto" == true ]] || ask "pull+push" || continue
            git_pull
            ask_if_skip_dirty_merge || continue
            git_push
            # If conflicts detected ask to skip (auto skip if auto enabled)
        elif [[ "$ret" == 2 ]]; then
            [[ "$branch_auto" == true ]] && continue
            ask_if_skip_dirty_merge || continue
            git_push
            # If changes detected, commit them
        elif [[ "$ret" == 3 ]]; then
            git_status
            # prompt for diff unless NODIFF specified
            [[ $1 != "NODIFF" ]] && ask "Show diff" && echo -e "$(git diff --color=always)\n"
            if ask "Stage all changes + commit"; then
                git add -A
                git_status
                git_commit
                git_status
                ask_if_push
            fi
            echo -e "$(git_status)\n\n"
        fi
        done
    done
}

die()

die() { echo "$*" >&2; exit 2; }  # complain to STDERR and exit with error

needs_arg()

needs_arg() { if [ -z "$OPTARG" ]; then die "No arg for --$OPT option"; fi; }

tui()

# Show menu
tui() {
    show_help
    while true; do
        opt=$(show_menu)

        if [[ $opt == "Search for git repos" ]]; then
        echo -e "\nFinding git repos in \$HOME"
        find_repos "$HOME"
        elif [[ $opt == "Quit" ]]; then
            break
        else
            echo
            using_repos
        fi

        if [[ $opt == "Pull and push" ]]; then
            echo -e "\n${yellow}Prompts you to pull+push each branch in your git repositories\n${reset}"
            push_pull
        elif [[ $opt == "Pull and push auto" ]]; then
            echo -e "\n${yellow}Prompts you to pull+push each branch in your git repositories\n${reset}"
            branch_auto=true
            push_pull
        elif [[ $opt == "Add all changes + commit + push" ]]; then
            echo -e "\n${yellow}Prompts you to Add all changes + commit + push each of your git repositories\n${reset}"
            commit_and_push
        elif [[ $opt == "Clean old branches" ]]; then
            echo -e "\n${yellow}Prompts you to delete any branches in your git repositories which are 0 commits ahead of master\n${reset}"
            clean_branches
        elif [[ $opt == "List branches" ]]; then
            echo -e "\n${yellow}Lists all the local branches in your git repositories\n${reset}"
            list_branches
        elif [[ $opt == "Compare master" ]]; then
            echo -e "\n${yellow}Compares each branch in your git repositories against origin/master\n${reset}"
            git_fetch_all_repos
            compare_branches "origin/master"
        elif [[ $opt == "Compare remote" ]]; then
            echo -e "\n${yellow}Compares each branch in your git repositories against it's remote branch\n${reset}"
            git_fetch_all_repos
            compare_branches "remote"
        elif [[ $opt == "Check for changes" ]]; then
            echo -e "\n${yellow}Checks each repository for changes which have not been comitted\n${reset}"
            repos_summary
        elif [[ $opt == "List changes" ]]; then
            echo -e "\n${yellow}Checks each repository for changes which have not been comitted\n${reset}"
            repos_status
        elif [[ $opt == "Merge origin/master into branches" ]]; then
            echo -e "\n${yellow}Prompts you to merge origin/master into each branch in your git repositories\n${reset}"
            merge_into_branches
        fi
        echo
    done
}

Help

show_help()

# Prints the help function
show_help() {
    echo    "Usage: gitmanage [OPTION]..."
    echo -e "Used to manage multiple branches across multiple git repositories\n"

    echo    "  -F          Search for git repos"
    echo -e "              Searches home directory for git repos"
    echo -e "              (This must completed at least once to update the cache used by other functions)\n"

    echo    "  -a          Branch auto"
    echo -e "              Automatically approves per branch confirmation prompts (To avoid pressing 'y' a bunch of times). Can be combined with -A\n"

    echo    "  -A          Repo auto"
    echo -e "              Automatically approves per repo confirmation prompts (To avoid pressing 'y' a bunch of times). Can be combined with -a\n"

    echo    "  -f          Fetch repos"
    echo -e "              Fetch and prune all repos\n"

    echo    "  -s          Check for changes"
    echo -e "              Checks each repository for changes which have not been comitted and provides a simple summary\n"

    echo    "  -S          List changes"
    echo -e "              List all changes in each repository which have not been comitted\n"

    echo    "  -p          Pull"
    echo -e "              Prompts you to pull each branch in your git repositories\n"

    echo    "  -P          Pull and push"
    echo -e "              Prompts you to pull+push each branch in your git repositories\n"

    echo    "  -c          Add all changes + commit + push"
    echo -e "              Prompts you to add all changes + commit + push each git repository\n"

    echo    "  -C          Add all changes + commit + push NO DIFF"
    echo -e "              Prompts you to add all changes + commit + push each git repository but will not prompt to show diff\n"

    echo    "  -m          Merge origin/master into branches"
    echo -e "              Prompts you to merge origin/master into each branch in your git repositories\n"

    echo    "  -M          Automerge origin/master into branches"
    echo -e "              Attempts to automatically merge origin/master into each branch in your git repositories\n"

    echo    "  -r          Rebase branches"
    echo -e "              Prompts you to rebase into each branch onto origin/master for every repo in your git repositories\n"

    echo    "  -R          Autorebase branches"
    echo -e "              Attempts to automatically rebase each branch onto origin/master for every repo in your git repositories\n"

    echo    "  -e          Everything"
    echo -e "              Prompts you to add, commit, pull, push all branches of all repos\n"

    echo    "  -E          Everything Auto"
    echo -e "              Attempts to automatically add, commit, pull, push all branches of all repos\n"

    echo    "  -b master   Compare master"
    echo -e "              Compares each branch in your git repositories against origin/master\n"

    echo    "  -b clean    Clean old branches"
    echo -e "              Prompts you to delete any branches in your git repositories which are 0 commits ahead of origin/master\n"

    echo    "  -B master   Compare master no fetch"
    echo -e "              Compares each branch in your git repositories against origin/master\n"

    echo    "  -b remote   Compare remote"
    echo -e "              Compares each branch in your git repositories against it's remote branch\n"

    echo    "  -B remote   Compare remote no fetch"
    echo -e "              Compares each branch in your git repositories against it's remote branch\n"

    echo    "  --sync      Sync local and remote"
    echo -e "              Push and pull all branches of all repositories.\n"

    echo    "  -h --help   Help"
    echo -e "              Displays this message"
}

Process args

# Process args
while getopts "aAeEfsSpPcCmMhiF:r:b:B:c-:" OPT; do
    if [ "$OPT" = "-" ]; then   # long option: reformulate OPT and OPTARG
      OPT="${OPTARG%%=*}"       # extract long option name
      OPTARG="${OPTARG#$OPT}"   # extract long option argument (may be empty)
      OPTARG="${OPTARG#=}"      # if long option argument, remove assigning `=`
    fi
    set_repos
    case "$OPT" in
        f) git_fetch_all_repos && exit;;
        s) repos_summary && exit;;
        S) repos_status && exit;;
        p) pull && exit;;
        P) push_pull && exit;;
        c) commit_and_push && exit;;
        C) commit_and_push "NODIFF" && exit;;
        m) merge_into_branches && exit;;
        M) merge_into_branches "AUTOMERGE" && exit;;
        r) rebase_branches "origin/master" && exit;;
        R) rebase_branches "origin/master" "AUTOREBASE" && exit;;
        e) everything && exit;;
        E) repo_auto=true; branch_auto=true; everything && exit;;
        h|help) show_help && exit;;
        a) branch_auto=true;;
        A) repo_auto=true;;
        sync) repo_auto=true; branch_auto=true; push_pull && exit;;
        i|interactive) tui && exit;;
        b)
            set_repos
            if [[ $OPTARG == "clean" ]]; then clean_branches; fi
            git_fetch_all_repos
            if [[ $OPTARG == "master" ]]; then compare_branches "origin/master"
            elif [[ $OPTARG == "remote" ]]; then compare_branches "remote"
            else
                compare_branches "$OPTARG"
            fi
            exit
        ;;
        B)
            set_repos
            if [[ $OPTARG == "master" ]]; then compare_branches "origin/master"
            elif [[ $OPTARG == "remote" ]]; then compare_branches "remote"
            else
                compare_branches "$OPTARG"
            fi
            exit
        ;;
        F)
            if [[ $OPTARG == "clean" ]]; then echo "TODO Clean repo.cache files"
            else
                find_repos $OPTARG
            fi
            exit
        ;;
        r)
            set_repos
            git_fetch_all_repos
            if [[ $OPTARG == "master" ]]; then rebase_branches "origin/master"
            else
                rebase_branches "$OPTARG"
            fi
            exit
        ;;
        ??*) die "Illegal option --$OPT" ;;  # bad long option
        ?) exit 2 ;;  # bad short option (error reported via getopts)
  esac
done
shift $((OPTIND-1)) # remove parsed options and args from $@ list

set_repos
repos_summary_compact

Manpage

.TH GIT_MANAGER 1 git_manager\-VERSION
.SH NAME
git_manager \- git repository manager
.SH SYNOPSIS
.B git_manager
.RB [ \-v ]
.SH DESCRIPTION
git_manager is a script for managing multiple git repositories and branches
.P
git_manager has options for findiding and updating multiple repos
.SH OPTIONS
.TP
.B \-F
SEARCH FOR GIT REPOS.
Searches home directory for git repos
(This must completed at least once to update the cache used by other functions)
.TP
.B \-a
BRANCH AUTO.
Automatically approves per branch confirmation prompts (To avoid pressing 'y' a bunch of times). Can be combined with -A
.TP
.B \-A
REPO AUTO.
Automatically approves per repo confirmation prompts (To avoid pressing 'y' a bunch of times). Can be combined with -a
.TP
.B \-f
FETCH REPOS.
Fetch and prune all repos
.TP
.B \-s
CHECK FOR CHANGES.
Checks each repository for changes which have not been comitted and provides a simple summary
.TP
.B \-S
LIST CHANGES.
List all changes in each repository which have not been comitted
.TP
.B \-p
PULL.
Prompts you to pull each branch in your git repositories
.TP
.B \-P
PULL AND PUSH.
Prompts you to pull+push each branch in your git repositories
.TP
.B \-c
ADD ALL CHANGES + COMMIT + PUSH.
Prompts you to add all changes + commit + push each git repository
.TP
.B \-C
ADD ALL CHANGES + COMMIT + PUSH NO DIFF.
Prompts you to add all changes + commit + push each git repository but will not prompt to show diff
.TP
.B \-m
MERGE ORIGIN/MASTER INTO BRANCHES.
Prompts you to merge origin/master into each branch in your git repositories
.TP
.B \-M
AUTOMERGE ORIGIN/MASTER INTO BRANCHES.
Attempts to automatically merge origin/master into each branch in your git repositories
.TP
.B \-r
REBASE BRANCHES.
Prompts you to rebase each branch on origin/master for each repo in your git repositories
.TP
.B \-R
AUTOREBASE BRANCHES.
Attempts to rebase each branch on origin/master for each repo in your git repositories
.TP
.B \-e
EVERYTHING.
Prompts you to add, commit, pull, push all branches of all repos
.TP
.B \-E
EVERYTHING AUTO.
Attempts to automatically add, commit, pull, push all branches of all repos
.TP
.B \-b clean
CLEAN OLD BRANCHES.
Prompts you to delete any branches in your git repositories which are 0 commits ahead of origin/master
.TP
.B \-b master
COMPARE MASTER.
Compares each branch in your git repositories against origin/master
.TP
.B \-B master
COMPARE MASTER NO FETCH.
Compares each branch in your git repositories against origin/master
.TP
.B \-b remote
COMPARE REMOTE.
Compares each branch in your git repositories against it's remote branch
.TP
.B \-B remote
COMPARE REMOTE NO FETCH.
Compares each branch in your git repositories against it's remote branch
.TP
.B \-\-sync
PUSH AND PULL ALL BRANCHES OF ALL REPOSITORIES.
.TP
.B \-h --help
HELP.
Displays this message
.SH USAGE
provide no args for interactive mode.
.SH SEE ALSO
.BR git (1)