Run hooks in user's shell environment and allow bypassing commit hooks#21319
Run hooks in user's shell environment and allow bypassing commit hooks#21319niik merged 85 commits intodevelopmentfrom
Conversation
Updated hooks-proxy to write stdin to a temporary file for hooks that require it, and refactored process spawning logic for improved error handling and resource management. The with-hooks-env module now creates a temporary directory for hook execution and passes it to the proxy.
Simplifies hook process output handling by only piping stderr, as Git hooks only write to stderr. Adds execution timing and improved debug logging for hook completion, including exit code and duration.
Added 'GIT_EXEC_PATH' and 'GIT_TEMPLATE_DIR' to the set of excluded environment variables to prevent leaking Dugite-specific settings into hook environments.
Introduces the vendor/printenvz directory containing a minimal Node.js native module for printing environment variables separated by null bytes. Includes C++ source, node-gyp build configuration, build scripts, TypeScript definitions, package metadata, and prebuilt binaries for cross-platform support.
Extracted shell and environment logic into get-shell and get-shell-env modules. Updated hooks-proxy and with-hooks-env to use the new shellEnv, improving reliability and separation of concerns. Added support for passing shell environment to hook proxy and locating git executable using the correct PATH.
Introduces a list of known Git hook names and updates getRepoHooks to filter out unknown hooks, .sample files, and handle Windows-specific executable checks. This improves accuracy and reliability when listing repository hooks.
Introduces HookProgress type and tracks hook execution status in repository state. Updates UI components to display commit hook progress and repository optimization status during commit operations. Enhances user feedback for commit hooks and git garbage collection events.
Eliminates the isRunningGitGC property and related logic from repository state, store, and UI components. Commit progress rendering now relies solely on hookProgress, and the commit message progress UI is updated for improved clarity and styling.
Introduces a new popup and flow to handle failed Git hooks, allowing users to choose whether to abort or ignore the failure. Updates commit, core, hooks, and app store logic to support the new onHookFailure callback, and adds the HookFailed dialog component for user interaction.
Added a list of hooks whose failures should be ignored, refined error handling logic in hooks-proxy, and updated the hook failure dialog to clarify the cancel action as 'Abort commit'.
Introduces a CommitProgress popup that displays live terminal output during commit operations. Refactors commit logic to support subscribing to commit output, updates state management to track output listeners, and wires up UI components to show the progress dialog when requested.
Replaces the custom terminal output rendering in the commit progress dialog with xterm.js for improved display and performance. Adds @xterm/xterm as a dependency, updates styles to support the new terminal, and refactors related logic in the commit progress component and git core.
Adds support for displaying the terminal output from failed Git hooks in the hook failure dialog using xterm.js. Updates relevant types, popup models, and UI components to pass and render the output, and introduces new styles for the dialog.
Introduces logic to track if a commit hook was aborted and prevents error propagation when aborted. This ensures that aborting a commit hook does not result in unnecessary error handling.
Introduces a new 'commit' kind to GitErrorContext, passes commit context to failable operations in AppStore, and updates AppError UI to display commit-specific error messages. This enhances error reporting and handling for commit operations.
Replaces direct xterm usage in HookFailed with a new StaticTerminal component for improved encapsulation and reusability. Adds static-terminal.tsx to handle terminal rendering and output display, simplifying HookFailed and centralizing terminal logic.
|
That's cool. Good job, bro. |
For further credit to Sarawut Kaewyana's Donor Advised Fund advised by Sarawut Kaewyana (ID: 7fc9050f-b221-4be4-98aa-73a0725f9e6a). |
| When enabled, GitHub Desktop will attempt to load environment | ||
| variables from your shell when executing Git hooks. This is useful if | ||
| your Git hooks depend on environment variables set in your shell | ||
| configuration files, a common practive for version managers such as |
There was a problem hiding this comment.
typo: practive 😅
Edit: #21697
Edit2: PR with a fix merged (GitHub doesn't allow me to resolve this though)
|
Today 9/03/2026 |















Trying to wrap up what I started during our bug bash a few weeks back. Allow users to execute hooks in their shell environment (leveraging the system installed Git).
If you're affected by any of the issues below and find your way here we'd really appreciate it if you could pull this down, build it, and try it out
Closes #21317
Closes #20887
Closes #19652
Closes #19520
Closes #19001
Closes #18547
Closes #15286
Closes #14319
Closes #13052
Closes #12586
Closes #12562
Closes #11997
Closes #11603
Closes #10828
Closes #10060
Closes #9351
Closes #11523
Supersedes #20939
Description
We have so many issues related to hooks that it's hard to list them all and I probably missed a few in the list above. The biggest problem is that GitHub Desktop ships with its own embedded Git installation on Windows and that environment is very likely to differ from what users have set up on their system. Hooks might rely on features not present in Desktop's GitHub (which is stripped to be as compact as possible) or they might rely on a version manager having been set up (like nvm) so it can't find node or some other tool it needs.
To complicate things further GitHub Desktop has no way of knowing whether a commit failed as a result of a Git hook rejecting the commit or something else. There's been attempts at addressing this by looking for patterns in the Git output (such as #20939) but this is fragile and will only catch well behaved hooks (like husky).
Hooks might also use terminal escape codes to add color to the output and GitHub Desktop hasn't had any way of interpreting this leading to issues like #18547).
In order to attempt to address all of this I've implemented an optional hook intercept option.
When enabled we tell Git to go look for hooks in a special directory controlled by Desktop instead of the repository hook directory. Our custom hooks folder contains hook executables which connect back to Desktop where Desktop can execute the actual hook and proxy the output and exit codes back to the hook invoked by Git. One benefit of this is that GitHub Desktop is aware that a hook is running...
...and can view the output as it's received by the actual hook. Another benefit is if the actual hook exits with an error we can choose to ignore that if the user chooses, allowing us to bypass a hook failure without having to first abort the commit and run it again with
--no-verify.But crucially this lets us have full control over what environment variables the hook gets invoked with. On macOS we will use shell specified by the
SHELLenvironment variable (typicallyzsh), on Windows we'll default to Git Bash but support cmd.exe and powershell as well. We'll launch the shell and use a little helper executable (printenvz) to grab all environment variables from that shell including whatever gets set by tools like nvm invoked via shell login scripts such as .bash_profile.I've incorporated xterm.js to let us render colors as they would be rendered in a terminal. This doesn't just improve the behavior for repositories with hooks, all Git relates errors now leverage xtermjs to display Git output meaning that any error from any Git operation will be rendered as it would have been on the terminal.
If we detect that a repository has commit hooks we'll also allow the user to preemptively bypass the
pre-commitandcommit-msghooks using the--no-verifyflag.Screenshots
CleanShot.2025-11-21.at.14.20.37.mp4
Release notes
Notes: