Preserve default hooks forwarding in wrapper mode#771
Preserve default hooks forwarding in wrapper mode#771
Conversation
| if let Some(default_hooks_dir) = default_forward_hooks_path(repo, managed_hooks_dir) { | ||
| return ( | ||
| ForwardMode::RepoLocal, | ||
| Some(default_hooks_dir.to_string_lossy().to_string()), | ||
| None, | ||
| ); | ||
| } |
There was a problem hiding this comment.
🟡 default_forward_hooks_path always returns Some, making ForwardMode::None preservation dead code and dropping original_local_hooks_path
default_forward_hooks_path (git_hook_handlers.rs:780-786) always returns Some because .git/hooks can never be classified as disallowed by is_disallowed_forward_hooks_path — it's not inside .git-ai, not inside .git/ai, and not equal to the managed dir .git/ai/hooks. This means the ForwardMode::None preservation block at lines 874-882 is unreachable dead code.
The practical consequence: when prior state had ForwardMode::None with a saved original_local_hooks_path (set during externally-managed mode at git_hook_handlers.rs:818), the default hooks dir fallback at lines 866-872 always fires first and returns original_local_hooks_path: None. This silently drops the saved path. Later, remove_repo_hooks (git_hook_handlers.rs:989-998) uses original_local_hooks_path to restore the user's original core.hooksPath — if it's been dropped to None, the config is unset instead of restored.
Scenario that triggers the bug
- User has
core.hooksPath = /custom/hooks - git-ai installs hooks (sets
core.hooksPathto managed dir, saves original path) git_hooks_externally_managedflag is enabled → state saved withForwardMode::None+original_local_hooks_path- Flag is disabled,
ensureruns → default hooks fallback always fires →original_local_hooks_pathlost git-hooks remove→ can't restore/custom/hooks, unsets config instead
Prompt for agents
In src/commands/git_hook_handlers.rs, the default_forward_hooks_path fallback at lines 866-872 in select_forward_target_for_repo always returns Some, which means the ForwardMode::None block at lines 874-882 is dead code and original_local_hooks_path from prior state is silently dropped.
To fix this, the default hooks dir fallback should preserve original_local_hooks_path from prior state. Change lines 866-872 from:
if let Some(default_hooks_dir) = default_forward_hooks_path(repo, managed_hooks_dir) {
return (
ForwardMode::RepoLocal,
Some(default_hooks_dir.to_string_lossy().to_string()),
None,
);
}
To something like:
if let Some(default_hooks_dir) = default_forward_hooks_path(repo, managed_hooks_dir) {
let preserved_original = prior_state
.and_then(|state| state.original_local_hooks_path.clone());
return (
ForwardMode::RepoLocal,
Some(default_hooks_dir.to_string_lossy().to_string()),
preserved_original,
);
}
This ensures that when falling back to the default hooks directory, any previously saved original_local_hooks_path is carried forward so that git-hooks remove can still restore the user's prior hooksPath.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
.git/hooksdirectory as a forward target when git-ai hook state exists without an explicitcore.hooksPathforward_mode: nonerepos to the default hooks dir in wrapper-managed child git invocationspost-checkoutforwarding in both normal repos and worktreesTesting