Skip to content

Commit

Permalink
Replace merge-tree in upstream_integration_statuses
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Nov 4, 2024
1 parent 7357d94 commit 922bdd9
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 162 deletions.
73 changes: 51 additions & 22 deletions crates/gitbutler-branch-actions/src/upstream_integration.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
use crate::{
branch_trees::{checkout_branch_trees, compute_updated_branch_head, BranchHeadAndTree},
BranchManagerExt, VirtualBranchesExt as _,
};
use anyhow::{anyhow, bail, Result};
use gitbutler_cherry_pick::RepositoryExt as _;
use gitbutler_command_context::CommandContext;
use gitbutler_oxidize::git2_to_gix_object_id;
use gitbutler_project::access::WorktreeWritePermission;
use gitbutler_repo::{
rebase::{cherry_rebase_group, gitbutler_merge_commits},
LogUntil, RepositoryExt as _,
GixRepositoryExt, LogUntil, RepositoryExt as _,
};
use gitbutler_repo_actions::RepoActionsExt as _;
use gitbutler_stack::{Stack, StackId, Target, VirtualBranchesHandle};
use gix::prelude::Write;
use serde::{Deserialize, Serialize};

use crate::{
branch_trees::{checkout_branch_trees, compute_updated_branch_head, BranchHeadAndTree},
BranchManagerExt, VirtualBranchesExt as _,
};

#[derive(Serialize, PartialEq, Debug)]
#[serde(tag = "type", content = "subject", rename_all = "camelCase")]
pub enum BranchStatus {
Expand Down Expand Up @@ -140,8 +141,19 @@ pub fn upstream_integration_statuses(
..
} = context;
// look up the target and see if there is a new oid
let old_target_tree = repository.find_real_tree(old_target, Default::default())?;
let new_target_tree = repository.find_real_tree(new_target, Default::default())?;
let old_target_tree_id = git2_to_gix_object_id(
repository
.find_real_tree(old_target, Default::default())?
.id(),
);
let new_target_tree_id = git2_to_gix_object_id(
repository
.find_real_tree(new_target, Default::default())?
.id(),
);
let gix_repo = gitbutler_command_context::gix_repository_for_merging(repository.path())?;
let gix_repo_in_memory = gix_repo.clone().with_object_memory();
let (merge_options_fail_fast, conflict_kind) = gix_repo.merge_options_fail_fast()?;

if new_target.id() == old_target.id() {
return Ok(BranchStatuses::UpToDate);
Expand All @@ -151,8 +163,10 @@ pub fn upstream_integration_statuses(
.iter()
.map(|virtual_branch| {
let tree = repository.find_tree(virtual_branch.tree)?;
let tree_id = git2_to_gix_object_id(tree.id());
let head = repository.find_commit(virtual_branch.head())?;
let head_tree = repository.find_real_tree(&head, Default::default())?;
let head_tree_id = git2_to_gix_object_id(head_tree.id());

// Try cherry pick the branch's head commit onto the target to
// see if it conflics. This is equivalent to doing a merge
Expand All @@ -168,25 +182,33 @@ pub fn upstream_integration_statuses(
};
}

let head_merge_index =
repository.merge_trees(&old_target_tree, &new_target_tree, &head_tree, None)?;
let mut tree_merge_index =
repository.merge_trees(&old_target_tree, &new_target_tree, &tree, None)?;
let mut tree_merge = gix_repo.merge_trees(
old_target_tree_id,
new_target_tree_id,
tree_id,
gix_repo.default_merge_labels(),
merge_options_fail_fast.clone(),
)?;

// Is the branch conflicted?
// A branch can't be integrated if its conflicted
{
let commits_conflicted = head_merge_index.has_conflicts();
let commits_conflicted = gix_repo_in_memory
.merge_trees(
old_target_tree_id,
new_target_tree_id,
head_tree_id,
Default::default(),
merge_options_fail_fast.clone(),
)?
.has_unresolved_conflicts(conflict_kind);
gix_repo_in_memory.objects.reset_object_memory();

// See whether uncommited changes are potentially conflicted
let potentially_conflicted_uncommited_changes = if has_uncommited_changes {
// If the commits are conflicted, we can guarentee that the
// tree will be conflicted.
if commits_conflicted {
true
} else {
tree_merge_index.has_conflicts()
}
commits_conflicted || tree_merge.has_unresolved_conflicts(conflict_kind)
} else {
// If there are no uncommited changes, then there can't be
// any conflicts.
Expand All @@ -205,13 +227,20 @@ pub fn upstream_integration_statuses(

// Is the branch fully integrated?
{
if tree_merge.has_unresolved_conflicts(conflict_kind) {
bail!(
"Merge result unexpectedly has conflicts between base, ours, theirs: {old_target_tree_id}, {new_target_tree_id}, {tree_id}"
)
}
// We're safe to write the tree as we've ensured it's
// unconflicted in the previous test.
let tree_merge_index_tree = tree_merge_index.write_tree_to(repository)?;
let tree_merge_index_tree_id = tree_merge
.tree
.write(|tree| gix_repo.write(tree))
.map_err(|err| anyhow!("{err}"))?;

// Identical trees will have the same Oid so we can compare
// the two
if tree_merge_index_tree == new_target_tree.id() {
// Identical trees will have the same Oid so we can compare the two
if tree_merge_index_tree_id == new_target_tree_id {
return Ok((virtual_branch.id, BranchStatus::FullyIntegrated));
}
}
Expand Down
Loading

0 comments on commit 922bdd9

Please sign in to comment.