Skip to content

Mount: self-heal stale hook configurations#2010

Open
tyrielv wants to merge 1 commit into
microsoft:masterfrom
tyrielv:tyrielv/mount-self-heal-hooks
Open

Mount: self-heal stale hook configurations#2010
tyrielv wants to merge 1 commit into
microsoft:masterfrom
tyrielv:tyrielv/mount-self-heal-hooks

Conversation

@tyrielv
Copy link
Copy Markdown
Contributor

@tyrielv tyrielv commented Jun 5, 2026

Summary

Regression fix from 9d3a09b3 ("Move pre-mount validation from gvfs.exe to gvfs.mount.exe").

Two related fixes that together let gvfs mount succeed against an enlistment whose pre-command hook is stale - typically because the GVFS install location baked into .git/hooks/pre-command.hooks at clone time has since moved (re-install, version-junction swap, system-to-user migration, etc.).

Before this change, a stale .hooks text file causes every git invocation that fires the pre-command hook to fail with:

fatal: pre-command hook aborted command

which makes the mount path unrecoverable.

Root cause

Commit 9d3a09b did two things that combined to break the self-heal path:

  1. Removed MountVerb.InstallHooks. Before 9d3a09b, MountVerb.Execute called HooksInstaller.InstallHooks (which refreshes both the .exe loader copies AND the .hooks text files) early in execution. 9d3a09b dropped that call when consolidating most pre-mount work into gvfs.mount.exe.
  2. Moved bootstrap git calls ahead of hook install. TrySetRequiredGitConfigSettings, LogEnlistmentInfoAndSetConfigValues, and TryCallGitCredential all moved into InProcessMount, where they now run before the existing TryUpdateHooks call at line 269.

InProcessMount.TryUpdateHooks only refreshed the .exe loader copies, never the .hooks text files - so even after it ran, a stale .hooks path persisted. And the bootstrap git calls now fire the (broken) pre-command hook before TryUpdateHooks ever gets a chance.

Changes

HooksInstaller.TryUpdateHooks

Also refresh the .hooks text files. Previously this method only refreshed the .exe copies of GitHooksLoader; the .hooks text file (containing the absolute path of GVFS.Hooks.exe that the loader execs) was only written at clone time by InstallHooks. The new TryInstallGitCommandHooks calls are idempotent: when the GVFS install path hasn't changed, the file is rewritten with the same content. This restores the self-heal behavior MountVerb.InstallHooks used to provide.

GitProcess

Pass usePreCommandHook:false on all git operations that run during the mount bootstrap path. These calls happen before gvfs.mount.exe reaches TryUpdateHooks, so without this flag they trip over the very stale-hook config we're trying to repair.

Affected: SetInLocalConfig, AddInLocalConfig, DeleteFromLocalConfig, TryGetAllConfig, TryGetConfigUrlMatch, TryGetCredential, TryGetCertificatePassword, TryDeleteCredential, TryStoreCredential.

GetFromConfig, GetFromLocalConfig and IsValidRepo also gain the flag (some via the existing GetOriginUrl pattern, some new). None of these operations mutate the working tree, so pre-command hook is semantically inappropriate anyway - skipping it is correct independent of the stale-hook scenario.

The mechanism: usePreCommandHook:false sets the COMMAND_HOOK_LOCK environment variable, which Microsoft Git itself reads to suppress pre-command hook invocation. The failure is bypassed at the git layer, not just inside GVFS.Hooks.exe.

Testing

  • 818/818 unit tests pass
  • Manually verified end-to-end with a real enlistment whose pre-command.hooks was corrupted to point at a non-existent path (C:\NonExistent\Path\GVFS.Hooks.exe). Before this change, mount failed with the "pre-command hook aborted command" error chain. After this change, mount succeeds and the .hooks file is rewritten to point at the currently-running GVFS install.

@tyrielv tyrielv force-pushed the tyrielv/mount-self-heal-hooks branch 2 times, most recently from b85c1d6 to 0d66177 Compare June 5, 2026 18:14
@tyrielv tyrielv changed the title Mount: self-heal stale hook configurations Mount: restore pre-mount hook install (regression fix) + config-read hardening Jun 5, 2026
@tyrielv tyrielv force-pushed the tyrielv/mount-self-heal-hooks branch from 0d66177 to 3ea8ab9 Compare June 5, 2026 18:33
@tyrielv tyrielv changed the title Mount: restore pre-mount hook install (regression fix) + config-read hardening Mount: self-heal stale hook configurations Jun 5, 2026
Two related fixes that together let `gvfs mount` succeed against an
enlistment whose pre-command hook is stale - typically because the GVFS
install location baked into .git/hooks/pre-command.hooks at clone time
has since moved (re-install, version-junction swap, system-to-user
migration, etc.).

Before this change, a stale .hooks text file causes every git invocation
that fires the pre-command hook to fail with:

  fatal: pre-command hook aborted command

which makes the mount path unrecoverable.

Changes:

  HooksInstaller.TryUpdateHooks
    Also refresh the .hooks text files. Previously TryUpdateHooks only
    refreshed the .exe copies of GitHooksLoader; the .hooks text file
    (containing the absolute path of GVFS.Hooks.exe that the loader
    execs) was only written at clone time by InstallHooks. When the
    GVFS install moves, the .exe copies stay valid but the .hooks
    path goes stale - and gvfs.mount.exe's existing TryUpdateHooks
    call didn't repair it.

    The new TryInstallGitCommandHooks calls are idempotent: when the
    GVFS install path hasn't changed, the file is rewritten with the
    same content.

  GitProcess
    Pass usePreCommandHook:false on all git operations that run during
    the mount bootstrap path. These calls happen before gvfs.mount.exe
    reaches TryUpdateHooks, so without this flag they trip over the
    very stale-hook config we're trying to repair.

    Affected: SetInLocalConfig, AddInLocalConfig, DeleteFromLocalConfig,
    TryGetAllConfig, TryGetConfigUrlMatch, TryGetCredential,
    TryGetCertificatePassword, TryDeleteCredential, TryStoreCredential.

    GetFromConfig, GetFromLocalConfig and IsValidRepo also gain the
    flag (some via the existing GetOriginUrl pattern, some new). None
    of these operations mutate the working tree, so pre-command hook
    is semantically inappropriate anyway - skipping it is correct
    independent of the stale-hook scenario.

    The mechanism: usePreCommandHook:false sets the COMMAND_HOOK_LOCK
    environment variable, which Microsoft Git itself reads to suppress
    pre-command hook invocation. So the failure is bypassed at the
    git layer, not just inside GVFS.Hooks.exe.

Testing:

  - 818/818 unit tests pass
  - Manually verified end-to-end with a real enlistment whose
    pre-command.hooks was corrupted to point at a non-existent path
    (C:\NonExistent\Path\GVFS.Hooks.exe). Before this change, `gvfs
    mount` failed with "pre-command hook aborted command". After this
    change, mount succeeds and the .hooks file is rewritten to point
    at the currently-running GVFS install.

Assisted-by: Claude Opus 4.7
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
@tyrielv tyrielv force-pushed the tyrielv/mount-self-heal-hooks branch from 3ea8ab9 to da65668 Compare June 5, 2026 18:34
@tyrielv tyrielv marked this pull request as ready for review June 5, 2026 18:37
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.

1 participant