1+ use crate :: {
2+ branch_trees:: { checkout_branch_trees, compute_updated_branch_head, BranchHeadAndTree } ,
3+ BranchManagerExt , VirtualBranchesExt as _,
4+ } ;
15use anyhow:: { anyhow, bail, Result } ;
26use gitbutler_cherry_pick:: RepositoryExt as _;
37use gitbutler_command_context:: CommandContext ;
8+ use gitbutler_oxidize:: git2_to_gix_object_id;
49use gitbutler_project:: access:: WorktreeWritePermission ;
510use gitbutler_repo:: {
611 rebase:: { cherry_rebase_group, gitbutler_merge_commits} ,
7- LogUntil , RepositoryExt as _,
12+ GixRepositoryExt , LogUntil , RepositoryExt as _,
813} ;
914use gitbutler_repo_actions:: RepoActionsExt as _;
1015use gitbutler_stack:: { Stack , StackId , Target , VirtualBranchesHandle } ;
16+ use gix:: prelude:: Write ;
1117use serde:: { Deserialize , Serialize } ;
1218
13- use crate :: {
14- branch_trees:: { checkout_branch_trees, compute_updated_branch_head, BranchHeadAndTree } ,
15- BranchManagerExt , VirtualBranchesExt as _,
16- } ;
17-
1819#[ derive( Serialize , PartialEq , Debug ) ]
1920#[ serde( tag = "type" , content = "subject" , rename_all = "camelCase" ) ]
2021pub enum BranchStatus {
@@ -140,8 +141,19 @@ pub fn upstream_integration_statuses(
140141 ..
141142 } = context;
142143 // look up the target and see if there is a new oid
143- let old_target_tree = repository. find_real_tree ( old_target, Default :: default ( ) ) ?;
144- let new_target_tree = repository. find_real_tree ( new_target, Default :: default ( ) ) ?;
144+ let old_target_tree_id = git2_to_gix_object_id (
145+ repository
146+ . find_real_tree ( old_target, Default :: default ( ) ) ?
147+ . id ( ) ,
148+ ) ;
149+ let new_target_tree_id = git2_to_gix_object_id (
150+ repository
151+ . find_real_tree ( new_target, Default :: default ( ) ) ?
152+ . id ( ) ,
153+ ) ;
154+ let gix_repo = gitbutler_command_context:: gix_repository_for_merging ( repository. path ( ) ) ?;
155+ let gix_repo_in_memory = gix_repo. clone ( ) . with_object_memory ( ) ;
156+ let ( merge_options_fail_fast, conflict_kind) = gix_repo. merge_options_fail_fast ( ) ?;
145157
146158 if new_target. id ( ) == old_target. id ( ) {
147159 return Ok ( BranchStatuses :: UpToDate ) ;
@@ -151,8 +163,10 @@ pub fn upstream_integration_statuses(
151163 . iter ( )
152164 . map ( |virtual_branch| {
153165 let tree = repository. find_tree ( virtual_branch. tree ) ?;
166+ let tree_id = git2_to_gix_object_id ( tree. id ( ) ) ;
154167 let head = repository. find_commit ( virtual_branch. head ( ) ) ?;
155168 let head_tree = repository. find_real_tree ( & head, Default :: default ( ) ) ?;
169+ let head_tree_id = git2_to_gix_object_id ( head_tree. id ( ) ) ;
156170
157171 // Try cherry pick the branch's head commit onto the target to
158172 // see if it conflics. This is equivalent to doing a merge
@@ -168,25 +182,33 @@ pub fn upstream_integration_statuses(
168182 } ;
169183 }
170184
171- let head_merge_index =
172- repository. merge_trees ( & old_target_tree, & new_target_tree, & head_tree, None ) ?;
173- let mut tree_merge_index =
174- repository. merge_trees ( & old_target_tree, & new_target_tree, & tree, None ) ?;
185+ let mut tree_merge = gix_repo. merge_trees (
186+ old_target_tree_id,
187+ new_target_tree_id,
188+ tree_id,
189+ gix_repo. default_merge_labels ( ) ,
190+ merge_options_fail_fast. clone ( ) ,
191+ ) ?;
175192
176193 // Is the branch conflicted?
177194 // A branch can't be integrated if its conflicted
178195 {
179- let commits_conflicted = head_merge_index. has_conflicts ( ) ;
196+ let commits_conflicted = gix_repo_in_memory
197+ . merge_trees (
198+ old_target_tree_id,
199+ new_target_tree_id,
200+ head_tree_id,
201+ Default :: default ( ) ,
202+ merge_options_fail_fast. clone ( ) ,
203+ ) ?
204+ . has_unresolved_conflicts ( conflict_kind) ;
205+ gix_repo_in_memory. objects . reset_object_memory ( ) ;
180206
181207 // See whether uncommited changes are potentially conflicted
182208 let potentially_conflicted_uncommited_changes = if has_uncommited_changes {
183209 // If the commits are conflicted, we can guarentee that the
184210 // tree will be conflicted.
185- if commits_conflicted {
186- true
187- } else {
188- tree_merge_index. has_conflicts ( )
189- }
211+ commits_conflicted || tree_merge. has_unresolved_conflicts ( conflict_kind)
190212 } else {
191213 // If there are no uncommited changes, then there can't be
192214 // any conflicts.
@@ -205,13 +227,20 @@ pub fn upstream_integration_statuses(
205227
206228 // Is the branch fully integrated?
207229 {
230+ if tree_merge. has_unresolved_conflicts ( conflict_kind) {
231+ bail ! (
232+ "Merge result unexpectedly has conflicts between base, ours, theirs: {old_target_tree_id}, {new_target_tree_id}, {tree_id}"
233+ )
234+ }
208235 // We're safe to write the tree as we've ensured it's
209236 // unconflicted in the previous test.
210- let tree_merge_index_tree = tree_merge_index. write_tree_to ( repository) ?;
237+ let tree_merge_index_tree_id = tree_merge
238+ . tree
239+ . write ( |tree| gix_repo. write ( tree) )
240+ . map_err ( |err| anyhow ! ( "{err}" ) ) ?;
211241
212- // Identical trees will have the same Oid so we can compare
213- // the two
214- if tree_merge_index_tree == new_target_tree. id ( ) {
242+ // Identical trees will have the same Oid so we can compare the two
243+ if tree_merge_index_tree_id == new_target_tree_id {
215244 return Ok ( ( virtual_branch. id , BranchStatus :: FullyIntegrated ) ) ;
216245 }
217246 }
0 commit comments