From e15fa4117031614968faca09e6a112cdfca344bc Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 6 Nov 2019 13:47:23 +0100 Subject: [PATCH] Initial commit --- README.md | 41 ++++++++++++++++++++++++++++ git-mirror.sh | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 README.md create mode 100755 git-mirror.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..c83e988 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Git mirror + +Run this script to synchronize a repository once. + +## Dependencies +* bash +* git +* Tor and torify (see section "Tor") + +## Usage +``` +./git-mirror.sh +``` + +## Get started +Create a git repo, add remotes for the leader and the followers: +```bash +mkdir ~/git-mirror-my-repo; cd ~/git-mirror-my-repo +git init +git remote add github https://github.com/interesting/repo +git remote add gitea ssh://git@git.example.org/me/interesting-repo +git remote add gitlab ssh://git@git.example.com/myself/interesting-repo +``` + +Make sure Tor is running (see section "Tor"). Then run the mirroring script: +```bash +cd +git clone https://git.zeus.gent/midgard/git-mirror.git; cd git-mirror +./git-mirror.sh ~/git-mirror-my-repo github gitea gitlab +``` +The first remote, github is the leader. The others are followers that are updated. + +## Tor +The leader is fetched over Tor through torify. Make sure Tor is running, or edit the script, +changing `torify git fetch` into `git fetch`. + +## Motivation +GitHub has always been hypocritical, promoting open source while keeping its own stack closed. When +GitHub was bought by Microsoft, the author decided to stop using it. Unfortunately a lot of +software still only has a presence on GitHub. To make it easier to work with those, this simple +mirror script was written. diff --git a/git-mirror.sh b/git-mirror.sh new file mode 100755 index 0000000..16c4288 --- /dev/null +++ b/git-mirror.sh @@ -0,0 +1,75 @@ +#!/bin/bash +set -euo pipefail +IFS=$'\n' + +if [[ $# -lt 3 ]]; then + echo "Usage: $0 " + exit 1 +fi + +cd "$1" + +leader="$2" +shift 2 +followers=( "$@" ) + +echo "Working directory: $PWD" +echo "Leader remote: $leader" +echo "Follower remotes: ${followers[*]}" | tr '\n' ' ' +echo; echo + +torify git fetch --prune --multiple "$leader" +git fetch --prune --multiple "${followers[@]}" +echo + +echo "Leader remote information:" +git remote show -n "$leader" +echo +echo "Follower remotes information:" +git remote show -n "${followers[@]}" +echo + +branches=( $(git show-ref | sed -n 's| refs/remotes/'"$leader"'/|\t|p') ) +echo -e "${#branches[*]} branches at leader:" +echo "${branches[*]}" + +IFS=$'\t' + +errors=( ) + +error() { + echo "XXX $1" >&2 + errors[${#errors[*]}]="$1" +} + +for branchspec in "${branches[@]}"; do + set $branchspec + leader_sha="$1" + branch="$2" + + for remote in "${followers[@]}"; do + echo + echo "Updating $remote/$branch" + + # Prepare state to push + follower_ref="refs/remotes/$remote/$branch" + if ! git show-ref --verify --quiet "$follower_ref"; then + echo "Branch doesn't exist yet at remote, creating" + git reset --hard "$leader_sha" -- + else + echo "Branch existed at remote, updating" + git reset --hard "$follower_ref" -- + git merge --ff-only "$leader_sha" || { error "$remote $branch: FF not possible"; continue; } + fi + + git push "$remote" "+HEAD:$branch" || { error "$remote $branch: failed to push"; continue; } + + done +done + +if [[ "${#errors[*]}" -ge 0 ]]; then + echo "Errors:" + echo "${errors[@]}" +else + echo "No errors." +fi