/ devlog / devscripts / first_old_file.sh
#!/bin/sh
# Says which one old file a patch has to bring up, and nothing where the
# patch has already brought one up.
#
#   sh devlog/devscripts/first_old_file.sh <check-script> <file> [<file> ...]
#
# It prints the name of the file that has to be fixed, or nothing where
# none does. Whatever the check itself printed for that file goes to
# standard error, so a caller can show it.
#
# WHY THIS EXISTS. A file written years ago may carry a hundred names or
# docblocks that today's rules would not allow. A patch that touches such
# a file cannot be asked to rewrite all of them and do what it came to do
# as well; that is how a round gets spent on nothing. So a patch brings
# up one old file and the rest wait for later patches.
#
# The part that took a round to get right is what counts as this patch's
# share. Refusing the first file with faults and then, once it is clean,
# refusing the next, asks a patch touching four old files to bring up all
# four. What counts here instead is whether the patch has already turned
# some file from faulty into clean: a file that had faults in gold and
# has none now is this patch's share, and every other file is named and
# left. Where the patch has brought none up, the first with faults is
# the one to fix.
GOLD=${GOLD:-/home/claude/gold}
check="$1"
shift
if [ -z "$check" ] || [ -z "$1" ]; then
    exit 0
fi
brought_up=""
first_faulty=""
waiting=""
was=/tmp/first_old_was.$$
now=/tmp/first_old_now.$$
for one in "$@"
do
    if [ ! -f "$one" ]; then
        continue
    fi
    # A check may print its faults and still exit zero, so what counts
    # is whether it named this file, not what it exited with.
    sh "$check" "$one" > "$now" 2>&1
    if grep -q "^$one:" "$now"; then
        faulty_now=1
    else
        faulty_now=0
    fi
    # A file the patch does not change cannot be its share either way.
    if git diff confirmed-head..HEAD --name-only -- "$one" | grep -q .
    then
        git show "confirmed-head:$one" > /tmp/first_old_copy.$$ 2>/dev/null
        if [ -s /tmp/first_old_copy.$$ ]; then
            here=`dirname "$one"`
            mkdir -p "/tmp/first_old_tree.$$/$here"
            cp /tmp/first_old_copy.$$ "/tmp/first_old_tree.$$/$one"
            # The copy of gold's file is read from a tree of its own,
            # so a check named by a path of its own must be made
            # absolute before the move.
            case "$check" in
                /*) whole_check="$check" ;;
                *) whole_check="`pwd`/$check" ;;
            esac
            (cd "/tmp/first_old_tree.$$" && \
                sh "$whole_check" "$one") > "$was" 2>&1
            if grep -q "^$one:" "$was"; then
                faulty_before=1
            else
                faulty_before=0
            fi
            rm -rf "/tmp/first_old_tree.$$"
            if [ "$faulty_before" != "0" ] && [ "$faulty_now" = "0" ]; then
                brought_up="$one"
            fi
        fi
        rm -f /tmp/first_old_copy.$$
    fi
    if [ "$faulty_now" != "0" ]; then
        if [ -z "$first_faulty" ]; then
            first_faulty="$one"
            cat "$now" >&2
        else
            waiting="$waiting $one"
        fi
    fi
done
rm -f "$was" "$now"
if [ -n "$waiting" ] || [ -n "$first_faulty" ]; then
    if [ -n "$brought_up" ]; then
        echo "left for a later patch:$first_faulty$waiting" >&2
        exit 0
    fi
    echo "left for a later patch:$waiting" >&2
fi
if [ -n "$first_faulty" ]; then
    echo "$first_faulty"
fi
exit 0
X