mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-29 20:43:37 +03:00
rewrite our oplog tests
This commit is contained in:
parent
1cbcbb3478
commit
dcc1b37331
@ -351,7 +351,6 @@ impl Oplog for Project {
|
||||
.get_name("workdir")
|
||||
.ok_or(anyhow!("failed to get workdir tree entry"))?;
|
||||
|
||||
|
||||
// make sure we reconstitute any commits that were in the snapshot that are not here for some reason
|
||||
// for every entry in the virtual_branches subtree, reconsitute the commits
|
||||
let vb_tree_entry = top_tree
|
||||
@ -626,188 +625,3 @@ fn get_exclude_list(repo: &git2::Repository) -> Result<String> {
|
||||
.join(" ");
|
||||
Ok(files_to_exclude)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Write;
|
||||
|
||||
use crate::virtual_branches::Branch;
|
||||
|
||||
use super::*;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_create_and_restore() {
|
||||
let dir = tempdir().unwrap();
|
||||
let repo = git2::Repository::init(dir.path()).unwrap();
|
||||
let file_path = dir.path().join("1.txt");
|
||||
std::fs::write(file_path, "test").unwrap();
|
||||
let file_path = dir.path().join("2.txt");
|
||||
std::fs::write(file_path, "test").unwrap();
|
||||
let mut index = repo.index().unwrap();
|
||||
index.add_path(&PathBuf::from("1.txt")).unwrap();
|
||||
index.add_path(&PathBuf::from("2.txt")).unwrap();
|
||||
let oid = index.write_tree().unwrap();
|
||||
let name = "Your Name";
|
||||
let email = "your.email@example.com";
|
||||
let signature = git2::Signature::now(name, email).unwrap();
|
||||
let initial_commit = repo
|
||||
.commit(
|
||||
Some("HEAD"),
|
||||
&signature,
|
||||
&signature,
|
||||
"initial commit",
|
||||
&repo.find_tree(oid).unwrap(),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// create a new branch called "gitbutler/integraion" from initial commit
|
||||
repo.branch(
|
||||
"gitbutler/integration",
|
||||
&repo.find_commit(initial_commit).unwrap(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let project = Project {
|
||||
path: dir.path().to_path_buf(),
|
||||
enable_snapshots: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
// create gb_dir folder
|
||||
std::fs::create_dir_all(project.gb_dir()).unwrap();
|
||||
|
||||
let vb_state = project.virtual_branches();
|
||||
|
||||
let target_sha = initial_commit.to_string();
|
||||
let default_target = crate::virtual_branches::target::Target {
|
||||
branch: crate::git::RemoteRefname::new("origin", "main"),
|
||||
remote_url: Default::default(),
|
||||
sha: crate::git::Oid::from_str(&target_sha).unwrap(),
|
||||
push_remote_name: None,
|
||||
};
|
||||
vb_state.set_default_target(default_target.clone()).unwrap();
|
||||
let file_path = dir.path().join("uncommitted.txt");
|
||||
std::fs::write(file_path, "test").unwrap();
|
||||
|
||||
let file_path = dir.path().join("large.txt");
|
||||
// write 33MB of random data in the file
|
||||
let mut file = std::fs::File::create(file_path).unwrap();
|
||||
for _ in 0..33 * 1024 {
|
||||
let data = [0u8; 1024];
|
||||
file.write_all(&data).unwrap();
|
||||
}
|
||||
|
||||
// Create conflict state
|
||||
let conflicts_path = dir.path().join(".git").join("conflicts");
|
||||
std::fs::write(&conflicts_path, "conflict A").unwrap();
|
||||
let base_merge_parent_path = dir.path().join(".git").join("base_merge_parent");
|
||||
std::fs::write(&base_merge_parent_path, "parent A").unwrap();
|
||||
|
||||
// create a snapshot
|
||||
project
|
||||
.create_snapshot(SnapshotDetails::new(OperationType::CreateCommit))
|
||||
.unwrap();
|
||||
|
||||
// The large file is still here but it will not be part of the snapshot
|
||||
let file_path = dir.path().join("large.txt");
|
||||
assert!(file_path.exists());
|
||||
|
||||
// Modify file 1, remove file 2, create file 3
|
||||
let file_path = dir.path().join("1.txt");
|
||||
std::fs::write(file_path, "TEST").unwrap();
|
||||
let file_path = dir.path().join("2.txt");
|
||||
std::fs::remove_file(file_path).unwrap();
|
||||
let file_path = dir.path().join("3.txt");
|
||||
std::fs::write(file_path, "something_new").unwrap();
|
||||
let file_path = dir.path().join("uncommitted.txt");
|
||||
std::fs::write(file_path, "TEST").unwrap();
|
||||
|
||||
// Create a fake branch in virtual_branches.toml
|
||||
let id = crate::id::Id::from_str("9acb2a3b-cddf-47d7-b531-a7798978c237").unwrap();
|
||||
vb_state
|
||||
.set_branch(Branch {
|
||||
id,
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap();
|
||||
assert!(vb_state.get_branch(&id).is_ok());
|
||||
|
||||
// remove remove conflict files
|
||||
std::fs::remove_file(&conflicts_path).unwrap();
|
||||
std::fs::remove_file(&base_merge_parent_path).unwrap();
|
||||
// New snapshot with the conflicts removed
|
||||
let conflicts_removed_snapshot = project
|
||||
.create_snapshot(SnapshotDetails::new(OperationType::UpdateWorkspaceBase))
|
||||
.unwrap();
|
||||
|
||||
let initial_snapshot = &project.list_snapshots(10, None).unwrap()[1];
|
||||
assert_eq!(
|
||||
initial_snapshot.files_changed,
|
||||
vec![
|
||||
PathBuf::from_str("1.txt").unwrap(),
|
||||
PathBuf::from_str("2.txt").unwrap(),
|
||||
PathBuf::from_str("uncommitted.txt").unwrap()
|
||||
]
|
||||
);
|
||||
assert_eq!(initial_snapshot.lines_added, 3);
|
||||
assert_eq!(initial_snapshot.lines_removed, 0);
|
||||
let second_snapshot = &project.list_snapshots(10, None).unwrap()[0];
|
||||
assert_eq!(
|
||||
second_snapshot.files_changed,
|
||||
vec![
|
||||
PathBuf::from_str("1.txt").unwrap(),
|
||||
PathBuf::from_str("2.txt").unwrap(),
|
||||
PathBuf::from_str("3.txt").unwrap(),
|
||||
PathBuf::from_str("uncommitted.txt").unwrap()
|
||||
]
|
||||
);
|
||||
assert_eq!(second_snapshot.lines_added, 3);
|
||||
assert_eq!(second_snapshot.lines_removed, 3);
|
||||
|
||||
// restore from the initial snapshot
|
||||
project
|
||||
.restore_snapshot(initial_snapshot.id.clone())
|
||||
.unwrap();
|
||||
|
||||
let file_path = dir.path().join("1.txt");
|
||||
let file_lines = std::fs::read_to_string(file_path).unwrap();
|
||||
assert_eq!(file_lines, "test");
|
||||
let file_path = dir.path().join("2.txt");
|
||||
assert!(file_path.exists());
|
||||
let file_lines = std::fs::read_to_string(file_path).unwrap();
|
||||
assert_eq!(file_lines, "test");
|
||||
let file_path = dir.path().join("3.txt");
|
||||
assert!(!file_path.exists());
|
||||
let file_path = dir.path().join("uncommitted.txt");
|
||||
let file_lines = std::fs::read_to_string(file_path).unwrap();
|
||||
assert_eq!(file_lines, "test");
|
||||
|
||||
// The large file is still here but it was not be part of the snapshot
|
||||
let file_path = dir.path().join("large.txt");
|
||||
assert!(file_path.exists());
|
||||
|
||||
// The fake branch is gone
|
||||
assert!(vb_state.get_branch(&id).is_err());
|
||||
|
||||
// The conflict files are restored
|
||||
let file_lines = std::fs::read_to_string(&conflicts_path).unwrap();
|
||||
assert_eq!(file_lines, "conflict A");
|
||||
let file_lines = std::fs::read_to_string(&base_merge_parent_path).unwrap();
|
||||
assert_eq!(file_lines, "parent A");
|
||||
|
||||
// Restore from the second snapshot
|
||||
project
|
||||
.restore_snapshot(conflicts_removed_snapshot.unwrap())
|
||||
.unwrap();
|
||||
|
||||
// The conflicts are not present
|
||||
assert!(!&conflicts_path.exists());
|
||||
assert!(!&base_merge_parent_path.exists());
|
||||
}
|
||||
|
||||
// test no oplog.toml found
|
||||
// test oplog.toml head is not a commit
|
||||
// test missing commits are recreated
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::{fs, path, str::FromStr};
|
||||
|
||||
use gitbutler_core::{
|
||||
git,
|
||||
projects::{self, ProjectId},
|
||||
projects::{self, Project, ProjectId},
|
||||
users,
|
||||
virtual_branches::{branch, errors, Controller},
|
||||
};
|
||||
@ -13,6 +13,7 @@ use gitbutler_testsupport::{paths, TestProject, VAR_NO_CLEANUP};
|
||||
struct Test {
|
||||
repository: TestProject,
|
||||
project_id: ProjectId,
|
||||
project: Project,
|
||||
projects: projects::Controller,
|
||||
controller: Controller,
|
||||
data_dir: Option<TempDir>,
|
||||
@ -43,6 +44,7 @@ impl Default for Test {
|
||||
project_id: project.id,
|
||||
controller: Controller::new(projects.clone(), users, helper),
|
||||
projects,
|
||||
project,
|
||||
data_dir: Some(data_dir),
|
||||
}
|
||||
}
|
||||
@ -59,6 +61,7 @@ mod init;
|
||||
mod insert_blank_commit;
|
||||
mod move_commit_file;
|
||||
mod move_commit_to_vbranch;
|
||||
mod oplog;
|
||||
mod references;
|
||||
mod reorder_commit;
|
||||
mod reset_virtual_branch;
|
||||
|
193
crates/gitbutler-core/tests/suite/virtual_branches/oplog.rs
Normal file
193
crates/gitbutler-core/tests/suite/virtual_branches/oplog.rs
Normal file
@ -0,0 +1,193 @@
|
||||
use std::io::Write;
|
||||
|
||||
use gitbutler_core::ops::oplog::Oplog;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_basic_oplog() {
|
||||
let Test {
|
||||
repository,
|
||||
project_id,
|
||||
controller,
|
||||
project,
|
||||
..
|
||||
} = &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();
|
||||
|
||||
// create commit
|
||||
fs::write(repository.path().join("file.txt"), "content").unwrap();
|
||||
let _commit1_id = controller
|
||||
.create_commit(project_id, &branch_id, "commit one", None, false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dont store large files
|
||||
let file_path = repository.path().join("large.txt");
|
||||
// write 33MB of random data in the file
|
||||
let mut file = std::fs::File::create(file_path).unwrap();
|
||||
for _ in 0..33 * 1024 {
|
||||
let data = [0u8; 1024];
|
||||
file.write_all(&data).unwrap();
|
||||
}
|
||||
|
||||
// create commit with large file
|
||||
fs::write(repository.path().join("file2.txt"), "content2").unwrap();
|
||||
fs::write(repository.path().join("file3.txt"), "content3").unwrap();
|
||||
let commit2_id = controller
|
||||
.create_commit(project_id, &branch_id, "commit two", None, false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Create conflict state
|
||||
let conflicts_path = repository.path().join(".git").join("conflicts");
|
||||
std::fs::write(&conflicts_path, "conflict A").unwrap();
|
||||
let base_merge_parent_path = repository.path().join(".git").join("base_merge_parent");
|
||||
std::fs::write(&base_merge_parent_path, "parent A").unwrap();
|
||||
|
||||
// create state with conflict state
|
||||
let _empty_branch_id = controller
|
||||
.create_virtual_branch(project_id, &branch::BranchCreateRequest::default())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
std::fs::remove_file(&base_merge_parent_path).unwrap();
|
||||
std::fs::remove_file(&conflicts_path).unwrap();
|
||||
|
||||
fs::write(repository.path().join("file4.txt"), "content4").unwrap();
|
||||
let _commit3_id = controller
|
||||
.create_commit(project_id, &branch_id, "commit three", None, false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let branch = controller
|
||||
.list_virtual_branches(project_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.0
|
||||
.into_iter()
|
||||
.find(|b| b.id == branch_id)
|
||||
.unwrap();
|
||||
|
||||
let branches = controller.list_virtual_branches(project_id).await.unwrap();
|
||||
assert_eq!(branches.0.len(), 2);
|
||||
|
||||
assert_eq!(branch.commits.len(), 3);
|
||||
assert_eq!(branch.commits[0].files.len(), 1);
|
||||
assert_eq!(branch.commits[1].files.len(), 3);
|
||||
|
||||
let snapshots = project.list_snapshots(10, None).unwrap();
|
||||
|
||||
let ops = snapshots
|
||||
.iter()
|
||||
.map(|c| &c.details.as_ref().unwrap().title)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
ops,
|
||||
vec![
|
||||
"CreateCommit",
|
||||
"CreateBranch",
|
||||
"CreateCommit",
|
||||
"CreateCommit",
|
||||
"CreateBranch",
|
||||
"SetBaseBranch",
|
||||
]
|
||||
);
|
||||
|
||||
project
|
||||
.restore_snapshot(snapshots[1].clone().id)
|
||||
.unwrap();
|
||||
|
||||
// restores the conflict files
|
||||
let file_lines = std::fs::read_to_string(&conflicts_path).unwrap();
|
||||
assert_eq!(file_lines, "conflict A");
|
||||
let file_lines = std::fs::read_to_string(&base_merge_parent_path).unwrap();
|
||||
assert_eq!(file_lines, "parent A");
|
||||
|
||||
assert_eq!(snapshots[2].lines_added, 2);
|
||||
assert_eq!(snapshots[2].lines_removed, 0);
|
||||
|
||||
project
|
||||
.restore_snapshot(snapshots[3].clone().id)
|
||||
.unwrap();
|
||||
|
||||
// the restore removed our new branch
|
||||
let branches = controller.list_virtual_branches(project_id).await.unwrap();
|
||||
assert_eq!(branches.0.len(), 1);
|
||||
|
||||
// assert that the conflicts file was removed
|
||||
assert!(!&conflicts_path.try_exists().unwrap());
|
||||
|
||||
// remove commit2_oid from odb
|
||||
let commit_str = &commit2_id.to_string();
|
||||
// find file in odb
|
||||
let file_path = repository.path().join(".git").join("objects").join(&commit_str[..2]);
|
||||
let file_path = file_path.join(&commit_str[2..]);
|
||||
assert!(file_path.exists());
|
||||
// remove file
|
||||
std::fs::remove_file(file_path).unwrap();
|
||||
|
||||
// try to look up that object
|
||||
let repo = git2::Repository::open(&project.path).unwrap();
|
||||
let commit = repo.find_commit(commit2_id.into());
|
||||
assert!(commit.is_err());
|
||||
|
||||
project
|
||||
.restore_snapshot(snapshots[2].clone().id)
|
||||
.unwrap();
|
||||
|
||||
// test missing commits are recreated
|
||||
let commit = repo.find_commit(commit2_id.into());
|
||||
assert!(commit.is_ok());
|
||||
|
||||
let file_path = repository.path().join("large.txt");
|
||||
assert!(file_path.exists());
|
||||
|
||||
let file_path = repository.path().join("file.txt");
|
||||
let file_lines = std::fs::read_to_string(file_path).unwrap();
|
||||
assert_eq!(file_lines, "content");
|
||||
}
|
||||
|
||||
// test oplog.toml head is not a commit
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_oplog_head_corrupt() {
|
||||
let Test {
|
||||
repository,
|
||||
project_id,
|
||||
controller,
|
||||
project,
|
||||
..
|
||||
} = &Test::default();
|
||||
|
||||
controller
|
||||
.set_base_branch(project_id, &"refs/remotes/origin/master".parse().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let snapshots = project.list_snapshots(10, None).unwrap();
|
||||
assert_eq!(snapshots.len(), 1);
|
||||
|
||||
// overwrite oplog head with a non-commit sha
|
||||
let file_path = repository.path().join(".git").join("operations-log.toml");
|
||||
fs::write(file_path, "head_sha = \"758d54f587227fba3da3b61fbb54a99c17903d59\"").unwrap();
|
||||
|
||||
controller
|
||||
.set_base_branch(project_id, &"refs/remotes/origin/master".parse().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// it should have just reset the oplog head, so only 1, not 2
|
||||
let snapshots = project.list_snapshots(10, None).unwrap();
|
||||
assert_eq!(snapshots.len(), 1);
|
||||
}
|
Loading…
Reference in New Issue
Block a user