Skip to content

Commit 93a117e

Browse files
committed
unapply_ownership uses gitoxide merging.
Note that this at best is done for getting better merge results, it's not significant for performance.
1 parent d191b6d commit 93a117e

File tree

3 files changed

+44
-21
lines changed

3 files changed

+44
-21
lines changed

crates/gitbutler-branch-actions/src/integration.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ pub(crate) fn get_workspace_head(ctx: &CommandContext) -> Result<git2::Oid> {
5353
workspace_tree = repo.find_commit(merge_base)?.tree()?;
5454
} else {
5555
let gix_repo = ctx.gix_repository_for_merging()?;
56-
let mut merge_options_fail_fast = gix_repo.tree_merge_options()?;
57-
let conflict_kind = gix::merge::tree::UnresolvedConflict::Renames;
58-
merge_options_fail_fast.fail_on_conflict = Some(conflict_kind);
56+
let (merge_options_fail_fast, conflict_kind) = gix_repo.merge_options_fail_fast()?;
5957
let merge_tree_id = git2_to_gix_object_id(repo.find_commit(target.sha)?.tree_id());
6058
for branch in virtual_branches.iter_mut() {
6159
let branch_head = repo.find_commit(branch.head())?;

crates/gitbutler-branch-actions/src/virtual.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use gitbutler_project::access::WorktreeWritePermission;
2525
use gitbutler_reference::{normalize_branch_name, Refname, RemoteRefname};
2626
use gitbutler_repo::{
2727
rebase::{cherry_rebase, cherry_rebase_group},
28-
LogUntil, RepositoryExt,
28+
GixRepositoryExt, LogUntil, RepositoryExt,
2929
};
3030
use gitbutler_repo_actions::RepoActionsExt;
3131
use gitbutler_stack::{
@@ -180,25 +180,37 @@ pub fn unapply_ownership(
180180
.find_commit(workspace_commit_id)
181181
.context("failed to find target commit")?;
182182

183-
let base_tree = target_commit.tree().context("failed to get target tree")?;
184-
let final_tree = applied_statuses.into_iter().fold(
185-
target_commit.tree().context("failed to get target tree"),
186-
|final_tree, status| {
187-
let final_tree = final_tree?;
183+
let base_tree_id = git2_to_gix_object_id(target_commit.tree_id());
184+
let gix_repo = ctx.gix_repository_for_merging()?;
185+
let (merge_options_fail_fast, conflict_kind) = gix_repo.merge_options_fail_fast()?;
186+
let final_tree_id = applied_statuses.into_iter().try_fold(
187+
git2_to_gix_object_id(target_commit.tree_id()),
188+
|final_tree_id, status| -> Result<_> {
188189
let files = status
189190
.1
190191
.into_iter()
191192
.map(|file| (file.path, file.hunks))
192193
.collect::<Vec<(PathBuf, Vec<VirtualBranchHunk>)>>();
193-
let tree_oid = gitbutler_diff::write::hunks_onto_oid(ctx, workspace_commit_id, files)?;
194-
let branch_tree = repo.find_tree(tree_oid)?;
195-
let mut result = repo.merge_trees(&base_tree, &final_tree, &branch_tree, None)?;
196-
let final_tree_oid = result.write_tree_to(ctx.repository())?;
197-
repo.find_tree(final_tree_oid)
198-
.context("failed to find tree")
194+
let branch_tree_id =
195+
gitbutler_diff::write::hunks_onto_oid(ctx, workspace_commit_id, files)?;
196+
let mut merge = gix_repo.merge_trees(
197+
base_tree_id,
198+
final_tree_id,
199+
git2_to_gix_object_id(branch_tree_id),
200+
gix_repo.default_merge_labels(),
201+
merge_options_fail_fast.clone(),
202+
)?;
203+
if merge.has_unresolved_conflicts(conflict_kind) {
204+
bail!("Tree has conflicts after merge")
205+
}
206+
merge
207+
.tree
208+
.write(|tree| gix_repo.write(tree))
209+
.map_err(|err| anyhow!("Could not write merged tree: {err}"))
199210
},
200211
)?;
201212

213+
let final_tree = repo.find_tree(gix_to_git2_oid(final_tree_id))?;
202214
let final_tree_oid = gitbutler_diff::write::hunks_onto_tree(ctx, &final_tree, diff, true)?;
203215
let final_tree = repo
204216
.find_tree(final_tree_oid)
@@ -1035,9 +1047,7 @@ impl IsCommitIntegrated<'_, '_, '_> {
10351047
}
10361048

10371049
// try to merge our tree into the upstream tree
1038-
let mut merge_options = self.gix_repo.tree_merge_options()?;
1039-
let conflict_kind = gix::merge::tree::UnresolvedConflict::Renames;
1040-
merge_options.fail_on_conflict = Some(conflict_kind);
1050+
let (merge_options, conflict_kind) = self.gix_repo.merge_options_fail_fast()?;
10411051
let mut merge_output = self
10421052
.gix_repo
10431053
.merge_trees(

crates/gitbutler-repo/src/repository_ext.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use gitbutler_oxidize::{
1717
};
1818
use gitbutler_reference::{Refname, RemoteRefname};
1919
use gix::fs::is_executable;
20+
use gix::merge::tree::{Options, UnresolvedConflict};
2021
use gix::objs::WriteTo;
2122
use tracing::instrument;
2223

@@ -731,6 +732,15 @@ pub trait GixRepositoryExt: Sized {
731732
other: Some("theirs".into()),
732733
}
733734
}
735+
736+
/// Return options suitable for merging so that the merge stops immediately after the first conflict.
737+
/// It also returns the conflict kind to use when checking for unresolved conflicts.
738+
fn merge_options_fail_fast(
739+
&self,
740+
) -> Result<(
741+
gix::merge::tree::Options,
742+
gix::merge::tree::UnresolvedConflict,
743+
)>;
734744
}
735745

736746
impl GixRepositoryExt for gix::Repository {
@@ -759,9 +769,7 @@ impl GixRepositoryExt for gix::Repository {
759769
our_tree: gix::ObjectId,
760770
their_tree: gix::ObjectId,
761771
) -> Result<bool> {
762-
let mut options = self.tree_merge_options()?;
763-
let conflict_kind = gix::merge::tree::UnresolvedConflict::Renames;
764-
options.fail_on_conflict = Some(conflict_kind);
772+
let (options, conflict_kind) = self.merge_options_fail_fast()?;
765773
let merge_outcome = self
766774
.merge_trees(
767775
ancestor_tree,
@@ -773,6 +781,13 @@ impl GixRepositoryExt for gix::Repository {
773781
.context("failed to merge trees")?;
774782
Ok(!merge_outcome.has_unresolved_conflicts(conflict_kind))
775783
}
784+
785+
fn merge_options_fail_fast(&self) -> Result<(Options, UnresolvedConflict)> {
786+
let mut options = self.tree_merge_options()?;
787+
let conflict_kind = gix::merge::tree::UnresolvedConflict::Renames;
788+
options.fail_on_conflict = Some(conflict_kind);
789+
Ok((options, conflict_kind))
790+
}
776791
}
777792

778793
type OidFilter = dyn Fn(&git2::Commit) -> Result<bool>;

0 commit comments

Comments
 (0)