Skip to content

Handle git aliases in hook processing#378

Closed
cnaples79 wants to merge 1 commit intogit-ai-project:mainfrom
cnaples79:handle-alias-hooks
Closed

Handle git aliases in hook processing#378
cnaples79 wants to merge 1 commit intogit-ai-project:mainfrom
cnaples79:handle-alias-hooks

Conversation

@cnaples79
Copy link
Copy Markdown

@cnaples79 cnaples79 commented Jan 19, 2026

Summary

  • resolve git aliases before dispatching hooks so aliased commands still trigger hook logic
  • expand aliases using git config (ignoring shell aliases) and prepend alias args to original args
  • add tests covering alias expansion and argument passthrough

Rationale

Commands invoked via git aliases (e.g., , ) were bypassing git-ai hooks. Resolving aliases to their underlying commands ensures hooks still run.

Changes

  • resolve alias definitions and rewrite parsed command/args prior to hook dispatch
  • ignore shell-style aliases
  • add unit tests for alias expansion behavior

Test Plan

  • unit tests added for alias expansion

Closes #377


Open with Devin

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Jan 19, 2026

CLA assistant check
All committers have signed the CLA.

@svarlamov svarlamov self-assigned this Jan 19, 2026
@svarlamov
Copy link
Copy Markdown
Member

@cnaples79 Thank you so much for your first contribution to Git AI!

Excited to get this functionality merged in! Do you have a some time to add more end-to-end tests via the TestRepo harness and possibly take a look at a native git config implementation? #377

I think gix-config is production-ready: https://github.com/GitoxideLabs/gitoxide/tree/main/gix-config

I'm open to merging without a native git config implementation, however, in that case, I think we should add a benchmark to verify we're not adding more than 5-10ms per command. Thanks again and happy to talk this through https://calendly.com/acunniffe/meeting-with-git-ai-authors anytime

@svarlamov
Copy link
Copy Markdown
Member

@cnaples79 I would love to be able to merge this soon. Have you had a chance to look at the CLA?

@cnaples79
Copy link
Copy Markdown
Author

@svarlamov I've signed the CLA! Sorry I missed your first comment. I can definitely take some time for your requests, it may be a couple days though I'm currently out of town.

@svarlamov
Copy link
Copy Markdown
Member

Just a heads up #376 is being kindly picked up by @bernoussama so will probably have to try to sync up these PRs. Probably the fix for #376 first and hopefully it'll all just work with this PR. Thanks folks!

@bernoussama bernoussama mentioned this pull request Feb 1, 2026
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment thread src/git/cli_parser.rs
Comment on lines +515 to +558
#[cfg(test)]
mod tests {
use super::{ParsedGitInvocation, expand_alias_with_resolver};

fn base_invocation(command: &str, args: &[&str]) -> ParsedGitInvocation {
ParsedGitInvocation {
global_args: vec![],
command: Some(command.to_string()),
command_args: args.iter().map(|s| s.to_string()).collect(),
saw_end_of_opts: false,
is_help: false,
}
}

#[test]
fn expands_simple_alias() {
let parsed = base_invocation("co", &["main"]);
let resolved = expand_alias_with_resolver(parsed, |alias| {
(alias == "co").then(|| "checkout".to_string())
});
assert_eq!(resolved.command.as_deref(), Some("checkout"));
assert_eq!(resolved.command_args, vec!["main"]);
}

#[test]
fn expands_alias_with_arguments() {
let parsed = base_invocation("unstage", &["file.txt"]);
let resolved = expand_alias_with_resolver(parsed, |alias| {
(alias == "unstage").then(|| "reset HEAD --".to_string())
});
assert_eq!(resolved.command.as_deref(), Some("reset"));
assert_eq!(resolved.command_args, vec!["HEAD", "--", "file.txt"]);
}

#[test]
fn ignores_shell_aliases() {
let parsed = base_invocation("cleanup", &["-n"]);
let resolved = expand_alias_with_resolver(parsed, |_alias| {
Some("!f() { echo hi; }".to_string())
});
assert_eq!(resolved.command.as_deref(), Some("cleanup"));
assert_eq!(resolved.command_args, vec!["-n"]);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Duplicate mod tests blocks cause compilation error

The PR introduces a new #[cfg(test)] mod tests block at line 515, but there was already an existing #[cfg(test)] mod tests block at line 654. In Rust, having two modules with the same name in the same scope is a compilation error.

Root Cause

The PR added new tests for expand_alias_with_resolver in a new mod tests block, but the file already had an existing mod tests block with tests for other functions like pos_command, derive_directory_from_url, and extract_clone_target_directory.

Impact: The code will fail to compile with an error like the name 'tests' is defined multiple times.

Prompt for agents
The new tests for expand_alias_with_resolver should be merged into the existing mod tests block at line 654 instead of creating a new mod tests block. Move the test functions (base_invocation, expands_simple_alias, expands_alias_with_arguments, ignores_shell_aliases) from lines 515-558 into the existing mod tests block at line 654, and remove the duplicate #[cfg(test)] mod tests declaration at line 515.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread src/git/cli_parser.rs
Comment on lines +139 to +147
let new_command = tokens.remove(0);
parsed.command = Some(new_command);
// Prepend alias arguments to existing command args
tokens.append(&mut parsed.command_args);
parsed.command_args = tokens;
}
}

parsed
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 is_help flag not updated after alias expansion

After alias expansion, the is_help field of ParsedGitInvocation is not recalculated. If a user has an alias like h = help, running git h will have is_help = false even though the expanded command is help.

Root Cause

The is_help field is computed during parse_git_cli_args() at src/git/cli_parser.rs:501-504 based on the original command and args. When expand_alias_with_resolver() changes the command (e.g., from h to help), the is_help field is not updated.

In src/commands/git_handlers.rs:145 and src/commands/git_handlers.rs:152, is_help is used to decide whether to run hooks:

if parsed_args.command.as_deref() == Some("clone") && !parsed_args.is_help && !skip_hooks {
let exit_status = if !parsed_args.is_help && has_repo && !skip_hooks {

Impact: When a user runs an alias that expands to help or includes -h/--help, hooks will incorrectly run. This could cause unnecessary side effects when the user is just asking for help.

Suggested change
let new_command = tokens.remove(0);
parsed.command = Some(new_command);
// Prepend alias arguments to existing command args
tokens.append(&mut parsed.command_args);
parsed.command_args = tokens;
}
}
parsed
let new_command = tokens.remove(0);
parsed.command = Some(new_command.clone());
// Prepend alias arguments to existing command args
tokens.append(&mut parsed.command_args);
parsed.command_args = tokens;
// Recalculate is_help after alias expansion
parsed.is_help = new_command == "help"
|| parsed.command_args.iter().any(|t| t == "--help" || t == "-h");
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@svarlamov svarlamov closed this Feb 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ensure hooks work for git aliased commands

3 participants