use std::env;
use std::fs;
use std::iter;
use std::path::Path;
use std::process::{Command, Stdio};
use itertools::Itertools;
use log::{Level, LevelFilter, Log, Metadata, Record};
use tempfile::TempDir;
use crate::{CommitId, Conflict, GitContext, GitWorkArea, MergeResult, MergeStatus};
fn setup_logging() {
struct SimpleLogger;
impl Log for SimpleLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Debug
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!("[{}] {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
static LOGGER: SimpleLogger = SimpleLogger;
let _ = log::set_logger(&LOGGER);
log::set_max_level(LevelFilter::Debug);
}
pub fn test_workspace_dir() -> TempDir {
setup_logging();
let mut working_dir = env::current_exe().unwrap();
working_dir.pop();
TempDir::new_in(working_dir).unwrap()
}
fn git_context_from(workspace_path: &Path, origin: &Path) -> GitContext {
let gitdir = workspace_path.join("origin");
let clone = Command::new("git")
.arg("clone")
.arg("--bare")
.arg(origin)
.arg(&gitdir)
.output()
.unwrap();
if !clone.status.success() {
panic!(
"origin clone failed: {}",
String::from_utf8_lossy(&clone.stderr),
);
}
GitContext::new(gitdir)
}
fn git_context(workspace_path: &Path) -> GitContext {
git_context_from(
workspace_path,
concat!(env!("CARGO_MANIFEST_DIR"), "/.git").as_ref(),
)
}
fn git_context_submodule(workspace_path: &Path, commit: &CommitId) -> GitContext {
let gitdir = workspace_path.join("origin");
let clone = Command::new("git")
.arg("clone")
.arg(concat!(env!("CARGO_MANIFEST_DIR"), "/.git"))
.arg(&gitdir)
.output()
.unwrap();
if !clone.status.success() {
panic!(
"origin clone failed: {}",
String::from_utf8_lossy(&clone.stderr),
);
}
let ctx = GitContext::new(gitdir.join(".git"));
let checkout = ctx
.git()
.arg("checkout")
.arg(commit.as_str())
.current_dir(&gitdir)
.output()
.unwrap();
if !checkout.status.success() {
panic!(
"checkout failed: {}",
String::from_utf8_lossy(&checkout.stderr),
);
}
let submodule_update = ctx
.git()
.arg("submodule")
.arg("update")
.arg("--init")
.current_dir(&gitdir)
.output()
.unwrap();
if !submodule_update.status.success() {
panic!(
"submodule update failed: {}",
String::from_utf8_lossy(&submodule_update.stderr),
);
}
ctx
}
fn git_context_new(workspace_path: &Path) -> GitContext {
let gitdir = workspace_path.join("new");
fs::create_dir_all(&gitdir).unwrap();
let init = Command::new("git")
.arg("init")
.current_dir(&gitdir)
.output()
.unwrap();
if !init.status.success() {
panic!("init failed: {}", String::from_utf8_lossy(&init.stderr));
}
let ctx = GitContext::new(gitdir.join(".git"));
let commit = ctx
.git()
.arg("commit")
.arg("--allow-empty")
.args(&["-m", "root commit"])
.output()
.unwrap();
if !commit.status.success() {
panic!("commit failed: {}", String::from_utf8_lossy(&commit.stderr));
}
ctx
}
const ARBITRARY_COMMIT: &str = "9266430a205bc43fcb669a9b4825f2c69d36c3db";
const BASE_COMMIT: &str = "6787f648d93d32e70aea9e94c85914422940a6c0";
const SUBMODULE_ADD_CUSTOM_NAME: &str = "d340326994009b57e70e0fc90730ba668d90dab4";
const SUBMODULE_UPDATE_COMMIT: &str = "1d1ac896e2908685d6c20f78821add835cb110e8";
const SUBMODULE_REWIND_COMMIT: &str = "6995e3989758531fdbd988ece628e7307ab59266";
const SUBMODULE_BRANCH1_COMMIT: &str = "6e8ae702722e39626ac686929234eea0dc3d48de";
const SUBMODULE_BRANCH2_COMMIT: &str = "0465e50432fcd101e88dc61ecd81ef89b611a991";
const SUBMODULE_UNMERGED_COMMIT: &str = "d8df6fab975c401632aecc366d34f9a91da82500";
const SUBMODULE_NOT_PRESENT_COMMIT: &str = "5c3eee710420e98fde8818a688bfbf2bb1a96ff8";
const SUBMODULE_RESOLUTION: &str = "892c174777e7b9a80383f8cd60f91af72ace2df2";
#[test]
fn test_gitdir() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
assert_eq!(ctx.gitdir(), tempdir.path().join("origin"));
}
#[test]
fn test_default_branch_config() {
let tempdir = test_workspace_dir();
let ctx = git_context_new(tempdir.path());
let config = ctx
.git()
.arg("config")
.arg("init.defaultBranchName")
.arg("default-branch")
.output()
.unwrap();
if !config.status.success() {
panic!(
"failed to set init.defaultBranchName configuration: {}",
String::from_utf8_lossy(&config.stderr),
);
}
let default_branch = ctx.default_branch().unwrap();
assert_eq!(default_branch, Some("default-branch".into()));
}
#[test]
fn test_default_branch_origin_head() {
let tempdir = test_workspace_dir();
let origin_dir = tempdir.path().join("origin");
let clone_dir = tempdir.path().join("clone");
let ctx_origin = git_context_new(&origin_dir);
let ctx_clone = git_context_new(&clone_dir);
let rename = ctx_origin
.git()
.arg("checkout")
.arg("-b")
.arg("upstream-head")
.output()
.unwrap();
if !rename.status.success() {
panic!(
"failed to rename branch: {}",
String::from_utf8_lossy(&rename.stderr),
);
}
let default_branch = ctx_origin.default_branch().unwrap();
assert_eq!(default_branch, Some("upstream-head".into()));
let remote_add = ctx_clone
.git()
.arg("remote")
.arg("add")
.arg("origin")
.arg(ctx_origin.gitdir())
.output()
.unwrap();
if !remote_add.status.success() {
panic!(
"failed to add origin remote: {}",
String::from_utf8_lossy(&remote_add.stderr),
);
}
let refnames: &[&str] = &[];
ctx_clone.fetch("origin", refnames).unwrap();
let default_branch = ctx_clone.default_branch().unwrap();
assert_eq!(default_branch, Some("upstream-head".into()));
}
#[test]
fn test_default_branch_ls_remote() {
let tempdir = test_workspace_dir();
let origin_dir = tempdir.path().join("origin");
let clone_dir = tempdir.path().join("clone");
let ctx_origin = git_context_new(&origin_dir);
let rename = ctx_origin
.git()
.arg("checkout")
.arg("-b")
.arg("upstream-head")
.output()
.unwrap();
if !rename.status.success() {
panic!(
"failed to rename branch: {}",
String::from_utf8_lossy(&rename.stderr),
);
}
let default_branch = ctx_origin.default_branch().unwrap();
assert_eq!(default_branch, Some("upstream-head".into()));
let ctx_clone = git_context_from(&clone_dir, ctx_origin.gitdir());
let checkout = ctx_clone
.git()
.arg("branch")
.arg("-m")
.arg("new-local-branch")
.output()
.unwrap();
if !checkout.status.success() {
panic!(
"failed to checkout a new local branch: {}",
String::from_utf8_lossy(&checkout.stderr),
);
}
let default_branch = ctx_clone.default_branch().unwrap();
assert_eq!(default_branch, Some("upstream-head".into()));
}
#[test]
fn test_default_branch_head() {
let tempdir = test_workspace_dir();
let ctx = git_context_new(tempdir.path());
let checkout = ctx
.git()
.arg("checkout")
.arg("-b")
.arg("new-local-branch")
.output()
.unwrap();
if !checkout.status.success() {
panic!(
"failed to checkout a new local branch: {}",
String::from_utf8_lossy(&checkout.stderr),
);
}
let default_branch = ctx.default_branch().unwrap();
assert_eq!(default_branch, Some("new-local-branch".into()));
}
#[test]
fn test_reserve_ref() {
let tempdir = test_workspace_dir();
let ctx_0 = git_context(tempdir.path());
let ctx_1 = GitContext::new(ctx_0.gitdir());
let commit = CommitId::new(ARBITRARY_COMMIT);
let reserve_ref_check = |ctx: &GitContext, name, expected| {
let (refname, count) = ctx.reserve_ref(name, &commit).unwrap();
assert_eq!(count, expected);
assert_eq!(refname, format!("refs/{}/heads/{}", name, expected));
let rev_parse = ctx
.git()
.arg("rev-parse")
.arg("--verify")
.arg(refname)
.output()
.unwrap();
assert!(rev_parse.status.success());
let actual_ref = String::from_utf8_lossy(&rev_parse.stdout);
assert_eq!(actual_ref.trim(), commit.as_str());
};
reserve_ref_check(&ctx_0, "example", 0);
reserve_ref_check(&ctx_1, "example", 1);
reserve_ref_check(&ctx_0, "example", 2);
reserve_ref_check(&ctx_1, "example", 3);
reserve_ref_check(&ctx_1, "other", 0);
reserve_ref_check(&ctx_0, "other", 1);
reserve_ref_check(&ctx_1, "other", 2);
reserve_ref_check(&ctx_0, "other", 3);
}
#[test]
fn test_reserve_ref_no_exist() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let commit = CommitId::new("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
let res = ctx.reserve_ref("name", &commit);
assert!(res.is_err());
}
#[test]
fn test_reserve_refs() {
let tempdir = test_workspace_dir();
let ctx_0 = git_context(tempdir.path());
let ctx_1 = GitContext::new(ctx_0.gitdir());
let commit = CommitId::new(ARBITRARY_COMMIT);
let reserve_refs_check = |ctx: &GitContext, name, expected| {
let (head, base) = ctx.reserve_refs(name, &commit).unwrap();
assert_eq!(head, format!("refs/{}/heads/{}", name, expected));
assert_eq!(base, format!("refs/{}/bases/{}", name, expected));
let rev_parse_head = ctx
.git()
.arg("rev-parse")
.arg("--verify")
.arg(head)
.output()
.unwrap();
assert!(rev_parse_head.status.success());
let actual_ref = String::from_utf8_lossy(&rev_parse_head.stdout);
assert_eq!(actual_ref.trim(), commit.as_str());
let rev_parse_base = ctx
.git()
.arg("rev-parse")
.arg("--verify")
.arg(base)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.unwrap();
assert!(!rev_parse_base.success())
};
reserve_refs_check(&ctx_0, "example", 0);
reserve_refs_check(&ctx_1, "example", 1);
reserve_refs_check(&ctx_0, "example", 2);
reserve_refs_check(&ctx_1, "example", 3);
reserve_refs_check(&ctx_1, "other", 0);
reserve_refs_check(&ctx_0, "other", 1);
reserve_refs_check(&ctx_1, "other", 2);
reserve_refs_check(&ctx_0, "other", 3);
}
#[test]
fn test_mergeable() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let base = CommitId::new(BASE_COMMIT);
let topic = CommitId::new(SUBMODULE_UPDATE_COMMIT);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::Mergeable(bases) = status {
assert_eq!(bases, &[base]);
} else {
panic!(
"topic {} should be mergeable into {}: {:?}",
topic, base, status,
);
}
}
#[test]
fn test_mergeable_already_merged() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let base = CommitId::new(SUBMODULE_UPDATE_COMMIT);
let topic = CommitId::new(BASE_COMMIT);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::AlreadyMerged = status {
} else {
panic!(
"topic {} should already be merged into {}: {:?}",
topic, base, status,
);
}
}
#[test]
fn test_mergeable_conflict() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let base = CommitId::new(SUBMODULE_BRANCH1_COMMIT);
let topic = CommitId::new(SUBMODULE_BRANCH2_COMMIT);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::Mergeable(bases) = status {
assert_eq!(bases, &[CommitId::new(BASE_COMMIT)]);
} else {
panic!(
"topic {} should be mergeable into {}: {:?}",
topic, base, status,
);
}
}
#[test]
fn test_mergeable_no_common_history() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let ctx_other = git_context_new(tempdir.path());
let refname = "other_root";
ctx.fetch_into(ctx_other.gitdir().to_string_lossy(), "master", refname)
.unwrap();
let base = CommitId::new(ARBITRARY_COMMIT);
let topic = CommitId::new(refname);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::NoCommonHistory = status {
} else {
panic!(
"topic {} should not have a common history with {}: {:?}",
topic, base, status,
);
}
}
fn check_workarea(workarea: &GitWorkArea, commit: &CommitId) {
let rev_parse = workarea
.git()
.arg("rev-parse")
.arg(format!("{}^{{tree}}", commit))
.output()
.unwrap();
assert!(rev_parse.status.success());
let write_tree = workarea.git().arg("write-tree").output().unwrap();
assert!(write_tree.status.success());
let actual = String::from_utf8_lossy(&write_tree.stdout);
let expected = String::from_utf8_lossy(&rev_parse.stdout);
assert_eq!(actual.trim(), expected.trim());
}
#[test]
fn test_work_area_setup() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let commit = CommitId::new(ARBITRARY_COMMIT);
let workarea = ctx.prepare(&commit).unwrap();
check_workarea(&workarea, &commit);
assert!(workarea.submodule_config().is_empty());
}
#[test]
fn test_work_area_setup_no_exist() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let commit = CommitId::new("0000000000000000000000000000000000000000");
let res = ctx.prepare(&commit);
assert!(res.is_err());
}
#[test]
fn test_work_area_setup_submodule_missing_links() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let commit = CommitId::new(BASE_COMMIT);
let workarea = ctx.prepare(&commit).unwrap();
check_workarea(&workarea, &commit);
assert!(workarea.submodule_config().is_empty());
}
#[test]
fn test_work_area_setup_submodule() {
let tempdir = test_workspace_dir();
let commit = CommitId::new(BASE_COMMIT);
let ctx = git_context_submodule(tempdir.path(), &commit);
let workarea = ctx.prepare(&commit).unwrap();
check_workarea(&workarea, &commit);
assert!(!workarea.submodule_config().is_empty());
let submodule_config = workarea
.submodule_config()
.iter()
.map(|(name, config)| {
(
name.as_str(),
config
.iter()
.map(|(key, value)| (key.as_str(), value.as_str()))
.sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
.collect::<Vec<_>>(),
)
})
.sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
.collect::<Vec<_>>();
assert_eq!(
submodule_config,
&[(
"submodule",
vec![
("path", "submodule"),
("url", "https://gitlab.kitware.com/utils/test-repo.git"),
]
)],
);
}
#[test]
fn test_work_area_setup_submodule_custom_name() {
let tempdir = test_workspace_dir();
let commit = CommitId::new(SUBMODULE_ADD_CUSTOM_NAME);
let ctx = git_context_submodule(tempdir.path(), &commit);
let workarea = ctx.prepare(&commit).unwrap();
check_workarea(&workarea, &commit);
assert!(!workarea.submodule_config().is_empty());
let submodule_config = workarea
.submodule_config()
.iter()
.map(|(name, config)| {
(
name.as_str(),
config
.iter()
.map(|(key, value)| (key.as_str(), value.as_str()))
.sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
.collect::<Vec<_>>(),
)
})
.sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
.collect::<Vec<_>>();
assert_eq!(
submodule_config,
&[(
"custom-name",
vec![
("path", "submodule"),
("url", "https://gitlab.kitware.com/utils/test-repo.git"),
]
)],
);
}
#[test]
fn test_setup_merge() {
let tempdir = test_workspace_dir();
let base = CommitId::new(BASE_COMMIT);
let ctx = git_context_submodule(tempdir.path(), &base);
let workarea = ctx.prepare(&base).unwrap();
assert!(!workarea.submodule_config().is_empty());
let topic = CommitId::new(SUBMODULE_UPDATE_COMMIT);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::Mergeable(bases) = status {
let merge = workarea.setup_merge(&bases, &base, &topic).unwrap();
if let MergeResult::Ready(command) = merge {
let commit = command.commit("commit message").unwrap();
let rev_parse = ctx
.git()
.arg("rev-parse")
.arg("--verify")
.arg(format!("{}^{{tree}}", commit))
.output()
.unwrap();
assert!(rev_parse.status.success());
let merged_tree = String::from_utf8_lossy(&rev_parse.stdout);
assert_eq!(
merged_tree.trim(),
"254e4e42cb914f935679319389e2af07eebb2157",
);
} else {
panic!(
"expected the merge from {} into {} to be fine: {:?}",
topic, base, merge,
);
}
} else {
panic!(
"topic {} should be mergeable into {}: {:?}",
topic, base, status,
);
}
}
#[test]
fn test_setup_merge_ours() {
let tempdir = test_workspace_dir();
let base = CommitId::new(BASE_COMMIT);
let ctx = git_context_submodule(tempdir.path(), &base);
let workarea = ctx.prepare(&base).unwrap();
assert!(!workarea.submodule_config().is_empty());
let topic = CommitId::new(SUBMODULE_UPDATE_COMMIT);
let merge = workarea.setup_update_merge(&base, &topic).unwrap();
if let MergeResult::Ready(command) = merge {
let commit = command.commit("commit message").unwrap();
let rev_parse = ctx
.git()
.arg("rev-parse")
.arg("--verify")
.arg(format!("{}^{{tree}}", commit))
.output()
.unwrap();
assert!(rev_parse.status.success());
let merged_tree = String::from_utf8_lossy(&rev_parse.stdout);
assert_eq!(
merged_tree.trim(),
"d6e9a7ef309d55ab0bc68e978e34bea528d7bb94",
);
} else {
panic!(
"expected the merge from {} into {} to be fine: {:?}",
topic, base, merge,
);
}
}
#[test]
fn test_setup_merge_rewind() {
let tempdir = test_workspace_dir();
let base = CommitId::new(BASE_COMMIT);
let ctx = git_context_submodule(tempdir.path(), &base);
let workarea = ctx.prepare(&base).unwrap();
assert!(!workarea.submodule_config().is_empty());
let topic = CommitId::new(SUBMODULE_REWIND_COMMIT);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::Mergeable(bases) = status {
let merge = workarea.setup_merge(&bases, &base, &topic).unwrap();
if let MergeResult::Ready(_) = merge {
} else {
panic!(
"expected the merge from {} into {} to be fine: {:?}",
topic, base, merge,
);
}
} else {
panic!(
"topic {} should be mergeable into {}: {:?}",
topic, base, status,
);
}
}
#[test]
fn test_setup_merge_conflict() {
let tempdir = test_workspace_dir();
let ctx = git_context(tempdir.path());
let base = CommitId::new(SUBMODULE_BRANCH1_COMMIT);
let workarea = ctx.prepare(&base).unwrap();
assert!(workarea.submodule_config().is_empty());
let topic = CommitId::new(SUBMODULE_BRANCH2_COMMIT);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::Mergeable(bases) = status {
let merge = workarea.setup_merge(&bases, &base, &topic).unwrap();
if let MergeResult::Conflict(conflicts) = merge {
assert_eq!(
conflicts
.iter()
.map(|conflict| conflict.path().to_string_lossy())
.collect::<Vec<_>>(),
&["submodule".to_string()],
);
if let Conflict::Path(_) = conflicts[0] {
} else {
panic!(
"expected the merge from {} into {} have a plain conflict: {:?}",
topic, base, conflicts[0],
);
}
} else {
panic!(
"expected the merge from {} into {} to conflict: {:?}",
topic, base, merge,
);
}
} else {
panic!(
"topic {} should be mergeable into {}: {:?}",
topic, base, status,
);
}
}
#[test]
fn test_setup_merge_submodule_conflict_resolution() {
let tempdir = test_workspace_dir();
let base = CommitId::new(SUBMODULE_BRANCH1_COMMIT);
let ctx = git_context_submodule(tempdir.path(), &base);
let workarea = ctx.prepare(&base).unwrap();
assert!(!workarea.submodule_config().is_empty());
let topic = CommitId::new(SUBMODULE_BRANCH2_COMMIT);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::Mergeable(bases) = status {
let merge = workarea.setup_merge(&bases, &base, &topic).unwrap();
if let MergeResult::Conflict(conflicts) = merge {
assert_eq!(
conflicts
.iter()
.map(|conflict| conflict.path().to_string_lossy())
.collect::<Vec<_>>(),
&["submodule".to_string()],
);
if let Conflict::SubmoduleWithFix(_, ref fix) = conflicts[0] {
assert_eq!(fix, &CommitId::new(SUBMODULE_RESOLUTION));
} else {
panic!(
"expected the merge from {} into {} have a conflict resolution: {:?}",
topic, base, conflicts[0],
);
}
} else {
panic!(
"expected the merge from {} into {} to conflict: {:?}",
topic, base, merge,
);
}
} else {
panic!(
"topic {} should be mergeable into {}: {:?}",
topic, base, status,
);
}
}
#[test]
fn test_setup_merge_submodule_not_merged() {
let tempdir = test_workspace_dir();
let base = CommitId::new(BASE_COMMIT);
let ctx = git_context_submodule(tempdir.path(), &base);
let workarea = ctx.prepare(&base).unwrap();
assert!(!workarea.submodule_config().is_empty());
let topic = CommitId::new(SUBMODULE_UNMERGED_COMMIT);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::Mergeable(bases) = status {
let merge = workarea.setup_merge(&bases, &base, &topic).unwrap();
if let MergeResult::Ready(_) = merge {
} else {
panic!(
"expected the merge from {} into {} to be fine: {:?}",
topic, base, merge,
);
}
} else {
panic!(
"topic {} should be mergeable into {}: {:?}",
topic, base, status,
);
}
}
#[test]
fn test_setup_merge_submodule_not_merged_conflict() {
let tempdir = test_workspace_dir();
let base = CommitId::new(SUBMODULE_BRANCH1_COMMIT);
let ctx = git_context_submodule(tempdir.path(), &base);
let workarea = ctx.prepare(&base).unwrap();
assert!(!workarea.submodule_config().is_empty());
let topic = CommitId::new(SUBMODULE_UNMERGED_COMMIT);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::Mergeable(bases) = status {
let merge = workarea.setup_merge(&bases, &base, &topic).unwrap();
if let MergeResult::Conflict(conflicts) = merge {
assert_eq!(
conflicts
.iter()
.map(|conflict| conflict.path().to_string_lossy())
.collect::<Vec<_>>(),
&["submodule".to_string()],
);
if let Conflict::SubmoduleNotMerged(_) = conflicts[0] {
} else {
panic!(
"expected the merge from {} into {} detect \
that the commit has not been merged: {:?}",
topic, base, conflicts[0],
);
}
} else {
panic!(
"expected the merge from {} into {} to conflict: {:?}",
topic, base, merge,
);
}
} else {
panic!(
"topic {} should be mergeable into {}: {:?}",
topic, base, status,
);
}
}
#[test]
fn test_setup_merge_submodule_not_present() {
let tempdir = test_workspace_dir();
let base = CommitId::new(SUBMODULE_BRANCH1_COMMIT);
let ctx = git_context_submodule(tempdir.path(), &base);
let workarea = ctx.prepare(&base).unwrap();
assert!(!workarea.submodule_config().is_empty());
let topic = CommitId::new(SUBMODULE_NOT_PRESENT_COMMIT);
let status = ctx.mergeable(&base, &topic).unwrap();
if let MergeStatus::Mergeable(bases) = status {
let merge = workarea.setup_merge(&bases, &base, &topic).unwrap();
if let MergeResult::Conflict(conflicts) = merge {
assert_eq!(
conflicts
.iter()
.map(|conflict| conflict.path().to_string_lossy())
.collect::<Vec<_>>(),
&["submodule".to_string()],
);
if let Conflict::SubmoduleNotPresent(_) = conflicts[0] {
} else {
panic!(
"expected the merge from {} into {} to complain \
that the commit is not available: {:?}",
topic, base, conflicts[0],
);
}
} else {
panic!(
"expected the merge from {} into {} to conflict: {:?}",
topic, base, merge,
);
}
} else {
panic!(
"topic {} should be mergeable into {}: {:?}",
topic, base, status,
);
}
}
#[test]
fn test_work_area_checkout() {
let tempdir = test_workspace_dir();
let base = CommitId::new(ARBITRARY_COMMIT);
let ctx = git_context(tempdir.path());
let mut workarea = ctx.prepare(&base).unwrap();
assert!(workarea.submodule_config().is_empty());
assert!(!workarea.__work_tree().join("rustfmt.toml").exists());
workarea.checkout(iter::once("rustfmt.toml")).unwrap();
assert!(workarea.__work_tree().join("rustfmt.toml").exists());
assert!(!workarea.__work_tree().join("Cargo.toml").exists());
workarea.checkout(iter::once("*.toml")).unwrap();
assert!(workarea.__work_tree().join("Cargo.toml").exists());
assert!(!workarea.__work_tree().join("src").exists());
workarea.checkout(iter::once("src/")).unwrap();
assert!(workarea.__work_tree().join("src").exists());
assert!(workarea.__work_tree().join("src/lib.rs").exists());
assert!(!workarea.__work_tree().join("README.md").exists());
workarea.checkout(&["*.md", "does-not-exist"]).unwrap();
assert!(workarea.__work_tree().join("README.md").exists());
}