Gustavo Jose de Sousa
10 years ago
committed by
Andrew Tridgell
1 changed files with 202 additions and 0 deletions
@ -0,0 +1,202 @@
@@ -0,0 +1,202 @@
|
||||
#!/bin/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 <<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 |
Loading…
Reference in new issue