Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

Script to stay synced to upstreams for Luanti mods

I added a script to my repo for initializing a Mineclonia installation that uses a separate location to synchronize my local repositories to the upstream paths, if they still exist.

files/2026/listings/mod-upstreams.sh (Source)

#!/bin/sh
# Startdate: 2026-01-30-6 13:35
# Purpose: List and track git remotes for Luanti mods I care about
# Config
host="http://server3/git/luanti"
WORKDIR="${WORKDIR:-${HOME}/mod-upstreams}"
# Functions
ginit() {
    err() {
        printf '%s\n' "${@}" 1>&2
    }
    err "# ginit ${*}"
    if test "${#}" -lt 1 ;
    then
        err "Usage: ginit <repository_url> [branch_name] [dirname]"
        return 1
    fi
    REPO_URL="${1}"
    REPO_DIR="$( basename "${REPO_URL}" .git )"
    BRANCH_NAME="${2}"
    DIRNAME="${3}"
    if test -n "${DIRNAME}" ; then REPO_DIR="${DIRNAME}" ; fi
    if test -d "${REPO_DIR}" ;
    then
        err "Directory ${REPO_DIR} already exists. Changing directory to it."
        cd "${REPO_DIR}" || return
        if test -n "${BRANCH_NAME}" ;
        then
            err "Switching to branch ${BRANCH_NAME}"
            git fetch --all
            # my old redirect trick to filter stderr
            {
                {
                    # Use 'git switch' (modern) or 'git checkout' (older versions)
                    # 'git switch -c' creates and switches to a new branch if it doesn't exist locally
                    git switch -c "${BRANCH_NAME}" "origin/${BRANCH_NAME}" || \
                        git checkout -b "${BRANCH_NAME}" "origin/${BRANCH_NAME}" || \
                        git checkout "${BRANCH_NAME}"
                } 2>&1 1>&3 | sed -r -e '/fatal: a branch named.*already exists/d' 1>&2
            } 3>&1
        fi
    else
        if test -n "${BRANCH_NAME}" ;
        then
            err "Cloning branch ${BRANCH_NAME} into ${REPO_DIR}"
            git clone -b "${BRANCH_NAME}" "${REPO_URL}" "${REPO_DIR}"
        else
            err "Cloning default branch into ${REPO_DIR}"
            git clone "${REPO_URL}" "${REPO_DIR}"
        fi
        if test -d "${REPO_DIR}" ;
        then
            cd "${REPO_DIR}" || return
            git pull
            err "Changed directory to ${REPO_DIR}"
        else
            err "Failed to clone repository"
            return 1
        fi
    fi
}
sync_repos() {
    _origin="${1}"
    _dest="${2}"
    _branches="${3}"
    cd "${WORKDIR}"
    if ginit "${_origin}" ;
    then
        { {
            git remote add dest "${_dest}"
        } 2>&1 1>&3 | sed -r -e '/error: remote.*already exists/d' 1>&2 ; } 3>&1
        git pull --all
        echo "${_branches},main,master" | tr ',' '\n' | awk '!x[$0]++' | while read branch ;
        do
            { {
                git switch -c "${branch}" "origin/${branch}" || \
                    git checkout -b "${branch}" "origin/${branch}" || \
                    git checkout "${branch}"
                git push dest "${branch}"
            } 2>&1 1>&3 | sed -r -e '/fatal: a branch named.*already exists/d' 1>&2 ; } 3>&1
        done
    fi
}
# sync_repos origin dest "branches,comma-separated"
sync_repos https://github.com/JamesClarke7283/enchantments_extractor "${host}/enchantments_extractor" "main"
# enchantments_workbench is mine
sync_repos https://codeberg.org/camelia/farmtools "${host}/farmtools" "main"
sync_repos https://codeberg.org/Wuzzy/minetest_inventory_icon "${host}/minetest_inventory_icon"
sync_repos https://github.com/JamesClarke7283/inventory_pouches "${host}/inventory_pouches"
sync_repos https://github.com/ketwaroo/minetest-k-ambient-light "${host}-readonly/minetest-k-ambient-light" "main"
sync_repos https://github.com/ketwaroo/minetest-k-recycler "${host}-readonly/minetest-k-recycler" "main"
# lava_furnace is mine
sync_repos https://codeberg.org/SilverSandstone/leads "${host}-readonly/leads" "main"
# list_to_file is mine
sync_repos https://github.com/minetest-mapserver/mapserver_mod "${host}-readonly/mapserver_mod" "master"
# mcl_chiseled_bookshelf is mine
# mcl_colored_chests is mine
sync_repos https://codeberg.org/TomCon/mcl_copper_stuff "${host}-readonly/mcl_copper_stuff" "master"
sync_repos https://codeberg.org/rudzik8/mcl_cozy "${host}/mcl_cozy" "master"
sync_repos https://codeberg.org/rudzik8/mcl_decor "${host}-readonly/mcl_decor" "master"
sync_repos https://codeberg.org/rudzik8/mcl_emerald_stuff "${host}/mcl_emerald_stuff" "main"
# mcl_lapis_stuff is mine
sync_repos https://github.com/ketwaroo/minetest-mcl-misk-recipes "${host}/mcl_misk_recipes" "main"
sync_repos https://codeberg.org/rudzik8/mcl_morefood "${host}/mcl_morefood" "main"
# milk_potion is mine
# molten_sailor_mcl is mine
sync_repos https://git.0x7be.net/dirk/mtimer "${host}/mtimer" "main"
sync_repos https://github.com/minetest-go/mtui_mod "${host}-readonly/mtui_mod" "master"
# obsidian_extra is mine
sync_repos https://github.com/LizzyFleckenstein03/playerlist "${host}/playerlist" "main"
sync_repos https://github.com/acmgit/minetest_poi "${host}/minetest_poi"
# privs_per_world is mine
sync_repos https://gitlab.com/cronvel/mt-respawn "${host}/mt-respawn"
# safe_chest upstream is gone, so I guess I am upstream now.
# sync_repos https://github.com/smnoe01/safe_chest "${host}/safe_chest"
sync_repos https://github.com/mt-historical/snippets "${host}-readonly/snippets"
sync_repos https://github.com/DonBatman/myairwand "${host}-readonly/myairwand"
sync_repos https://github.com/Twinsonian/magicsquare "${host}/magicsquare" "main"

The purpose of this script is to make synchronizing to upstream easy. I tend to have very small changes to upstream. On occasion my changes get merged into upstream, and I don't need any feature branches! For some of these, I have private patches that have been declined by upstream or never sent to them in the first place.

This script also documents the mods I care about. Any of the "readonly" ones are ones for which I have no changes at all. I have changed my mind and have had to move some around, when I realized I wanted to patch them after all.

Comments