2024-09-30 01:22:35 +03:00
|
|
|
use anyhow::Result;
|
|
|
|
use gitbutler_branch::VirtualBranchesHandle;
|
|
|
|
use gitbutler_command_context::CommandContext;
|
2024-10-01 17:12:49 +03:00
|
|
|
use gitbutler_commit::commit_ext::CommitExt;
|
2024-09-30 01:22:35 +03:00
|
|
|
use gitbutler_patch_reference::{CommitOrChangeId, PatchReference};
|
|
|
|
use gitbutler_repo::{LogUntil, RepositoryExt as _};
|
|
|
|
use gitbutler_stack::Stack;
|
|
|
|
use tempfile::TempDir;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn init_success() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let test_ctx = test_ctx(&ctx)?;
|
|
|
|
let mut branch = test_ctx.branch;
|
|
|
|
assert!(!branch.initialized());
|
|
|
|
assert_eq!(branch.heads.len(), 0);
|
|
|
|
let result = branch.init(&ctx);
|
|
|
|
assert!(result.is_ok());
|
|
|
|
assert!(branch.initialized());
|
|
|
|
assert_eq!(branch.heads.len(), 1);
|
|
|
|
assert_eq!(branch.heads[0].name, "virtual"); // matches the stack name
|
|
|
|
assert_eq!(
|
|
|
|
branch.heads[0].target,
|
2024-10-01 17:12:49 +03:00
|
|
|
CommitOrChangeId::ChangeId(
|
|
|
|
ctx.repository()
|
|
|
|
.find_commit(branch.head)?
|
|
|
|
.change_id()
|
|
|
|
.unwrap()
|
|
|
|
)
|
2024-09-30 01:22:35 +03:00
|
|
|
);
|
|
|
|
// Assert persisted
|
|
|
|
assert_eq!(branch, test_ctx.handle.get_branch(branch.id)?);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn init_already_initialized_fails() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let test_ctx = test_ctx(&ctx)?;
|
|
|
|
let mut branch = test_ctx.branch;
|
|
|
|
let result = branch.init(&ctx);
|
|
|
|
assert!(result.is_ok());
|
|
|
|
let result = branch.init(&ctx);
|
|
|
|
assert!(result.is_err());
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn add_branch_success() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
test_ctx.branch.init(&ctx)?;
|
|
|
|
let reference = PatchReference {
|
|
|
|
name: "asdf".into(),
|
2024-10-01 17:12:49 +03:00
|
|
|
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
2024-09-30 01:22:35 +03:00
|
|
|
};
|
2024-10-01 17:12:49 +03:00
|
|
|
let result = test_ctx.branch.add_branch(&ctx, reference, None);
|
2024-09-30 01:22:35 +03:00
|
|
|
assert!(result.is_ok());
|
|
|
|
assert_eq!(test_ctx.branch.heads.len(), 2);
|
|
|
|
// Assert persisted
|
|
|
|
assert_eq!(
|
|
|
|
test_ctx.branch,
|
|
|
|
test_ctx.handle.get_branch(test_ctx.branch.id)?
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn add_branch_uninitialized_fails() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
let reference = PatchReference {
|
|
|
|
name: "asdf".into(),
|
|
|
|
target: CommitOrChangeId::CommitId(test_ctx.commits[0].id().to_string()),
|
|
|
|
};
|
2024-10-01 17:12:49 +03:00
|
|
|
let result = test_ctx.branch.add_branch(&ctx, reference, None);
|
2024-09-30 01:22:35 +03:00
|
|
|
assert_eq!(
|
|
|
|
result.err().unwrap().to_string(),
|
|
|
|
"Stack has not been initialized"
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn add_branch_invalid_name_fails() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
test_ctx.branch.init(&ctx)?;
|
|
|
|
let reference = PatchReference {
|
|
|
|
name: "name with spaces".into(),
|
|
|
|
target: CommitOrChangeId::CommitId(test_ctx.commits[0].id().to_string()),
|
|
|
|
};
|
2024-10-01 17:12:49 +03:00
|
|
|
let result = test_ctx.branch.add_branch(&ctx, reference, None);
|
2024-09-30 01:22:35 +03:00
|
|
|
assert_eq!(result.err().unwrap().to_string(), "Invalid branch name");
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn add_branch_duplicate_name_fails() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
test_ctx.branch.init(&ctx)?;
|
|
|
|
let reference = PatchReference {
|
|
|
|
name: "asdf".into(),
|
2024-10-01 17:12:49 +03:00
|
|
|
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
2024-09-30 01:22:35 +03:00
|
|
|
};
|
2024-10-01 17:12:49 +03:00
|
|
|
let result = test_ctx.branch.add_branch(&ctx, reference.clone(), None);
|
2024-09-30 01:22:35 +03:00
|
|
|
assert!(result.is_ok());
|
2024-10-01 17:12:49 +03:00
|
|
|
let result = test_ctx.branch.add_branch(&ctx, reference, None);
|
2024-09-30 01:22:35 +03:00
|
|
|
assert_eq!(
|
|
|
|
result.err().unwrap().to_string(),
|
|
|
|
"A patch reference with the name asdf exists"
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn add_branch_matching_git_ref_fails() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
test_ctx.branch.init(&ctx)?;
|
|
|
|
let reference = PatchReference {
|
|
|
|
name: "existing-branch".into(),
|
|
|
|
target: CommitOrChangeId::CommitId(test_ctx.commits[0].id().to_string()),
|
|
|
|
};
|
2024-10-01 17:12:49 +03:00
|
|
|
let result = test_ctx.branch.add_branch(&ctx, reference.clone(), None);
|
2024-09-30 01:22:35 +03:00
|
|
|
assert_eq!(
|
|
|
|
result.err().unwrap().to_string(),
|
|
|
|
"A git reference with the name existing-branch exists"
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn add_branch_including_refs_head_fails() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
test_ctx.branch.init(&ctx)?;
|
|
|
|
let reference = PatchReference {
|
|
|
|
name: "refs/heads/my-branch".into(),
|
|
|
|
target: CommitOrChangeId::CommitId(test_ctx.commits[0].id().to_string()),
|
|
|
|
};
|
2024-10-01 17:12:49 +03:00
|
|
|
let result = test_ctx.branch.add_branch(&ctx, reference.clone(), None);
|
2024-09-30 01:22:35 +03:00
|
|
|
assert_eq!(
|
|
|
|
result.err().unwrap().to_string(),
|
|
|
|
"Stack head name cannot start with 'refs/heads'"
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn add_branch_target_commit_doesnt_exist() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
test_ctx.branch.init(&ctx)?;
|
|
|
|
let reference = PatchReference {
|
|
|
|
name: "my-branch".into(),
|
|
|
|
target: CommitOrChangeId::CommitId("30696678319e0fa3a20e54f22d47fc8cf1ceaade".into()), // does not exist
|
|
|
|
};
|
2024-10-01 17:12:49 +03:00
|
|
|
let result = test_ctx.branch.add_branch(&ctx, reference.clone(), None);
|
2024-09-30 01:22:35 +03:00
|
|
|
assert!(result
|
|
|
|
.err()
|
|
|
|
.unwrap()
|
|
|
|
.to_string()
|
|
|
|
.contains("object not found"),);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn add_branch_target_change_id_doesnt_exist() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
test_ctx.branch.init(&ctx)?;
|
|
|
|
let reference = PatchReference {
|
|
|
|
name: "my-branch".into(),
|
|
|
|
target: CommitOrChangeId::ChangeId("does-not-exist".into()), // does not exist
|
|
|
|
};
|
2024-10-01 17:12:49 +03:00
|
|
|
let result = test_ctx.branch.add_branch(&ctx, reference.clone(), None);
|
2024-09-30 01:22:35 +03:00
|
|
|
assert_eq!(
|
|
|
|
result.err().unwrap().to_string(),
|
|
|
|
"Commit with change id does-not-exist not found"
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn add_branch_target_commit_not_in_stack() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
test_ctx.branch.init(&ctx)?;
|
|
|
|
let other_commit_id = test_ctx.other_commits.last().unwrap().id().to_string();
|
|
|
|
let reference = PatchReference {
|
|
|
|
name: "my-branch".into(),
|
|
|
|
target: CommitOrChangeId::CommitId(other_commit_id.clone()), // does not exist
|
|
|
|
};
|
2024-10-01 17:12:49 +03:00
|
|
|
let result = test_ctx.branch.add_branch(&ctx, reference.clone(), None);
|
2024-09-30 01:22:35 +03:00
|
|
|
assert_eq!(
|
|
|
|
result.err().unwrap().to_string(),
|
|
|
|
format!(
|
|
|
|
"The commit {} is not between the stack head and the stack base",
|
|
|
|
other_commit_id
|
|
|
|
)
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn remove_branch_uninitialized_fails() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
let result = test_ctx.branch.remove_branch(&ctx, "some-name".to_string());
|
|
|
|
assert_eq!(
|
|
|
|
result.err().unwrap().to_string(),
|
|
|
|
"Stack has not been initialized"
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn remove_branch_last_fails() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
test_ctx.branch.init(&ctx)?;
|
|
|
|
let result = test_ctx
|
|
|
|
.branch
|
|
|
|
.remove_branch(&ctx, test_ctx.branch.heads[0].name.clone());
|
|
|
|
assert_eq!(
|
|
|
|
result.err().unwrap().to_string(),
|
|
|
|
"Cannot remove the last branch from the stack"
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn remove_branch_nonexistent_fails() -> Result<()> {
|
|
|
|
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
|
|
|
let mut test_ctx = test_ctx(&ctx)?;
|
|
|
|
test_ctx.branch.init(&ctx)?;
|
|
|
|
let result = test_ctx
|
|
|
|
.branch
|
|
|
|
.remove_branch(&ctx, "does-not-exist".to_string());
|
|
|
|
assert_eq!(
|
|
|
|
result.err().unwrap().to_string(),
|
|
|
|
"Series with name does-not-exist not found"
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn command_ctx(name: &str) -> Result<(CommandContext, TempDir)> {
|
|
|
|
gitbutler_testsupport::writable::fixture("stacking.sh", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn test_ctx(ctx: &CommandContext) -> Result<TestContext> {
|
|
|
|
let handle = VirtualBranchesHandle::new(ctx.project().gb_dir());
|
|
|
|
let branches = handle.list_all_branches()?;
|
|
|
|
let branch = branches.iter().find(|b| b.name == "virtual").unwrap();
|
|
|
|
let other_branch = branches.iter().find(|b| b.name != "virtual").unwrap();
|
|
|
|
let target = handle.get_default_target()?;
|
|
|
|
let branch_commits = ctx
|
|
|
|
.repository()
|
|
|
|
.log(branch.head, LogUntil::Commit(target.sha))?;
|
|
|
|
let other_commits = ctx
|
|
|
|
.repository()
|
|
|
|
.log(other_branch.head, LogUntil::Commit(target.sha))?;
|
|
|
|
Ok(TestContext {
|
|
|
|
branch: branch.clone(),
|
|
|
|
commits: branch_commits,
|
|
|
|
// other_branch: other_branch.clone(),
|
|
|
|
other_commits,
|
|
|
|
handle,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
struct TestContext<'a> {
|
|
|
|
branch: gitbutler_branch::Branch,
|
|
|
|
commits: Vec<git2::Commit<'a>>,
|
|
|
|
#[allow(dead_code)]
|
|
|
|
other_commits: Vec<git2::Commit<'a>>,
|
|
|
|
handle: VirtualBranchesHandle,
|
|
|
|
}
|