#!/bin/bash
EXTERNALS_LOC="subdir"
EXTERNALS_DIR="external-content"
TRAILER_TOKEN="externals-update-from"

############################################
# utilities

git_svn_url (){
    git cat-file commit "$1" \
        | sed -rne "s|^git-svn-id: (.*@[0-9]+)\b.*$|\1|p"
}

map_url_to_commit (){
    local svn_url_rev="$1"
    git rev-list -n1 --all --grep "git-svn-id: $svn_url_rev\b"
}

############################################
# index-filter

implant_externals (){
    # find URL/REV of 'git-svn-id' trailer
    local cur_svn_url=$(git_svn_url "$ GIT_COMMIT ")
        [ "$cur_svn_url" ] ||
        die "cannot find 'git-svn-id'trailer"
    # append subdirectory containing the
    # 'svn:externals' property
    local property_url=$(echo "$cur_svn_url" |
    sed -r "s|@|/$EXTERNALS_LOC@|")
    # fetch 'svn:externals' property from server
    local externals
    externals=$(svn propget svn:externals "$property_url") ||
        die "svn propget: $externals"
    # transform answer into URL@REV format of target
    local externals_url="$(echo "$externals" | sed -rn \
        -e "s|^\^/|$SVN_ROOT/|" \
        -e "s|^(.*)@([0-9]+)\s+$ EXTERNALS_DIR \s*$|\1@\2|p")"
    # lookup target commit
    local new_ext_rev
    new_ext_rev="$(map_url_to_commit \
        "$externals_url")" ||
        die "no commit for svn:externals target"
    # message to --msg-filter
    if [ "$CUR_EXT_REV" = "$new_ext_rev" ]
    then
        unset TRAILER_VALUE
    else
        TRAILER_VALUE="$externals_url"
    fi
    # remember for next iteration
    CUR_EXT_REV=$new_ext_rev
    # update externals-tree
    git rm -r --cached --ignore-unmatch --quiet \
        "$EXTERNALS_LOC/$EXTERNALS_DIR"
    [ "$CUR_EXT_REV" ] && git read-tree \
        --prefix "$EXTERNALS_LOC/$EXTERNALS_DIR" \
        "$CUR_EXT_REV^{tree}"
    return 0
}
############################################
# message-filter

add_externals_trailer (){
    if [ -z "$TRAILER_VALUE" ]
    then
        cat
    else
	git -c trailers.ifexists=addIfDifferent \
	    interpret-trailers --trailer \
        "$TRAILER_TOKEN: $TRAILER_VALUE"
    fi
}

############################################
# get SVN repository root

git_svn_url=$(git_svn_url HEAD)
    [ "$git_svn_url" ] ||
        { echo >&2 "no git-svn-id in HEAD"; exit 1; }
    SVN_ROOT=$(svn info --show-item repos-root-url \
        --no-newline "$git_svn_url")
    [ "$SVN_ROOT" ] ||
    { echo >&2 "no SVN root URL"; exit 1;
}

############################################
# search last trailer

last_update=$(git rev-list -n1 --abbrev-commit \
    --grep "^$TRAILER_TOKEN: " HEAD)
if [ "$last_update" ]
then
    last_trailer_value="$(git rev-list \
        --format='%(trailers)' $last_update^! |
        sed -nre "s/^$TRAILER_TOKEN://p")"
    CUR_EXT_REV ="$(map_url_to_commit \
        "$last_trailer_value")"
    echo "found trailer $TRAILER_TOKEN" - \
        "rewriting $last_update..HEAD"
    filter_range="$last_update..HEAD"
else
    echo "found no trailer $TRAILER_TOKEN" - \
        "rewriting all commits..."
    filter_range="HEAD"
fi

############################################
# rewrite revisions

PATH="$(git --exec-path):$PATH"
source git-filter-branch \
	 --index-filter implant_externals \
	 --msg-filter add_externals_trailer \
	 "$filter_range"
