2023-10-18 18:26:54 +03:00
|
|
|
//TODO:
|
|
|
|
#![allow(
|
|
|
|
clippy::redundant_closure_for_method_calls,
|
2023-12-05 17:58:43 +03:00
|
|
|
clippy::rest_pat_in_fully_bound_structs,
|
|
|
|
clippy::dbg_macro
|
2023-10-18 18:26:54 +03:00
|
|
|
)]
|
|
|
|
|
2023-12-05 17:58:43 +03:00
|
|
|
mod common;
|
|
|
|
|
2023-11-17 16:05:00 +03:00
|
|
|
use std::{fs, path, str::FromStr};
|
2023-10-12 15:25:01 +03:00
|
|
|
|
2023-10-18 17:39:14 +03:00
|
|
|
use gblib::{
|
2023-11-17 11:55:47 +03:00
|
|
|
error::Error,
|
2023-10-13 12:00:00 +03:00
|
|
|
git, keys,
|
|
|
|
projects::{self, ProjectId},
|
|
|
|
users,
|
2023-11-17 11:55:47 +03:00
|
|
|
virtual_branches::{branch, controller::ControllerError, errors, Controller},
|
2023-10-12 15:25:01 +03:00
|
|
|
};
|
|
|
|
|
2023-12-13 15:40:41 +03:00
|
|
|
use self::common::{paths, TestProject};
|
|
|
|
|
2023-10-12 15:25:01 +03:00
|
|
|
struct Test {
|
|
|
|
repository: TestProject,
|
2023-10-13 12:00:00 +03:00
|
|
|
project_id: ProjectId,
|
2023-12-11 15:01:17 +03:00
|
|
|
projects: projects::Controller,
|
2023-10-12 15:25:01 +03:00
|
|
|
controller: Controller,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Test {
|
|
|
|
fn default() -> Self {
|
|
|
|
let data_dir = paths::data_dir();
|
|
|
|
let keys = keys::Controller::from(&data_dir);
|
|
|
|
let projects = projects::Controller::from(&data_dir);
|
|
|
|
let users = users::Controller::from(&data_dir);
|
2023-12-06 19:24:48 +03:00
|
|
|
let helper = git::credentials::Helper::from(&data_dir);
|
2023-10-12 15:25:01 +03:00
|
|
|
|
|
|
|
let test_project = TestProject::default();
|
|
|
|
let project = projects
|
|
|
|
.add(test_project.path())
|
|
|
|
.expect("failed to add project");
|
|
|
|
|
|
|
|
Self {
|
|
|
|
repository: test_project,
|
|
|
|
project_id: project.id,
|
2023-12-06 19:24:48 +03:00
|
|
|
controller: Controller::new(&data_dir, &projects, &users, &keys, &helper),
|
2023-12-11 15:01:17 +03:00
|
|
|
projects,
|
2023-10-12 15:25:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-01 15:27:12 +03:00
|
|
|
mod create_commit {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_lock_updated_hunks() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
repository,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-11-01 15:27:12 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
// by default, hunks are not locked
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(branch.files.len(), 1);
|
|
|
|
assert_eq!(branch.files[0].path.display().to_string(), "file.txt");
|
|
|
|
assert_eq!(branch.files[0].hunks.len(), 1);
|
|
|
|
assert!(!branch.files[0].hunks[0].locked);
|
|
|
|
}
|
|
|
|
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "test", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
2023-11-27 15:06:03 +03:00
|
|
|
// change in the committed hunks leads to hunk locking
|
2023-11-01 15:27:12 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "updated content").unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(branch.files.len(), 1);
|
|
|
|
assert_eq!(branch.files[0].path.display().to_string(), "file.txt");
|
|
|
|
assert_eq!(branch.files[0].hunks.len(), 1);
|
|
|
|
assert!(branch.files[0].hunks[0].locked);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-18 16:17:09 +03:00
|
|
|
mod references {
|
2023-10-18 13:20:37 +03:00
|
|
|
use super::*;
|
|
|
|
|
2023-10-18 16:17:09 +03:00
|
|
|
mod create_virtual_branch {
|
2023-10-18 13:20:37 +03:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn simple() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-10-18 16:17:09 +03:00
|
|
|
repository,
|
2023-10-18 13:20:37 +03:00
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-18 13:20:37 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
2023-10-18 17:39:14 +03:00
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
2023-10-18 13:20:37 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
2023-10-18 16:17:09 +03:00
|
|
|
assert_eq!(branches[0].name, "Virtual branch");
|
|
|
|
|
|
|
|
let refnames = repository
|
|
|
|
.references()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|reference| reference.name().map(|name| name.to_string()))
|
|
|
|
.collect::<Vec<_>>();
|
2023-10-18 18:26:54 +03:00
|
|
|
assert!(refnames.contains(&"refs/gitbutler/virtual-branch".to_string()));
|
2023-10-18 13:20:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn duplicate_name() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-10-18 16:17:09 +03:00
|
|
|
repository,
|
2023-10-18 13:20:37 +03:00
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-18 13:20:37 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = controller
|
|
|
|
.create_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 17:39:14 +03:00
|
|
|
&gblib::virtual_branches::branch::BranchCreateRequest {
|
2023-10-18 13:20:37 +03:00
|
|
|
name: Some("name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch2_id = controller
|
|
|
|
.create_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 17:39:14 +03:00
|
|
|
&gblib::virtual_branches::branch::BranchCreateRequest {
|
2023-10-18 13:20:37 +03:00
|
|
|
name: Some("name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 2);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].name, "name");
|
|
|
|
assert_eq!(branches[1].id, branch2_id);
|
2023-10-18 16:17:09 +03:00
|
|
|
assert_eq!(branches[1].name, "name 1");
|
|
|
|
|
|
|
|
let refnames = repository
|
|
|
|
.references()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|reference| reference.name().map(|name| name.to_string()))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert!(refnames.contains(&"refs/gitbutler/name".to_string()));
|
|
|
|
assert!(refnames.contains(&"refs/gitbutler/name-1".to_string()));
|
2023-10-18 13:20:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-18 16:17:09 +03:00
|
|
|
mod update_virtual_branch {
|
2023-10-18 13:20:37 +03:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn simple() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-10-18 16:17:09 +03:00
|
|
|
repository,
|
2023-10-18 13:20:37 +03:00
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-18 13:20:37 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
2023-10-18 16:17:09 +03:00
|
|
|
.create_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 18:26:54 +03:00
|
|
|
&branch::BranchCreateRequest {
|
2023-10-18 16:17:09 +03:00
|
|
|
name: Some("name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
2023-10-18 13:20:37 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.update_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 18:26:54 +03:00
|
|
|
branch::BranchUpdateRequest {
|
2023-10-18 13:20:37 +03:00
|
|
|
id: branch_id,
|
|
|
|
name: Some("new name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert_eq!(branches[0].name, "new name");
|
2023-10-18 16:17:09 +03:00
|
|
|
|
|
|
|
let refnames = repository
|
|
|
|
.references()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|reference| reference.name().map(|name| name.to_string()))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert!(!refnames.contains(&"refs/gitbutler/name".to_string()));
|
|
|
|
assert!(refnames.contains(&"refs/gitbutler/new-name".to_string()));
|
2023-10-18 13:20:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn duplicate_name() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-10-18 16:17:09 +03:00
|
|
|
repository,
|
2023-10-18 13:20:37 +03:00
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-18 13:20:37 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = controller
|
|
|
|
.create_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 18:26:54 +03:00
|
|
|
&branch::BranchCreateRequest {
|
2023-10-18 13:20:37 +03:00
|
|
|
name: Some("name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch2_id = controller
|
|
|
|
.create_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 18:26:54 +03:00
|
|
|
&branch::BranchCreateRequest {
|
2023-10-18 13:20:37 +03:00
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.update_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 18:26:54 +03:00
|
|
|
branch::BranchUpdateRequest {
|
2023-10-18 13:20:37 +03:00
|
|
|
id: branch2_id,
|
|
|
|
name: Some("name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 2);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].name, "name");
|
|
|
|
assert_eq!(branches[1].id, branch2_id);
|
2023-10-18 16:17:09 +03:00
|
|
|
assert_eq!(branches[1].name, "name 1");
|
|
|
|
|
|
|
|
let refnames = repository
|
|
|
|
.references()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|reference| reference.name().map(|name| name.to_string()))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert!(refnames.contains(&"refs/gitbutler/name".to_string()));
|
|
|
|
assert!(refnames.contains(&"refs/gitbutler/name-1".to_string()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod push_virtual_branch {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn simple() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
repository,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-18 16:17:09 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = controller
|
|
|
|
.create_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 18:26:54 +03:00
|
|
|
&branch::BranchCreateRequest {
|
2023-10-18 16:17:09 +03:00
|
|
|
name: Some("name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "test", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch1_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].name, "name");
|
|
|
|
assert_eq!(
|
2023-10-19 10:12:20 +03:00
|
|
|
branches[0].upstream.as_ref().unwrap().name.to_string(),
|
|
|
|
"refs/remotes/origin/name"
|
2023-10-18 16:17:09 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
let refnames = repository
|
|
|
|
.references()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|reference| reference.name().map(|name| name.to_string()))
|
|
|
|
.collect::<Vec<_>>();
|
2023-10-19 10:12:20 +03:00
|
|
|
assert!(refnames.contains(&branches[0].upstream.clone().unwrap().name.to_string()));
|
2023-10-18 16:17:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn duplicate_names() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
repository,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-18 16:17:09 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = {
|
|
|
|
// create and push branch with some work
|
|
|
|
let branch1_id = controller
|
|
|
|
.create_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 18:26:54 +03:00
|
|
|
&branch::BranchCreateRequest {
|
2023-10-18 16:17:09 +03:00
|
|
|
name: Some("name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "test", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch1_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
branch1_id
|
|
|
|
};
|
|
|
|
|
|
|
|
// rename first branch
|
|
|
|
controller
|
|
|
|
.update_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 18:26:54 +03:00
|
|
|
branch::BranchUpdateRequest {
|
2023-10-18 16:17:09 +03:00
|
|
|
id: branch1_id,
|
|
|
|
name: Some("updated name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch2_id = {
|
|
|
|
// create another branch with first branch's old name and push it
|
|
|
|
let branch2_id = controller
|
|
|
|
.create_virtual_branch(
|
|
|
|
&project_id,
|
2023-10-18 18:26:54 +03:00
|
|
|
&branch::BranchCreateRequest {
|
2023-10-18 16:17:09 +03:00
|
|
|
name: Some("name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "updated content").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch2_id, "test", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch2_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
branch2_id
|
|
|
|
};
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 2);
|
|
|
|
// first branch is pushing to old ref remotely
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].name, "updated name");
|
|
|
|
assert_eq!(
|
2023-10-19 10:12:20 +03:00
|
|
|
branches[0].upstream.as_ref().unwrap().name,
|
|
|
|
"refs/remotes/origin/name".parse().unwrap()
|
2023-10-18 16:17:09 +03:00
|
|
|
);
|
|
|
|
// new branch is pushing to new ref remotely
|
|
|
|
assert_eq!(branches[1].id, branch2_id);
|
2023-10-18 13:20:37 +03:00
|
|
|
assert_eq!(branches[1].name, "name");
|
2023-10-18 16:17:09 +03:00
|
|
|
assert_eq!(
|
2023-10-19 10:12:20 +03:00
|
|
|
branches[1].upstream.as_ref().unwrap().name,
|
|
|
|
"refs/remotes/origin/name-1".parse().unwrap()
|
2023-10-18 16:17:09 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
let refnames = repository
|
|
|
|
.references()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|reference| reference.name().map(|name| name.to_string()))
|
|
|
|
.collect::<Vec<_>>();
|
2023-10-19 10:12:20 +03:00
|
|
|
assert!(refnames.contains(&branches[0].upstream.clone().unwrap().name.to_string()));
|
|
|
|
assert!(refnames.contains(&branches[1].upstream.clone().unwrap().name.to_string()));
|
2023-10-18 13:20:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-05 13:35:41 +03:00
|
|
|
mod delete_virtual_branch {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_unapply_diff() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
repository,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// write some
|
|
|
|
std::fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.delete_virtual_branch(&project_id, &branches[0].id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 0);
|
|
|
|
assert!(!repository.path().join("file.txt").exists());
|
|
|
|
|
|
|
|
let refnames = repository
|
|
|
|
.references()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|reference| reference.name().map(|name| name.to_string()))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert!(!refnames.contains(&"refs/gitbutler/name".to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_remove_reference() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
repository,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let id = controller
|
|
|
|
.create_virtual_branch(
|
|
|
|
&project_id,
|
|
|
|
&branch::BranchCreateRequest {
|
|
|
|
name: Some("name".to_string()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.delete_virtual_branch(&project_id, &id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 0);
|
|
|
|
|
|
|
|
let refnames = repository
|
|
|
|
.references()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|reference| reference.name().map(|name| name.to_string()))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert!(!refnames.contains(&"refs/gitbutler/name".to_string()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-12 15:25:01 +03:00
|
|
|
mod set_base_branch {
|
|
|
|
use super::*;
|
|
|
|
|
2023-11-20 18:17:22 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn success() {
|
2023-10-12 15:25:01 +03:00
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-12 15:25:01 +03:00
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
mod errors {
|
|
|
|
use super::*;
|
|
|
|
|
2023-11-20 18:17:22 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn missing() {
|
2023-10-12 15:25:01 +03:00
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
2023-10-12 16:45:29 +03:00
|
|
|
assert!(matches!(
|
2023-11-20 18:17:22 +03:00
|
|
|
controller
|
|
|
|
.set_base_branch(
|
|
|
|
&project_id,
|
2023-11-22 12:33:31 +03:00
|
|
|
&git::RemoteRefname::from_str("refs/remotes/origin/missing").unwrap(),
|
2023-11-20 18:17:22 +03:00
|
|
|
)
|
|
|
|
.await,
|
2023-11-17 11:55:47 +03:00
|
|
|
Err(Error::UserError { .. })
|
2023-10-12 16:45:29 +03:00
|
|
|
));
|
2023-10-12 15:25:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-21 16:29:58 +03:00
|
|
|
mod unapply {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn unapply_with_data() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
repository,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-21 16:29:58 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
std::fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branches[0].id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(!repository.path().join("file.txt").exists());
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert!(!branches[0].active);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn delete_if_empty() {
|
|
|
|
let Test {
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-21 16:29:58 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branches[0].id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
mod apply_virtual_branch {
|
2023-10-12 15:25:01 +03:00
|
|
|
use super::*;
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn deltect_conflict() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-11-27 15:06:03 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = {
|
|
|
|
let branch1_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "branch one").unwrap();
|
|
|
|
|
|
|
|
branch1_id
|
|
|
|
};
|
|
|
|
|
|
|
|
// unapply first vbranch
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branch1_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
// create another vbranch that conflicts with the first one
|
|
|
|
controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "branch two").unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// it should not be possible to apply the first branch
|
|
|
|
assert!(!controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch1_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
|
|
|
|
|
|
|
assert!(matches!(
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch1_id)
|
|
|
|
.await,
|
|
|
|
Err(ControllerError::Action(
|
|
|
|
errors::ApplyBranchError::BranchConflicts(_)
|
|
|
|
))
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn rebase_commit() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-11-27 15:06:03 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file.txt"), "one").unwrap();
|
|
|
|
fs::write(repository.path().join("another_file.txt"), "").unwrap();
|
|
|
|
let first_commit_oid = repository.commit_all("first");
|
|
|
|
fs::write(repository.path().join("file.txt"), "two").unwrap();
|
|
|
|
repository.commit_all("second");
|
|
|
|
repository.push();
|
|
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
|
|
}
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = {
|
|
|
|
// create a branch with some commited work
|
|
|
|
let branch1_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("another_file.txt"), "virtual").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "virtual commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
|
|
|
|
branch1_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// unapply first vbranch
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branch1_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("another_file.txt")).unwrap(),
|
|
|
|
""
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"one"
|
|
|
|
);
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert!(!branches[0].active);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// fetch remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// branch is stil unapplied
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert!(!branches[0].active);
|
|
|
|
assert!(!branches[0].conflicted);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("another_file.txt")).unwrap(),
|
|
|
|
""
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"two"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// apply first vbranch again
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch1_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// it should be rebased
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(!branches[0].conflicted);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("another_file.txt")).unwrap(),
|
|
|
|
"virtual"
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"two"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn rebase_work() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-11-27 15:06:03 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
|
|
{
|
|
|
|
let first_commit_oid = repository.commit_all("first");
|
|
|
|
fs::write(repository.path().join("file.txt"), "").unwrap();
|
|
|
|
repository.commit_all("second");
|
|
|
|
repository.push();
|
|
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
|
|
}
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = {
|
|
|
|
// make a branch with some work
|
|
|
|
let branch1_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("another_file.txt"), "").unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
|
|
|
|
|
|
|
branch1_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// unapply first vbranch
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branch1_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
|
|
|
assert!(!branches[0].active);
|
|
|
|
|
|
|
|
assert!(!repository.path().join("another_file.txt").exists());
|
|
|
|
assert!(!repository.path().join("file.txt").exists());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// fetch remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// first branch is stil unapplied
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
|
|
|
assert!(!branches[0].active);
|
|
|
|
assert!(!branches[0].conflicted);
|
|
|
|
|
|
|
|
assert!(!repository.path().join("another_file.txt").exists());
|
|
|
|
assert!(repository.path().join("file.txt").exists());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// apply first vbranch again
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch1_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// workdir should be rebased, and work should be restored
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(!branches[0].conflicted);
|
|
|
|
|
|
|
|
assert!(repository.path().join("another_file.txt").exists());
|
|
|
|
assert!(repository.path().join("file.txt").exists());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn resolve_conflict_flow() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-11-27 15:57:50 +03:00
|
|
|
} = 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));
|
|
|
|
}
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
let branch1_id = {
|
|
|
|
// make a branch that conflicts with the remote branch, but doesn't know about it yet
|
|
|
|
let branch1_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
|
|
|
|
branch1_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// fetch remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// there is a conflict now, so the branch should be inactive
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert!(!branches[0].active);
|
|
|
|
}
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
{
|
|
|
|
// when we apply conflicted branch, it has conflict
|
2023-11-27 15:06:03 +03:00
|
|
|
controller
|
2023-11-27 15:57:50 +03:00
|
|
|
.apply_virtual_branch(&project_id, &branch1_id)
|
2023-11-27 15:06:03 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
|
|
|
|
// and the conflict markers are in the file
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nconflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// can't commit conflicts
|
|
|
|
assert!(matches!(
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit conflicts", None)
|
|
|
|
.await,
|
|
|
|
Err(ControllerError::Action(errors::CommitError::Conflicted(_)))
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// fixing the conflict removes conflicted mark
|
|
|
|
fs::write(repository.path().join("file.txt"), "resolved").unwrap();
|
|
|
|
let commit_oid = controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "resolution", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let commit = repository.find_commit(commit_oid).unwrap();
|
|
|
|
assert_eq!(commit.parent_count(), 2);
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(!branches[0].conflicted);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod update_base_branch {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
mod unapplied_branch {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn conflicts_with_uncommitted_work() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
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_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-27 15:06:03 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
let branch_id = {
|
2023-11-27 17:32:22 +03:00
|
|
|
// make a branch that is unapplied and contains not commited conflict
|
2023-11-27 15:57:50 +03:00
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
branch_id
|
|
|
|
};
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
{
|
|
|
|
// when fetching remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// branch should not be changed.
|
2023-11-27 17:32:22 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.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!(!controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nconflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:06:03 +03:00
|
|
|
}
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn commited_conflict_not_pushed() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
controller
|
2023-11-27 15:57:50 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-27 15:06:03 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
let branch_id = {
|
|
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
|
|
// that conflict
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "conflicting commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
|
|
|
|
controller
|
2023-11-27 15:57:50 +03:00
|
|
|
.unapply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
branch_id
|
|
|
|
};
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
{
|
|
|
|
// when fetching remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// should not change the branch.
|
2023-11-27 15:06:03 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.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(), 0);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert!(!controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nconflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:57:50 +03:00
|
|
|
}
|
2023-10-12 15:25:01 +03:00
|
|
|
|
|
|
|
#[tokio::test]
|
2023-11-27 15:57:50 +03:00
|
|
|
async fn commited_conflict_pushed() {
|
2023-11-27 15:06:03 +03:00
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
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_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = {
|
2023-11-27 15:57:50 +03:00
|
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
|
|
// that conflict
|
2023-11-27 15:06:03 +03:00
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
2023-11-27 15:57:50 +03:00
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "conflicting commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 17:32:22 +03:00
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:57:50 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
2023-11-27 15:57:50 +03:00
|
|
|
// when fetching remote
|
2023-11-27 15:06:03 +03:00
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// should not change the branch.
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(!branches[0].active);
|
|
|
|
assert!(!branches[0].base_current);
|
2023-11-27 15:57:50 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-27 15:06:03 +03:00
|
|
|
assert!(!controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nconflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:06:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
2023-11-27 15:57:50 +03:00
|
|
|
async fn commited_conflict_not_pushed_fixed_with_more_work() {
|
2023-11-27 15:06:03 +03:00
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
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_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = {
|
2023-11-27 15:57:50 +03:00
|
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
|
|
// that conflict
|
2023-11-27 15:06:03 +03:00
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "conflicting commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "fix conflict").unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
2023-11-27 15:57:50 +03:00
|
|
|
// when fetching remote
|
2023-11-27 15:06:03 +03:00
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// should rebase upstream, and leave uncommited file as is
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(!branches[0].active);
|
2023-11-27 17:32:14 +03:00
|
|
|
assert!(!branches[0].base_current); // TODO: should be true
|
2023-11-27 15:57:50 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-27 17:32:14 +03:00
|
|
|
assert!(!controller
|
2023-11-27 15:06:03 +03:00
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
2023-11-27 17:32:14 +03:00
|
|
|
.unwrap()); // TODO: should be true
|
2023-11-27 15:06:03 +03:00
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nfix conflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:06:03 +03:00
|
|
|
}
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn commited_conflict_pushed_fixed_with_more_work() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
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_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = {
|
|
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
|
|
// that conflict
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "conflicting commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "fix conflict").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// when fetching remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// should not touch the branch
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.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].commits.len(), 1);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert!(!controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nfix conflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:57:50 +03:00
|
|
|
}
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn no_conflicts() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
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_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.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_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file2.txt"), "no conflict").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "non conflicting commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file2.txt"), "still no conflicts").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
2023-11-27 15:57:50 +03:00
|
|
|
// fetching remote
|
2023-11-27 15:06:03 +03:00
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// should update branch base
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.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!(controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(!branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"second"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:06:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn integrated_commit_plus_work() {
|
2023-10-12 15:25:01 +03:00
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-11-27 15:06:03 +03:00
|
|
|
..
|
2023-10-12 15:25:01 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
|
|
repository.commit_all("first");
|
|
|
|
repository.push();
|
|
|
|
}
|
|
|
|
|
2023-10-12 15:25:01 +03:00
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-12 15:25:01 +03:00
|
|
|
.unwrap();
|
|
|
|
|
2023-11-28 15:59:17 +03:00
|
|
|
let branch_id = {
|
2023-11-27 15:06:03 +03:00
|
|
|
// make a branch that conflicts with the remote branch, but doesn't know about it yet
|
|
|
|
let branch_id = controller
|
2023-10-18 18:26:54 +03:00
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
2023-10-16 15:44:09 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "second", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-28 15:59:17 +03:00
|
|
|
|
|
|
|
// more local work in the same branch
|
|
|
|
fs::write(repository.path().join("file2.txt"), "other").unwrap();
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
// merge branch upstream
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
2023-11-28 15:59:17 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
repository.merge(&branch.upstream.as_ref().unwrap().name);
|
|
|
|
repository.fetch();
|
|
|
|
}
|
|
|
|
|
2023-10-16 15:44:09 +03:00
|
|
|
controller
|
2023-11-27 15:06:03 +03:00
|
|
|
.unapply_virtual_branch(&project_id, &branch_id)
|
2023-10-16 15:44:09 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
branch_id
|
|
|
|
};
|
2023-10-16 15:44:09 +03:00
|
|
|
|
|
|
|
{
|
2023-11-27 15:06:03 +03:00
|
|
|
// fetch remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// should remove integrated commit, but leave work
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
2023-11-28 15:59:17 +03:00
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(!branches[0].active);
|
2023-11-29 15:11:47 +03:00
|
|
|
dbg!(&branches[0]);
|
2023-11-28 15:59:17 +03:00
|
|
|
assert!(branches[0].base_current);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
2023-11-29 15:11:47 +03:00
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
2023-11-28 15:59:17 +03:00
|
|
|
assert!(controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
2023-10-16 15:44:09 +03:00
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(!branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"second"
|
|
|
|
);
|
2023-11-29 15:11:47 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file2.txt")).unwrap(),
|
|
|
|
"other"
|
|
|
|
);
|
2023-11-29 12:14:25 +03:00
|
|
|
}
|
2023-10-16 15:44:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
2023-11-27 15:06:03 +03:00
|
|
|
async fn all_integrated() {
|
2023-10-16 15:44:09 +03:00
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-11-27 15:06:03 +03:00
|
|
|
..
|
2023-10-16 15:44:09 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
|
|
{
|
2023-11-27 15:06:03 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
let first_commit_oid = repository.commit_all("first");
|
2023-11-27 15:06:03 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
repository.commit_all("second");
|
|
|
|
repository.push();
|
2023-10-31 10:45:53 +03:00
|
|
|
repository.reset_hard(Some(first_commit_oid));
|
2023-10-16 15:44:09 +03:00
|
|
|
}
|
|
|
|
|
2023-10-12 15:25:01 +03:00
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-12 15:25:01 +03:00
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
{
|
|
|
|
// make a branch that conflicts with the remote branch, but doesn't know about it yet
|
|
|
|
let branch_id = controller
|
2023-10-18 18:26:54 +03:00
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
2023-10-16 15:44:09 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
|
|
|
|
controller
|
2023-11-27 15:06:03 +03:00
|
|
|
.create_commit(&project_id, &branch_id, "second", None)
|
2023-10-16 15:44:09 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
2023-11-27 15:06:03 +03:00
|
|
|
.unapply_virtual_branch(&project_id, &branch_id)
|
2023-10-16 15:44:09 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
};
|
2023-10-16 15:44:09 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// fetch remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// should remove identical branch
|
|
|
|
|
2023-10-16 15:44:09 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
assert_eq!(branches.len(), 0);
|
2023-10-16 15:44:09 +03:00
|
|
|
}
|
|
|
|
}
|
2023-11-27 15:06:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
mod applied_branch {
|
|
|
|
use super::*;
|
2023-10-16 15:44:09 +03:00
|
|
|
|
|
|
|
#[tokio::test]
|
2023-11-27 15:06:03 +03:00
|
|
|
async fn conflicts_with_uncommitted_work() {
|
2023-10-16 15:44:09 +03:00
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-11-27 15:06:03 +03:00
|
|
|
..
|
2023-10-16 15:44:09 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
|
|
|
{
|
2023-11-27 15:06:03 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
let first_commit_oid = repository.commit_all("first");
|
2023-11-27 15:06:03 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
repository.commit_all("second");
|
|
|
|
repository.push();
|
2023-10-31 10:45:53 +03:00
|
|
|
repository.reset_hard(Some(first_commit_oid));
|
2023-10-16 15:44:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-16 15:44:09 +03:00
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
let branch_id = {
|
|
|
|
// make a branch that conflicts with the remote branch, but doesn't know about it yet
|
|
|
|
let branch_id = controller
|
2023-10-18 18:26:54 +03:00
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
2023-10-16 15:44:09 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
branch_id
|
2023-10-16 15:44:09 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
{
|
2023-11-27 15:06:03 +03:00
|
|
|
// fetch remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// should stash conflicing branch
|
|
|
|
|
2023-10-16 15:44:09 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
2023-11-27 15:06:03 +03:00
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(!branches[0].active);
|
|
|
|
assert!(!branches[0].base_current);
|
2023-10-16 15:44:09 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
2023-11-27 15:06:03 +03:00
|
|
|
assert!(!controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
2023-10-16 15:44:09 +03:00
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nconflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:06:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
2023-11-27 15:57:50 +03:00
|
|
|
async fn commited_conflict_not_pushed() {
|
2023-11-27 15:06:03 +03:00
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
2023-10-16 15:44:09 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
2023-10-16 15:44:09 +03:00
|
|
|
{
|
2023-11-27 15:06:03 +03:00
|
|
|
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));
|
|
|
|
}
|
2023-10-16 15:44:09 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
let branch_id = {
|
2023-11-27 15:57:50 +03:00
|
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
|
|
// that conflict
|
2023-11-27 15:06:03 +03:00
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
controller
|
2023-11-27 15:06:03 +03:00
|
|
|
.create_commit(&project_id, &branch_id, "conflicting commit", None)
|
2023-10-16 15:44:09 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// when fetching remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// should stash the branch.
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.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(), 0);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert!(!controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nconflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:57:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn commited_conflict_pushed() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
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");
|
2023-11-27 15:06:03 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
2023-11-27 15:57:50 +03:00
|
|
|
repository.commit_all("second");
|
|
|
|
repository.push();
|
|
|
|
repository.reset_hard(Some(first_commit_oid));
|
|
|
|
}
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = {
|
|
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
|
|
// that conflict
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "conflicting commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 17:32:22 +03:00
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
2023-11-27 15:57:50 +03:00
|
|
|
// when fetching remote
|
2023-11-27 15:06:03 +03:00
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// should stash the branch.
|
|
|
|
|
2023-10-16 15:44:09 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
2023-11-27 15:06:03 +03:00
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(!branches[0].active);
|
|
|
|
assert!(!branches[0].base_current);
|
2023-11-27 15:57:50 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
2023-11-27 15:06:03 +03:00
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert!(!controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
2023-10-16 15:44:09 +03:00
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nconflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
2023-10-12 15:25:01 +03:00
|
|
|
}
|
2023-10-12 16:07:43 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn commited_conflict_not_pushed_fixed_with_more_work() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
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_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = {
|
|
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
|
|
// that conflict
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "conflicting commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "fix conflict").unwrap();
|
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// when fetching remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// should rebase upstream, and leave uncommited file as is
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(!branches[0].active);
|
2023-11-27 17:32:14 +03:00
|
|
|
assert!(!branches[0].base_current); // TODO: should be true
|
2023-11-27 15:57:50 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-27 17:32:14 +03:00
|
|
|
assert!(!controller
|
2023-11-27 15:57:50 +03:00
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
2023-11-27 17:32:14 +03:00
|
|
|
.unwrap()); // TODO: should be true
|
2023-11-27 15:57:50 +03:00
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nfix conflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:57:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn commited_conflict_pushed_fixed_with_more_work() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
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_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = {
|
|
|
|
// make a branch with a commit that conflicts with upstream, and work that fixes
|
|
|
|
// that conflict
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "conflicting commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "fix conflict").unwrap();
|
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// when fetching remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// should merge upstream, and leave uncommited file as is.
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(!branches[0].active);
|
2023-11-27 17:32:14 +03:00
|
|
|
assert!(!branches[0].base_current); // TODO: should be true
|
|
|
|
assert_eq!(branches[0].commits.len(), 1); // TODO: should be 2
|
2023-11-27 15:57:50 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
2023-11-27 17:32:14 +03:00
|
|
|
assert!(!controller
|
2023-11-27 15:57:50 +03:00
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
2023-11-27 17:32:14 +03:00
|
|
|
.unwrap()); // TODO: should be true
|
2023-11-27 15:57:50 +03:00
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-29 12:14:25 +03:00
|
|
|
assert_eq!(
|
|
|
|
std::fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nfix conflict\n=======\nsecond\n>>>>>>> theirs\n"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:57:50 +03:00
|
|
|
}
|
|
|
|
|
2023-12-11 15:01:17 +03:00
|
|
|
mod no_conflicts_pushed {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn force_push_ok() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
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,
|
|
|
|
ok_with_force_push: Some(true),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = {
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file2.txt"), "no conflict").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "no conflicts", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file2.txt"), "still no conflict").unwrap();
|
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// fetch remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// rebases branch, since the branch is pushed and force pushing is
|
|
|
|
// allowed
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.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);
|
2023-12-11 16:03:34 +03:00
|
|
|
assert!(!branches[0].commits[0].is_remote);
|
2023-12-11 15:01:17 +03:00
|
|
|
assert!(!branches[0].commits[0].is_integrated);
|
|
|
|
assert!(controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn force_push_not_ok() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-12 20:32:18 +03:00
|
|
|
projects,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
|
|
|
} = 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_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = {
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file2.txt"), "no conflict").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "no conflicts", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file2.txt"), "still no conflict").unwrap();
|
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
2023-12-12 20:32:18 +03:00
|
|
|
projects
|
|
|
|
.update(&projects::UpdateRequest {
|
|
|
|
id: project_id,
|
|
|
|
ok_with_force_push: Some(false),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-12-11 15:01:17 +03:00
|
|
|
{
|
|
|
|
// fetch remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// creates a merge commit, since the branch is pushed
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
2023-12-12 20:32:18 +03:00
|
|
|
assert!(!branches[0].requires_force);
|
2023-12-11 15:01:17 +03:00
|
|
|
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);
|
|
|
|
assert!(controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-12 16:07:43 +03:00
|
|
|
#[tokio::test]
|
2023-11-27 15:06:03 +03:00
|
|
|
async fn no_conflicts() {
|
2023-10-12 16:07:43 +03:00
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-11-27 15:06:03 +03:00
|
|
|
..
|
2023-10-12 16:07:43 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
2023-10-16 15:44:09 +03:00
|
|
|
{
|
|
|
|
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();
|
2023-10-31 10:45:53 +03:00
|
|
|
repository.reset_hard(Some(first_commit_oid));
|
2023-10-16 15:44:09 +03:00
|
|
|
}
|
2023-10-12 16:07:43 +03:00
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-12 16:07:43 +03:00
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
let branch_id = {
|
|
|
|
let branch_id = controller
|
2023-10-18 18:26:54 +03:00
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
2023-10-16 15:44:09 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-10-12 16:07:43 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
fs::write(repository.path().join("file2.txt"), "no conflict").unwrap();
|
2023-10-16 15:44:09 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "no conflicts", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file2.txt"), "still no conflict").unwrap();
|
|
|
|
|
|
|
|
branch_id
|
2023-10-16 15:44:09 +03:00
|
|
|
};
|
2023-10-12 16:07:43 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// fetch remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// just rebases branch
|
|
|
|
|
2023-10-12 16:07:43 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
2023-11-27 15:06:03 +03:00
|
|
|
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!(controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
2023-10-12 16:07:43 +03:00
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(!branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-11-29 12:14:25 +03:00
|
|
|
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"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:06:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn integrated_commit_plus_work() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
2023-10-12 16:07:43 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
2023-10-12 16:07:43 +03:00
|
|
|
{
|
2023-11-27 15:06:03 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
|
|
repository.commit_all("first");
|
|
|
|
repository.push();
|
|
|
|
}
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.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_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
|
|
|
2023-10-12 16:07:43 +03:00
|
|
|
controller
|
2023-11-27 15:06:03 +03:00
|
|
|
.create_commit(&project_id, &branch_id, "second", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
2023-10-12 16:07:43 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
{
|
|
|
|
// merge branch upstream
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.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_id).await.unwrap();
|
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// should remove integrated commit, but leave non integrated work as is
|
|
|
|
|
2023-10-12 16:07:43 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
2023-11-27 15:06:03 +03:00
|
|
|
assert_eq!(branches[0].id, branch_id);
|
2023-10-12 16:07:43 +03:00
|
|
|
assert!(branches[0].active);
|
2023-11-27 15:06:03 +03:00
|
|
|
assert!(branches[0].base_current);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
2023-11-29 15:11:47 +03:00
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
2023-11-27 15:06:03 +03:00
|
|
|
assert!(controller
|
|
|
|
.can_apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap());
|
2023-10-12 16:07:43 +03:00
|
|
|
}
|
2023-11-29 12:14:25 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// applying the branch should produce conflict markers
|
|
|
|
controller
|
|
|
|
.apply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(!branches[0].conflicted);
|
|
|
|
assert!(branches[0].base_current);
|
2023-11-29 17:28:21 +03:00
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
2023-11-29 12:14:25 +03:00
|
|
|
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"
|
|
|
|
);
|
|
|
|
}
|
2023-11-27 15:06:03 +03:00
|
|
|
}
|
|
|
|
|
2023-12-12 15:32:48 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn integrated_with_locked_hunks() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.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_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "first", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.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_id).await.unwrap()[0].clone();
|
|
|
|
repository.merge(&branch.upstream.as_ref().unwrap().name);
|
|
|
|
}
|
|
|
|
|
|
|
|
repository.fetch();
|
|
|
|
|
|
|
|
{
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// removes integrated commit, leaves non commited work as is
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.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].files.is_empty());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn integrated_with_non_locked_hunks() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.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_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "first").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "first", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
branch_id
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.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_id).await.unwrap()[0].clone();
|
|
|
|
repository.merge(&branch.upstream.as_ref().unwrap().name);
|
|
|
|
}
|
|
|
|
|
|
|
|
repository.fetch();
|
|
|
|
|
|
|
|
{
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
|
|
|
|
|
|
|
// removes integrated commit, leaves non commited work as is
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.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].files.is_empty());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn all_integrated() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
2023-10-12 16:07:43 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
// make sure we have an undiscovered commit in the remote branch
|
2023-10-12 16:07:43 +03:00
|
|
|
{
|
2023-11-27 15:06:03 +03:00
|
|
|
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));
|
2023-10-12 16:07:43 +03:00
|
|
|
}
|
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-10-12 16:07:43 +03:00
|
|
|
{
|
2023-11-27 15:06:03 +03:00
|
|
|
// make a branch that conflicts with the remote branch, but doesn't know about it yet
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "second").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "second", None)
|
2023-10-12 16:07:43 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
};
|
2023-10-12 16:07:43 +03:00
|
|
|
|
2023-11-27 15:06:03 +03:00
|
|
|
{
|
|
|
|
// fetch remote
|
|
|
|
controller.update_base_branch(&project_id).await.unwrap();
|
2023-10-12 16:14:02 +03:00
|
|
|
|
2023-11-27 15:57:50 +03:00
|
|
|
// just removes integrated branch
|
|
|
|
|
2023-10-12 16:07:43 +03:00
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
2023-11-27 15:06:03 +03:00
|
|
|
assert_eq!(branches.len(), 0);
|
2023-10-12 16:07:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-10-12 15:25:01 +03:00
|
|
|
}
|
2023-10-18 14:23:55 +03:00
|
|
|
|
2023-10-23 16:51:31 +03:00
|
|
|
mod reset_virtual_branch {
|
2023-11-17 11:55:47 +03:00
|
|
|
use gblib::virtual_branches::{controller::ControllerError, errors::ResetBranchError};
|
|
|
|
|
2023-10-18 14:23:55 +03:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn to_head() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-18 14:23:55 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-18 14:23:55 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = controller
|
2023-10-18 18:26:54 +03:00
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
2023-10-18 14:23:55 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let oid = {
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
|
|
|
|
// commit changes
|
|
|
|
let oid = controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits[0].id, oid);
|
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"content"
|
|
|
|
);
|
|
|
|
|
|
|
|
oid
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// reset changes to head
|
|
|
|
controller
|
|
|
|
.reset_virtual_branch(&project_id, &branch1_id, oid)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits[0].id, oid);
|
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"content"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn to_target() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-18 14:23:55 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
let base_branch = controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-18 14:23:55 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = controller
|
2023-10-18 18:26:54 +03:00
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
2023-10-18 14:23:55 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
|
|
|
|
// commit changes
|
|
|
|
let oid = controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits[0].id, oid);
|
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"content"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// reset changes to head
|
|
|
|
controller
|
|
|
|
.reset_virtual_branch(&project_id, &branch1_id, base_branch.base_sha)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].commits.len(), 0);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"content"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn to_commit() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-18 14:23:55 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-18 14:23:55 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = controller
|
2023-10-18 18:26:54 +03:00
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
2023-10-18 14:23:55 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let first_commit_oid = {
|
|
|
|
// commit some changes
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
|
|
|
|
let oid = controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits[0].id, oid);
|
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"content"
|
|
|
|
);
|
|
|
|
|
|
|
|
oid
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// commit some more
|
|
|
|
fs::write(repository.path().join("file.txt"), "more content").unwrap();
|
|
|
|
|
|
|
|
let second_commit_oid = controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].commits.len(), 2);
|
|
|
|
assert_eq!(branches[0].commits[0].id, second_commit_oid);
|
|
|
|
assert_eq!(branches[0].commits[1].id, first_commit_oid);
|
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"more content"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// reset changes to the first commit
|
|
|
|
controller
|
|
|
|
.reset_virtual_branch(&project_id, &branch1_id, first_commit_oid)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits[0].id, first_commit_oid);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"more content"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn to_non_existing() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-18 14:23:55 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-18 14:23:55 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = controller
|
2023-10-18 18:26:54 +03:00
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
2023-10-18 14:23:55 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
|
|
|
|
// commit changes
|
|
|
|
let oid = controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits[0].id, oid);
|
|
|
|
assert_eq!(branches[0].files.len(), 0);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"content"
|
|
|
|
);
|
|
|
|
|
|
|
|
oid
|
|
|
|
};
|
|
|
|
|
|
|
|
assert!(matches!(
|
|
|
|
controller
|
|
|
|
.reset_virtual_branch(
|
|
|
|
&project_id,
|
|
|
|
&branch1_id,
|
|
|
|
"fe14df8c66b73c6276f7bb26102ad91da680afcb".parse().unwrap()
|
|
|
|
)
|
|
|
|
.await,
|
2023-11-17 11:55:47 +03:00
|
|
|
Err(ControllerError::Action(
|
|
|
|
ResetBranchError::CommitNotFoundInBranch(_)
|
|
|
|
))
|
2023-10-18 14:23:55 +03:00
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
2023-10-19 10:59:57 +03:00
|
|
|
|
|
|
|
mod upstream {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn detect_upstream_commits() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-19 10:59:57 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-19 10:59:57 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let oid1 = {
|
|
|
|
// create first commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let oid2 = {
|
|
|
|
// create second commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content2").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
// push
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch1_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let oid3 = {
|
|
|
|
// create third commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content3").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// should correctly detect pushed commits
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].commits.len(), 3);
|
|
|
|
assert_eq!(branches[0].commits[0].id, oid3);
|
|
|
|
assert!(!branches[0].commits[0].is_remote);
|
|
|
|
assert_eq!(branches[0].commits[1].id, oid2);
|
|
|
|
assert!(branches[0].commits[1].is_remote);
|
|
|
|
assert_eq!(branches[0].commits[2].id, oid1);
|
|
|
|
assert!(branches[0].commits[2].is_remote);
|
|
|
|
}
|
|
|
|
}
|
2023-10-24 14:49:06 +03:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn detect_integrated_commits() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-24 14:49:06 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-24 14:49:06 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch1_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let oid1 = {
|
|
|
|
// create first commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let oid2 = {
|
|
|
|
// create second commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content2").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
// push
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch1_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
// merge branch upstream
|
2023-10-24 14:57:43 +03:00
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch1_id)
|
|
|
|
.unwrap();
|
2023-10-24 14:49:06 +03:00
|
|
|
repository.merge(&branch.upstream.as_ref().unwrap().name);
|
2023-10-24 15:10:06 +03:00
|
|
|
repository.fetch();
|
2023-10-24 14:49:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
let oid3 = {
|
|
|
|
// create third commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content3").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch1_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// should correctly detect pushed commits
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch1_id);
|
|
|
|
assert_eq!(branches[0].commits.len(), 3);
|
|
|
|
assert_eq!(branches[0].commits[0].id, oid3);
|
|
|
|
assert!(!branches[0].commits[0].is_integrated);
|
|
|
|
assert_eq!(branches[0].commits[1].id, oid2);
|
|
|
|
assert!(branches[0].commits[1].is_integrated);
|
|
|
|
assert_eq!(branches[0].commits[2].id, oid1);
|
|
|
|
assert!(branches[0].commits[2].is_integrated);
|
|
|
|
}
|
|
|
|
}
|
2023-10-19 10:59:57 +03:00
|
|
|
}
|
2023-10-23 16:51:31 +03:00
|
|
|
|
|
|
|
mod cherry_pick {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
mod cleanly {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn applied() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-23 16:51:31 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-23 16:51:31 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-10-31 10:45:53 +03:00
|
|
|
let commit_one = {
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let commit_two = {
|
|
|
|
fs::write(repository.path().join("file.txt"), "content two").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.reset_virtual_branch(&project_id, &branch_id, commit_one)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
repository.reset_hard(None);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"content"
|
|
|
|
);
|
|
|
|
|
2023-10-23 16:51:31 +03:00
|
|
|
let cherry_picked_commit_oid = controller
|
2023-10-31 10:45:53 +03:00
|
|
|
.cherry_pick(&project_id, &branch_id, commit_two)
|
2023-10-23 16:51:31 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert!(cherry_picked_commit_oid.is_some());
|
|
|
|
assert!(repository.path().join("file.txt").exists());
|
2023-10-31 10:45:53 +03:00
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"content two"
|
|
|
|
);
|
2023-10-23 16:51:31 +03:00
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
2023-10-31 10:45:53 +03:00
|
|
|
assert_eq!(branches[0].commits.len(), 2);
|
2023-10-23 16:51:31 +03:00
|
|
|
assert_eq!(branches[0].commits[0].id, cherry_picked_commit_oid.unwrap());
|
2023-10-31 10:45:53 +03:00
|
|
|
assert_eq!(branches[0].commits[1].id, commit_one);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn to_different_branch() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-31 10:45:53 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-31 10:45:53 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let commit_one = {
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let commit_two = {
|
|
|
|
fs::write(repository.path().join("file_two.txt"), "content two").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.reset_virtual_branch(&project_id, &branch_id, commit_one)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
repository.reset_hard(None);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"content"
|
|
|
|
);
|
|
|
|
assert!(!repository.path().join("file_two.txt").exists());
|
|
|
|
|
|
|
|
let branch_two_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let cherry_picked_commit_oid = controller
|
|
|
|
.cherry_pick(&project_id, &branch_two_id, commit_two)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert!(cherry_picked_commit_oid.is_some());
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert!(repository.path().join("file_two.txt").exists());
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file_two.txt")).unwrap(),
|
|
|
|
"content two"
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(branches.len(), 2);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(!branches[0].active);
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert_eq!(branches[0].commits[0].id, commit_one);
|
|
|
|
|
|
|
|
assert_eq!(branches[1].id, branch_two_id);
|
|
|
|
assert!(branches[1].active);
|
|
|
|
assert_eq!(branches[1].commits.len(), 1);
|
|
|
|
assert_eq!(branches[1].commits[0].id, cherry_picked_commit_oid.unwrap());
|
2023-10-23 16:51:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn non_applied() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-23 16:51:31 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-23 16:51:31 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let commit_one_oid = {
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file_two.txt"), "content two").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let commit_three_oid = {
|
|
|
|
fs::write(repository.path().join("file_three.txt"), "content three").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.reset_virtual_branch(&project_id, &branch_id, commit_one_oid)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(matches!(
|
|
|
|
controller
|
|
|
|
.cherry_pick(&project_id, &branch_id, commit_three_oid)
|
|
|
|
.await,
|
2023-11-17 11:55:47 +03:00
|
|
|
Err(ControllerError::Action(errors::CherryPickError::NotApplied))
|
2023-10-23 16:51:31 +03:00
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod with_conflicts {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn applied() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-23 16:51:31 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-23 16:51:31 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-10-31 10:45:53 +03:00
|
|
|
let commit_one = {
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
controller
|
2023-10-31 13:52:11 +03:00
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
2023-10-31 10:45:53 +03:00
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2023-10-31 13:52:11 +03:00
|
|
|
{
|
2023-10-31 10:45:53 +03:00
|
|
|
fs::write(repository.path().join("file_two.txt"), "content two").unwrap();
|
|
|
|
controller
|
2023-10-31 13:52:11 +03:00
|
|
|
.create_commit(&project_id, &branch_id, "commit two", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let commit_three = {
|
|
|
|
fs::write(repository.path().join("file_three.txt"), "content three").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit three", None)
|
2023-10-31 10:45:53 +03:00
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.reset_virtual_branch(&project_id, &branch_id, commit_one)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
repository.reset_hard(None);
|
|
|
|
assert_eq!(
|
|
|
|
fs::read_to_string(repository.path().join("file.txt")).unwrap(),
|
|
|
|
"content"
|
|
|
|
);
|
2023-10-31 13:52:11 +03:00
|
|
|
assert!(!repository.path().join("file_two.txt").exists());
|
|
|
|
assert!(!repository.path().join("file_three.txt").exists());
|
2023-10-31 10:45:53 +03:00
|
|
|
|
2023-10-23 16:51:31 +03:00
|
|
|
// introduce conflict with the remote commit
|
2023-10-31 13:52:11 +03:00
|
|
|
fs::write(repository.path().join("file_three.txt"), "conflict").unwrap();
|
2023-10-23 16:51:31 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
// cherry picking leads to conflict
|
|
|
|
let cherry_picked_commit_oid = controller
|
2023-10-31 13:52:11 +03:00
|
|
|
.cherry_pick(&project_id, &branch_id, commit_three)
|
2023-10-23 16:51:31 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert!(cherry_picked_commit_oid.is_none());
|
|
|
|
|
|
|
|
assert_eq!(
|
2023-10-31 13:52:11 +03:00
|
|
|
fs::read_to_string(repository.path().join("file_three.txt")).unwrap(),
|
|
|
|
"<<<<<<< ours\nconflict\n=======\ncontent three\n>>>>>>> theirs\n"
|
2023-10-23 16:51:31 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
|
|
|
assert!(branches[0].conflicted);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert!(branches[0].files[0].conflicted);
|
2023-10-31 10:45:53 +03:00
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
2023-10-23 16:51:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// conflict can be resolved
|
2023-10-31 13:52:11 +03:00
|
|
|
fs::write(repository.path().join("file_three.txt"), "resolved").unwrap();
|
2023-10-23 16:51:31 +03:00
|
|
|
let commited_oid = controller
|
|
|
|
.create_commit(&project_id, &branch_id, "resolution", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let commit = repository.find_commit(commited_oid).unwrap();
|
|
|
|
assert_eq!(commit.parent_count(), 2);
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].id, branch_id);
|
|
|
|
assert!(branches[0].active);
|
2023-10-31 13:52:11 +03:00
|
|
|
assert!(branches[0].requires_force);
|
2023-10-23 16:51:31 +03:00
|
|
|
assert!(!branches[0].conflicted);
|
2023-10-31 13:52:11 +03:00
|
|
|
assert_eq!(branches[0].commits.len(), 2);
|
2023-10-23 16:51:31 +03:00
|
|
|
// resolution commit is there
|
|
|
|
assert_eq!(branches[0].commits[0].id, commited_oid);
|
2023-10-31 13:52:11 +03:00
|
|
|
assert_eq!(branches[0].commits[1].id, commit_one);
|
2023-10-23 16:51:31 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn non_applied() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-11 15:01:17 +03:00
|
|
|
..
|
2023-10-23 16:51:31 +03:00
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
let commit_oid = {
|
|
|
|
let first = repository.commit_all("commit");
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
let second = repository.commit_all("commit");
|
|
|
|
repository.push();
|
2023-10-31 10:45:53 +03:00
|
|
|
repository.reset_hard(Some(first));
|
2023-10-23 16:51:31 +03:00
|
|
|
second
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-10-23 16:51:31 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// introduce conflict with the remote commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "conflict").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.unapply_virtual_branch(&project_id, &branch_id)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(matches!(
|
|
|
|
controller
|
|
|
|
.cherry_pick(&project_id, &branch_id, commit_oid)
|
|
|
|
.await,
|
2023-11-17 11:55:47 +03:00
|
|
|
Err(ControllerError::Action(errors::CherryPickError::NotApplied))
|
2023-10-23 16:51:31 +03:00
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-11-01 15:45:13 +03:00
|
|
|
|
|
|
|
mod amend {
|
|
|
|
use super::*;
|
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn to_default_target() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
2023-11-01 16:44:47 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-11-02 12:25:54 +03:00
|
|
|
.unwrap();
|
2023-11-01 15:45:13 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-01 16:44:47 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
// amend without head commit
|
|
|
|
fs::write(repository.path().join("file2.txt"), "content").unwrap();
|
|
|
|
let to_amend: branch::Ownership = "file2.txt:1-2".parse().unwrap();
|
2023-11-17 11:55:47 +03:00
|
|
|
assert!(matches!(
|
2023-11-01 16:44:47 +03:00
|
|
|
controller
|
2023-11-02 12:25:54 +03:00
|
|
|
.amend(&project_id, &branch_id, &to_amend)
|
|
|
|
.await
|
2023-11-17 11:55:47 +03:00
|
|
|
.unwrap_err(),
|
|
|
|
ControllerError::Action(errors::AmendError::BranchHasNoCommits)
|
|
|
|
));
|
2023-11-02 12:25:54 +03:00
|
|
|
}
|
|
|
|
|
2023-12-12 11:26:48 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn forcepush_allowed() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
projects,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
projects
|
|
|
|
.update(&projects::UpdateRequest {
|
|
|
|
id: project_id,
|
|
|
|
ok_with_force_push: Some(true),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
// create commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
// amend another hunk
|
|
|
|
fs::write(repository.path().join("file2.txt"), "content2").unwrap();
|
|
|
|
let to_amend: branch::Ownership = "file2.txt:1-2".parse().unwrap();
|
|
|
|
controller
|
|
|
|
.amend(&project_id, &branch_id, &to_amend)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
assert!(branch.requires_force);
|
|
|
|
assert_eq!(branch.commits.len(), 1);
|
|
|
|
assert_eq!(branch.files.len(), 0);
|
|
|
|
assert_eq!(branch.commits[0].files.len(), 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-11 17:49:43 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn forcepush_forbidden() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-12 20:32:18 +03:00
|
|
|
projects,
|
2023-12-11 17:49:43 +03:00
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-12-12 20:32:18 +03:00
|
|
|
projects
|
|
|
|
.update(&projects::UpdateRequest {
|
|
|
|
id: project_id,
|
|
|
|
ok_with_force_push: Some(false),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-12-11 17:49:43 +03:00
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
// create commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file2.txt"), "content2").unwrap();
|
|
|
|
let to_amend: branch::Ownership = "file2.txt:1-2".parse().unwrap();
|
|
|
|
assert!(matches!(
|
|
|
|
controller
|
|
|
|
.amend(&project_id, &branch_id, &to_amend)
|
|
|
|
.await
|
|
|
|
.unwrap_err(),
|
|
|
|
ControllerError::Action(errors::AmendError::ForcePushNotAllowed(_))
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn non_locked_hunk() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-11-02 12:25:54 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
// create commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
2023-11-01 16:44:47 +03:00
|
|
|
.unwrap();
|
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
2023-11-01 16:44:47 +03:00
|
|
|
.await
|
2023-11-02 12:25:54 +03:00
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
2023-11-01 16:44:47 +03:00
|
|
|
.unwrap();
|
2023-11-02 12:25:54 +03:00
|
|
|
assert_eq!(branch.commits.len(), 1);
|
|
|
|
assert_eq!(branch.files.len(), 0);
|
|
|
|
assert_eq!(branch.commits[0].files.len(), 1);
|
|
|
|
};
|
2023-11-01 16:44:47 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
{
|
|
|
|
// amend another hunk
|
|
|
|
fs::write(repository.path().join("file2.txt"), "content2").unwrap();
|
|
|
|
let to_amend: branch::Ownership = "file2.txt:1-2".parse().unwrap();
|
2023-11-01 16:44:47 +03:00
|
|
|
controller
|
|
|
|
.amend(&project_id, &branch_id, &to_amend)
|
|
|
|
.await
|
2023-11-02 12:25:54 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(branch.commits.len(), 1);
|
|
|
|
assert_eq!(branch.files.len(), 0);
|
|
|
|
assert_eq!(branch.commits[0].files.len(), 2);
|
2023-11-01 16:44:47 +03:00
|
|
|
}
|
2023-11-02 12:25:54 +03:00
|
|
|
}
|
2023-11-01 16:44:47 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn locked_hunk() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-11-02 12:25:54 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-01 16:44:47 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
{
|
|
|
|
// create commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
2023-11-01 16:44:47 +03:00
|
|
|
controller
|
2023-11-02 12:25:54 +03:00
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
2023-11-01 16:44:47 +03:00
|
|
|
.unwrap();
|
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(branch.commits.len(), 1);
|
|
|
|
assert_eq!(branch.files.len(), 0);
|
|
|
|
assert_eq!(branch.commits[0].files.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
branch.commits[0].files[0].hunks[0].diff,
|
|
|
|
"@@ -0,0 +1 @@\n+content\n\\ No newline at end of file\n"
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
// amend another hunk
|
|
|
|
fs::write(repository.path().join("file.txt"), "more content").unwrap();
|
|
|
|
let to_amend: branch::Ownership = "file.txt:1-2".parse().unwrap();
|
|
|
|
controller
|
|
|
|
.amend(&project_id, &branch_id, &to_amend)
|
2023-11-01 16:44:47 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
2023-11-01 16:44:47 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
assert_eq!(branch.commits.len(), 1);
|
|
|
|
assert_eq!(branch.files.len(), 0);
|
|
|
|
assert_eq!(branch.commits[0].files.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
branch.commits[0].files[0].hunks[0].diff,
|
|
|
|
"@@ -0,0 +1 @@\n+more content\n\\ No newline at end of file\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2023-11-01 16:44:47 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn non_existing_ownership() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
2023-11-01 16:44:47 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-11-02 12:25:54 +03:00
|
|
|
.unwrap();
|
2023-11-01 15:45:13 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-11-01 16:44:47 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
{
|
|
|
|
// create commit
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
2023-11-01 16:44:47 +03:00
|
|
|
controller
|
2023-11-02 12:25:54 +03:00
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
2023-11-01 16:44:47 +03:00
|
|
|
.unwrap();
|
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
2023-11-01 16:44:47 +03:00
|
|
|
.await
|
2023-11-02 12:25:54 +03:00
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
2023-11-01 16:44:47 +03:00
|
|
|
.unwrap();
|
2023-11-02 12:25:54 +03:00
|
|
|
assert_eq!(branch.commits.len(), 1);
|
|
|
|
assert_eq!(branch.files.len(), 0);
|
|
|
|
assert_eq!(branch.commits[0].files.len(), 1);
|
|
|
|
};
|
2023-11-01 16:44:47 +03:00
|
|
|
|
2023-11-02 12:25:54 +03:00
|
|
|
{
|
|
|
|
// amend non existing hunk
|
|
|
|
let to_amend: branch::Ownership = "file2.txt:1-2".parse().unwrap();
|
2023-11-17 11:55:47 +03:00
|
|
|
assert!(matches!(
|
2023-11-01 16:44:47 +03:00
|
|
|
controller
|
|
|
|
.amend(&project_id, &branch_id, &to_amend)
|
|
|
|
.await
|
2023-11-17 11:55:47 +03:00
|
|
|
.unwrap_err(),
|
|
|
|
ControllerError::Action(errors::AmendError::TargetOwnerhshipNotFound(_))
|
|
|
|
));
|
2023-11-01 15:45:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-11-03 16:30:30 +03:00
|
|
|
|
|
|
|
mod init {
|
|
|
|
use super::*;
|
|
|
|
|
2023-11-17 16:05:00 +03:00
|
|
|
#[tokio::test]
|
2023-11-21 12:09:21 +03:00
|
|
|
async fn twice() {
|
|
|
|
let data_dir = paths::data_dir();
|
|
|
|
let keys = keys::Controller::from(&data_dir);
|
|
|
|
let projects = projects::Controller::from(&data_dir);
|
|
|
|
let users = users::Controller::from(&data_dir);
|
2023-12-06 19:24:48 +03:00
|
|
|
let helper = git::credentials::Helper::from(&data_dir);
|
2023-11-21 12:09:21 +03:00
|
|
|
|
|
|
|
let test_project = TestProject::default();
|
|
|
|
|
2023-12-06 19:24:48 +03:00
|
|
|
let controller = Controller::new(&data_dir, &projects, &users, &keys, &helper);
|
2023-11-21 12:09:21 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
let project = projects
|
|
|
|
.add(test_project.path())
|
|
|
|
.expect("failed to add project");
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project.id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-21 12:09:21 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert!(controller
|
|
|
|
.list_virtual_branches(&project.id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.is_empty());
|
|
|
|
projects.delete(&project.id).await.unwrap();
|
|
|
|
controller
|
|
|
|
.list_virtual_branches(&project.id)
|
|
|
|
.await
|
|
|
|
.unwrap_err();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let project = projects.add(test_project.path()).unwrap();
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project.id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-21 12:09:21 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// even though project is on gitbutler/integration, we should not import it
|
|
|
|
assert!(controller
|
|
|
|
.list_virtual_branches(&project.id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.is_empty());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
2023-12-05 17:18:34 +03:00
|
|
|
async fn dirty_non_target() {
|
|
|
|
// a situation when you initialize project while being on the local verison of the master
|
|
|
|
// that has uncommited changes.
|
2023-11-17 16:05:00 +03:00
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
2023-12-05 17:18:34 +03:00
|
|
|
repository.checkout("refs/heads/some-feature".parse().unwrap());
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
2023-11-17 16:05:00 +03:00
|
|
|
.unwrap();
|
2023-12-05 17:18:34 +03:00
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].files[0].hunks.len(), 1);
|
|
|
|
assert!(branches[0].upstream.is_none());
|
|
|
|
assert_eq!(branches[0].name, "some-feature");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn dirty_target() {
|
|
|
|
// a situation when you initialize project while being on the local verison of the master
|
|
|
|
// that has uncommited changes.
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
2023-11-17 16:05:00 +03:00
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-11-17 16:05:00 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].files[0].hunks.len(), 1);
|
2023-12-05 17:18:34 +03:00
|
|
|
assert!(branches[0].upstream.is_none());
|
|
|
|
assert_eq!(branches[0].name, "master");
|
2023-11-17 16:05:00 +03:00
|
|
|
}
|
|
|
|
|
2023-11-03 16:30:30 +03:00
|
|
|
#[tokio::test]
|
2023-12-05 17:18:34 +03:00
|
|
|
async fn commit_on_non_target_local() {
|
2023-11-03 16:30:30 +03:00
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
2023-12-05 17:18:34 +03:00
|
|
|
repository.checkout("refs/heads/some-feature".parse().unwrap());
|
2023-11-03 16:30:30 +03:00
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
2023-12-05 17:18:34 +03:00
|
|
|
repository.commit_all("commit on target");
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
dbg!(&branches);
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert!(branches[0].files.is_empty());
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert!(branches[0].upstream.is_none());
|
|
|
|
assert_eq!(branches[0].name, "some-feature");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn commit_on_non_target_remote() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
repository.checkout("refs/heads/some-feature".parse().unwrap());
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
repository.commit_all("commit on target");
|
|
|
|
repository.push_branch(&"refs/heads/some-feature".parse().unwrap());
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
dbg!(&branches);
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert!(branches[0].files.is_empty());
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert!(branches[0].upstream.is_some());
|
|
|
|
assert_eq!(branches[0].name, "some-feature");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn commit_on_target() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
|
|
|
repository.commit_all("commit on target");
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert!(branches[0].files.is_empty());
|
|
|
|
assert_eq!(branches[0].commits.len(), 1);
|
|
|
|
assert!(branches[0].upstream.is_none());
|
|
|
|
assert_eq!(branches[0].name, "master");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn submodule() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
let submodule_url: git::Url = TestProject::default()
|
|
|
|
.path()
|
|
|
|
.display()
|
|
|
|
.to_string()
|
|
|
|
.parse()
|
|
|
|
.unwrap();
|
|
|
|
repository.add_submodule(&submodule_url, path::Path::new("submodule"));
|
2023-11-03 16:30:30 +03:00
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-11-03 16:30:30 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branches = controller.list_virtual_branches(&project_id).await.unwrap();
|
|
|
|
assert_eq!(branches.len(), 1);
|
|
|
|
assert_eq!(branches[0].files.len(), 1);
|
|
|
|
assert_eq!(branches[0].files[0].hunks.len(), 1);
|
|
|
|
}
|
|
|
|
}
|
2023-11-09 15:00:14 +03:00
|
|
|
|
|
|
|
mod squash {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn head() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-11-09 15:00:14 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-23 16:15:52 +03:00
|
|
|
{
|
2023-11-09 15:00:14 +03:00
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2023-11-23 16:15:52 +03:00
|
|
|
{
|
2023-11-09 15:00:14 +03:00
|
|
|
fs::write(repository.path().join("file two.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit two", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2023-11-23 16:15:52 +03:00
|
|
|
{
|
2023-11-09 15:00:14 +03:00
|
|
|
fs::write(repository.path().join("file three.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit three", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2023-11-23 16:15:52 +03:00
|
|
|
let commit_four_oid = {
|
|
|
|
fs::write(repository.path().join("file four.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit four", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2023-11-09 15:00:14 +03:00
|
|
|
controller
|
2023-11-23 16:15:52 +03:00
|
|
|
.squash(&project_id, &branch_id, commit_four_oid)
|
2023-11-09 15:00:14 +03:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-23 16:15:52 +03:00
|
|
|
let descriptions = branch
|
|
|
|
.commits
|
|
|
|
.iter()
|
|
|
|
.map(|c| c.description.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(
|
|
|
|
descriptions,
|
|
|
|
vec!["commit three\ncommit four", "commit two", "commit one"]
|
|
|
|
);
|
2023-11-09 15:00:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn middle() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-11-09 15:00:14 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-23 16:15:52 +03:00
|
|
|
{
|
2023-11-09 15:00:14 +03:00
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let commit_two_oid = {
|
|
|
|
fs::write(repository.path().join("file two.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit two", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2023-11-23 16:15:52 +03:00
|
|
|
{
|
2023-11-09 15:00:14 +03:00
|
|
|
fs::write(repository.path().join("file three.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit three", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2023-11-23 16:15:52 +03:00
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file four.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit four", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2023-11-09 15:00:14 +03:00
|
|
|
controller
|
|
|
|
.squash(&project_id, &branch_id, commit_two_oid)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-23 16:15:52 +03:00
|
|
|
let descriptions = branch
|
|
|
|
.commits
|
|
|
|
.iter()
|
|
|
|
.map(|c| c.description.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(
|
|
|
|
descriptions,
|
|
|
|
vec!["commit four", "commit three", "commit one\ncommit two"]
|
|
|
|
);
|
2023-11-09 15:00:14 +03:00
|
|
|
}
|
|
|
|
|
2023-12-12 11:45:16 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn forcepush_allowed() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
projects,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
projects
|
|
|
|
.update(&projects::UpdateRequest {
|
|
|
|
id: project_id,
|
|
|
|
ok_with_force_push: Some(true),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let commit_two_oid = {
|
|
|
|
fs::write(repository.path().join("file two.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit two", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file three.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit three", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file four.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit four", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.squash(&project_id, &branch_id, commit_two_oid)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let descriptions = branch
|
|
|
|
.commits
|
|
|
|
.iter()
|
|
|
|
.map(|c| c.description.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(
|
|
|
|
descriptions,
|
|
|
|
vec!["commit four", "commit three", "commit one\ncommit two"]
|
|
|
|
);
|
|
|
|
assert!(branch.requires_force);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn forcepush_forbidden() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-12 20:32:18 +03:00
|
|
|
projects,
|
2023-12-12 11:45:16 +03:00
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-12-12 20:32:18 +03:00
|
|
|
projects
|
|
|
|
.update(&projects::UpdateRequest {
|
|
|
|
id: project_id,
|
|
|
|
ok_with_force_push: Some(false),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-12-12 11:45:16 +03:00
|
|
|
let commit_two_oid = {
|
|
|
|
fs::write(repository.path().join("file two.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit two", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file three.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit three", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file four.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit four", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
assert!(matches!(
|
|
|
|
controller
|
|
|
|
.squash(&project_id, &branch_id, commit_two_oid)
|
|
|
|
.await
|
|
|
|
.unwrap_err(),
|
|
|
|
ControllerError::Action(errors::SquashError::ForcePushNotAllowed(_))
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2023-11-09 15:00:14 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn root() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
2023-11-22 12:33:31 +03:00
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
2023-11-20 18:17:22 +03:00
|
|
|
.await
|
2023-11-09 15:00:14 +03:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let commit_one_oid = {
|
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2023-11-17 11:55:47 +03:00
|
|
|
assert!(matches!(
|
2023-11-09 15:00:14 +03:00
|
|
|
controller
|
|
|
|
.squash(&project_id, &branch_id, commit_one_oid)
|
|
|
|
.await
|
2023-11-17 11:55:47 +03:00
|
|
|
.unwrap_err(),
|
|
|
|
ControllerError::Action(errors::SquashError::CantSquashRootCommit)
|
|
|
|
));
|
2023-11-09 15:00:14 +03:00
|
|
|
}
|
|
|
|
}
|
2023-11-23 16:08:40 +03:00
|
|
|
|
|
|
|
mod update_commit_message {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn head() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file two.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit two", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let commit_three_oid = {
|
|
|
|
fs::write(repository.path().join("file three.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit three", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.update_commit_message(
|
|
|
|
&project_id,
|
|
|
|
&branch_id,
|
|
|
|
commit_three_oid,
|
|
|
|
"commit three updated",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let descriptions = branch
|
|
|
|
.commits
|
|
|
|
.iter()
|
|
|
|
.map(|c| c.description.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
descriptions,
|
|
|
|
vec!["commit three updated", "commit two", "commit one"]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn middle() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let commit_two_oid = {
|
|
|
|
fs::write(repository.path().join("file two.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit two", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file three.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit three", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.update_commit_message(
|
|
|
|
&project_id,
|
|
|
|
&branch_id,
|
|
|
|
commit_two_oid,
|
|
|
|
"commit two updated",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let descriptions = branch
|
|
|
|
.commits
|
|
|
|
.iter()
|
|
|
|
.map(|c| c.description.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(
|
|
|
|
descriptions,
|
|
|
|
vec!["commit three", "commit two updated", "commit one"]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-12-12 12:47:00 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn forcepush_allowed() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
projects,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
projects
|
|
|
|
.update(&projects::UpdateRequest {
|
|
|
|
id: project_id,
|
|
|
|
ok_with_force_push: Some(true),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let commit_one_oid = {
|
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.update_commit_message(
|
|
|
|
&project_id,
|
|
|
|
&branch_id,
|
|
|
|
commit_one_oid,
|
|
|
|
"commit one updated",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let descriptions = branch
|
|
|
|
.commits
|
|
|
|
.iter()
|
|
|
|
.map(|c| c.description.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(descriptions, vec!["commit one updated"]);
|
|
|
|
assert!(branch.requires_force);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn forcepush_forbidden() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
2023-12-12 20:32:18 +03:00
|
|
|
projects,
|
2023-12-12 12:47:00 +03:00
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-12-12 20:32:18 +03:00
|
|
|
projects
|
|
|
|
.update(&projects::UpdateRequest {
|
|
|
|
id: project_id,
|
|
|
|
ok_with_force_push: Some(false),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-12-12 12:47:00 +03:00
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let commit_one_oid = {
|
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.push_virtual_branch(&project_id, &branch_id, false)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(matches!(
|
|
|
|
controller
|
|
|
|
.update_commit_message(
|
|
|
|
&project_id,
|
|
|
|
&branch_id,
|
|
|
|
commit_one_oid,
|
|
|
|
"commit one updated",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap_err(),
|
|
|
|
ControllerError::Action(errors::UpdateCommitMessageError::ForcePushNotAllowed(_))
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2023-11-23 16:08:40 +03:00
|
|
|
#[tokio::test]
|
|
|
|
async fn root() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let commit_one_oid = {
|
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file two.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit two", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
fs::write(repository.path().join("file three.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit three", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
controller
|
|
|
|
.update_commit_message(
|
|
|
|
&project_id,
|
|
|
|
&branch_id,
|
|
|
|
commit_one_oid,
|
|
|
|
"commit one updated",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch = controller
|
|
|
|
.list_virtual_branches(&project_id)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.find(|b| b.id == branch_id)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let descriptions = branch
|
|
|
|
.commits
|
|
|
|
.iter()
|
|
|
|
.map(|c| c.description.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(
|
|
|
|
descriptions,
|
|
|
|
vec!["commit three", "commit two", "commit one updated"]
|
|
|
|
);
|
|
|
|
}
|
2023-11-23 16:15:52 +03:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn empty() {
|
|
|
|
let Test {
|
|
|
|
repository,
|
|
|
|
project_id,
|
|
|
|
controller,
|
|
|
|
..
|
|
|
|
} = Test::default();
|
|
|
|
|
|
|
|
controller
|
|
|
|
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let branch_id = controller
|
|
|
|
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let commit_one_oid = {
|
|
|
|
fs::write(repository.path().join("file one.txt"), "").unwrap();
|
|
|
|
controller
|
|
|
|
.create_commit(&project_id, &branch_id, "commit one", None)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
assert!(matches!(
|
|
|
|
controller
|
|
|
|
.update_commit_message(&project_id, &branch_id, commit_one_oid, "",)
|
|
|
|
.await,
|
|
|
|
Err(ControllerError::Action(
|
|
|
|
errors::UpdateCommitMessageError::EmptyMessage
|
|
|
|
))
|
|
|
|
));
|
|
|
|
}
|
2023-11-23 16:08:40 +03:00
|
|
|
}
|