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.