mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-24 10:02:26 +03:00
4d495cb3bc
This is relevant when all commits are equal by tree, but seem changed due to the added GitButler headers. For added safety, we also compare by commit message, date and authors, basically everything that isn't the headers.
1049 lines
36 KiB
Rust
1049 lines
36 KiB
Rust
use super::*;
|
|
|
|
mod applied_branch {
|
|
use gitbutler_branch::BranchCreateRequest;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn conflicts_with_uncommitted_work() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
{
|
|
// make a branch that conflicts with the remote branch, but doesn't know about it yet
|
|
controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
}
|
|
|
|
let unapplied_branch = {
|
|
// fetch remote
|
|
let unapplied_branches = controller.update_base_branch(project).unwrap();
|
|
assert_eq!(unapplied_branches.len(), 1);
|
|
|
|
// should stash conflicting branch
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 0);
|
|
|
|
Refname::from_str(unapplied_branches[0].as_str()).unwrap()
|
|
};
|
|
|
|
{
|
|
// applying the branch should produce conflict markers
|
|
controller
|
|
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
|
.unwrap();
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert!(branches[0].conflicted);
|
|
assert!(branches[0].base_current);
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
assert_eq!(
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
"<<<<<<< ours\nconflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn commited_conflict_not_pushed() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
{
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
// that conflict
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
controller
|
|
.create_commit(project, branch_id, "conflicting commit", None, false)
|
|
.unwrap();
|
|
}
|
|
|
|
let unapplied_branch = {
|
|
// when fetching remote
|
|
let unapplied_branches = controller.update_base_branch(project).unwrap();
|
|
assert_eq!(unapplied_branches.len(), 1);
|
|
|
|
// should stash the branch.
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 0);
|
|
|
|
Refname::from_str(unapplied_branches[0].as_str()).unwrap()
|
|
};
|
|
|
|
{
|
|
// applying the branch should produce conflict markers
|
|
controller
|
|
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
|
.unwrap();
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert!(branches[0].conflicted);
|
|
assert!(branches[0].base_current);
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
assert_eq!(
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
"<<<<<<< ours\nconflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn commited_conflict_pushed() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
{
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
// that conflict
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
controller
|
|
.create_commit(project, branch_id, "conflicting commit", None, false)
|
|
.unwrap();
|
|
|
|
controller
|
|
.push_virtual_branch(project, branch_id, false, None)
|
|
.unwrap();
|
|
}
|
|
|
|
let unapplied_branch = {
|
|
// when fetching remote
|
|
let unapplied_branches = controller.update_base_branch(project).unwrap();
|
|
assert_eq!(unapplied_branches.len(), 1);
|
|
|
|
// should stash the branch.
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 0);
|
|
|
|
Refname::from_str(unapplied_branches[0].as_str()).unwrap()
|
|
};
|
|
|
|
{
|
|
// applying the branch should produce conflict markers
|
|
controller
|
|
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
|
.unwrap();
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert!(branches[0].conflicted);
|
|
assert!(branches[0].base_current);
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
assert_eq!(
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
"<<<<<<< ours\nconflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn commited_conflict_not_pushed_fixed_with_more_work() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
{
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
// that conflict
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
controller
|
|
.create_commit(project, branch_id, "conflicting commit", None, false)
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "fix conflict").unwrap();
|
|
}
|
|
|
|
let unapplied_branch = {
|
|
// when fetching remote
|
|
let unapplied_branches = controller.update_base_branch(project).unwrap();
|
|
assert_eq!(unapplied_branches.len(), 1);
|
|
|
|
// should rebase upstream, and leave uncommited file as is
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 0);
|
|
|
|
Refname::from_str(unapplied_branches[0].as_str()).unwrap()
|
|
};
|
|
|
|
{
|
|
// applying the branch should produce conflict markers
|
|
controller
|
|
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
|
.unwrap();
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert!(branches[0].conflicted);
|
|
assert!(branches[0].base_current);
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
assert_eq!(branches[0].commits.len(), 2);
|
|
assert_eq!(
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
"<<<<<<< ours\nfix conflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn commited_conflict_pushed_fixed_with_more_work() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
{
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
// that conflict
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
controller
|
|
.create_commit(project, branch_id, "conflicting commit", None, false)
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "fix conflict").unwrap();
|
|
}
|
|
|
|
let unapplied_branch = {
|
|
// when fetching remote
|
|
let unapplied_branches = controller.update_base_branch(project).unwrap();
|
|
assert_eq!(unapplied_branches.len(), 1);
|
|
|
|
// should merge upstream, and leave uncommited file as is.
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 0);
|
|
|
|
Refname::from_str(unapplied_branches[0].as_str()).unwrap()
|
|
};
|
|
|
|
{
|
|
// applying the branch should produce conflict markers
|
|
controller
|
|
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
|
.unwrap();
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert!(branches[0].conflicted);
|
|
assert!(branches[0].base_current);
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
assert_eq!(branches[0].commits.len(), 2);
|
|
assert_eq!(
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
"<<<<<<< ours\nfix conflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
);
|
|
}
|
|
}
|
|
|
|
mod no_conflicts_pushed {
|
|
use gitbutler_branch::BranchUpdateRequest;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn force_push_ok() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
project_id,
|
|
controller,
|
|
projects,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
projects
|
|
.update(&projects::UpdateRequest {
|
|
id: *project_id,
|
|
..Default::default()
|
|
})
|
|
.unwrap();
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
let branch_id = {
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file2.txt"), "no conflict").unwrap();
|
|
|
|
controller
|
|
.create_commit(project, branch_id, "no conflicts", None, false)
|
|
.unwrap();
|
|
controller
|
|
.push_virtual_branch(project, branch_id, false, None)
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file2.txt"), "still no conflict").unwrap();
|
|
|
|
branch_id
|
|
};
|
|
|
|
{
|
|
// fetch remote
|
|
controller.update_base_branch(project).unwrap();
|
|
|
|
// rebases branch, since the branch is pushed and force pushing is
|
|
// allowed
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert_eq!(branches[0].id, branch_id);
|
|
assert!(branches[0].active);
|
|
assert!(branches[0].requires_force);
|
|
assert!(branches[0].base_current);
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
assert!(!branches[0].commits[0].is_remote);
|
|
assert!(
|
|
branches[0].commits[0].copied_from_remote_id.is_some(),
|
|
"it's copied, which displays things differently in the \
|
|
UI which knows what remote commit it relates to"
|
|
);
|
|
assert!(!branches[0].commits[0].is_integrated);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn force_push_not_ok() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
let branch_id = {
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file2.txt"), "no conflict").unwrap();
|
|
|
|
controller
|
|
.create_commit(project, branch_id, "no conflicts", None, false)
|
|
.unwrap();
|
|
controller
|
|
.push_virtual_branch(project, branch_id, false, None)
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file2.txt"), "still no conflict").unwrap();
|
|
|
|
branch_id
|
|
};
|
|
|
|
controller
|
|
.update_virtual_branch(
|
|
project,
|
|
BranchUpdateRequest {
|
|
id: branch_id,
|
|
allow_rebasing: Some(false),
|
|
..Default::default()
|
|
},
|
|
)
|
|
.unwrap();
|
|
|
|
{
|
|
// fetch remote
|
|
controller.update_base_branch(project).unwrap();
|
|
|
|
// creates a merge commit, since the branch is pushed
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert_eq!(branches[0].id, branch_id);
|
|
assert!(branches[0].active);
|
|
assert!(!branches[0].requires_force);
|
|
assert!(branches[0].base_current);
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
assert_eq!(branches[0].commits.len(), 2);
|
|
assert!(!branches[0].commits[0].is_remote);
|
|
assert!(!branches[0].commits[0].is_integrated);
|
|
assert!(branches[0].commits[1].is_remote);
|
|
assert!(!branches[0].commits[1].is_integrated);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn no_conflicts() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
let branch_id = {
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file2.txt"), "no conflict").unwrap();
|
|
|
|
controller
|
|
.create_commit(project, branch_id, "no conflicts", None, false)
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file2.txt"), "still no conflict").unwrap();
|
|
|
|
branch_id
|
|
};
|
|
|
|
{
|
|
// fetch remote
|
|
controller.update_base_branch(project).unwrap();
|
|
|
|
// just rebases branch
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert_eq!(branches[0].id, branch_id);
|
|
assert!(branches[0].active);
|
|
assert!(branches[0].base_current);
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert_eq!(
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
"second"
|
|
);
|
|
assert_eq!(
|
|
std::fs::read_to_string(repository.path().join("file2.txt")).unwrap(),
|
|
"still no conflict"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn integrated_commit_plus_work() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
repository.commit_all("first");
|
|
repository.push();
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
let branch_id = {
|
|
// make a branch that conflicts with the remote branch, but doesn't know about it yet
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
|
|
controller
|
|
.create_commit(project, branch_id, "second", None, false)
|
|
.unwrap();
|
|
controller
|
|
.push_virtual_branch(project, branch_id, false, None)
|
|
.unwrap();
|
|
|
|
{
|
|
// merge branch upstream
|
|
let branch = controller
|
|
.list_virtual_branches(project)
|
|
.unwrap()
|
|
.0
|
|
.into_iter()
|
|
.find(|b| b.id == branch_id)
|
|
.unwrap();
|
|
repository.merge(&branch.upstream.as_ref().unwrap().name);
|
|
repository.fetch();
|
|
}
|
|
|
|
// more local work in the same branch
|
|
fs::write(repository.path().join("file2.txt"), "other").unwrap();
|
|
|
|
branch_id
|
|
};
|
|
|
|
{
|
|
// fetch remote
|
|
controller.update_base_branch(project).unwrap();
|
|
|
|
// should remove integrated commit, but leave non integrated work as is
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert_eq!(branches[0].id, branch_id);
|
|
assert!(branches[0].active);
|
|
assert!(branches[0].base_current);
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
assert_eq!(branches[0].commits.len(), 0);
|
|
|
|
assert_eq!(
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
"second"
|
|
);
|
|
assert_eq!(
|
|
std::fs::read_to_string(repository.path().join("file2.txt")).unwrap(),
|
|
"other"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn integrated_with_locked_conflicting_hunks() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(
|
|
repository.path().join("file.txt"),
|
|
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n",
|
|
)
|
|
.unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(
|
|
repository.path().join("file.txt"),
|
|
"1\n2\n3\n4\n5\n6\n17\n8\n9\n10\n11\n12\n",
|
|
)
|
|
.unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
// branch has no conflict
|
|
let branch_id = {
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(
|
|
repository.path().join("file.txt"),
|
|
"1\n2\n3\n4\n5\n6\n7\n8\n19\n10\n11\n12\n",
|
|
)
|
|
.unwrap();
|
|
|
|
controller
|
|
.create_commit(project, branch_id, "fourth", None, false)
|
|
.unwrap();
|
|
|
|
branch_id
|
|
};
|
|
|
|
// push the branch
|
|
controller
|
|
.push_virtual_branch(project, branch_id, false, None)
|
|
.unwrap();
|
|
|
|
// another locked conflicting hunk
|
|
fs::write(
|
|
repository.path().join("file.txt"),
|
|
"1\n2\n3\n4\n5\n6\n77\n8\n19\n10\n11\n12\n",
|
|
)
|
|
.unwrap();
|
|
|
|
{
|
|
// merge branch remotely
|
|
let branch = controller.list_virtual_branches(project).unwrap().0[0].clone();
|
|
repository.merge(&branch.upstream.as_ref().unwrap().name);
|
|
}
|
|
|
|
repository.fetch();
|
|
|
|
let unapplied_refname = {
|
|
let unapplied_refnames = controller.update_base_branch(project).unwrap();
|
|
assert_eq!(unapplied_refnames.len(), 1);
|
|
|
|
// removes integrated commit, leaves non commited work as is
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 0);
|
|
assert_eq!(
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
"1\n2\n3\n4\n5\n6\n17\n8\n19\n10\n11\n12\n"
|
|
);
|
|
|
|
unapplied_refnames[0].clone()
|
|
};
|
|
|
|
{
|
|
controller
|
|
.create_virtual_branch_from_branch(
|
|
project,
|
|
&Refname::from_str(unapplied_refname.as_str()).unwrap(),
|
|
None,
|
|
)
|
|
.unwrap();
|
|
|
|
let vb_state = VirtualBranchesHandle::new(project.gb_dir());
|
|
let ctx = CommandContext::open(project).unwrap();
|
|
update_gitbutler_integration(&vb_state, &ctx).unwrap();
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert!(branches[0].active);
|
|
assert!(branches[0].conflicted);
|
|
assert!(branches[0].base_current);
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
assert_eq!(branches[0].files[0].hunks.len(), 1);
|
|
assert_eq!(
|
|
branches[0].files[0].hunks[0].diff,
|
|
"@@ -4,7 +4,11 @@\n 4\n 5\n 6\n-7\n+<<<<<<< ours\n+77\n+=======\n+17\n+>>>>>>> theirs\n 8\n 19\n 10\n"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn integrated_with_locked_hunks() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
project_id,
|
|
controller,
|
|
projects,
|
|
..
|
|
} = &Test::default();
|
|
|
|
projects
|
|
.update(&projects::UpdateRequest {
|
|
id: *project_id,
|
|
..Default::default()
|
|
})
|
|
.unwrap();
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
let branch_id = {
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
|
|
controller
|
|
.create_commit(project, branch_id, "first", None, false)
|
|
.unwrap();
|
|
|
|
branch_id
|
|
};
|
|
|
|
controller
|
|
.push_virtual_branch(project, branch_id, false, None)
|
|
.unwrap();
|
|
|
|
// another non-locked hunk
|
|
fs::write(repository.path().join("file.txt"), "first\nsecond").unwrap();
|
|
|
|
{
|
|
// push and merge branch remotely
|
|
let branch = controller.list_virtual_branches(project).unwrap().0[0].clone();
|
|
repository.merge(&branch.upstream.as_ref().unwrap().name);
|
|
}
|
|
|
|
repository.fetch();
|
|
|
|
{
|
|
controller.update_base_branch(project).unwrap();
|
|
|
|
// removes integrated commit, leaves non commited work as is
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert_eq!(branches[0].id, branch_id);
|
|
assert!(branches[0].active);
|
|
assert!(branches[0].commits.is_empty());
|
|
assert!(branches[0].upstream.is_none());
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn integrated_with_non_locked_hunks() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
let branch_id = {
|
|
// make a branch that conflicts with the remote branch, but doesn't know about it yet
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
|
|
controller
|
|
.create_commit(project, branch_id, "first", None, false)
|
|
.unwrap();
|
|
|
|
branch_id
|
|
};
|
|
|
|
controller
|
|
.push_virtual_branch(project, branch_id, false, None)
|
|
.unwrap();
|
|
|
|
// another non-locked hunk
|
|
fs::write(repository.path().join("another_file.txt"), "first").unwrap();
|
|
|
|
{
|
|
// push and merge branch remotely
|
|
let branch = controller.list_virtual_branches(project).unwrap().0[0].clone();
|
|
repository.merge(&branch.upstream.as_ref().unwrap().name);
|
|
}
|
|
|
|
repository.fetch();
|
|
|
|
{
|
|
controller.update_base_branch(project).unwrap();
|
|
|
|
// removes integrated commit, leaves non commited work as is
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert_eq!(branches[0].id, branch_id);
|
|
assert!(branches[0].active);
|
|
assert!(branches[0].commits.is_empty());
|
|
assert!(branches[0].upstream.is_none());
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn all_integrated() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
{
|
|
// make a branch that conflicts with the remote branch, but doesn't know about it yet
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
|
|
controller
|
|
.create_commit(project, branch_id, "second", None, false)
|
|
.unwrap();
|
|
};
|
|
|
|
{
|
|
// fetch remote
|
|
controller.update_base_branch(project).unwrap();
|
|
|
|
// just removes integrated branch
|
|
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn integrate_work_while_being_behind() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
{
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
}
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
let branch_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
{
|
|
// open pr
|
|
fs::write(repository.path().join("file2.txt"), "new file").unwrap();
|
|
controller
|
|
.create_commit(project, branch_id, "second", None, false)
|
|
.unwrap();
|
|
controller
|
|
.push_virtual_branch(project, branch_id, false, None)
|
|
.unwrap();
|
|
}
|
|
|
|
{
|
|
// merge pr
|
|
let branch = controller.list_virtual_branches(project).unwrap().0[0].clone();
|
|
repository.merge(&branch.upstream.as_ref().unwrap().name);
|
|
repository.fetch();
|
|
}
|
|
|
|
{
|
|
// fetch remote
|
|
controller.update_base_branch(project).unwrap();
|
|
|
|
// just removes integrated branch
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 0);
|
|
}
|
|
}
|
|
|
|
// Ensure integrated branch is dropped and that a commit on another
|
|
// branch does not lead to the introduction of gost/phantom diffs.
|
|
#[test]
|
|
fn should_not_get_confused_by_commits_in_other_branches() {
|
|
let Test {
|
|
repository,
|
|
project,
|
|
controller,
|
|
..
|
|
} = &Test::default();
|
|
|
|
fs::write(repository.path().join("file-1.txt"), "one").unwrap();
|
|
let first_commit_oid = repository.commit_all("first");
|
|
fs::write(repository.path().join("file-2.txt"), "two").unwrap();
|
|
repository.commit_all("second");
|
|
repository.push();
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
|
|
controller
|
|
.set_base_branch(project, &"refs/remotes/origin/master".parse().unwrap())
|
|
.unwrap();
|
|
|
|
let branch_1_id = controller
|
|
.create_virtual_branch(project, &BranchCreateRequest::default())
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file-3.txt"), "three").unwrap();
|
|
controller
|
|
.create_commit(project, branch_1_id, "third", None, false)
|
|
.unwrap();
|
|
|
|
let branch_2_id = controller
|
|
.create_virtual_branch(
|
|
project,
|
|
&BranchCreateRequest {
|
|
selected_for_changes: Some(true),
|
|
..Default::default()
|
|
},
|
|
)
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file-4.txt"), "four").unwrap();
|
|
|
|
controller
|
|
.create_commit(project, branch_2_id, "fourth", None, false)
|
|
.unwrap();
|
|
|
|
controller
|
|
.push_virtual_branch(project, branch_2_id, false, None)
|
|
.unwrap();
|
|
|
|
let branch = controller.list_virtual_branches(project).unwrap().0[1].clone();
|
|
|
|
repository.merge(&branch.upstream.as_ref().unwrap().name);
|
|
repository.fetch();
|
|
|
|
// TODO(mg): Figure out why test fails without listing first.
|
|
controller.list_virtual_branches(project).unwrap();
|
|
controller.update_base_branch(project).unwrap();
|
|
|
|
// Verify we have only the first branch left, and that no files
|
|
// are present.
|
|
let (branches, _) = controller.list_virtual_branches(project).unwrap();
|
|
assert_eq!(branches.len(), 1);
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
}
|
|
}
|