#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct MoveHeadFlags {
pub force: bool,
}
pub struct MoveHeadContext {
pub active: bool,
pub populated: bool,
pub has_dirty_index: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MoveHeadVerdict {
Ok,
WouldLose,
}
pub fn check_move_head(
ctx: &MoveHeadContext,
old_head: Option<&str>,
_new_head: Option<&str>,
flags: MoveHeadFlags,
) -> MoveHeadVerdict {
if !ctx.active {
return MoveHeadVerdict::Ok;
}
if old_head.is_some() && !ctx.populated {
return MoveHeadVerdict::Ok;
}
if old_head.is_some() && !flags.force && ctx.has_dirty_index {
return MoveHeadVerdict::WouldLose;
}
MoveHeadVerdict::Ok
}
pub fn check_submodule_move_head(
is_submodule: bool,
ctx: &MoveHeadContext,
old_id: Option<&str>,
new_id: Option<&str>,
reset: bool,
) -> MoveHeadVerdict {
if !is_submodule {
return MoveHeadVerdict::Ok;
}
let flags = MoveHeadFlags { force: reset };
check_move_head(ctx, old_id, new_id, flags)
}
pub fn verify_clean_submodule(
is_submodule: bool,
ctx: &MoveHeadContext,
old_sha1: Option<&str>,
new_oid_hex: &str,
reset: bool,
) -> MoveHeadVerdict {
if !is_submodule {
return MoveHeadVerdict::Ok;
}
check_submodule_move_head(true, ctx, old_sha1, Some(new_oid_hex), reset)
}
#[cfg(test)]
mod tests {
use super::*;
fn ctx(active: bool, populated: bool, dirty: bool) -> MoveHeadContext {
MoveHeadContext {
active,
populated,
has_dirty_index: dirty,
}
}
#[test]
fn inactive_submodule_is_always_ok() {
let c = ctx(false, true, true);
assert_eq!(
check_move_head(&c, Some("old"), Some("new"), MoveHeadFlags { force: false }),
MoveHeadVerdict::Ok
);
}
#[test]
fn unpopulated_with_old_head_is_ok() {
let c = ctx(true, false, true);
assert_eq!(
check_move_head(&c, Some("old"), Some("new"), MoveHeadFlags::default()),
MoveHeadVerdict::Ok
);
}
#[test]
fn dirty_index_blocks_non_forced_move() {
let c = ctx(true, true, true);
assert_eq!(
check_move_head(&c, Some("old"), Some("new"), MoveHeadFlags { force: false }),
MoveHeadVerdict::WouldLose
);
}
#[test]
fn force_bypasses_dirty_index() {
let c = ctx(true, true, true);
assert_eq!(
check_move_head(&c, Some("old"), Some("new"), MoveHeadFlags { force: true }),
MoveHeadVerdict::Ok
);
}
#[test]
fn coming_into_existence_skips_dirty_check() {
let c = ctx(true, true, true);
assert_eq!(
check_move_head(&c, None, Some("new"), MoveHeadFlags::default()),
MoveHeadVerdict::Ok
);
}
#[test]
fn unpack_trees_wrappers_short_circuit_non_submodule() {
let c = ctx(true, true, true);
assert_eq!(
check_submodule_move_head(false, &c, Some("old"), Some("new"), false),
MoveHeadVerdict::Ok
);
assert_eq!(
verify_clean_submodule(false, &c, Some("old"), "newoid", false),
MoveHeadVerdict::Ok
);
}
#[test]
fn verify_clean_submodule_rejects_dirty() {
let c = ctx(true, true, true);
assert_eq!(
verify_clean_submodule(true, &c, Some("old"), "newoid", false),
MoveHeadVerdict::WouldLose
);
}
#[test]
fn reset_sets_force() {
let c = ctx(true, true, true);
assert_eq!(
check_submodule_move_head(true, &c, Some("old"), Some("new"), true),
MoveHeadVerdict::Ok
);
}
}