Skip to content

Instantly share code, notes, and snippets.

@dhensby
Last active February 2, 2016 17:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhensby/0813dca4617c3d71fb00 to your computer and use it in GitHub Desktop.
Save dhensby/0813dca4617c3d71fb00 to your computer and use it in GitHub Desktop.
A shell utility to sync your (local) branches with an arbitrary remote
#!/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