2023-12-04 05:55:08 -04:00
|
|
|
#!/usr/bin/env bash
|
2015-09-03 19:15:58 -03:00
|
|
|
|
|
|
|
SCRIPT_DIR=$(dirname $(realpath ${BASH_SOURCE[0]}))
|
|
|
|
GIT_DIR=$(git rev-parse --git-dir)
|
|
|
|
GIT_ROOT=$(git rev-parse --show-toplevel)
|
|
|
|
|
|
|
|
usage() {
|
|
|
|
cat >&$1 <<EOF
|
|
|
|
Usage: git commit-subsystems [OPTIONS]
|
|
|
|
|
|
|
|
Ardupilot's git extension.
|
|
|
|
|
|
|
|
Create a different commit for each ardupilot's subsystem (vehicles, libraries
|
|
|
|
and folders in the project's root). The items in OPTIONS are passed down to the
|
|
|
|
original git commit command with exception of options --message and --file (and
|
|
|
|
their short versions). Boths are related to the commit message:
|
|
|
|
commit-subsystems will treat the commit message from one of those two options
|
|
|
|
as a template such that occurrences of \$subsystem are replaced with the
|
|
|
|
subsystem being currently committed.
|
|
|
|
|
|
|
|
If neither --message or --file are passed, then the first commit's message will
|
|
|
|
be used as a template for the other messages and option --edit will be used for
|
|
|
|
the next commits. To avoid that behavior use --naive.
|
|
|
|
|
|
|
|
Another custom option is --raw: don't use commit message as a template.
|
|
|
|
EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
MSG_FILE="$GIT_DIR/COMMIT_SUBSYSTEM_MSG"
|
|
|
|
extra_options=
|
|
|
|
|
|
|
|
process_msg() {
|
|
|
|
local subsystem=$1
|
|
|
|
local prev_subsystem=$2
|
|
|
|
if [[ -n "$option_m" || -n "$option_F" ]]; then
|
|
|
|
if [[ $option_F == - ]]; then
|
|
|
|
echo "Please, type the commits message template:"
|
|
|
|
option_F="$GIT_DIR/COMMIT_SUBSYSTEM_TEMPLATE"
|
|
|
|
cat - > "$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
|