prefer rebase when merging upstream into vbranch

This commit is contained in:
Nikita Galaiko 2024-02-27 09:32:40 +01:00
parent 789181fdbe
commit 4b4f6e210f
2 changed files with 77 additions and 16 deletions

View File

@ -947,7 +947,7 @@ fn test_add_new_hunk_to_the_end() -> Result<()> {
} }
#[test] #[test]
fn test_merge_vbranch_upstream_clean() -> Result<()> { fn test_merge_vbranch_upstream_clean_rebase() -> Result<()> {
let suite = Suite::default(); let suite = Suite::default();
let Case { let Case {
project_repository, project_repository,
@ -1064,15 +1064,10 @@ fn test_merge_vbranch_upstream_clean() -> Result<()> {
); );
let contents = std::fs::read(std::path::Path::new(&project.path).join(file_path2))?; let contents = std::fs::read(std::path::Path::new(&project.path).join(file_path2))?;
assert_eq!("file2\n", String::from_utf8(contents)?); assert_eq!("file2\n", String::from_utf8(contents)?);
assert_eq!(branch1.files.len(), 0); assert_eq!(branch1.files.len(), 1);
assert_eq!(branch1.commits.len(), 3); assert_eq!(branch1.commits.len(), 2);
// assert_eq!(branch1.upstream.as_ref().unwrap().commits.len(), 0); // assert_eq!(branch1.upstream.as_ref().unwrap().commits.len(), 0);
// make sure the last commit was signed
let last_id = &branch1.commits[0].id;
let last_commit = project_repository.git_repository.find_commit(*last_id)?;
assert!(last_commit.raw_header().unwrap().contains("SSH SIGNATURE"));
Ok(()) Ok(())
} }

View File

@ -5,7 +5,7 @@ use std::os::unix::prelude::*;
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use diffy::{apply_bytes, Patch}; use diffy::{apply_bytes, Patch};
use git2_hooks::HookResult; use git2_hooks::{HookResult, PrepareCommitMsgSource};
use regex::Regex; use regex::Regex;
use serde::Serialize; use serde::Serialize;
@ -1430,20 +1430,88 @@ pub fn merge_virtual_branch_upstream(
Some(upstream_commit.id()), Some(upstream_commit.id()),
)?; )?;
} else { } else {
// get the merge tree oid from writing the index out
let merge_tree_oid = merge_index let merge_tree_oid = merge_index
.write_tree_to(repo) .write_tree_to(repo)
.context("failed to write tree")?; .context("failed to write tree")?;
let merge_tree = repo
.find_tree(merge_tree_oid)
.context("failed to find merge tree")?;
let branch_writer =
branch::Writer::new(gb_repository).context("failed to create writer")?;
if *project_repository.project().ok_with_force_push {
// attempt a rebase
let (_, committer) = project_repository.git_signatures(user)?;
let mut rebase_options = git2::RebaseOptions::new();
rebase_options.quiet(true);
rebase_options.inmemory(true);
let mut rebase = repo
.rebase(
Some(branch.head),
Some(upstream_commit.id()),
None,
Some(&mut rebase_options),
)
.context("failed to rebase")?;
let mut rebase_success = true;
// check to see if these commits have already been pushed
let mut last_rebase_head = upstream_commit.id();
while rebase.next().is_some() {
let index = rebase
.inmemory_index()
.context("failed to get inmemory index")?;
if index.has_conflicts() {
rebase_success = false;
break;
}
if let Ok(commit_id) = rebase.commit(None, &committer.clone().into(), None) {
last_rebase_head = commit_id.into();
} else {
rebase_success = false;
break;
}
}
if rebase_success {
// rebase worked out, rewrite the branch head
rebase.finish(None).context("failed to finish rebase")?;
project_repository
.git_repository
.checkout_tree(&merge_tree)
.force()
.checkout()
.context("failed to checkout tree")?;
branch.head = last_rebase_head;
branch.tree = merge_tree_oid;
branch_writer.write(&mut branch)?;
super::integration::update_gitbutler_integration(
gb_repository,
project_repository,
)?;
return Ok(());
}
rebase.abort().context("failed to abort rebase")?;
}
let head_commit = repo let head_commit = repo
.find_commit(branch.head) .find_commit(branch.head)
.context("failed to find head commit")?; .context("failed to find head commit")?;
let merge_tree = repo
.find_tree(merge_tree_oid)
.context("failed to find merge tree")?;
let new_branch_head = project_repository.commit( let new_branch_head = project_repository.commit(
user, user,
"merged from upstream", format!(
"Merged {}/{} into {}",
upstream_branch.remote(),
upstream_branch.branch(),
branch.name
)
.as_str(),
&merge_tree, &merge_tree,
&[&head_commit, &upstream_commit], &[&head_commit, &upstream_commit],
signing_key, signing_key,
@ -1456,8 +1524,6 @@ pub fn merge_virtual_branch_upstream(
.context("failed to checkout tree")?; .context("failed to checkout tree")?;
// write the branch data // write the branch data
let branch_writer =
branch::Writer::new(gb_repository).context("failed to create writer")?;
branch.head = new_branch_head; branch.head = new_branch_head;
branch.tree = merge_tree_oid; branch.tree = merge_tree_oid;
branch_writer.write(&mut branch)?; branch_writer.write(&mut branch)?;