hooks: put the existing hook back if Entire's hook cannot be written
Commit

prepareHookBackup renames the current hook (and, when rotating, the previous backup) before Entire's hook is written. If that write failed (disk full, quota, temp-file creation), the install returned with no hook at the active path, where previously the foreign hook stayed in place. Losing pre-push also loses Entire's privacy check until a later install.
Record each rename that actually happened (renameIfStillForeign skips the move when another process just installed Entire's hook) and reverse them, newest first, when the write fails or a rotation fails halfway. Undo never renames onto an existing file, so a hook another process wrote meanwhile is kept, and its errors are joined with the write error. The backup notice now prints only after the write succeeds.
installOneHook takes the writer as a parameter so tests inject a failing one per call and stay parallel.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com Entire-Checkpoint: 01M46YZ4576ZVX3NGZ9J73TQ3D