#!/usr/bin/env zsh # Bring the default branch up to date in a fork-model repo, and keep my fork's # copy of it aligned: fast-forward it from whichever remote owns it, then push # it to origin. Named git-sync and installed on the $PATH, so git finds it as # `git sync`. set -e git rev-parse --git-dir > /dev/null # The branch belongs to upstream in a fork, and to origin when there is no # fork in play. if git remote get-url upstream > /dev/null 2>&1; then source_remote=upstream else source_remote=origin fi if ! git remote get-url origin > /dev/null 2>&1; then echo "git sync: no 'origin' remote to sync" >&2 exit 1 fi # Ask the server which branch is the default rather than reading # refs/remotes//HEAD. That ref is a cache written at clone time, and # git does not refresh it when the default branch changes upstream - which is # the whole reason this script exists. branch=$(git ls-remote --symref $source_remote HEAD | awk '/^ref:/ { sub("refs/heads/", "", $2); print $2; exit }') if [[ -z $branch ]]; then echo "git sync: could not work out the default branch of $source_remote" >&2 exit 1 fi echo "Syncing $branch from $source_remote" git fetch --prune $source_remote # Re-point the cached HEADs at what the server just told us, so `git # default-branch` and nvim's diff-against-branch prompt agree with this script. git remote set-head origin --auto > /dev/null if [[ $source_remote != origin ]]; then git remote set-head $source_remote --auto > /dev/null fi # Be explicit about what the local branch tracks. A bare `git switch` has to # guess, and refuses to when several remotes carry the same branch name - which # they do as soon as a colleague's fork is added as a remote. if git show-ref --verify --quiet refs/heads/$branch; then git switch $branch elif git show-ref --verify --quiet refs/remotes/origin/$branch; then # Track origin, not upstream: this script keeps the fork's copy current, and # it means a stray `git push` here goes somewhere I can actually write to. git switch --create $branch --track origin/$branch else git switch --create $branch --track $source_remote/$branch fi # Never a merge commit: if the default branch has diverged locally, that is # something to look at, not something to paper over. git merge --ff-only $source_remote/$branch if [[ $source_remote != origin ]]; then git push origin $branch fi