Commit 425e096a authored by Michal Sojka's avatar Michal Sojka

Add support for multiple BRUTE repositories (not backward compatible!!!)

This commit changes how is the hook configured. Instead of having
brutegit remote and brutegit.glob values in git configuration, now
everything is configured in .brutegit-ae-sync file in the root
directory of the repo. See this repo for an example.

The reason for this breaking change is that we have to support
multiple remotes and that we want the content of the AE repos
configurable via the course repo itself (i.e. via .brutegit-ae-sync
file in the repo).
parent f1c6c705
INPUT = 244f8d49940fde16255b2e4eba0c79907412e7ad \
INPUT = 1f6e3f62c730ae432b42eba1d25c827660e8134b \
$(shell git rev-parse master) \
refs/heads/master
.PHONY: test
test:
git branch -D ae || :
echo $(INPUT) | bash -x ./brutegit-ae-sync
git branch -D ae01 ae02 || :
echo $(INPUT) | bash ./brutegit-ae-sync
# brutegit-ae-sync
git post-receive hook to update BRUTE automatic evaluation (AE) git
repositories. After pushing to a course git repository, this script
creates one or more subset of the course repository containing only
files for AE and pushes these subsets to BRUTE's AE git repositories.
Which files form the subsets and where to push them is configured in
the `.brutegit-ae-sync` file in the root repo directory. See
`.brutegit-ae-sync` file in this repo for an example.
#!/bin/bash
# git post-receive hook to update BRUTE automatic evaluation (AE) git
# repository. After pushing to a course git repository, this script
# creates a subset of the course repository containing only files for
# AE (by default directories */ae | */auto_evaluation) and pushes this
# subset to BRUTE's AE git repository.
#
# For the script to work, you have to manually configure brutegit
# remote in the (server) repository, where this hook is installed,
# e.g.:
#
# git remote add brutegit git@brutegit.felk.cvut.cz:2017z_b4b35osy/ae.git
#
# Optionally, you can configure what to sync to BRUTE by:
#
# git config brutegit.glob '*/ae'
# git config --add brutegit.glob '*/auto_eval'
# etc.
# repositories. After pushing to a course git repository, this script
# creates one or more subset of the course repository containing only
# files for AE and pushes these subsets to BRUTE's AE git
# repositories. Which files form the subsets and where to push them is
# configured in the `.brutegit-ae-sync` file in the root repo
# directory. See `.brutegit-ae-sync` file in this repo for an example.
#
# You can test this script in your local repository by giving it a
# commit as an argument:
......@@ -24,11 +14,11 @@
# brutegit-ae-sync HEAD~1
# brutegit-ae-sync HEAD
#
# This creates the branch ae with one or two commits (depending on
# whether the AE data were changed between HEAD~1 and HEAD). You can
# see the commit(s) with:
# This creates the AE branches (according to the .brutegit-ae-sync
# file) with one or two commits (depending on whether the AE data were
# changed between HEAD~1 and HEAD). You can see the commit(s) with:
#
# git log ae
# git log ae01 ae02
set -e
......@@ -36,71 +26,90 @@ set -e
# continues and produces empty tree, which is bad.
set -o pipefail
# Needed for +(...|...) pattern matching
shopt -s extglob
if [ -n "$(git config --get-all brutegit.glob)" ]; then
echo >&2 "Error: Old config brutegit.glob detected - switch to new .brutegit-ae-sync file!"
exit 1
fi
glob=$(git config --get-all brutegit.glob| paste -s -d '|') || :
if [ -z "$glob" ]; then glob='*/ae|*/auto_evaluation'; fi
get_ae_branches() {
local commit=${1:?}
git config --blob "${commit}:.brutegit-ae-sync" --get-regexp --name-only '^.*\.path$' \
| awk -F . '{print $1}' \
| uniq
}
handle_commit() {
local commit=${1:?}
local branch=${2:?}
local glob="+(${3:?})"
new_tree=$(
git ls-tree -r -d "$commit" | tac | \
(
declare -A trees=()
while read -r mode type hash path; do
path="./$path"
case "$path" in
$glob)
local branches=$(get_ae_branches "$commit") || :
for branch in $branches; do
local glob="+($(git config --get-all --blob "${commit}:.brutegit-ae-sync" "$branch.path" | paste -s -d '|'))" || :
new_tree=$(
# This lists only directories - if we want to match individual files, change -d to -t
git ls-tree -r -d "$commit" | tac | \
(
declare -A trees=()
while read -r mode type hash path; do
# prepend ./ to make upper_path always valid dir
path="./$path"
if [[ "${path#./}" == $glob ]]; then # pattern matching test
upper_path="${path%/*}"
trees[$upper_path]+="$(echo -e "$mode tree $hash\t${path##*/}")"$'\n'
;;
$upper_path)
trees[$upper_path]+="$mode tree $hash"$'\t'"${path##*/}"$'\n'
elif [ "$path" = "$upper_path" ]; then # string equality test
new_hash=$(echo -n "${trees[$path]}" | git mktree)
upper_path="${path%/*}"
trees[$upper_path]+="$(echo -e "040000 tree $new_hash\t${path##*/}")"$'\n'
;;
esac
done
echo -n "${trees[.]}" | git mktree ))
trees[$upper_path]+="040000 tree $new_hash"$'\t'"${path##*/}"$'\n'
fi
done
echo -n "${trees[.]}" | git mktree
))
local ae=$(git rev-parse --verify --quiet "refs/heads/$branch") || :
if [ -z "$ae" ] || ! git diff-tree --quiet $ae $new_tree; then
export GIT_AUTHOR_NAME="$(git log --format=%an -1 $commit)"
export GIT_AUTHOR_EMAIL="$(git log --format=%ae -1 $commit)"
export GIT_AUTHOR_DATE="$(git log --format=%aI -1 $commit)"
[ "$ae" ] && parent="-p $ae"
new_commit=$(git commit-tree $new_tree $parent -m "$(git cat-file commit $commit|sed -e '0,/^$/d')")
git update-ref refs/heads/ae $new_commit $ae
echo "ae-sync: Created new commit ${new_commit:0:7} on branch ae ($(git log -1 --format=%s "$commit"))"
else
commit_hash=$(git rev-parse "$commit")
echo "ae-sync: No AE changes in commit ${commit_hash:0:7} ($(git log -1 --format=%s "$commit"))"
fi
local old_commit=$(git rev-parse --verify --quiet "refs/heads/$branch" || :)
if [ -z "$old_commit" ] || ! git diff-tree --quiet $old_commit $new_tree; then
export GIT_AUTHOR_NAME="$(git log --format=%an -1 $commit)"
export GIT_AUTHOR_EMAIL="$(git log --format=%ae -1 $commit)"
export GIT_AUTHOR_DATE="$(git log --format=%aI -1 $commit)"
[ "$old_commit" ] && parent="-p $old_commit"
new_commit=$(git commit-tree $new_tree $parent -m "$(git cat-file commit $commit|sed -e '0,/^$/d')")
git update-ref refs/heads/$branch $new_commit $old_commit
echo "ae-sync: Created new commit ${new_commit:0:7} on branch $branch ($(git log -1 --format=%s "$commit"))"
fi
done
}
if [ "$1" ]; then
# local testing
handle_commit "$1" ae "$glob"
handle_commit "$1"
else
# git hook
while read oldrev newrev refname; do
if [ "$refname" = "refs/heads/master" ]; then
while read -r oldrev newrev refname; do
if [ "${refname:?}" = "refs/heads/master" ]; then
echo "ae-sync: Checking for AE updates"
ae=$(git rev-parse --verify --quiet refs/heads/ae) || :
for rev in $(git rev-list --reverse $oldrev..$newrev); do
handle_commit "$rev" ae "$glob"
declare -A old_hash=()
for branch in $(get_ae_branches "$refname"); do
old_hash[$branch]=$(git rev-parse --verify --quiet refs/heads/"$branch" || :)
done
if [ "$ae" != $(git rev-parse --verify --quiet refs/heads/ae) ]; then
if git config remote.brutegit.url >/dev/null; then
echo "ae-sync: Pushing AE to BRUTE"
git push -f --quiet brutegit ae:master
else
echo "ae-sync: Not pushing AE to BRUTE because brutegit remote is not configured"
for commit in $(git rev-list --reverse "${oldrev:?}..${newrev:?}"); do
handle_commit "$commit" || :
done
for branch in $(get_ae_branches "$commit"); do
if [ "${old_hash[$branch]}" != "$(git rev-parse --verify --quiet refs/heads/"$branch")" ]; then
new_ae_commit=1
url=$(git config --blob "${commit}:.brutegit-ae-sync" --get "$branch".url || :)
if [[ "$url" ]]; then
echo "ae-sync: Pushing AE $branch to $url"
git push -f --quiet "$url" "$branch":master
else
echo "ae-sync: Not pushing AE to BRUTE because url for $branch is not configured in .brutegit-ae-sync"
fi
fi
done
if [[ -z "$new_ae_commit" ]]; then
echo "ae-sync: No AE updates detected"
fi
fi
done
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment