#!/usr/bin/env bash


set -o pipefail

DATE_FMT="+%F_%H.%M%z"
SNAPSHOT_PREFIX="refs/snapshots"
#D=debug


usage() {
    cat << EOF
Usage:  `basename "$0"` [--help] [<remote>]
        <remote>: Remote to fetch data from. (defaults to 'origin')

Fetches all references available on the remote (i.e. non-standard ones as
well) and prune local references if necessary to match current state of remote.
* It stores a snapshot of all current references (which are never pruned) in
  '$SNAPSHOT_PREFIX/<date>'.
* It removes the default fetch configuration to ensure, that the snapshots
  can't be pruned with a plain 'git fetch --prune' by accident.
* This also reminds the user that she should use this script to update the
  mirror.
EOF
}

error() {
    local errorcode=$?

    echo "$*" >&2
    exit $errorcode
}

debug() {
    echo "DEBUG: $*" >&2
    "$@"
}

ok() {
    "$@"
    true
}

accept_ec() {
    case " $* " in
        *" $? "*)
            return 0
            ;;
        *)
            return $?
            ;;
    esac
}


update_mirror() {
    local remote="$1"
    local mirror
    local date
    local snapshot

    mirror="`
        get_first_level_refs "$remote" |
        grep -v -xF "$SNAPSHOT_PREFIX" |
        sed 's:.*:+&/*\:&/*:'
    `" &&
    $D git fetch "$remote" --prune $mirror &&
    true ||
    error "Failed to update mirror from '$remote'."

    date="`TZ=UTC date "$DATE_FMT"`" &&
    snapshot="refs/*:$SNAPSHOT_PREFIX/$date/*" &&
    $D git fetch "$remote" $snapshot 2>/dev/null &&
    echo " * [new snapshot]      ${snapshot/:/ -> }" >&2 &&
    true ||
    error "Failed to create snapshot for '$remote'."
}

get_first_level_refs() {
    local remote="$1"

    {
        $D git ls-remote --refs --quiet "$remote" &&
        $D ok git show-ref
    } |
    grep -oP '[[:space:]]\Krefs/[^/]+' |
    sort -u
}

disable_plain_fetch() {
    local remote="$1"

    git config --unset-all "remote.$remote.fetch"
    accept_ec 5 ||
    error "Failed to disable plain fetch for '$remote'."
}


while [ $# -gt 0 ]; do
    case "$1" in
        --help)
            usage
            exit 0
            ;;
        --)
            shift
            break
            ;;
        --*)
            error "Invalid option: '$1'"
            ;;
        *)
            break
            ;;
    esac
    shift
done
REMOTE="${1:-origin}"
shift

update_mirror "$REMOTE" &&
disable_plain_fetch "$REMOTE" &&
true
