1
+ use crate :: {
2
+ branch_trees:: { checkout_branch_trees, compute_updated_branch_head, BranchHeadAndTree } ,
3
+ BranchManagerExt , VirtualBranchesExt as _,
4
+ } ;
1
5
use anyhow:: { anyhow, bail, Result } ;
2
6
use gitbutler_cherry_pick:: RepositoryExt as _;
3
7
use gitbutler_command_context:: CommandContext ;
8
+ use gitbutler_oxidize:: git2_to_gix_object_id;
4
9
use gitbutler_project:: access:: WorktreeWritePermission ;
5
10
use gitbutler_repo:: {
6
11
rebase:: { cherry_rebase_group, gitbutler_merge_commits} ,
7
- LogUntil , RepositoryExt as _,
12
+ GixRepositoryExt , LogUntil , RepositoryExt as _,
8
13
} ;
9
14
use gitbutler_repo_actions:: RepoActionsExt as _;
10
15
use gitbutler_stack:: { Stack , StackId , Target , VirtualBranchesHandle } ;
16
+ use gix:: prelude:: Write ;
11
17
use serde:: { Deserialize , Serialize } ;
12
18
13
- use crate :: {
14
- branch_trees:: { checkout_branch_trees, compute_updated_branch_head, BranchHeadAndTree } ,
15
- BranchManagerExt , VirtualBranchesExt as _,
16
- } ;
17
-
18
19
#[ derive( Serialize , PartialEq , Debug ) ]
19
20
#[ serde( tag = "type" , content = "subject" , rename_all = "camelCase" ) ]
20
21
pub enum BranchStatus {
@@ -140,8 +141,19 @@ pub fn upstream_integration_statuses(
140
141
..
141
142
} = context;
142
143
// 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 ( ) ?;
145
157
146
158
if new_target. id ( ) == old_target. id ( ) {
147
159
return Ok ( BranchStatuses :: UpToDate ) ;
@@ -151,8 +163,10 @@ pub fn upstream_integration_statuses(
151
163
. iter ( )
152
164
. map ( |virtual_branch| {
153
165
let tree = repository. find_tree ( virtual_branch. tree ) ?;
166
+ let tree_id = git2_to_gix_object_id ( tree. id ( ) ) ;
154
167
let head = repository. find_commit ( virtual_branch. head ( ) ) ?;
155
168
let head_tree = repository. find_real_tree ( & head, Default :: default ( ) ) ?;
169
+ let head_tree_id = git2_to_gix_object_id ( head_tree. id ( ) ) ;
156
170
157
171
// Try cherry pick the branch's head commit onto the target to
158
172
// see if it conflics. This is equivalent to doing a merge
@@ -168,25 +182,33 @@ pub fn upstream_integration_statuses(
168
182
} ;
169
183
}
170
184
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
+ ) ?;
175
192
176
193
// Is the branch conflicted?
177
194
// A branch can't be integrated if its conflicted
178
195
{
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 ( ) ;
180
206
181
207
// See whether uncommited changes are potentially conflicted
182
208
let potentially_conflicted_uncommited_changes = if has_uncommited_changes {
183
209
// If the commits are conflicted, we can guarentee that the
184
210
// 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)
190
212
} else {
191
213
// If there are no uncommited changes, then there can't be
192
214
// any conflicts.
@@ -205,13 +227,20 @@ pub fn upstream_integration_statuses(
205
227
206
228
// Is the branch fully integrated?
207
229
{
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
+ }
208
235
// We're safe to write the tree as we've ensured it's
209
236
// 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}" ) ) ?;
211
241
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 {
215
244
return Ok ( ( virtual_branch. id , BranchStatus :: FullyIntegrated ) ) ;
216
245
}
217
246
}
0 commit comments