Git Reflector Script

Here’s a small script to reflect a git repo from some source like GitHub to some destination like an internal gitea mirror. That’s my usecase. You could also use this to go the other direction, or whatever you like.

It’s designed to be run periodically with something like cron. If anything goes wrong during pull/push (for example, upstream rewrites history), that repo sync will fail but others will continue. Modify this to watch for errors if you like or run it manually on occasion to check for errors yourself.

Intermediary copies of repos are stored locally in $PWD/repos/. The repos/ folder is created automatically. Run the script from whatever working directory you want to be the parent of repos/, or change the script to put them somewhere else.

Repo list is in format:

local/repo/name    source_url    dest_url

See script repos= line for examples. Change this to set up your own repo list. You must use spaces as the delimiter, or change the script to use a different delimiter by changing IFS=. For example, to use tabs, set IFS=$'\t'.

Here’s the script:

#!/usr/bin/env sh

# local/path   source_url   dest_url
# repos are cloned from source_url into folder <local/path>
#   local/path must not contain spaces because i dont want to deal with that.
#   If the folder doesn't exist, its a clone
#   If the folder exists, its an incremental pull
# after that, push the repo to dest_url.
#   If there's an error (like source overwrote history with --force) then this
#   doesn't happen. Resolve errors manually.

repos='linux       git@
faithanalog/rtmouse  git@
xf86-video-amdgpu  git@

printf '%s' "$repos" | while IFS=' ' read -r path source_url dest_url; do
    # work in repos subdir so we can easily gitignore it

    # make parent dir of repo
    mkdir -p "$(dirname "$path")"

    # clone if it doesnt exist already
    if ! [ -d "$path" ]; then
        echo "cloning $source_url into $path"
        git clone --mirror "$source_url" "$path"

        # enter repo
        cd "$path"
        # update all branches from source
        # docs for `git fetch -t` say it fetches tags in addition to a normal
        # fetch. in our testing this was not fully the case, and `git fetch -t`
        # did not fetch new branches.
        echo "pulling updates from $source_url"
        git fetch "$source_url"
        git fetch -t "$source_url"

        # push branches to dest
        echo "pushing updates to $dest_url"
        git push --all "$dest_url"
        git push --tags "$dest_url"