#!/bin/bash set -f && shopt -s extglob || exit function get_common_prefix { local a b i IFS=/ a=($1) b=($2) i=0 for (( ; i < ${#a[@]}; ++i )); do [[ ${a[i]} == "${b[i]}" ]] || break done __=${a[*]:0:i} } function get_common_prefix_multi { __=${1%/*} while shift; [[ ${1+.} ]]; do get_common_prefix "$__" "${1%/*}" done } function is_message { [[ $1 != *([[:blank:]])'#'* && $1 == *[![:blank:]]* ]] } function main { local commit_msg_file=$1 common_prefix content contents_changed=false files_to_commit \ first_message_index= has_multiple_first_message_lines=false prefix= warning temp i __ local PREFIX_PATTERN='+([![:blank:]]):*' readarray -t content < "${commit_msg_file}" || exit for (( i = 0; i < ${#content[@]}; ++i )); do if [[ ${content[i]} == "# ----"* ]]; then break elif is_message "${content[i]}"; then first_message_index=$i if (( ++i < ${#content[@]} )) && is_message "${content[i]}"; then has_multiple_first_message_lines=true fi break fi done readarray -t files_to_commit < <(git status --porcelain | awk '/^[AMD]/ { print substr($0, 4) }') get_common_prefix_multi "${files_to_commit[@]}" common_prefix=$__ if [[ -z ${first_message_index} || ${has_multiple_first_message_lines} == false && \ ${content[first_message_index]} != ${PREFIX_PATTERN} && \ ${content[first_message_index]} != @(squash|fixup|ammend)"! "* ]]; then case ${common_prefix} in +([!/-])-+([!/-])|helpers/git-hooks) prefix=${common_prefix} ;; +([!/-])-+([!/-])/*) prefix=${common_prefix##+([!/])/+([!/])} prefix=${common_prefix:0:${#common_prefix} - ${#prefix}} ;; eclass|eclass/*) if [[ ${#files_to_commit[@]} -eq 1 ]]; then prefix=${files_to_commit#eclass/} else prefix=${common_prefix} fi ;; esac if [[ ${prefix} ]]; then if [[ ${first_message_index} ]]; then i=${first_message_index} content[i]="${prefix%/}: ${content[i]##+([[:blank:]])}" else for (( i = 0; i < ${#content[@]}; ++i )); do if [[ ${content[i]} == *([[:blank:]]) ]]; then unset 'content[i]' else break fi done content=("${prefix%/}: " "" "${content[@]}") fi contents_changed=true fi fi readarray -t excluded < <(exec git status "${common_prefix:-.}" --porcelain | awk '/^[\? ]/ { print $2 }') if [[ ${#excluded[@]} -gt 0 ]]; then warning=( "# WARNING: The following files are untracked or not being committed:" "# " ) for __ in "${excluded[@]}"; do warning+=("# $__") done warning+=("") for (( i = 0; i < ${#content[@]}; ++i )); do if [[ ${content[i]} == '#'* ]]; then content=("${content[@]:0:i}" "${warning[@]}" "${content[@]:i}") warning=() break fi done [[ ${#warning[@]} -gt 0 ]] && content+=("${warning[@]}") contents_changed=true fi for (( i = 0; i < ${#content[@]}; ++i )); do if [[ ${content[i]} == '#'* ]]; then if [[ i -lt 2 ]]; then temp=("${content[@]:0:i}" "" "") content=("${temp[@]:0:2}" "${content[@]:i}") contents_changed=true fi break fi done [[ ${contents_changed} == false ]] || printf '%s\n' "${content[@]}" > "${commit_msg_file}" } main "$@"