Skip to content

Release M279 - #2080

Merged
tyrielv merged 18 commits into
releases/shippedfrom
milestones/M279
Aug 10, 2026
Merged

Release M279#2080
tyrielv merged 18 commits into
releases/shippedfrom
milestones/M279

Conversation

@tyrielv

@tyrielv tyrielv commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Changes:

….vfs.0.5

Update default Microsoft Git version to v2.54.0.vfs.0.5
Add `vnext` to build.yaml's pull_request/push branch filters so the
feature-integration branch gets the same CI (build + unit + functional
tests) as master. This lets PRs targeting vnext produce the required
status checks.

Assisted-by: Claude Opus 4.8
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
…ause

The GVFS telemetry "Blob hydration failure" and "Directory enumeration
failure" buckets conflate causes outside gvfs.exe's control (network,
local disk/IO, ProjFS) with actionable ones (a missing object on the
server, a size mismatch, or GVFS's own stale-enumeration eviction).

Stamp a cause tag on the failure telemetry so the release-readiness
dashboard can bucket them apart. No behavior changes:

- BlobHydrationFailureCategory (nested in GVFSGitObjects) is now returned
  from TryCopyBlobContentStream via an out parameter as well as stamped on
  the terminal telemetry, so the virtualizer's own terminal event is tagged
  with the same cause rather than left uncategorized. Categories:
  NetworkUnavailable / DownloadFailed / LocalIO / ProjFSWriteFailed (not
  gvfs-fixable) vs ObjectNotOnServer / LocalCopyFailed / SizeMismatch /
  Unexpected (actionable). The size-mismatch, IOException, and WriteFileData
  failure sites were previously logged with a message the dashboard did not
  match; they now carry the tag so they are counted.

- EnumerationFailureReason (nested enum) on "Failed to find active
  enumeration ID": Evicted (GVFS eviction removed a live enumeration;
  self-inflicted) vs Unknown (ProjFS delivered an id GVFS never held or
  already ended). The eviction-tracking map is populated before the entry
  is removed from the active set (closing a mislabel race) and pruned on
  every sweep so it cannot outlive its window.

Unit tests assert each cause value deterministically.

