gsync: A Nicer Way to Sync Branches Between Remotes
A small zsh function for when you constantly push and pull the same branch between multiple remotes.
At work we keep two git remotes and I have to sync branches between them constantly. Writing it out in plain git every time was a nightmare, so I wrote a small wrapper.
For example, I have a branch fix-all-bugs that has changes in remote2 that I want to sync with remote1. Granted I’m on the fix-all-bugs branch locally, I can simply run:
gsync remote2 -f # pull from remote2 into current branch
gsync remote1 -t # push current branch to remote1Maybe you want to push/pull from a different branch on remote1?
gsync remote1 -t backup/fix-all-bugs # push current branch to remote1 as backup/fix-all-bugs
gsync remote1 -f backup/fix-all-bugs # pull from remote1 backup/fix-all-bugs into current branchThat’s it. gsync <remote> -t pushes, gsync <remote> -f pulls. Branch name defaults to whatever you’re on. Pass a different one if you need it.
It checks you’re in a repo, not in detached HEAD, and that the remote actually exists before doing anything. Catches typos like gsync orign.
The push uses a refspec (current:remote) because that’s how you push to a differently-named remote branch. It’s not hard to remember, it’s just annoying to type every time. This hides it behind a flag.
Here’s the code, hope it helps :)
No dependencies beyond git. ~50 lines of zsh. Drop it in your .zshrc. (or anything bash compatible)
gsync() {
local target="$1"
local arg2="$2"
local arg3="$3"
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
echo "Error: Not inside a git repository."
return 1
}
local current_branch
current_branch="$(git rev-parse --abbrev-ref HEAD)"
if [[ "$current_branch" == "HEAD" ]]; then
echo "Error: Detached HEAD state."
return 1
fi
if ! git remote get-url "$target" >/dev/null 2>&1; then
echo "Error: Unknown remote '$target'."
return 1
fi
local action="push"
local remote_branch="$current_branch"
case "$arg2" in
--from|-f) action="pull"; remote_branch="${arg3:-$current_branch}" ;;
--to|-t) action="push"; remote_branch="${arg3:-$current_branch}" ;;
"") ;;
*) remote_branch="$arg2" ;;
esac
[[ -z "$remote_branch" ]] && { echo "Error: Remote branch is empty."; return 1; }
if [[ "$action" == "pull" ]]; then
echo "⬇️ Pulling '$remote_branch' into '$current_branch'..."
git pull "$target" "$remote_branch" || { echo "Error: Pull failed."; return 1; }
else
echo "⬆️ Pushing '$current_branch' to '$remote_branch'..."
git push "$target" "$current_branch:$remote_branch" || { echo "Error: Push failed."; return 1; }
fi
}