Skip to content

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

Merged
Johannes Schindelin (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
Johannes Schindelin (dscho) merged 1 commit into
microsoft:vfs-2.54.0from
tyrielv:tyrielv/fix-reset-mixed-hydrated

Conversation

@tyrielv

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 entriesif (!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 threadbuiltin/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
tyrielvforce-pushed the tyrielv/fix-reset-mixed-hydrated branch from edb6cb1 to 7e07875CompareJuly 6, 2026 15:34
@dscho
Johannes Schindelin (dscho) merged commit d263e81 into microsoft:vfs-2.54.0Jul 23, 2026
149 of 150 checks passed
Johannes Schindelin (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 freeto 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

@tyrielv@dscho