#!/usr/bin/env bash SCRIPT_DIR=$(dirname $(realpath ${BASH_SOURCE[0]})) GIT_DIR=$(git rev-parse --git-dir) GIT_ROOT=$(git rev-parse --show-toplevel) usage() { cat >&$1 < "$option_F" fi if [[ -n "$option_m" ]]; then echo "$option_m" else cat "$option_F" fi | if $option_raw; then cat else sed "s,\$subsystem,$cur_subsystem,g" fi > "$MSG_FILE" extra_options=(-F "$MSG_FILE") elif [[ -n $prev_subsystem ]] && ! $option_naive; then # try to be "smart" cat $GIT_DIR/COMMIT_EDITMSG \ | sed -e "/^\s*#/d" \ -e "s/.*\<$prev_subsystem\>.*/\0\n#\0/" \ | sed "/^[^#]/ s/\<$prev_subsystem\>/$subsystem/g" > "$MSG_FILE" echo >> "$MSG_FILE" echo "# This commit message was adapted by commit-subsystems" >> "$MSG_FILE" extra_options=(-F "$MSG_FILE" --edit) fi } commit_subsystem() { local subsystem=$1 local prev_subsystem=$2 shift 2 process_msg $subsystem $prev_subsystem if ! git commit ${extra_options[*]} "$@"; then echo "Couldn't commit subsystem $subsystem, aborting..." >&2 exit 1 fi } args=() option_m= option_F= option_naive=false option_raw=false while [[ -n "$1" ]]; do opt="$1" case "$opt" in -h|--help) usage 1 exit 0 ;; -m|--message) shift if [[ -z "$1" ]]; then echo "Option $opt requires a commit message." >&2 exit 1 fi option_m="$1" ;; -F|--file) shift if [[ -z "$1" ]]; then echo "Option $opt requires a file name." >&2 exit 1 fi option_F="$1" ;; --naive) option_naive=true ;; --raw) option_raw=true ;; *) args+=("$1") ;; esac shift done if [[ -n $option_m && -n $option_F ]]; then echo "Options -m and -F can't be combined." >&2 exit 1 fi set -- "${args[@]}" LIST=$GIT_DIR/COMMIT_SUBSYSTEMS_LIST git diff --name-only --staged | $SCRIPT_DIR/path-libraries.sh -p > $LIST git diff --name-only --staged | $SCRIPT_DIR/path-nonlibraries.sh -p >> $LIST if [[ $(cat "$LIST" | wc -l) -eq 0 ]]; then echo "Nothing to commit." >&2 exit 1 fi echo "Reseting changes in order to add files separately..." git reset >/dev/null # head before commits - for recovery RECOVERY_HEAD=$(git log -n 1 --format=%H) exit_hook() { local last_error=$? set +e [[ -a /dev/fd/3 ]] && exec 3<&- [[ $last_error -eq 0 ]] && return 0 echo echo "Program interrupted or finished with error(s), reseting head..." >&2 git reset $RECOVERY_HEAD >/dev/null echo "Trying to re-add files..." >&2 if [[ ! -f $LIST ]]; then echo "File with list of added files not found..." >&2 else error=false cat $LIST | while read subsystem path; do if ! git add -- "$GIT_ROOT/$path"; then echo "Couldn't add \"$path\"..." >&2 error=true fi done if $error; then echo "This is embarrassing, couldn't re-add all files. Sorry." >&2 else echo "Files re-added." >&2 fi fi return 1 } set -e trap "exit 1" SIGINT trap exit_hook EXIT echo "Adding and committing subsystems..." exec 3< $LIST cur_subsystem= prev_subsystem= empty=true while read -u 3 subsystem path; do empty=false if [[ $cur_subsystem != $subsystem ]]; then if [[ -n $cur_subsystem ]]; then commit_subsystem "$cur_subsystem" "$prev_subsystem" "$@" echo fi prev_subsystem=$cur_subsystem cur_subsystem=$subsystem fi if ! git add -- "$GIT_ROOT/$path"; then echo "Couldn't add \"$path\", aborting..." >&2 exit 1 fi done # the last one commit_subsystem "$cur_subsystem" "$prev_subsystem" "$@" echo