#!/usr/bin/env bash
# To enable this hook, move this file to ".git/hooks/post-commit"

set -euo pipefail

# utility function to display message in color (if supported)
highlight() {
  tput -S << EOF
bold
setaf $1
EOF
  echo "$2"
  tput sgr0
}

# utility function to search for a program (in a subshell)
find_program() (
  PATH="$(git rev-parse --git-path hooks):/opt/rocm-4.3.1/llvm/bin:/opt/rocm-4.3.0/llvm/bin:$PATH"
  export PATH
  command -v "$1" >/dev/null
)

# utility function to find the number of parents of a git commit
get_commit_parent_count() {
  git log -n 1 --format=%P "$1^!" | wc --words
}

# gather commit information
commit=$(git rev-parse HEAD)
short_commit=$(git rev-parse --short "$commit")

# setup cleanup
ok=0
on_exit() {
  if [ $ok -ne 1 ]; then
    highlight 1 'post-commit: formatting failed'
  fi
  chmod +x "${BASH_SOURCE[0]}" # re-enable hook
}
trap on_exit EXIT

# check for unstaged changes
if git diff-index --quiet HEAD; then
  dirty=false
else
  dirty=true
fi

>&2 echo "post-commit: processing commit $short_commit"

# save workspace
if [ "$dirty" = true ]; then
  >&2 echo 'post-commit: stashing uncommitted changes'
  git stash push --quiet --message 'post-commit: uncommitted changes'
fi

# check if clang-format is available
declare -a format_options
if ! find_program clang-format; then
  format_options+=('--no-clang-format')
  >&2 highlight 3 "post-commit: clang-format not found"
fi

# apply formatting and stage changes
script_dir=$(dirname "${BASH_SOURCE[0]}")
while IFS= read -r -d '' file; do
  >&2 echo "post-commit: reformatting $file"
  "$script_dir/reformat-files" "${format_options[@]}" -- "$file"
  git add --update -- "$file"
done < <(git diff-tree --no-commit-id --name-only -r --diff-filter=d -z HEAD)

# commit changes and restore workspace
if git diff --quiet --staged; then
  >&2 echo 'post-commit: files unchanged after reformatting'
else
  >&2 echo 'post-commit: amending commit with reformatted files'
  chmod -x "${BASH_SOURCE[0]}" # prevent recursion
  git config advice.ignoredHook false # disable warning
  git commit --amend --allow-empty --no-edit --no-verify --no-post-rewrite

  # warn if commit is empty
  if [ "$(get_commit_parent_count HEAD)" -eq 1 ] && git diff --quiet HEAD HEAD^@; then
    >&2 highlight 3 "post-commit: commit is empty"
  fi
fi

# restore workspace
if [ "$dirty" = true ]; then
  >&2 echo 'post-commit: restoring uncommitted changes from stash'
  git stash pop --quiet
fi
ok=1
