@@ -99,10 +99,11 @@ pub fn update_uncommitted_changes_with_tree(
9999 always_checkout : Option < bool > ,
100100 _perm : & mut RepoExclusive ,
101101) -> Result < ( ) > {
102+ let gix_repo = ctx. clone_repo_for_merging ( ) ?;
102103 let repo = & * ctx. git2_repo . get ( ) ?;
103104 if let Some ( worktree_id) = old_uncommitted_changes {
104105 let mut new_uncommitted_changes =
105- move_tree_between_workspaces ( repo, worktree_id, old, new) ?;
106+ move_tree_between_workspaces ( repo, & gix_repo , worktree_id, old, new) ?;
106107
107108 // If the new tree and old tree are the same, then we don't need to do anything
108109 if !new_uncommitted_changes. has_conflicts ( ) && !always_checkout. unwrap_or ( false ) {
@@ -122,9 +123,8 @@ pub fn update_uncommitted_changes_with_tree(
122123 ) ,
123124 ) ?;
124125 } else {
125- let old_tree_id = merge_workspace ( repo, old) ?. to_gix ( ) ;
126- let new_tree_id = merge_workspace ( repo, new) ?. to_gix ( ) ;
127- let gix_repo = ctx. clone_repo_for_merging ( ) ?;
126+ let old_tree_id = merge_workspace ( & gix_repo, & old) ?;
127+ let new_tree_id = merge_workspace ( & gix_repo, & new) ?;
128128 but_core:: worktree:: safe_checkout (
129129 old_tree_id,
130130 new_tree_id,
@@ -139,12 +139,13 @@ pub fn update_uncommitted_changes_with_tree(
139139/// like if they were on top of the new workspace.
140140fn move_tree_between_workspaces (
141141 repo : & git2:: Repository ,
142+ gix_repo : & gix:: Repository ,
142143 tree : git2:: Oid ,
143144 old : WorkspaceState ,
144145 new : WorkspaceState ,
145146) -> Result < git2:: Index > {
146- let old_workspace = merge_workspace ( repo , old) ?;
147- let new_workspace = merge_workspace ( repo , new) ?;
147+ let old_workspace = merge_workspace ( gix_repo , & old) ?. to_git2 ( ) ;
148+ let new_workspace = merge_workspace ( gix_repo , & new) ?. to_git2 ( ) ;
148149 move_tree ( repo, tree, old_workspace, new_workspace)
149150}
150151
@@ -167,26 +168,32 @@ pub fn move_tree(
167168 Ok ( merge)
168169}
169170
170- /// Octopus merge
171- /// What: Takes N trees and a base tree and all the heads together with respect
172- /// to the given base.
171+ /// Octopus merge using gix, which correctly resolves adjacent-hunk changes that git2 treats as conflicts.
172+ /// Takes N trees and a base tree and merges all the heads together with respect to the given base.
173173///
174174/// If there are no heads provided, the base will be returned.
175- pub fn merge_workspace ( repo : & git2:: Repository , workspace : WorkspaceState ) -> Result < git2:: Oid > {
176- let mut output = workspace. base ;
175+ pub fn merge_workspace (
176+ repo : & gix:: Repository ,
177+ workspace : & WorkspaceState ,
178+ ) -> Result < gix:: ObjectId > {
179+ let mut output = workspace. base . to_gix ( ) ;
180+ let base = workspace. base . to_gix ( ) ;
177181
178- for head in workspace. heads {
179- let mut merge_options = git2:: MergeOptions :: new ( ) ;
180- merge_options. fail_on_conflict ( true ) ;
182+ let ( merge_options, conflict_kind) = repo. merge_options_fail_fast ( ) ?;
181183
184+ for head in & workspace. heads {
182185 let mut merge = repo. merge_trees (
183- & repo. find_tree ( workspace. base ) ?,
184- & repo. find_tree ( output) ?,
185- & repo. find_tree ( head) ?,
186- Some ( & merge_options) ,
186+ base,
187+ output,
188+ head. to_gix ( ) ,
189+ repo. default_merge_labels ( ) ,
190+ merge_options. clone ( ) ,
187191 ) ?;
188192
189- output = merge. write_tree_to ( repo) ?;
193+ if merge. has_unresolved_conflicts ( conflict_kind) {
194+ anyhow:: bail!( "merge conflict when computing workspace tree" ) ;
195+ }
196+ output = merge. tree . write ( ) ?. detach ( ) ;
190197 }
191198
192199 Ok ( output)
0 commit comments