@@ -6,25 +6,26 @@ use std::{
66 time:: Duration ,
77} ;
88
9+ use super :: {
10+ entry:: { OperationKind , Snapshot , SnapshotDetails , Trailer } ,
11+ reflog:: set_reference_to_oplog,
12+ state:: OplogHandle ,
13+ } ;
914use anyhow:: { anyhow, bail, Context , Result } ;
1015use git2:: { DiffOptions , FileMode } ;
1116use gitbutler_command_context:: RepositoryExtLite ;
1217use gitbutler_diff:: { hunks_by_filepath, FileDiff } ;
18+ use gitbutler_oxidize:: { git2_to_gix_object_id, gix_to_git2_oid} ;
1319use gitbutler_project:: {
1420 access:: { WorktreeReadPermission , WorktreeWritePermission } ,
1521 Project ,
1622} ;
17- use gitbutler_repo:: RepositoryExt ;
1823use gitbutler_repo:: SignaturePurpose ;
24+ use gitbutler_repo:: { GixRepositoryExt , RepositoryExt } ;
1925use gitbutler_stack:: { Stack , VirtualBranchesHandle , VirtualBranchesState } ;
26+ use gix:: prelude:: Write ;
2027use tracing:: instrument;
2128
22- use super :: {
23- entry:: { OperationKind , Snapshot , SnapshotDetails , Trailer } ,
24- reflog:: set_reference_to_oplog,
25- state:: OplogHandle ,
26- } ;
27-
2829const SNAPSHOT_FILE_LIMIT_BYTES : u64 = 32 * 1024 * 1024 ;
2930
3031/// The Oplog allows for crating snapshots of the current state of the project as well as restoring to a previous snapshot.
@@ -165,8 +166,9 @@ impl OplogExt for Project {
165166 limit : usize ,
166167 oplog_commit_id : Option < git2:: Oid > ,
167168 ) -> Result < Vec < Snapshot > > {
168- let repo_path = self . path . as_path ( ) ;
169- let repo = git2:: Repository :: open ( repo_path) ?;
169+ let worktree_dir = self . path . as_path ( ) ;
170+ let repo = git2:: Repository :: open ( worktree_dir) ?;
171+ let gix_repo = gitbutler_command_context:: gix_repository_for_merging ( worktree_dir) ?;
170172
171173 let traversal_root_id = match oplog_commit_id {
172174 Some ( id) => id,
@@ -208,15 +210,16 @@ impl OplogExt for Project {
208210 }
209211
210212 // Get tree id from cache or calculate it
211- let wd_tree = get_workdir_tree ( & mut wd_trees_cache, commit_id, & repo) ?;
213+ let wd_tree = get_workdir_tree ( & mut wd_trees_cache, commit_id, & repo, & gix_repo ) ?;
212214
213215 let details = commit
214216 . message ( )
215217 . and_then ( |msg| SnapshotDetails :: from_str ( msg) . ok ( ) ) ;
216218
217219 if let Ok ( parent) = commit. parent ( 0 ) {
218220 // Get tree id from cache or calculate it
219- let parent_tree = get_workdir_tree ( & mut wd_trees_cache, parent. id ( ) , & repo) ?;
221+ let parent_tree =
222+ get_workdir_tree ( & mut wd_trees_cache, parent. id ( ) , & repo, & gix_repo) ?;
220223
221224 let mut opts = DiffOptions :: new ( ) ;
222225 opts. include_untracked ( true ) ;
@@ -279,13 +282,14 @@ impl OplogExt for Project {
279282
280283 fn snapshot_diff ( & self , sha : git2:: Oid ) -> Result < HashMap < PathBuf , FileDiff > > {
281284 let worktree_dir = self . path . as_path ( ) ;
285+ let gix_repo = gitbutler_command_context:: gix_repository_for_merging ( worktree_dir) ?;
282286 let repo = git2:: Repository :: init ( worktree_dir) ?;
283287
284288 let commit = repo. find_commit ( sha) ?;
285289
286- let wd_tree_id = tree_from_applied_vbranches ( & repo , commit. id ( ) ) ?;
290+ let wd_tree_id = tree_from_applied_vbranches ( & gix_repo , commit. id ( ) ) ?;
287291 let wd_tree = repo. find_tree ( wd_tree_id) ?;
288- let old_wd_tree_id = tree_from_applied_vbranches ( & repo , commit. parent ( 0 ) ?. id ( ) ) ?;
292+ let old_wd_tree_id = tree_from_applied_vbranches ( & gix_repo , commit. parent ( 0 ) ?. id ( ) ) ?;
289293 let old_wd_tree = repo. find_tree ( old_wd_tree_id) ?;
290294
291295 repo. ignore_large_files_in_diffs ( SNAPSHOT_FILE_LIMIT_BYTES ) ?;
@@ -317,9 +321,10 @@ fn get_workdir_tree<'a>(
317321 wd_trees_cache : & mut HashMap < git2:: Oid , git2:: Oid > ,
318322 commit_id : git2:: Oid ,
319323 repo : & ' a git2:: Repository ,
324+ gix_repo : & gix:: Repository ,
320325) -> Result < git2:: Tree < ' a > , anyhow:: Error > {
321326 if let Entry :: Vacant ( e) = wd_trees_cache. entry ( commit_id) {
322- if let Ok ( wd_tree_id) = tree_from_applied_vbranches ( repo , commit_id) {
327+ if let Ok ( wd_tree_id) = tree_from_applied_vbranches ( gix_repo , commit_id) {
323328 e. insert ( wd_tree_id) ;
324329 }
325330 }
@@ -574,7 +579,8 @@ fn restore_snapshot(
574579 "We will not change a worktree which for some reason isn't on the workspace branch" ,
575580 ) ?;
576581
577- let workdir_tree_id = tree_from_applied_vbranches ( & repo, snapshot_commit_id) ?;
582+ let gix_repo = gitbutler_command_context:: gix_repository_for_merging ( worktree_dir) ?;
583+ let workdir_tree_id = tree_from_applied_vbranches ( & gix_repo, snapshot_commit_id) ?;
578584 let workdir_tree = repo. find_tree ( workdir_tree_id) ?;
579585
580586 repo. ignore_large_files_in_diffs ( SNAPSHOT_FILE_LIMIT_BYTES ) ?;
@@ -794,59 +800,57 @@ fn deserialize_commit(
794800}
795801
796802/// Creates a tree that is the merge of all applied branches from a given snapshot and returns the tree id.
803+ /// Note that `repo` must have caching setup for merges.
797804fn tree_from_applied_vbranches (
798- repo : & git2 :: Repository ,
805+ repo : & gix :: Repository ,
799806 snapshot_commit_id : git2:: Oid ,
800807) -> Result < git2:: Oid > {
801- let snapshot_commit = repo. find_commit ( snapshot_commit_id) ?;
808+ let snapshot_commit = repo. find_commit ( git2_to_gix_object_id ( snapshot_commit_id) ) ?;
802809 let snapshot_tree = snapshot_commit. tree ( ) ?;
803810
804811 let target_tree_entry = snapshot_tree
805- . get_name ( "target_tree" )
806- . context ( "failed to get target tree entry" ) ?;
807- let target_tree = repo
808- . find_tree ( target_tree_entry. id ( ) )
809- . context ( "failed to convert target tree entry to tree" ) ?;
812+ . lookup_entry_by_path ( "target_tree" ) ?
813+ . context ( "no entry at 'target_entry'" ) ?;
814+ let target_tree_id = target_tree_entry. id ( ) . detach ( ) ;
810815
811816 let vb_toml_entry = snapshot_tree
812- . get_name ( "virtual_branches.toml" )
817+ . lookup_entry_by_path ( "virtual_branches.toml" ) ?
813818 . context ( "failed to get virtual_branches.toml blob" ) ?;
814- // virtual_branches.toml blob
815819 let vb_toml_blob = repo
816820 . find_blob ( vb_toml_entry. id ( ) )
817821 . context ( "failed to convert virtual_branches tree entry to blob" ) ?;
818822
819- let vbs_from_toml: VirtualBranchesState = toml:: from_str ( from_utf8 ( vb_toml_blob. content ( ) ) ?) ?;
820- let applied_branch_trees: Vec < git2 :: Oid > = vbs_from_toml
823+ let vbs_from_toml: VirtualBranchesState = toml:: from_str ( from_utf8 ( & vb_toml_blob. data ) ?) ?;
824+ let applied_branch_trees: Vec < _ > = vbs_from_toml
821825 . list_branches_in_workspace ( ) ?
822826 . iter ( )
823- . map ( |b| b. tree )
827+ . map ( |b| git2_to_gix_object_id ( b. tree ) )
824828 . collect ( ) ;
825829
826- let mut workdir_tree_id = target_tree. id ( ) ;
827- let base_tree = target_tree;
828- let mut current_ours = base_tree. clone ( ) ;
829-
830- for branch in applied_branch_trees {
831- let branch_tree = repo. find_tree ( branch) ?;
832- let mut merge_options: git2:: MergeOptions = git2:: MergeOptions :: new ( ) ;
833- merge_options. fail_on_conflict ( false ) ;
834- let mut workdir_temp_index = repo. merge_trees (
835- & base_tree,
836- & current_ours,
837- & branch_tree,
838- Some ( & merge_options) ,
830+ let mut workdir_tree_id = target_tree_id;
831+ let base_tree_id = target_tree_id;
832+ let mut current_ours_id = target_tree_id;
833+
834+ let ( merge_option_fail_fast, conflict_kind) = repo. merge_options_fail_fast ( ) ?;
835+ for branch_id in applied_branch_trees {
836+ let mut merge = repo. merge_trees (
837+ base_tree_id,
838+ current_ours_id,
839+ branch_id,
840+ repo. default_merge_labels ( ) ,
841+ merge_option_fail_fast. clone ( ) ,
839842 ) ?;
840- match workdir_temp_index. write_tree_to ( repo) {
841- Ok ( id) => {
842- workdir_tree_id = id;
843- current_ours = repo. find_tree ( workdir_tree_id) ?;
844- }
845- Err ( _err) => {
846- tracing:: warn!( "Failed to merge tree {branch} - this branch is probably applied at a time when it should not be" ) ;
847- }
843+ if merge. has_unresolved_conflicts ( conflict_kind) {
844+ tracing:: warn!( "Failed to merge tree {branch_id} - this branch is probably applied at a time when it should not be" ) ;
845+ } else {
846+ let id = merge
847+ . tree
848+ . write ( |tree| repo. write ( tree) )
849+ . map_err ( |err| anyhow ! ( "{err}" ) ) ?;
850+ workdir_tree_id = id;
851+ current_ours_id = id;
848852 }
849853 }
850854
851- Ok ( workdir_tree_id)
855+ Ok ( gix_to_git2_oid ( workdir_tree_id) )
852856}
0 commit comments