Assisted-by: Claude Opus 4.8
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5 to 6.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](actions/setup-dotnet@v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
…ions/setup-dotnet-6

Bump actions/setup-dotnet from 5 to 6
….vfs.0.3

Update default Microsoft Git version to v2.55.0.vfs.0.3
The health verb always labeled its final line 'Repository status: ...' even when the calculation was scoped to a subdirectory (via -d or when run from a subdirectory of the enlistment). That could report a highly hydrated subtree as 'Highly Hydrated' at the repository level, which is misleading.

When TargetDirectory is non-empty, print 'Directory status (<path>): ...' instead and add a hint suggesting the user re-run from the repo root for the full-repo status. Update the functional test regex to accept either label.
…lemetry

Split blob-hydration and directory-enumeration failure telemetry by cause
RetryableException wraps its real cause in InnerException. The blob-
hydration failure categorization checked the RetryableException type
itself, so every RetryableException - the largest hydration failure
bucket in the field - was tagged NetworkUnavailable, even when the real
cause was local disk/IO.

On this branch the RetryableException reaches OnFailure from
Context.Repository.TryCopyBlobContentStream - typically StreamUtil
wrapping an IOException while reading a corrupt or truncated local loose
object. Unwrap RetryableException.InnerException before categorizing, and
map IOException / UnauthorizedAccessException / Win32Exception (the local
disk/IO family) to LocalIO. A RetryableException whose inner cause is not
local (e.g. HttpRequestException), or that has no inner cause, stays
NetworkUnavailable. Telemetry metadata only; no behavior change.

Add unit tests for each inner-cause mapping (IOException, Unauthorized-
AccessException, Win32Exception -> LocalIO; HttpRequestException and no
inner -> NetworkUnavailable), and reset the process-global
RetryCircuitBreaker in the fixture SetUp and TearDown so these failure-
driving tests cannot open the circuit for one another or for a later
fixture.

Stacked follow-up to PR #2071; do not publish until #2071 merges.

Assisted-by: Claude Opus 4.8
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Attribute wrapped blob-hydration failures to their inner cause
….vfs.0.6

Update the default Microsoft Git version used by VFS for Git
to the newly promoted [`v2.55.0.vfs.0.6`](https://github.com/microsoft/git/releases/tag/v2.55.0.vfs.0.6) release.
…larification

gvfs health: distinguish directory-scoped status from repository status
On the GVFS 2.0 line, mount startup failed roughly twice as often as the
1.0.26014.1 LKG. The dominant 2.0-specific cause is the hook update on mount.

After a GVFS upgrade changes a native hook binary, the next mount re-copies it
into the enlistment via a copy-to-temp-then-rename (HooksInstaller.TryUpdateHook).
The rename can fail transiently with Win32Exception (5) ERROR_ACCESS_DENIED when
the existing enlistment hook is locked (open handle, AV scan). CopyHook wraps
that in a retryable RetryableException, but the mount-time call site failed
immediately with no retry - unlike the clone-time InstallHooks path, which
retries with backoff. So a transient lock failed the whole mount.

Fix 1 - retry at mount time (primary): wrap the mount-time CopyHook in the
existing TryHooksInstallationAction retry helper (3x exponential backoff),
matching the clone-time path. A transient ACCESS_DENIED rename is now retried,
not fatal.

Fix 2 - tolerate locked-but-already-correct: after retries are exhausted, if the
enlistment hook already matches the installed one, treat it as success instead
of failing the mount.

Fix 3 - compare path resilience: reading the hook version opens the hook files,
which can be transiently locked too. A compare failure no longer hard-fails the
mount; it logs a telemetry warning and falls through to the resilient copy path.

Change detection compares the hook FileVersion. These native (C++) hook binaries
embed their GVFS version in the PE version resource, so the version differs only
when a GVFS upgrade changed the hook - which is rare (roughly monthly) compared
to daily mounts - so the common mount does no copy. The comparison reads the
version through a small context.FileSystem.GetFileVersion seam instead of a
direct static FileVersionInfo call, so the mount-time path can be unit-tested. A
null/empty version is treated as "cannot confirm identical" (not a match) so a
version-less binary forces the resilient copy rather than being assumed correct.
The unused FileVersionInfo-returning GetVersionInfo/FileVersionsMatch/
ProductVersionsMatch (and their mock overrides) are removed.

Every mount-hook outcome (MissingFromEnlistment, CompareFailed,
LockedButAlreadyCorrect, CopyFailed) now emits with Keywords.Telemetry and a
stable HookUpdateResult field so all outcomes are queryable together; previously
the missing-hook warning bound to the params-object overload and silently dropped
its metadata.

Worktree behavior is unchanged (InProcessMount already skips hook install for
worktrees). PhysicalFileSystem.TryCopyToTempFileAndRename is made virtual so the
copy path can be unit-tested.

Adds GVFS.UnitTests HooksInstallerMountUpdateTests covering: identical hook not
copied, different/missing hook copied, copy-when-version-differs-despite-same-
content, no-copy-when-version-matches-despite-different-content, copy-when-both-
versions-null, transient copy failure retried then succeeds, locked-but-already-
correct hook does not fail the mount, persistent copy failure still fails, a
compare failure refreshes the hook without failing, and a missing installed hook
fails without copying. Full unit suite: 887 passed.

Assisted-by: Claude Opus 4.8
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Mount: retry hook copy and tolerate transiently-locked hooks
@tyrielv
tyrielv enabled auto-merge August 10, 2026 18:14
@tyrielv
tyrielv merged commit 28805f7 into releases/shipped Aug 10, 2026
79 checks passed
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.

3 participants