mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2025-01-06 01:27:24 +03:00
add tests, remove other branches before merging, checkout conflict state
This commit is contained in:
parent
b2833e2549
commit
aff8f261e2
@ -1427,6 +1427,111 @@ fn test_update_base_branch_no_commits_no_conflict() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merge_vbranch_upstream() -> Result<()> {
|
||||
let TestDeps {
|
||||
repository,
|
||||
project,
|
||||
gb_repo_path,
|
||||
user_store,
|
||||
project_store,
|
||||
..
|
||||
} = new_test_deps()?;
|
||||
|
||||
let gb_repo =
|
||||
gb_repository::Repository::open(gb_repo_path, &project.id, project_store, user_store)?;
|
||||
let project_repository = project_repository::Repository::open(&project)?;
|
||||
|
||||
// create a commit and set the target
|
||||
let file_path = std::path::Path::new("test.txt");
|
||||
std::fs::write(
|
||||
std::path::Path::new(&project.path).join(file_path),
|
||||
"line1\nline2\nline3\nline4\n",
|
||||
)?;
|
||||
test_utils::commit_all(&repository);
|
||||
let target_oid = repository.head().unwrap().target().unwrap();
|
||||
|
||||
std::fs::write(
|
||||
std::path::Path::new(&project.path).join(file_path),
|
||||
"line1\nline2\nline3\nline4\nupstream\n",
|
||||
)?;
|
||||
// add a commit to the target branch it's pointing to so there is something "upstream"
|
||||
test_utils::commit_all(&repository);
|
||||
let last_push = repository.head().unwrap().target().unwrap();
|
||||
|
||||
// coworker adds some work
|
||||
std::fs::write(
|
||||
std::path::Path::new(&project.path).join(file_path),
|
||||
"line1\nline2\nline3\nline4\nupstream\ncoworker work\n",
|
||||
)?;
|
||||
|
||||
test_utils::commit_all(&repository);
|
||||
let coworker_work = repository.head().unwrap().target().unwrap();
|
||||
|
||||
//update repo ref refs/remotes/origin/master to up_target oid
|
||||
repository.reference(
|
||||
"refs/remotes/origin/master",
|
||||
coworker_work,
|
||||
true,
|
||||
"update target",
|
||||
)?;
|
||||
|
||||
// revert to our file
|
||||
std::fs::write(
|
||||
std::path::Path::new(&project.path).join(file_path),
|
||||
"line1\nline2\nline3\nline4\nupstream\n",
|
||||
)?;
|
||||
|
||||
set_test_target(&gb_repo, &project_repository, &repository)?;
|
||||
target::Writer::new(&gb_repo).write_default(&target::Target {
|
||||
branch: "refs/remotes/origin/master".parse().unwrap(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: target_oid,
|
||||
})?;
|
||||
|
||||
// add some uncommitted work
|
||||
let file_path2 = std::path::Path::new("test2.txt");
|
||||
std::fs::write(
|
||||
std::path::Path::new(&project.path).join(file_path2),
|
||||
"file2\n",
|
||||
)?;
|
||||
|
||||
let remote_branch: git::RemoteBranchName = "refs/remotes/origin/master".parse().unwrap();
|
||||
let branch_writer = branch::Writer::new(&gb_repo);
|
||||
let mut branch = create_virtual_branch(&gb_repo, &BranchCreateRequest::default())
|
||||
.expect("failed to create virtual branch");
|
||||
branch.upstream = Some(remote_branch.clone());
|
||||
branch.head = last_push;
|
||||
branch_writer
|
||||
.write(&branch)
|
||||
.context("failed to write target branch after push")?;
|
||||
|
||||
// create the branch
|
||||
let branches = list_virtual_branches(&gb_repo, &project_repository)?;
|
||||
let branch1 = &branches[0];
|
||||
assert_eq!(branch1.files.len(), 1);
|
||||
assert_eq!(branch1.commits.len(), 1);
|
||||
assert_eq!(branch1.upstream_commits.len(), 1);
|
||||
|
||||
merge_virtual_branch_upstream(&gb_repo, &project_repository, &branch1.id)?;
|
||||
|
||||
let branches = list_virtual_branches(&gb_repo, &project_repository)?;
|
||||
let branch1 = &branches[0];
|
||||
|
||||
let contents = std::fs::read(std::path::Path::new(&project.path).join(file_path))?;
|
||||
assert_eq!(
|
||||
"line1\nline2\nline3\nline4\nupstream\ncoworker work\n",
|
||||
String::from_utf8(contents)?
|
||||
);
|
||||
let contents = std::fs::read(std::path::Path::new(&project.path).join(file_path2))?;
|
||||
assert_eq!("file2\n", String::from_utf8(contents)?);
|
||||
assert_eq!(branch1.files.len(), 0);
|
||||
assert_eq!(branch1.commits.len(), 3);
|
||||
assert_eq!(branch1.upstream_commits.len(), 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_target_with_conflicts_in_vbranches() -> Result<()> {
|
||||
let TestDeps {
|
||||
|
@ -858,10 +858,21 @@ pub fn merge_virtual_branch_upstream(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// look up the target to figure out a merge base
|
||||
let target = get_default_target(¤t_session_reader)
|
||||
.context("failed to get target")?
|
||||
.context("no target found")?;
|
||||
// if any other branches are applied, unapply them
|
||||
let applied_branches = Iterator::new(¤t_session_reader)
|
||||
.context("failed to create branch iterator")?
|
||||
.collect::<Result<Vec<branch::Branch>, reader::Error>>()
|
||||
.context("failed to read virtual branches")?
|
||||
.into_iter()
|
||||
.filter(|b| b.applied)
|
||||
.filter(|b| b.id != branch_id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// unapply all other branches
|
||||
for other_branch in applied_branches {
|
||||
unapply_branch(gb_repository, project_repository, &other_branch.id)
|
||||
.context("failed to unapply branch")?;
|
||||
}
|
||||
|
||||
// get merge base from remote branch commit and target commit
|
||||
let merge_base = repo
|
||||
@ -877,14 +888,29 @@ pub fn merge_virtual_branch_upstream(
|
||||
.merge_trees(&merge_tree, &wd_tree, &remote_tree)
|
||||
.context("failed to merge trees")?;
|
||||
|
||||
// three scenarios:
|
||||
// - clean merge, clean wd, upstream is a fast forward, just fast forward it
|
||||
// - clean merge, upstream is not a fast forward, merge it
|
||||
// - upstream is not a fast forward, and cannot be merged cleanly
|
||||
// - unapply all other branches, create the merge conflicts in the wd
|
||||
|
||||
if merge_index.has_conflicts() {
|
||||
bail!("cannot merge upstream, conflicts found");
|
||||
// checkout the conflicts
|
||||
let mut checkout_options = git2::build::CheckoutBuilder::new();
|
||||
checkout_options
|
||||
.allow_conflicts(true)
|
||||
.conflict_style_merge(true)
|
||||
.force();
|
||||
repo.checkout_index(Some(&mut merge_index), Some(&mut checkout_options))?;
|
||||
|
||||
// mark conflicts
|
||||
let conflicts = merge_index.conflicts()?;
|
||||
let mut merge_conflicts = Vec::new();
|
||||
for path in conflicts.flatten() {
|
||||
if let Some(ours) = path.our {
|
||||
let path = std::str::from_utf8(&ours.path)?.to_string();
|
||||
merge_conflicts.push(path);
|
||||
}
|
||||
}
|
||||
conflicts::mark(
|
||||
project_repository,
|
||||
&merge_conflicts,
|
||||
Some(upstream_commit.id()),
|
||||
)?;
|
||||
} else {
|
||||
// get the merge tree oid from writing the index out
|
||||
let merge_tree_oid = merge_index
|
||||
@ -912,15 +938,14 @@ pub fn merge_virtual_branch_upstream(
|
||||
repo.checkout_tree(&merge_tree, Some(&mut checkout_options))?;
|
||||
|
||||
// write the branch data
|
||||
// TODO: update ownership?
|
||||
let branch_writer = branch::Writer::new(gb_repository);
|
||||
branch.head = new_branch_head;
|
||||
branch.tree = merge_tree_oid;
|
||||
branch_writer.write(&branch)?;
|
||||
|
||||
super::integration::update_gitbutler_integration(gb_repository, project_repository)?;
|
||||
}
|
||||
|
||||
super::integration::update_gitbutler_integration(gb_repository, project_repository)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user