Fix workspace merge failure when stacks have adjacent edits#13155
Fix workspace merge failure when stacks have adjacent edits#13155
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
c59241f to
b9401de
Compare
There was a problem hiding this comment.
Pull request overview
This PR fixes false-positive merge conflicts during workspace mutations (e.g., squash/reorder) caused by a divergence between git2’s Myers diff merge behavior and gix’s merge behavior when stacks modify adjacent but non-overlapping hunks. It aligns workspace tree computation with the already-used gix merge approach so valid workspace states don’t later fail during recomputation.
Changes:
- Replaced the
git2-based octopus merge inmerge_workspacewith agix-based merge usingmerge_options_fail_fast+has_unresolved_conflicts. - Updated workspace tree recomputation paths to use a merge-optimized
gix::Repository, converting results togit2OIDs only at git2 API boundaries.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
crates/gitbutler-workspace/src/branch_trees.rs |
Switches workspace tree octopus merge from git2 to gix and threads a merge-optimized gix repo through uncommitted-change update flows. |
crates/but-worktrees/src/integrate.rs |
Updates integration conflict checks to compute workspace trees via gix and convert to git2 only when calling move_tree. |
|
@Byron this is different from the others but it has a similar consequence. In this screenshot there are two commits that touch Segment.svelte, they do not overlap, but have adjacent edits on lines 24:25. As a result, any operation that recomputes the workspace commit failed, and knowing why takes knowledge of this bug. It took me a few minutes to find the culprit, even though I knew what to look for.
|
b9401de to
c149222
Compare
When two stacks each modify adjacent but non-overlapping lines in the same file, git2 treats the adjacent hunks as conflicting. This caused `update_uncommitted_changes` to fail with `MergeConflict (-24)` on any mutation that recomputed the workspace tree (squash, reorder, etc.) — even though the stacks don't actually conflict. The workspace commit was already built using gix (`remerged_workspace_tree_v2`), which resolves adjacent hunks cleanly, so it was possible to reach a valid workspace state that then broke on every subsequent operation. Fix by replacing the git2 octopus merge in `merge_workspace` with the same gix-based approach used for workspace commit creation: `merge_options_fail_fast` + `has_unresolved_conflicts`. The function now returns `gix::ObjectId`; callers in `integrate.rs` that pass the result into git2 APIs convert with `.to_git2()`. Add a regression test that calls `merge_workspace` directly with two stacks whose edits are adjacent from both sides (Stack A owns lines 1–5 and 11–15, Stack B owns lines 6–10). The test confirms the fix by failing with `MergeConflict (-24)` under git2 and passing under gix.
c149222 to
d954a50
Compare
|
It's good to see that |

When two stacks each modify adjacent but non-overlapping lines in the
same file, git2 treats the adjacent hunks as conflicting. This caused
update_uncommitted_changesto fail withMergeConflict (-24)on anymutation that recomputed the workspace tree (squash, reorder, etc.)
— even though the stacks don't actually conflict.
The workspace commit was already built using gix
(
remerged_workspace_tree_v2), which resolves adjacent hunks cleanly,so it was possible to reach a valid workspace state that then broke on
every subsequent operation.
Fix
Replace the git2 octopus merge in
merge_workspacewith the samegix-based approach used for workspace commit creation:
merge_options_fail_fast+has_unresolved_conflicts. The functionnow returns
gix::ObjectId; callers inintegrate.rsthat pass theresult into git2 APIs convert with
.to_git2().Test
Add a regression test that calls
merge_workspacedirectly with twostacks whose edits are adjacent from both sides (Stack A owns lines 1–5
and 11–15, Stack B owns lines 6–10). Verified the test fails with
MergeConflict (-24)under git2 and passes under gix.