Last active
February 2, 2016 17:23
-
-
Save dhensby/0813dca4617c3d71fb00 to your computer and use it in GitHub Desktop.
A shell utility to sync your (local) branches with an arbitrary remote
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
REMOTE_TO_SYNC='upstream' # default remote if none passed | |
QUIET=false # run in quiet mode | |
DO_PUSH=false # push to the branches upstream as part of the sync | |
ONLY_LOCAL=false # Only sync local branches (don't pull down from remote) | |
SKIP_FETCH=false # Skip the fetch step - really only useful if you know your up to date | |
Q_FLAG='' # the -q flag for CLI commands - changes to `-q` when running in quiet mode | |
DEBUG=false | |
# Our help function | |
function usage() { | |
debug "Usage running" | |
echo -e "Syntax `basename $0` [-l] [-d] [-h] [-p] [-q] [-s] [-r remote-name] | |
-h Show this help | |
-l Only update local branches | |
-p Push changes up to the repo | |
-q Quiet - minise output | |
-s Skip fetching the remote (may cause stale commits to be pushed) | |
-r [remote-name] Use this remote to sync with, defaults to ${REMOTE_TO_SYNC} | |
-d Debug mode" | |
debug "Usage ended" | |
} | |
function debug() { | |
${DEBUG} && echo -e "$*" | |
return 0 | |
} | |
function quiet() { | |
[[ ! ${DEBUG} && ${QUIET} ]] || echo -e "$*" | |
return 0 | |
} | |
# Heavy lifting function to coordinate the parts | |
function run() { | |
debug "Starting run function" | |
# store the local branch as we need this later to avoid a git error with forcing branch commits | |
local current_branch | |
current_branch=$(git rev-parse --abbrev-ref HEAD) | |
debug "Determined current branch is ${current_branch}" | |
# if the remote exists, we can continue | |
if find_remote `git remote`; then | |
debug "Found remote ${REMOTE_TO_SYNC}" | |
# Fetch the remote if required | |
if ! ${SKIP_FETCH}; then | |
quiet echo "Fetching remote" | |
git fetch "${REMOTE_TO_SYNC}" | |
else | |
debug "Skipping fetch" | |
fi | |
# populate the global BRANCHES var | |
get_branches_to_sync | |
debug "Going through branches to sync:\n" ${BRANCHES} | |
# go through all the candidate branches and sync them if needed | |
for branch in ${BRANCHES[@]}; do | |
debug "Checking branch ${branch}" | |
# If we want to sync all branches OR check a branch exists locally | |
if ! ${ONLY_LOCAL} || branch_exists_locally "${branch}"; then | |
quiet "Resetting branch ${branch}" | |
# If the branch is currently checkedout we have to use git reset | |
if [[ "${current_branch}" == "${branch}" ]]; then | |
debug "Syncing current branch, using reset method" | |
git reset -q --hard "${REMOTE_TO_SYNC}/${branch}" | |
else | |
debug "Reseting ${branch} to ${REMOTE_TO_SYNC}/${branch}" | |
# force the branch name to point to the remote ref | |
git branch -f --no-track "${branch}" "${REMOTE_TO_SYNC}/${branch}" | |
fi | |
# Push up to the default remote if needed | |
if [ ${DO_PUSH} ]; then | |
ORIGIN=$(git config --get branch."${branch}".remote) | |
if [ -z "${ORIGIN}" ]; then | |
ORIGIN='origin' | |
fi | |
debug "Pushing ${branch} to origin: ${ORIGIN}" | |
git push ${Q_FLAG} -f "${ORIGIN}" "${branch}":"${branch}" | |
else | |
debug "Skipping push" | |
fi | |
else | |
debug "Skipping branch" | |
fi | |
done | |
debug "All branches evaluated" | |
return 0 | |
else | |
echo "Couldn't find remote: ${REMOTE_TO_SYNC}" | |
return 1 | |
fi | |
} | |
# Confirm the remote is in a list of passed remotes | |
function find_remote() { | |
debug "Looking for remote ${REMOTE_TO_SYNC}" | |
local e | |
for e in "$@"; do | |
[[ "$e" == "$REMOTE_TO_SYNC" ]] && debug "Remote found" && return 0; | |
done | |
debug "Remote not found" | |
return 1 | |
} | |
# Get the name of all the branches on the remote | |
function get_branches_to_sync() { | |
quiet "Getting branches" | |
BRANCHES=$(git branch -r | grep -e "^ *${REMOTE_TO_SYNC}\/" | sed "s/ *${REMOTE_TO_SYNC}\///") | |
} | |
# Configm the passed branch name exists locally | |
function branch_exists_locally() { | |
debug "Checking if $1 exists locally" | |
if [[ ! ${LOCAL_BRANCHES} ]]; then | |
debug "Finding local branches" | |
LOCAL_BRANCHES=$(git branch | sed 's/\** *//') | |
debug "Found ${LOCAL_BRANCHES}" | |
fi | |
for e in ${LOCAL_BRANCHES[@]}; do | |
[[ "$e" == "$1" ]] && debug "Found branch $e locally" && return 0 | |
done | |
debug "Didn't find branch $1 locally" | |
return 1 | |
} | |
# Handle the CLI params | |
while getopts "dhpqlsr:" OPTION; do | |
case ${OPTION} in | |
r ) REMOTE_TO_SYNC="$OPTARG" | |
debug "Setting remote to ${OPTARG}" | |
;; | |
p ) DO_PUSH=true | |
debug "Enabling push to origin" | |
;; | |
q ) QUIET=true | |
Q_FLAG='-q' | |
debug "Entering quiet mode" | |
;; | |
l ) ONLY_LOCAL=true | |
debug "Only updating local branches" | |
;; | |
s ) SKIP_FETCH=true | |
debug "Skipping fetch" | |
;; | |
d ) DEBUG=true | |
debug "Entering debug mode" | |
;; | |
h ) usage | |
debug "Running help" | |
exit 0 | |
;; | |
esac | |
done | |
run | |
exit $? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment