Background
As stated, when running git commit, git starts your editor pointing to
the $GIT_DIR/COMMIT_EDITMSG file. Unless the commit-msg hook in question
moves/deletes/damages the file, the message should still be there.
I suppose that reusing the message is not the default behavior because it might
interfere with the prepare-commit-msg hook. Ideally, there would be a toggle
available to enable reusing by default, in order to avoid data loss. The
next-best thing would be to override a git sub-command with a git alias,
but unfortunately it is currently not possible and that is
unlikely to change. So we are left with creating a custom alias for it.
I went with an alias similar to the one in the accepted answer:
git config alias.recommit \
'!git commit -F "$(git rev-parse --git-dir)/COMMIT_EDITMSG" --edit'
Then, when running git recommit, the rejected commit message's content should
appear in the editor.
Addition
Note that both aliases would fail for the first commit in the repository, since the
COMMIT_EDITMSG file would not have been created yet. To make it also work in
that case, it looks a bit more convoluted:
git config alias.recommit \
'!test -f "$(git rev-parse --git-dir)/COMMIT_EDITMSG" &&
git commit -F "$(git rev-parse --git-dir)/COMMIT_EDITMSG" --edit ||
git commit'
Which can be shortened to:
git config alias.recommit \
'!cm="$(git rev-parse --git-dir)/COMMIT_EDITMSG" &&
test -f "$cm" && git commit -F "$cm" --edit || git commit'
Either way, considering the added safety, for interactive usage you could even
use one of the aforementioned aliases by default instead of git commit.
You could also make a wrapper for git itself and divert the calls based
on the arguments (i.e.: on the sub-command), though that would require ensuring
that all subsequent calls to git refer to the original binary, lest they
result in infinite recursion:
git () {
cm="$(git rev-parse --git-dir)/COMMIT_EDITMSG"
case "$1" in
commit)
shift
test -f "$cm" && command git commit -F "$cm" --edit "$@" ||
command git commit "$@"
;;
*)
command git "$@";;
esac
}
Note that if the above is added to your rc file (e.g.: ~/.bashrc), then every
call to git present in it will refer to the wrapper, unless you prepend them
with command as well.
Novelty
Finally, I just learned that aliasing to a wrapper file with a different
name is an option:
PATH="$HOME/bin:$PATH"
export PATH
alias git='my-git'
So the wrapper (e.g.: ~/bin/my-git) can be much simpler:
#!/bin/sh
cm="$(git rev-parse --git-dir)/COMMIT_EDITMSG"
case "$1" in
commit)
shift
test -f "$cm" && git commit -F "$cm" --edit "$@" ||
git commit "$@"
;;
*)
git "$@";;
esac
And also avoid interference, as aliases are not expanded when used in external
scripts.