If you do:
PS1='$(set -x; sleep 3; echo STATUS)$ '
You'll see that ^C works. It's just that then bash tries to redraw a prompt and runs sleep 3 again.
Same goes in:
$ PROMPT_COMMAND='git_status=$(set -x; sleep 3; echo STATUS)' PS1='$git_status$ '
That can be worked around with:
PROMPT_COMMAND='
interrupted=0
trap interrupted=1 INT QUIT
git_status=$(set -x; sleep 3; echo STATUS)
trap - INT QUIT
if (( interrupted )); then
git_status+="<INTERRUPTED>"
fi'
PS1='$git_status$ '
Where we add a handler on SIGINT. As the signal is handled and $PROMPT_COMMAND finishes normally, bash doesn't redraw the prompt. We can also add information that the command was interrupted in the prompt.
You'd replace set -x; sleep 3; echo STATUS here used as an example place holder with your git status or equivalent.
In zsh, you'd do something similar and a bit more cleanly with:
precmd() {
set -o localoptions -o localtraps
local interrupted=0
trap interrupted=1 INT QUIT
psvar[1]=$(set -x; sleep 3; echo STATUS)
if (( interrupted )) psvar[1]+='<INTERRUPTED>'
}
PS1='%1v$ '
cd, or do you run them fromPROMPT_COMMANDor ... Remove the commands where they were originally added.