#!/bin/sh
# Gold moves by the settle script and by nothing else. Where its head
# does not carry the message of the last patch the ledger says was
# settled, something was applied, reset, or committed there by hand, and
# every patch cut afterwards is against a base that is nobody's tree.
# Gold is meant to hold the same tree Chris committed, and until now
# nothing compared the two: the check read commit messages, which match
# even where the contents do not. A patch of mine once added four
# sentences to a plan that his tree never took, and every later patch was
# cut against that difference and would not apply. The confirmed history
# is fetchable, so the tree itself is compared against it.
REMOTE=${REMOTE:-https://www.seekquarry.com/group/2/yioop-repo.git}
check_gold_tree()
{
if [ "${SKIP_REMOTE_CHECK:-}" = "1" ]; then
return 0
fi
where=`mktemp -d`
if ! timeout -s KILL 150 git clone -q --depth 25 "$REMOTE" \
"$where" 2>/dev/null; then
echo "check_sync: the confirmed history could not be reached;"
echo " gold's tree was not compared against it"
rm -rf "$where"
return 0
fi
# Chris commits on his own machine and pushes later, so the
# published history is often a patch or two behind gold. Where it
# does not yet carry gold's newest commit there is nothing to
# compare against, and saying so is the answer rather than refusing
# a cut.
mine=`git -C "$GOLD" log -1 --format=%s`
if ! git -C "$where" log --format=%s | grep -qxF "$mine"; then
echo "check_sync: gold is ahead of the published history;"
echo " its tree was not compared"
rm -rf "$where"
return 0
fi
# Gold's own commit is the one to compare against, not whatever the
# published history has at its tip. Chris may have committed a patch
# and then rejected it, which leaves the published history ahead of
# gold with a commit gold must never take. Comparing against the tip
# would call gold stale for holding exactly the tree it should.
at_theirs=`git -C "$where" log --format='%H %s' | \
grep -F " $mine" | head -1 | cut -d' ' -f1`
theirs=`git -C "$where" rev-parse "$at_theirs^{tree}"`
ours=`git -C "$GOLD" rev-parse HEAD^{tree}`
ahead=`git -C "$where" log --oneline "$at_theirs"..HEAD | wc -l`
rm -rf "$where"
if [ "$theirs" != "$ours" ]; then
echo "check_sync: FAIL gold's tree is not the one Chris"
echo " committed. Rebuild gold from $REMOTE before cutting."
return 1
fi
if [ "$ahead" -gt 0 ]; then
echo "check_sync: the published history carries $ahead commit(s)"
echo " past gold's; gold takes one only when Chris says so"
fi
return 0
}
check_gold_head()
{
last=`grep ' settled$' "$LEDGER" | tail -1 | cut -d' ' -f2`
if [ -z "$last" ]; then
return 0
fi
head_message=`git -C "$GOLD" log -1 --format=%s`
as_name=`echo "$head_message" | tr 'A-Z' 'a-z' | tr -c 'a-z0-9' '-' | \
sed 's/--*/-/g; s/^-//; s/-$//'`
# Chris names his commit as he pleases: he may add a word or a
# number to the message the patch was cut under. What matters is
# that his message is the patch's name with something after it,
# not that the two match letter for letter.
case "$as_name" in
"$last"|"$last"-*)
return 0
;;
esac
if [ "$as_name" != "$last" ]; then
echo "check_sync: FAIL gold's head is \"$head_message\" but the"
echo " ledger last settled \"$last\". Gold was moved by hand."
return 1
fi
return 0
}
# Refuses to let a patch be cut when gold and work have drifted apart, or
# when the ledger has been written by anything but the two scripts. Every
# check below stands for a failure that has happened and cost a round.
GOLD=${GOLD:-/home/claude/gold}
WORK=${WORK:-/home/claude/work}
LEDGER=${LEDGER:-/home/claude/gold_ledger.txt}
fail=0
# Gold is a vault: its working tree must equal its own confirmed-head, so
# nothing has been applied, edited or checked out inside it by hand.
gold_head=`git -C "$GOLD" rev-parse confirmed-head^{tree} 2>/dev/null`
gold_now=`git -C "$GOLD" rev-parse HEAD^{tree} 2>/dev/null`
if [ "$gold_head" != "$gold_now" ]; then
echo "check_sync: FAIL gold's head is not its confirmed-head; something"
echo " was done inside gold by hand."
fail=1
fi
if [ -n "`git -C "$GOLD" status --porcelain 2>/dev/null`" ]; then
echo "check_sync: FAIL gold has uncommitted changes; it is a vault."
fail=1
fi
# Work's confirmed-head must name the same tree as gold's. When gold has
# been settled and work was not re-cloned, work's tag is stale and every
# patch cut from it is against a base Chris does not have.
work_head=`git -C "$WORK" rev-parse confirmed-head^{tree} 2>/dev/null`
if [ "$work_head" != "$gold_head" ]; then
echo "check_sync: FAIL work's confirmed-head is not gold's tree."
echo " Re-clone work from gold and restore work_directory."
fail=1
fi
# The ledger is written by settle_gold.sh and cut_patch.sh alone. Each
# writes a checksum beside it; a mismatch means it was edited by hand.
if [ -f "$LEDGER.sum" ]; then
want=`cat "$LEDGER.sum"`
have=`cksum < "$LEDGER" | awk '{print $1"-"$2}'`
if [ "$want" != "$have" ]; then
echo "check_sync: FAIL the ledger was changed by something other"
echo " than the two scripts."
fail=1
fi
fi
if [ "$fail" = "0" ]; then
if ! check_gold_head; then
exit 1
fi
if ! check_gold_tree; then
exit 1
fi
# The settings file in work may differ from gold in the line naming
# the work directory and nothing else. A saved copy of that file put
# over it drops whatever gold has gained since, and the patch then
# reads as taking those settings away.
#
# A patch that adds a setting is a different thing from that, and it is
# told apart by whether the change is committed. What a round means to
# deliver has been committed, so only lines still uncommitted are counted
# here: a stale copy dropped over the file shows up as uncommitted
# whatever it holds, while a setting the round added is already in a
# commit and passes.
settings=`git diff HEAD --numstat -- src/configs/Config.php \
2>/dev/null | awk '{print $1 + $2}'`
if [ -n "$settings" ] && [ "$settings" -gt 2 ]; then
echo "check_sync: FAIL the settings file has $settings uncommitted"
echo " line(s); it may differ by the work directory line alone."
echo " Take gold copy and set that one line, or commit the"
echo " settings this round adds."
exit 1
fi
echo "check_sync: PASS gold and work agree, gold holds the tree"
echo " Chris committed, and the ledger is untouched by hand"
fi
exit $fail