Skip to content

Commit 2c3f763

Browse files
Use new "merge twice" algorithm to resolve push conflicts (josh-project#1279)
Change: merge-twice
1 parent 4ea1894 commit 2c3f763

File tree

1 file changed

+47
-16
lines changed

1 file changed

+47
-16
lines changed

josh-core/src/history.rs

+47-16
Original file line numberDiff line numberDiff line change
@@ -461,23 +461,51 @@ pub fn unapply_filter(
461461

462462
if tid == git2::Oid::zero() && parent_count == 2 {
463463
// If we could not select one of the parents, try to merge them.
464-
465-
if let Ok(mut merged_index) = transaction.repo().merge_commits(
464+
// We expect conflicts to occur only in the paths that are present in
465+
// the filtered commit.
466+
// As we are going to replace the contents of these files with commit being
467+
// pushed, we can ignore those conflicts. To do that we perform the merge
468+
// twice: Once with the "ours" and once with the "theirs" merge file favor.
469+
// After that we do "unapply()" on both resulting trees, which will replace
470+
// the files selected by the filter with the content being pushed.
471+
// If our assumption was correct and all conflicts were in filtered files,
472+
// both resulting trees will be the same and we can pick the result to proceed.
473+
474+
let mut mergeopts = git2::MergeOptions::new();
475+
mergeopts.file_favor(git2::FileFavor::Ours);
476+
477+
let mut merged_index = transaction.repo().merge_commits(
478+
original_parents_refs[0],
479+
original_parents_refs[1],
480+
Some(&mergeopts),
481+
)?;
482+
let base_tree = merged_index.write_tree_to(transaction.repo())?;
483+
let tid_ours = filter::unapply(
484+
transaction,
485+
filterobj,
486+
tree.clone(),
487+
transaction.repo().find_tree(base_tree)?,
488+
)?
489+
.id();
490+
491+
mergeopts.file_favor(git2::FileFavor::Theirs);
492+
493+
let mut merged_index = transaction.repo().merge_commits(
466494
original_parents_refs[0],
467495
original_parents_refs[1],
468-
None,
469-
) {
470-
// If we can auto merge without conflicts, take the result.
471-
if !merged_index.has_conflicts() {
472-
let base_tree = merged_index.write_tree_to(transaction.repo())?;
473-
tid = filter::unapply(
474-
transaction,
475-
filterobj,
476-
tree,
477-
transaction.repo().find_tree(base_tree)?,
478-
)?
479-
.id();
480-
}
496+
Some(&mergeopts),
497+
)?;
498+
let base_tree = merged_index.write_tree_to(transaction.repo())?;
499+
let tid_theirs = filter::unapply(
500+
transaction,
501+
filterobj,
502+
tree.clone(),
503+
transaction.repo().find_tree(base_tree)?,
504+
)?
505+
.id();
506+
507+
if tid_ours == tid_theirs {
508+
tid = tid_ours;
481509
}
482510
}
483511

@@ -486,11 +514,14 @@ pub fn unapply_filter(
486514
// more and maybe consider allowing a manual override as last resort.
487515
tracing::warn!("rejecting merge");
488516
let msg = format!(
489-
"rejecting merge with {} parents:\n{:?}\n1) {:?}\n2) {:?}",
517+
"rejecting merge with {} parents:\n{:?} ({:?})\n1) {:?} ({:?})\n2) {:?} ({:?})",
490518
parent_count,
491519
module_commit.summary().unwrap_or_default(),
520+
module_commit.id(),
492521
original_parents_refs[0].summary().unwrap_or_default(),
522+
original_parents_refs[0].id(),
493523
original_parents_refs[1].summary().unwrap_or_default(),
524+
original_parents_refs[1].id(),
494525
);
495526
return Err(josh_error(&msg));
496527
}

0 commit comments

Comments
 (0)