Auto-dehydrate before checkout when hydration thresholds exceeded#1931
Auto-dehydrate before checkout when hydration thresholds exceeded#1931tyrielv wants to merge 2 commits intomicrosoft:masterfrom
Conversation
- GitCommandLineParser.TryGetBranchSwitchTarget: detect branch-switching
checkout/switch commands, handling -b/-B/-c/-C, --detach, --orphan,
--patch, and file checkout patterns
- GitIndexHelper.ReadEntryCount: reusable 4-byte index header read,
extracted from EnlistmentHydrationSummary
- LibGit2Repo.GetConfigInt: git_config_get_int32 P/Invoke for reading
integer config values on live (non-snapshot) config handles
- LibGit2RepoInvoker.GetConfigIntOrDefault: convenience wrapper
- EnlistmentHydrationSummary: add percentage properties
(PlaceholderFilePercent, ModifiedFilePercent, etc.)
- GVFSConstants: add auto-dehydrate threshold config constants
(gvfs.auto-dehydrate-{placeholder,modified,folder}-percent)
- Unit tests for TryGetBranchSwitchTarget and GitIndexHelper
Assisted-by: Claude Opus 4.6
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Pre-command hook detects branch-switching checkouts and triggers automatic dehydration when configured hydration thresholds are exceeded. Flow: 1. Parse checkout/switch args — bail if not a branch switch 2. Read threshold configs via libgit2 — bail if all disabled (-1) 3. Query mount's cached hydration summary via named pipe 4. Compare placeholder/modified/folder percentages (OR logic) 5. Check cached git status (5s timeout) — pass --no-status if clean 6. Run gvfs dehydrate --confirm Config settings (all default to -1 = disabled): gvfs.auto-dehydrate-placeholder-percent gvfs.auto-dehydrate-modified-percent gvfs.auto-dehydrate-folder-percent GitStatusCache.UpdateHydrationSummary now also runs when any auto-dehydrate threshold is configured, so users don't need to separately enable gvfs.show-hydration-status. Lazy<LibGit2RepoInvoker> shared across pre-command hook avoids multiple expensive repo handle initializations. Functional test verifies end-to-end dehydrate trigger. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
| @@ -125,10 +142,9 @@ private static bool HasShortFlag(string arg, string flag) | |||
|
|
|||
| private static bool ConfigurationAllowsHydrationStatus() | |||
| { | |||
There was a problem hiding this comment.
LazyRepoInvoker may need to be initialized, it might be null if called from the PostCommandHook path.
| () => new LibGit2RepoInvoker(NullTracer.Instance, normalizedCurrentDirectory)); | ||
| try | ||
| { | ||
| TryDehydrateBeforeCheckout(args); |
There was a problem hiding this comment.
Check if TryDehydrateBeforeCheckout runs before acquiring gvfs lock, may run into concurrency issues if it's not locking first.
There was a problem hiding this comment.
The locking here (plus status gating) is actually really complicated and I need to step back and think about it. We don't want to try to dehydrate if the status isn't clean, but checking if the status is clean if the cache isn't up-to-date can take a really long time if there are lots of hydrated files - the time when we most want to dehydrate.
| { | ||
| shouldDehydrate = true; | ||
| } | ||
|
|
There was a problem hiding this comment.
Can you check if git status --porcelain actually gates dehydration
|
|
||
| if (!shouldDehydrate) | ||
| { | ||
| return; |
There was a problem hiding this comment.
Consider checking for exit code gvfs dehydrate --confirm, if there's a failure, checkout might still proceed.
| } | ||
|
|
||
| /// <summary> | ||
| /// Query the mount process for the cached hydration status via named pipe. |
There was a problem hiding this comment.
50x timeout here 100ms -> 5000ms and 50ms -> 2000ms is a big change, what's the rational here?
| return null; | ||
| } | ||
|
|
||
| /// <summary> |
There was a problem hiding this comment.
We might end up with recursive hooks if git status --porcelain gets spawned in the pre-command hook. Might create a risk for lock contention?
| GVFSConstants.GitConfig.DehydrateOnCheckoutDisabled); | ||
| int modifiedThreshold = repoInvoker.GetConfigIntOrDefault( | ||
| GVFSConstants.GitConfig.DehydrateOnCheckoutModifiedPercent, | ||
| GVFSConstants.GitConfig.DehydrateOnCheckoutDisabled); |
There was a problem hiding this comment.
Should we handle control + c interrupts? Question: Why are we specifying do not interrupt?
f340599 to
7bcfed4
Compare
Summary
Adds opt-in automatic dehydration before branch-switching checkouts. When configured hydration thresholds are exceeded, the pre-command hook runs
gvfs dehydrate --confirmbefore the checkout proceeds, keeping the repo clean for subsequent operations.Config (all disabled by default)
Any threshold set to a non-negative value enables the feature. OR logic — dehydrate triggers if any threshold is exceeded. Set to -1 (or unset) to disable.
Review Guide
Commit 1 — Infrastructure (safe, no behavior change)
GitCommandLineParser.TryGetBranchSwitchTarget— checkout/switch arg parsingGitIndexHelper.ReadEntryCount— extracted from EnlistmentHydrationSummaryLibGit2Repo.GetConfigInt/GetConfigIntOrDefault— git_config_get_int32 P/InvokeEnlistmentHydrationSummary— percentage helper propertiesCommit 2 — Feature (gated behind config flags)
Program.TryDehydrateBeforeCheckout— main hook logic, wrapped in try/catchTryGetCachedHydrationStatus— named pipe query to mount (5s timeout)GetOrCreateSharedRepoInvoker— lazy shared LibGit2RepoInvokerGitStatusCache.UpdateHydrationSummary— also runs when thresholds configuredKey safety properties
TryDehydrateBeforeCheckoutreturns immediatelycatch (Exception); never blocks checkout