Skip to content

Instantly share code, notes, and snippets.

@dannysauer
Created July 30, 2020 02:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dannysauer/282fc529d94cf87eb35577774e9d3dc2 to your computer and use it in GitHub Desktop.
Save dannysauer/282fc529d94cf87eb35577774e9d3dc2 to your computer and use it in GitHub Desktop.
Script to sync fork's head branch with upstream

When I fork a repo, I checkout out my fork as origin and add a remote for the upstream repo as upstream. I hate having a fork where the default (usually called master) branch is way out of date, though. So this gets run from a scheduler to walk through checked out repos under a couple of directories in $HOME/dev, figure out the default branch, and sync that with the same-name branch in my fork.

The main interesting thing it does is to use a worktree. So it doesn't need to check out the whole repo again, but also doesn't disturb my main working repository. It also uses a unique temporary directory and a branch with the same name to do the sync, as I couldn't get it to work properly without creating a branch. I could swear I had made that work before (completely avoiding a branch name), but I can't for the life of me remember how. :D The tempname should be unique enough.

#!/bin/bash
set -o errexit
wanted_remotes=(
"origin"
"upstream"
)
readarray -t repos <<<$(
find \
$HOME/dev/suse \
$HOME/dev/github \
-maxdepth 3 -type d -name .git -printf "%h\n"
)
# if $debug is set, $q should be empty; otherwise it's "-q"
q="-q"
(( $debug )) && q=""
# STDERR output!
function warn {
printf -- "$@\n" >&2
true
}
function debug {
(( $debug )) && warn "$@"
true
}
for repo in "${repos[@]}"
do
printf -- "Processing $repo\n"
#remotes=( $(git -C "$repo" remote) )
# using readarray allows weird chars; they're invalid in remotes but whatevz
readarray -t remotes <<<"$( git -C "$repo" remote )"
if [[ ${#remotes[@]} == 1 ]]
then
case $remotes in # $remotes == ${remotes[0]}
upstream )
warn "Only upstream exists; no fork to sync"
continue
;;
origin )
# if you're not me, you probably want to change this
if [[ $repo == @(.com/dannysauer/) ]]
then
warn "my origin; skipping"
continue
fi
;;
* ) true ;;
esac
fi
needed=0
found=0
for remote in "${wanted_remotes[@]}"
do
: $(( needed++ ))
for r in "${remotes[@]}"
do
if [[ "$r" == "$remote" ]]
then
debug "Found '$remote'" >&2
: $(( found++ ))
break
fi
done
if (( needed > found ))
then
warn "Didn't find '$remote' in $repo (remotes: '${remotes[*]}')"
break
fi
done
if (( needed > found ))
then
warn "Didn't find all of the necessary remotes for $repo"
continue
fi
debug "needed $needed; found $found"
D=$( mktemp -d )
branchname=$(basename "$D")
master=$( basename $(git -C "$repo" rev-parse --abbrev-ref origin/HEAD) )
git -C "$repo" fetch $q --all -p --tags
git -C "$repo" worktree add $q -b "$branchname" "$D" upstream/"$master" \
|| { warn "Failed adding worktree"; continue; }
git -C "$D" pull $q upstream "$master"
git -C "$D" push origin +"$branchname":"$master"
git -C "$D" push --tags origin +"$branchname":"$master"
git -C "$repo" worktree remove --force "$D"
git -C "$repo" branch $q -D "$branchname"
#[[ -d "$D" ]] && rm -r "$D"
done
@dannysauer
Copy link
Author

The right way to do this would be to use the github API to find my forks, see if they're checked out locally or not, and if not, check those out - adding the upstream automatically. Then do the work.

Maybe in version 2... :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment