Skip to content

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

Merged
dscho merged 1 commit into
microsoft:vfs-2.54.0from
tyrielv:tyrielv/fix-reset-mixed-hydrated
Jul 23, 2026
Merged

reset --mixed: clear skip-worktree for all changed entries in VFS mode#935
dscho merged 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

Comment thread builtin/reset.c
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>
@tyrielv
tyrielv force-pushed the tyrielv/fix-reset-mixed-hydrated branch from edb6cb1 to 7e07875 Compare July 6, 2026 15:34
@dscho
dscho merged commit d263e81 into microsoft:vfs-2.54.0 Jul 23, 2026
149 of 150 checks passed
dscho added a commit that referenced this pull request Jul 23, 2026
#963)

This is a companion of #935, porting the changes from vfs-2.54.0 to
vfs-2.55.0.

## 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)
```c
if (core_virtualfilesystem && !file_exists(two->path))
{
    respect_skip_worktree = 0;      // only for missing files
    // ... write content to disk ...
}
```

### After (fixed)
```c
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
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.

2 participants