Fix "Specified HEAD didn't match actual HEAD^{tree}" error on branch apply#13735
Draft
mtsgrd wants to merge 1 commit into
Draft
Fix "Specified HEAD didn't match actual HEAD^{tree}" error on branch apply#13735mtsgrd wants to merge 1 commit into
mtsgrd wants to merge 1 commit into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a race-condition failure when applying a branch by making worktree-change snapshotting use the repository’s actual HEAD^{tree} (instead of a potentially stale caller-provided tree), preventing the "Specified HEAD didn't match actual HEAD^{tree}" abort.
Changes:
- Removed the strict “caller-provided source tree must match actual HEAD tree” assertion and instead always base snapshots on
repo.head_tree_id_or_empty(). - Marked the
source_tree_idparameter as unused (_source_tree_id) to reflect the updated behavior. - Added a regression test covering checkout success when the caller’s source tree diverges from actual HEAD.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| crates/but-core/src/worktree/checkout/utils.rs | Stops bailing on HEAD-tree mismatch; always uses actual HEAD tree as snapshot base for consistency with worktree diffs. |
| crates/but-core/tests/core/worktree/checkout.rs | Adds regression test reproducing the stale-source-tree scenario and asserting checkout succeeds while preserving worktree changes. |
Comment on lines
+79
to
+83
| // Worktree changes are always relative to the actual HEAD tree (via the index), | ||
| // so the snapshot must use it as its base. If the caller's source tree diverges | ||
| // (e.g. due to a concurrent workspace commit update), we use the actual HEAD tree | ||
| // to keep the snapshot consistent with the worktree diff. | ||
| let source_tree_id = actual_head_tree_id.detach(); |
8f138e6 to
1cabea6
Compare
1cabea6 to
cd0089c
Compare
…apply Users are hitting this error when applying a branch. It's caused by a race condition: `apply()` reads `prev_head_id` from the workspace graph and passes it to `safe_checkout`, but between those two calls the file watcher can run `update_workspace_commit`, which moves the `gitbutler/workspace` ref to a new commit with a different tree. Inside `merge_worktree_changes_into_destination_or_keep_snapshot`, the caller's stale `source_tree_id` was compared against `repo.head_tree_id_or_empty()` and the mismatch triggered a `bail!()`. The fix removes that assertion and always reads the tree from `repo.head_tree_id_or_empty()`. This is correct because worktree diffs (via the git index) are always relative to the actual HEAD tree, not whatever the caller cached — so the snapshot base must match. Includes regression test: `checkout_succeeds_when_source_tree_diverges_from_head` Remove unused source_tree_id parameter Remove the now-unused source_tree_id parameter from merge_worktree_changes_into_destination_or_keep_snapshot and its callers. The parameter was unused in the function body and documentation, so eliminating it cleans up the function signature, related docs, and the call site in function.rs to avoid passing an unnecessary value. This simplifies the API and reduces confusion about the expected inputs.
cd0089c to
fa63bcd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Users are hitting this error when applying a branch. It's caused by a race condition:
apply()readsprev_head_idfrom the workspace graph and passes it tosafe_checkout, but between those two calls the file watcher can runupdate_workspace_commit, which moves thegitbutler/workspaceref to a new commit with a different tree.Inside
merge_worktree_changes_into_destination_or_keep_snapshotincheckout/utils.rs, the caller's stalesource_tree_idwas compared againstrepo.head_tree_id_or_empty()and the mismatch triggered abail!().The fix removes that assertion and always reads the tree from
repo.head_tree_id_or_empty(). This is correct because worktree diffs (via the git index) are always relative to the actual HEAD tree, not whatever the caller cached — so the snapshot base must match.Includes regression test:
checkout_succeeds_when_source_tree_diverges_from_head