use anyhow::{Result, bail};
use crate::core::hunk_select::{self, HunkArgs};
use crate::core::repo::{self, Target, TargetKind};
use crate::core::staging;
use crate::core::{graph, msg};
use crate::git;
pub fn run(
files: Vec<String>,
patch: bool,
hunks: HunkArgs,
git_args: Vec<String>,
theme: &graph::Theme,
) -> Result<()> {
if patch || files.is_empty() {
if !git_args.is_empty() {
bail!(
"staging hunks interactively takes no `git add` arguments after `--`\n\
Files go before the separator: `loom add <files> -- <git args>`"
);
}
return run_patch(files, hunks, theme);
}
let repo = repo::open_repo()?;
let workdir = repo::require_workdir(&repo, "add")?.to_path_buf();
let git_opts: Vec<&str> = git_args.iter().map(String::as_str).collect();
let report = |text: &str| {
if git_opts.is_empty() {
msg::success(text);
}
};
if files.iter().any(|f| f == "zz") {
git::stage_all_opts(&workdir, &git_opts)?;
report("Staged all changes");
return Ok(());
}
let mut paths = Vec::new();
for arg in &files {
match repo::resolve_arg(&repo, arg, &[TargetKind::File])? {
Target::File(path) => paths.push(path),
_ => unreachable!(),
}
}
let path_refs: Vec<&str> = paths.iter().map(|s| s.as_str()).collect();
git::stage_files_opts(&workdir, &path_refs, &git_opts)?;
report(&format!("Staged {} file(s)", paths.len()));
Ok(())
}
fn run_patch(files: Vec<String>, hunks: HunkArgs, theme: &graph::Theme) -> Result<()> {
let repo = repo::open_repo()?;
let workdir = repo::require_workdir(&repo, "add")?.to_path_buf();
let picker = hunk_select::worktree_picker(
hunks,
hunk_select::patch_command("loom add", &files),
None,
&[],
);
let filter = staging::filter_paths(&repo, &files)?;
let picked = staging::run_hunk_picker(
&repo,
&workdir,
filter.as_deref(),
&picker,
staging::LeftOut::Unstaged,
theme,
)?;
if picked.is_none() {
return Err(msg::cancelled());
}
Ok(())
}
#[cfg(test)]
#[path = "add_test.rs"]
mod tests;
#[cfg(test)]
#[path = "add_patch_test.rs"]
mod patch_tests;