The mechanism
The terminal itself is not directly aware what processes receive SIGINT upon Ctrl+C. It sends the signal to the foreground process group. Then it's the kernel who delivers the signal to each member of the group. Usually there's also a shell, but the shell does not relay the signal; it only informs the terminal which process group is in the foreground.
This mechanism allows Ctrl+C to interrupt all processes in a foreground pipeline. All the terminal knows is PGID (process group ID) of the current foreground process group.
Modifying terminal
You can modify the terminal, so if it's about to send SIGINT, it will check if there's git in the foreground process group and act accordingly. (I think you need to modify the tty/pts driver rather than your favorite terminal emulator; but it's way beyond my expertise, so I may be wrong.)
Modifying shell
If the shell relayed SIGINT, you could modify its code to ask for confirmation before relaying to git (or to a process group containing git). But the shell does not relay the signal. To relay the signal, a modified shell should always place itself alone in the foreground (so SIGINT from the terminal goes only to the shell) and handle an additional level of foreground-background abstraction to always be able to tell which process group it should relay the signal to. Such naive approach would not be enough (investigate SIGTTIN and SIGTTOU), the shell should provide a pts. Providing a pts is what terminal multiplexers already do.
Terminal multiplexer
I'm not familiar with screen, I use tmux. The tool is useful by itself, but now the most important thing is it can solve your problem.
Add this line to your .tmux.conf:
bind -T root 'C-c' if-shell 'tty="#{pane_tty}"; ps -eo tty,stat,comm | grep "^${tty#/dev/} .*+.* git$"' 'confirm-before -p "Git detected. Confirm SIGINT? (y/n)" "send-keys C-c"' 'send-keys C-c'
Notes:
- I tested this with
sleep, not git (because I don't use git).
ps -o stat is not portable. I used ps from procps-ng 3.3.15 on Debian 10. stat is useful because it prints + for processes in foreground process groups. The idea is to ignore git in the background (if any) because Ctrl+C won't send SIGINT to it anyway.
This solution is my personal choice (partially because I use tmux daily). It's the only solution in my answer that actually asks for confirmation.
Modifying git
You can modify git to behave differently. I don't necessarily mean Git (i.e. the tool, although modifying it is also possible), but the command. You can create a wrapper script or a shell function named git that would:
- run the real
git in a way that would make it ignore SIGINT,
- or run the real
git in yet another process group that is never in the foreground,
- or reconfigure the controlling terminal, run the real
git, configure the terminal back.
Below are example functions, tested in Bash. If you decide to create a wrapper script (instead of a function) under the name git and available via PATH, then use a full path to the real git to avoid the script calling itself.
To make the real git ignore SIGINT, wrap it in the following function:
git () ( trap '' INT; exec git "$@" )
Note the function body runs in a subshell, so trap will not affect the main shell. There are disadvantages:
- The real
git will ignore every SIGINT (it will not ignore SIGTERM though).
- If
git is used in a pipeline then Ctrl+C will still affect other parts of it. Then the real git may exit anyway because of SIGPIPE. We could trap '' PIPE as well, but I guess you'd like not to interrupt the pipe in the first place.
Running the real git in yet another process group (no matter how you do it) is IMO not better, sometimes worse than the above "ignore SIGINT" solution.
This is how you reconfigure the terminal back and forth:
git () ( trap '' INT; stty -F /dev/tty intr '^G'; ( trap - INT; exec git "$@" ); stty -F /dev/tty intr '^C' )
Now as long as git runs, Ctrl+C will not work; use Ctrl+G instead if your really want to interrupt it.
Notes:
- If
git is used in a pipeline then Ctrl+C will not affect other parts of it. Ctrl+G will affect the whole pipeline.
- If you run
git (the wrapper) in the background then it will be suspended immediately because of stty trying to interact with the controlling terminal. To circumvent, you can start git in the foreground and Ctrl+Z after the first stty runs, then bg. On Ctrl+Z the shell may restore tty settings (Bash does), which is fortunate if you plan to bg; but if you fg then you will be able to terminate the real git with Ctrl+C again.
- If there are two or more
gits in a foreground pipeline then the first one to exit will restore the default setting, while you would certainly prefer only the last one to do it.