Skip to content

Use worktree-specific Git index paths - #2113

Merged
Tyrie Vella (tyrielv) merged 7 commits into
microsoft:masterfrom
tyrielv:tyrielv/worktree-indexpath
Oct 28, 2026
Merged

Tyrie Vella (tyrielv) merged 7 commits into
microsoft:masterfrom
tyrielv:tyrielv/worktree-indexpath

Conversation

@tyrielv

@tyrielv Tyrie Vella (tyrielv) commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Problem and Context

Linked worktrees store their index in a per-worktree Git directory. DotGitRoot points to shared Git state instead.

Several index callers still used DotGitRoot. A linked-worktree operation can therefore read, replace, repair, or back up the primary index.

Full dehydrate assumes that .git is a directory. It temporarily moves and restores the shared Git directory, which linked worktrees still need.

Worktree backups used the primary enlistment root. Separate worktrees can select the same backup path or require a move across volumes.

Changes

  • Use GitIndexPath for index generation, repair, FastFetch, and dehydrate backups.
  • Store temporary indexes beside the active index. Record IndexPath and IsWorktree in the local CreateFromHeadTree activity trace.
  • Back up lock files from the active worktree Git directory.
  • Reject full dehydrate from a linked worktree or a primary enlistment with known linked worktrees.
  • Block full dehydrate when reverse gitdir pointers are unreadable or malformed. Missing or empty pointers also block if other administrative metadata remains.
  • Ignore empty extra directories and entries containing only an empty gitdir file. Keep nondestructive discovery unchanged.
  • Resolve relative registration paths against their registration directory. Preserve the existing hook behavior.
  • List known worktree paths and explain remove, prune, and unlock commands, with a reminder to preserve user work.
  • Store worktree backups at <worktree-parent>\dehydrate_backup\<worktree-name>\<timestamp> on the same volume. Trim trailing separators so backups stay outside the worktree.
  • Change timestamps from yyyyMMdd_HHmmss to yyyyMMdd_HHmmss_fffffff to distinguish sub-second inputs.
  • Build backup destinations from file names. Preserve both paths and the underlying error for failed index, lock, and database transfers.
  • Return a nonzero exit code after a backup failure. Retain the full-dehydrate remount attempt and keep exception stacks in diagnostic logs.
  • Add unit tests for registration policies, path selection, trace metadata, backup errors, and distinct sub-second timestamps. Use mock://repo URLs.
  • Add functional tests that create real worktrees and resolve index paths through Git.
    The tests verify a byte-identical primary index and the correct worktree-index backup, including CLI paths with trailing separators.
  • Add functional tests that verify full-dehydrate rejection before mutation and a nonzero exit code after a locked-index backup failure.
  • Add functional tests for missing, empty, and unreadable reverse pointers. The tests prove that Git omits a usable worktree, then verify rejection before index mutation.

Known gaps and deliberate decisions

  • Registration visibility - A linked worktree can remain usable when git worktree list omits it.
    The destructive guard therefore blocks unverifiable metadata instead of using the listing as a safety signal.
  • Conservative guard - Known unmounted worktrees also block full dehydrate. Mounted-only handling remains outside this change.
  • Backup location - Backups remain beside each worktree for same-volume moves. A shared location redesign remains outside this change.
  • Concurrency and replacement - This change does not serialize concurrent Git commands or replace the existing delete-then-move index sequence.
    Those mechanisms need separate changes. No follow-up PR is linked.

Route index generation, repair, FastFetch, and dehydrate backups through GitIndexPath.

Reject full dehydrate for linked worktrees before it moves the .git file.

Assisted-by: GPT-5.6 Sol

Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Reject full dehydrate when linked worktrees exist.

Store worktree backups beside each worktree with unique paths.

Preserve backup errors and add regression coverage.

Assisted-by: GPT-5.6 Sol

Signed-off-by: Tyrie Vella <tyrielv@gmail.com>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review pass over this PR. 7 comment(s).

Comment thread GVFS/GVFS/CommandLine/DehydrateVerb.cs
Comment thread GVFS/GVFS.UnitTests/CommandLine/GitIndexPathCallerTests.cs
Comment thread GVFS/GVFS/CommandLine/DehydrateVerb.cs
Comment thread GVFS/GVFS/CommandLine/DehydrateVerb.cs
Comment thread GVFS/GVFS.Common/Git/GitIndexGenerator.cs
Comment thread GVFS/GVFS/CommandLine/DehydrateVerb.cs
Comment thread GVFS/GVFS.UnitTests/CommandLine/GitIndexPathCallerTests.cs
Block full dehydrate when worktree registration reads fail. List the registered paths and explain stale-registration cleanup.

Preserve per-file backup errors and trace the resolved index destination. Add a real-worktree dehydrate test and unit coverage for guard failures, trace metadata, and sub-second backup paths.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Keep backups outside worktrees when paths include a trailing separator. Resolve relative worktree registrations and retain full exceptions in diagnostic logs.

Report both index backup paths and return a failed dehydrate exit code after backup errors. Add CLI tests for full-dehydrate guards, backup failures, and trailing separators.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Match Git by ignoring directories that contain no worktree registration. Continue to block full dehydrate when a present gitdir file is unreadable or malformed.

Cover empty metadata entries alone and alongside a valid worktree registration.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Skip missing, zero-byte, and unreadable gitdir files. Record skipped read failures in diagnostic logs instead of blocking full dehydrate on entries Git does not list.

Keep nonempty malformed registrations and enumeration errors blocking. Test whitespace-only content and mixed readable and unreadable registrations.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Reject unreadable reverse gitdir pointers. Ignore missing or empty pointers only when the administrative entry contains no other metadata. Keep nondestructive discovery unchanged.

Add real-worktree tests that demonstrate Git omitting usable worktrees and verify full dehydrate stops before index mutation.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed the registration-guard fixes and their regression cases. No new correctness issues found in the delta.

Medium (nonblocking): The new missing, empty, and unreadable-registration functional cases pass, and the guard now fails closed. I could not find a changed-line coverage report for this delta. Please link one if available, or record the unavailable differential coverage in the PR validation notes. This is nonblocking.

@tyrielv
Tyrie Vella (tyrielv) merged commit e8fd480 into microsoft:master Oct 28, 2026
35 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.

2 participants