@@ -20,19 +20,20 @@ use gitbutler_commit::{commit_ext::CommitExt, commit_headers::HasCommitHeaders};
2020use gitbutler_diff:: { trees, GitHunk , Hunk } ;
2121use gitbutler_error:: error:: Code ;
2222use gitbutler_operating_modes:: assure_open_workspace_mode;
23- use gitbutler_oxidize:: git2_signature_to_gix_signature;
23+ use gitbutler_oxidize:: { git2_signature_to_gix_signature, git2_to_gix_object_id , gix_to_git2_oid } ;
2424use gitbutler_project:: access:: WorktreeWritePermission ;
2525use gitbutler_reference:: { normalize_branch_name, Refname , RemoteRefname } ;
2626use gitbutler_repo:: {
2727 rebase:: { cherry_rebase, cherry_rebase_group} ,
28- LogUntil , RepositoryExt ,
28+ GixRepositoryExt , LogUntil , RepositoryExt ,
2929} ;
3030use gitbutler_repo_actions:: RepoActionsExt ;
3131use gitbutler_stack:: {
3232 reconcile_claims, BranchOwnershipClaims , ForgeIdentifier , Stack , StackId , Target ,
3333 VirtualBranchesHandle ,
3434} ;
3535use gitbutler_time:: time:: now_since_unix_epoch_ms;
36+ use gix:: objs:: Write ;
3637use serde:: Serialize ;
3738use std:: collections:: HashSet ;
3839use std:: { collections:: HashMap , path:: PathBuf , vec} ;
@@ -300,8 +301,15 @@ pub fn list_virtual_branches_cached(
300301
301302 let branches_span =
302303 tracing:: debug_span!( "handle branches" , num_branches = status. branches. len( ) ) . entered ( ) ;
304+ let repo = ctx. repository ( ) ;
305+ let gix_repo = ctx
306+ . gix_repository ( ) ?
307+ . for_tree_diffing ( ) ?
308+ . with_object_memory ( ) ;
309+ // We will perform virtual merges, no need to write them to the ODB.
310+ let cache = gix_repo. commit_graph_if_enabled ( ) ?;
311+ let mut graph = gix_repo. revision_graph ( cache. as_ref ( ) ) ;
303312 for ( mut branch, mut files) in status. branches {
304- let repo = ctx. repository ( ) ;
305313 update_conflict_markers ( ctx, files. clone ( ) ) ?;
306314
307315 let upstream_branch = match branch. clone ( ) . upstream {
@@ -323,13 +331,18 @@ pub fn list_virtual_branches_cached(
323331 . as_ref ( )
324332 . map (
325333 |upstream| -> Result < ( HashSet < git2:: Oid > , HashMap < CommitData , git2:: Oid > ) > {
326- let merge_base =
327- repo. merge_base ( upstream. id ( ) , default_target. sha )
328- . context ( format ! (
329- "failed to find merge base between {} and {}" ,
330- upstream. id( ) ,
331- default_target. sha
332- ) ) ?;
334+ let merge_base = gix_repo
335+ . merge_base_with_graph (
336+ git2_to_gix_object_id ( upstream. id ( ) ) ,
337+ git2_to_gix_object_id ( default_target. sha ) ,
338+ & mut graph,
339+ )
340+ . context ( format ! (
341+ "failed to find merge base between {} and {}" ,
342+ upstream. id( ) ,
343+ default_target. sha
344+ ) ) ?;
345+ let merge_base = gitbutler_oxidize:: gix_to_git2_oid ( merge_base) ;
333346 let remote_commit_ids = HashSet :: from_iter ( repo. l (
334347 upstream. id ( ) ,
335348 LogUntil :: Commit ( merge_base) ,
@@ -356,7 +369,8 @@ pub fn list_virtual_branches_cached(
356369
357370 // find all commits on head that are not on target.sha
358371 let commits = repo. log ( branch. head ( ) , LogUntil :: Commit ( default_target. sha ) , false ) ?;
359- let check_commit = IsCommitIntegrated :: new ( ctx, & default_target) ?;
372+ let mut check_commit =
373+ IsCommitIntegrated :: new ( ctx, & default_target, & gix_repo, & mut graph) ?;
360374 let vbranch_commits = {
361375 let _span = tracing:: debug_span!(
362376 "is-commit-integrated" ,
@@ -397,9 +411,14 @@ pub fn list_virtual_branches_cached(
397411 . collect :: < Result < Vec < _ > > > ( ) ?
398412 } ;
399413
400- let merge_base = repo
401- . merge_base ( default_target. sha , branch. head ( ) )
414+ let merge_base = gix_repo
415+ . merge_base_with_graph (
416+ git2_to_gix_object_id ( default_target. sha ) ,
417+ git2_to_gix_object_id ( branch. head ( ) ) ,
418+ check_commit. graph ,
419+ )
402420 . context ( "failed to find merge base" ) ?;
421+ let merge_base = gix_to_git2_oid ( merge_base) ;
403422 let base_current = true ;
404423
405424 let upstream = upstream_branch. and_then ( |upstream_branch| {
@@ -436,8 +455,9 @@ pub fn list_virtual_branches_cached(
436455 ctx,
437456 & mut branch,
438457 & default_target,
439- & check_commit,
458+ & mut check_commit,
440459 remote_commit_data,
460+ & vbranch_commits,
441461 ) {
442462 Ok ( ( series, force) ) => {
443463 if series. iter ( ) . any ( |s| s. upstream_reference . is_some ( ) ) {
@@ -943,40 +963,50 @@ pub(crate) fn push(
943963 } )
944964}
945965
946- pub ( crate ) struct IsCommitIntegrated < ' repo > {
947- repo : & ' repo git2:: Repository ,
948- target_commit_id : git2:: Oid ,
949- remote_head_id : git2:: Oid ,
966+ type MergeBaseCommitGraph < ' repo , ' cache > = gix:: revwalk:: Graph <
967+ ' repo ,
968+ ' cache ,
969+ gix:: revision:: plumbing:: graph:: Commit < gix:: revision:: plumbing:: merge_base:: Flags > ,
970+ > ;
971+
972+ pub ( crate ) struct IsCommitIntegrated < ' repo , ' cache , ' graph > {
973+ gix_repo : & ' repo gix:: Repository ,
974+ graph : & ' graph mut MergeBaseCommitGraph < ' repo , ' cache > ,
975+ target_commit_id : gix:: ObjectId ,
976+ upstream_tree_id : gix:: ObjectId ,
950977 upstream_commits : Vec < git2:: Oid > ,
951- /// A repository opened at the same path as `repo`, but with an in-memory ODB attached
952- /// to avoid writing intermediate objects.
953- inmemory_repo : git2:: Repository ,
954978}
955979
956- impl < ' repo > IsCommitIntegrated < ' repo > {
957- pub ( crate ) fn new ( ctx : & ' repo CommandContext , target : & Target ) -> anyhow:: Result < Self > {
980+ impl < ' repo , ' cache , ' graph > IsCommitIntegrated < ' repo , ' cache , ' graph > {
981+ pub ( crate ) fn new (
982+ ctx : & ' repo CommandContext ,
983+ target : & Target ,
984+ gix_repo : & ' repo gix:: Repository ,
985+ graph : & ' graph mut MergeBaseCommitGraph < ' repo , ' cache > ,
986+ ) -> anyhow:: Result < Self > {
958987 let remote_branch = ctx
959988 . repository ( )
960989 . maybe_find_branch_by_refname ( & target. branch . clone ( ) . into ( ) ) ?
961990 . ok_or ( anyhow ! ( "failed to get branch" ) ) ?;
962991 let remote_head = remote_branch. get ( ) . peel_to_commit ( ) ?;
963- let upstream_commits =
992+ let mut upstream_commits =
964993 ctx. repository ( )
965994 . l ( remote_head. id ( ) , LogUntil :: Commit ( target. sha ) , false ) ?;
966- let inmemory_repo = ctx. repository ( ) . in_memory_repo ( ) ?;
995+ upstream_commits. sort ( ) ;
996+ let upstream_tree_id = ctx. repository ( ) . find_commit ( remote_head. id ( ) ) ?. tree_id ( ) ;
967997 Ok ( Self {
968- repo : ctx. repository ( ) ,
969- target_commit_id : target. sha ,
970- remote_head_id : remote_head. id ( ) ,
998+ gix_repo,
999+ graph,
1000+ target_commit_id : git2_to_gix_object_id ( target. sha ) ,
1001+ upstream_tree_id : git2_to_gix_object_id ( upstream_tree_id) ,
9711002 upstream_commits,
972- inmemory_repo,
9731003 } )
9741004 }
9751005}
9761006
977- impl IsCommitIntegrated < ' _ > {
978- pub ( crate ) fn is_integrated ( & self , commit : & git2:: Commit ) -> Result < bool > {
979- if self . target_commit_id == commit. id ( ) {
1007+ impl IsCommitIntegrated < ' _ , ' _ , ' _ > {
1008+ pub ( crate ) fn is_integrated ( & mut self , commit : & git2:: Commit ) -> Result < bool > {
1009+ if self . target_commit_id == git2_to_gix_object_id ( commit. id ( ) ) {
9801010 // could not be integrated if heads are the same.
9811011 return Ok ( false ) ;
9821012 }
@@ -986,44 +1016,54 @@ impl IsCommitIntegrated<'_> {
9861016 return Ok ( false ) ;
9871017 }
9881018
989- if self . upstream_commits . contains ( & commit. id ( ) ) {
1019+ if self . upstream_commits . binary_search ( & commit. id ( ) ) . is_ok ( ) {
9901020 return Ok ( true ) ;
9911021 }
9921022
993- let merge_base_id = self . repo . merge_base ( self . target_commit_id , commit. id ( ) ) ?;
994- if merge_base_id. eq ( & commit. id ( ) ) {
1023+ let merge_base_id = self . gix_repo . merge_base_with_graph (
1024+ self . target_commit_id ,
1025+ git2_to_gix_object_id ( commit. id ( ) ) ,
1026+ self . graph ,
1027+ ) ?;
1028+ if gix_to_git2_oid ( merge_base_id) . eq ( & commit. id ( ) ) {
9951029 // if merge branch is the same as branch head and there are upstream commits
9961030 // then it's integrated
9971031 return Ok ( true ) ;
9981032 }
9991033
1000- let merge_base = self . repo . find_commit ( merge_base_id) ?;
1001- let merge_base_tree = merge_base. tree ( ) ?;
1002- let upstream = self . repo . find_commit ( self . remote_head_id ) ?;
1003- let upstream_tree = upstream. tree ( ) ?;
1004-
1005- if merge_base_tree. id ( ) == upstream_tree. id ( ) {
1034+ let merge_base_tree_id = self . gix_repo . find_commit ( merge_base_id) ?. tree_id ( ) ?;
1035+ if merge_base_tree_id == self . upstream_tree_id {
10061036 // if merge base is the same as upstream tree, then it's integrated
10071037 return Ok ( true ) ;
10081038 }
10091039
10101040 // try to merge our tree into the upstream tree
1011- let mut merge_index = self
1012- . repo
1013- . merge_trees ( & merge_base_tree, & commit. tree ( ) ?, & upstream_tree, None )
1041+ let mut merge_options = self . gix_repo . tree_merge_options ( ) ?;
1042+ let conflict_kind = gix:: merge:: tree:: UnresolvedConflict :: Renames ;
1043+ merge_options. fail_on_conflict = Some ( conflict_kind) ;
1044+ let mut merge_output = self
1045+ . gix_repo
1046+ . merge_trees (
1047+ merge_base_tree_id,
1048+ git2_to_gix_object_id ( commit. tree_id ( ) ) ,
1049+ self . upstream_tree_id ,
1050+ Default :: default ( ) ,
1051+ merge_options,
1052+ )
10141053 . context ( "failed to merge trees" ) ?;
10151054
1016- if merge_index . has_conflicts ( ) {
1055+ if merge_output . has_unresolved_conflicts ( conflict_kind ) {
10171056 return Ok ( false ) ;
10181057 }
10191058
1020- let merge_tree_oid = merge_index
1021- . write_tree_to ( & self . inmemory_repo )
1022- . context ( "failed to write tree" ) ?;
1059+ let merge_tree_id = merge_output
1060+ . tree
1061+ . write ( |tree| self . gix_repo . write ( tree) )
1062+ . map_err ( |err| anyhow ! ( "failed to write tree: {err}" ) ) ?;
10231063
10241064 // if the merge_tree is the same as the new_target_tree and there are no files (uncommitted changes)
10251065 // then the vbranch is fully merged
1026- Ok ( merge_tree_oid == upstream_tree . id ( ) )
1066+ Ok ( merge_tree_id == self . upstream_tree_id )
10271067 }
10281068}
10291069
0 commit comments