#!/usr/bin/env bash
#
# A git commit hook that will automatically run format checking and DCO signoff
# checking before the push is successful.
#
# To enable this hook, run `bootstrap`, or run the following from the root of
# the repo. (Note that `bootstrap` will correctly install this even if this code
# is located in a submodule, while the following won't.)
#
# $ ln -s ../../support/hooks/pre-push .git/hooks/pre-push

DUMMY_SHA=0000000000000000000000000000000000000000

echo "Running pre-push check; to skip this step use 'push --no-verify'"

while read LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA
do
  if [ "$LOCAL_SHA" = $DUMMY_SHA ]
  then
    # Branch deleted. Do nothing.
    exit 0
  else
    if [ "$REMOTE_SHA" = $DUMMY_SHA ]
    then
      # New branch. Verify the last commit, since this is very likely where the new code is
      # (though there is no way to know for sure). In the extremely uncommon case in which someone
      # pushes more than 1 new commit to a branch, CI will enforce full checking.
      RANGE="$LOCAL_SHA~1..$LOCAL_SHA"
    else
      # Updating branch. Verify new commits.
      RANGE="$REMOTE_SHA..$LOCAL_SHA"
    fi

    # Verify DCO signoff. We do this before the format checker, since it has
    # some probability of failing spuriously, while this check never should.
    #
    # In general, we can't assume that the commits are signed off by author
    # pushing, so we settle for just checking that there is a signoff at all.
    SIGNED_OFF=$(git rev-list --no-merges --grep "^Signed-off-by: " "$RANGE")
    NOT_SIGNED_OFF=$(git rev-list --no-merges "$RANGE" | grep -Fxv "$SIGNED_OFF")
    if [ -n "$NOT_SIGNED_OFF" ]
    then
      echo >&2 "ERROR: The following commits do not have DCO signoff:"
      while read -r commit; do
        echo "  $(git log --pretty=oneline --abbrev-commit -n 1 $commit)"
      done <<< "$NOT_SIGNED_OFF"
      exit 1
    fi
  fi
done

exit 0
