use std::process::ExitCode;
use crate::git_changes::{Changeset, Lines};
pub(crate) fn resolve(
mr: bool,
uncommitted: bool,
explicit_paths: bool,
full: bool,
everything: bool,
) -> Result<Option<Changeset>, String> {
if !(mr || uncommitted || (!explicit_paths && !full && !everything)) {
return Ok(None);
}
if uncommitted {
return Changeset::load("HEAD").map(Some);
}
let head = Changeset::load("HEAD");
if mr {
return branch_scope(head);
}
smart_default(head)
}
fn smart_default(head: Result<Changeset, String>) -> Result<Option<Changeset>, String> {
match head {
Ok(cs) if !cs.files.is_empty() => {
eprintln!(
"note: uncommitted changes detected; scanning uncommitted work only \
(--mr adds the branch diff vs its base)"
);
Ok(Some(cs))
}
head => branch_scope(head),
}
}
fn branch_scope(head: Result<Changeset, String>) -> Result<Option<Changeset>, String> {
match load_mr_scope() {
Ok(mr_cs) => Ok(Some(union_with_head(mr_cs, head))),
Err(e) => head_fallback(head, &e),
}
}
fn head_fallback(
head: Result<Changeset, String>,
reason: &str,
) -> Result<Option<Changeset>, String> {
match head {
Ok(h) if !h.files.is_empty() => {
eprintln!("note: no MR scope ({reason}); scanning uncommitted HEAD work only");
Ok(Some(h))
}
_ => {
eprintln!("note: no MR scope ({reason}); scanning the full tree");
Ok(None)
}
}
}
fn union_with_head(base: Changeset, extra: Result<Changeset, String>) -> Changeset {
let Ok(extra) = extra else { return base };
use std::collections::btree_map::Entry;
let mut base = base;
for (path, lines) in extra.files {
match base.files.entry(path) {
Entry::Vacant(v) => {
v.insert(lines);
}
Entry::Occupied(mut o) => {
let merged = match (o.get(), lines) {
(Lines::All, _) | (_, Lines::All) => Lines::All,
(Lines::Ranges(a), Lines::Ranges(b)) => {
let mut u = a.clone();
u.extend(b);
Lines::Ranges(u)
}
};
o.insert(merged);
}
}
}
base
}
fn load_mr_scope() -> Result<Changeset, String> {
let (base, label) = crate::mr_scope::mr_base()?;
eprintln!("--mr scope: {label} (base {base})");
Changeset::load(&base)
}
pub(crate) fn error(e: String) -> ExitCode {
eprintln!("{e}");
ExitCode::from(2)
}
pub(crate) fn note_if_empty(changeset: Option<&Changeset>, files: &[std::path::PathBuf]) {
if changeset.is_some() && files.is_empty() {
eprintln!("note: nothing changed in scope; try --full to scan the whole tree");
}
}