From 8233abd23fd3edec3651e969fde49b706d44c5d3 Mon Sep 17 00:00:00 2001 From: Nathan Broadbent Date: Wed, 26 Sep 2018 23:04:51 +0700 Subject: [PATCH] Save/restore the git commit message in case a git pre-commit hook fails. Also rename the APPEND var to GIT_COMMIT_MSG_SUFFIX. --- lib/git/keybindings.sh | 4 +- lib/git/status_shortcuts.sh | 63 ++++++++++++++++++++------- test/lib/git/status_shortcuts_test.sh | 5 ++- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/lib/git/keybindings.sh b/lib/git/keybindings.sh index b286075..dc9f186 100644 --- a/lib/git/keybindings.sh +++ b/lib/git/keybindings.sh @@ -35,11 +35,11 @@ if [[ "$git_keyboard_shortcuts_enabled" = "true" ]]; then if [[ $shell == "zsh" ]]; then _bind "$git_commit_all_keys" " git_commit_all""$RETURN_CHAR" _bind "$git_add_and_commit_keys" " \033[1~ git_add_and_commit ""$RETURN_CHAR" - _bind "$git_commit_all_with_ci_skip_keys" " \033[1~ APPEND='[ci skip]' git_commit_all ""$RETURN_CHAR" + _bind "$git_commit_all_with_ci_skip_keys" " \033[1~ GIT_COMMIT_MSG_SUFFIX='[ci skip]' git_commit_all ""$RETURN_CHAR" else _bind "$git_commit_all_keys" "\" git_commit_all$RETURN_CHAR\"" _bind "$git_add_and_commit_keys" "\"\C-A git_add_and_commit $RETURN_CHAR\"" - _bind "$git_commit_all_with_ci_skip_keys" "\"\C-A APPEND='[ci skip]' git_commit_all $RETURN_CHAR\"" + _bind "$git_commit_all_with_ci_skip_keys" "\"\C-A GIT_COMMIT_MSG_SUFFIX='[ci skip]' git_commit_all $RETURN_CHAR\"" fi fi diff --git a/lib/git/status_shortcuts.sh b/lib/git/status_shortcuts.sh index d63bb0c..bd585c0 100644 --- a/lib/git/status_shortcuts.sh +++ b/lib/git/status_shortcuts.sh @@ -203,29 +203,62 @@ theirs(){ _git_resolve_merge_conflict "their" "$@"; } # * Add escaped commit command and unescaped message to bash history. git_commit_prompt() { local commit_msg + local saved_commit_msg + if [ -f "/tmp/.git_commit_message~" ]; then + saved_commit_msg="$(cat /tmp/.git_commit_message~)" + echo -e "\033[0;36mLeave blank to use saved commit message: \033[0m$saved_commit_msg" + fi if [[ $shell == "zsh" ]]; then vared -h -p "Commit Message: " commit_msg else read -r -e -p "Commit Message: " commit_msg fi - if [ -n "$commit_msg" ]; then - eval $@ # run any prequisite commands - # Add $APPEND to commit message, if given. (Used to append things like [ci skip] for Travis CI) - if [ -n "$APPEND" ]; then commit_msg="$commit_msg $APPEND"; fi - echo $commit_msg | git commit -F - | tail -n +2 - else - echo -e "\033[0;31mAborting commit due to empty commit message.\033[0m" + if [ -z "$commit_msg" ]; then + if [ -n "$saved_commit_msg" ]; then + commit_msg="$saved_commit_msg" + else + echo -e "\033[0;31mAborting commit due to empty commit message.\033[0m" + return + fi fi - escaped=$(echo "$commit_msg" | sed -e 's/"/\\"/g' -e 's/!/"'"'"'!'"'"'"/g') + # Add $GIT_COMMIT_MSG_SUFFIX to commit message, if given. + # (Used to append things like [ci skip] for Travis CI) + if [ -n "$GIT_COMMIT_MSG_SUFFIX" ]; then + commit_msg="$commit_msg $GIT_COMMIT_MSG_SUFFIX" + fi + + # Exclamation marks are really difficult to escape properly in a bash prompt. + # They must always be enclosed with single quotes. + escaped_msg=$(echo "$commit_msg" | sed -e 's/"/\\"/g' -e "s/!/\"'!'\"/g") + # Add command to bash history, so that if a git pre-commit hook fails, + # you can just press "up" and "return" to retry the commit. if [[ $shell == "zsh" ]]; then - print -s "git commit -m \"${escaped//\\/\\\\}\"" # zsh's print needs double escaping - print -s "$commit_msg" + # zsh's print needs double escaping + print -s "git commit -m \"${escaped_msg//\\/\\\\}\"" else - echo "git commit -m \"$escaped\"" >> $HISTFILE - # Also add unescaped commit message, for git prompt - echo "$commit_msg" >> $HISTFILE + history -s "git commit -m \"$escaped_msg\"" + # Need to write history to a file for tests + if [ -n "$SHUNIT_VERSION" ]; then history -w $HISTFILE; fi + fi + + # Also save the commit message to a temp file in case git commit fails + echo "$commit_msg" > "/tmp/.git_commit_message~" + eval $@ # run any prequisite commands + + echo "$commit_msg" | git commit -F - | tail -n +2 + + # Fetch the pipe status (for both bash and zsh): + GIT_PIPE_STATUS=("${PIPESTATUS[@]}${pipestatus[@]}") + if [[ $shell == "zsh" ]]; then + git_exit_status="${GIT_PIPE_STATUS[2]}" # zsh array indexes start at 1 + else + git_exit_status="${GIT_PIPE_STATUS[1]}" + fi + if [[ "$git_exit_status" == 0 ]]; then + # Delete saved commit message if commit was successful + rm -f "/tmp/.git_commit_message~" fi } @@ -234,8 +267,8 @@ git_commit_all() { fail_if_not_git_repo || return 1 changes=$(git status --porcelain | wc -l | tr -d ' ') if [ "$changes" -gt 0 ]; then - if [ -n "$APPEND" ]; then - local appending=" | \033[0;36mappending '\033[1;36m$APPEND\033[0;36m' to commit message.\033[0m" + if [ -n "$GIT_COMMIT_MSG_SUFFIX" ]; then + local appending=" | \033[0;36mappending '\033[1;36m$GIT_COMMIT_MSG_SUFFIX\033[0;36m' to commit message.\033[0m" fi echo -e "\033[0;33mCommitting all files (\033[0;31m$changes\033[0;33m)\033[0m$appending" git_commit_prompt "git add --all ." diff --git a/test/lib/git/status_shortcuts_test.sh b/test/lib/git/status_shortcuts_test.sh index d83d9ca..638858a 100755 --- a/test/lib/git/status_shortcuts_test.sh +++ b/test/lib/git/status_shortcuts_test.sh @@ -260,9 +260,10 @@ test_git_commit_prompt() { if [[ $shell == "zsh" ]]; then test_history="$(history)" else + # Need to load history from $HISTFILE + # (Couldn't get the 'history' builtin to work during tests.) test_history="$(cat $HISTFILE)" fi - assertIncludes "$test_history" "$commit_msg" assertIncludes "$test_history" "git commit -m \"$dbl_escaped_msg\"" } @@ -284,7 +285,7 @@ test_git_commit_prompt_with_append() { # Test the git commit prompt, by piping a commit message # instead of user input. - echo "$commit_msg" | APPEND="[ci skip]" git_commit_prompt > /dev/null + echo "$commit_msg" | GIT_COMMIT_MSG_SUFFIX="[ci skip]" git_commit_prompt > /dev/null git_show_output=$(git show --oneline --name-only) assertIncludes "$git_show_output" "$commit_msg \[ci skip\]"