Skip to content

reset --mixed: clear skip-worktree for all changed entries in VFS mode#935

Draft
tyrielv wants to merge 1 commit into
microsoft:vfs-2.54.0from
tyrielv:tyrielv/fix-reset-mixed-hydrated
Draft

reset --mixed: clear skip-worktree for all changed entries in VFS mode#935
tyrielv wants to merge 1 commit into
microsoft:vfs-2.54.0from
tyrielv:tyrielv/fix-reset-mixed-hydrated

Conversation

@tyrielv

@tyrielv tyrielv commented Jun 11, 2026

Copy link
Copy Markdown

Problem

In virtual filesystem (VFS/GVFS) mode, reset --mixed fails to report hydrated files as modified.

A hydrated file is one that has been read (e.g., via blame or cat-file) and materialized on disk by ProjFS, but not modified — so it is not in GVFS's ModifiedPaths database and retains the skip-worktree bit in the index.

Reproduction

  1. gvfs clone a repo
  2. git blame Readme.md — hydrates the file (ProjFS materializes content on disk)
  3. git reset --mixed HEAD~1 (where Readme.md differs between HEAD and HEAD~1)
  4. Expected: Readme.md appears in reset output and git status shows it as modified
  5. Actual: Readme.md is missing from output; git status reports clean

Root cause

The VFS-specific code in update_index_from_diff() uses file_exists() to decide whether to clear skip-worktree:

  • Files NOT on disk (virtual/placeholder): file_exists() returns false → skip-worktree cleared, pre-reset content written to disk via checkout_entry() → correctly reported as modified ✓
  • Files on disk (hydrated): file_exists() returns true → skip-worktree left set → refresh_index() skips the entry → invisible to status ✗

The original code assumed that if a file exists on disk, it must already be tracked properly. But hydrated-but-not-modified files exist on disk with stale content and are NOT in ModifiedPaths.

Fix

Always clear skip-worktree (respect_skip_worktree = 0) for all entries processed by update_index_from_diff() when VFS mode is active. The file_exists() check now only controls whether pre-reset content needs to be written to disk — it no longer gates the skip-worktree decision.

After the reset, GVFS's GitIndexParser detects the cleared skip-worktree bits via the post-index-change hook and adds affected paths to ModifiedPaths, so subsequent git commands also see them correctly.

Before (buggy)

if (core_virtualfilesystem && !file_exists(two->path))
{
    respect_skip_worktree = 0;      // only for missing files
    // ... write content to disk ...
}

After (fixed)

if (core_virtualfilesystem)
{
    respect_skip_worktree = 0;      // for ALL changed entries

    if (!file_exists(two->path))    // only write content for missing files
    {
        // ... write content to disk ...
    }
}

Testing

  • 3 new tests in t1093-virtualfilesystem.sh:
    • Hydrated file: file exists on disk, should appear in reset output
    • Non-hydrated file: file missing from disk, should be written and appear in output
    • Partial reset: unchanged files retain skip-worktree
  • Verified manually against a real GVFS enlistment: GVFS and control repo now produce identical output (224 modified files including Readme.md)
  • All 25 existing t1093 tests pass

In virtual filesystem (VFS/GVFS) mode, reset --mixed failed to report
hydrated files as modified. A hydrated file is one that has been read
(e.g. via blame or cat-file) and materialized on disk by ProjFS, but
not modified — so it is not in GVFS's ModifiedPaths database and
retains the skip-worktree bit in the index.

The existing VFS-specific code in update_index_from_diff() used
file_exists() to decide whether to clear skip-worktree:

  - Files NOT on disk (virtual/placeholder): file_exists() returns
    false, skip-worktree is cleared, and the pre-reset content is
    written to disk via checkout_entry(). These files correctly
    appear as modified. This path remains unchanged.

  - Files already on disk (hydrated): file_exists() returns true,
    so the code left skip-worktree set. refresh_index() then skipped
    the file entirely, hiding the working-tree vs index mismatch.
    The file was invisible to both the reset output and subsequent
    git status.

Fix this by always clearing skip-worktree (respect_skip_worktree = 0)
for all entries processed by update_index_from_diff() when VFS mode is
active. The file_exists() check now only controls whether pre-reset
content needs to be written to disk — it no longer affects the
skip-worktree decision.

After the reset, GVFS's GitIndexParser detects the cleared
skip-worktree bits via the post-index-change hook and adds the
affected paths to ModifiedPaths, so subsequent git commands also
see them correctly.

Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
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