use crate::ops::oplog;
use anyhow::Result;
use std::path::Path;
pub fn add(path: &Path, pathspecs: &[String], all: bool, update: bool) -> Result<()> {
let desc = if all {
"add -A".to_string()
} else if update {
"add -u".to_string()
} else {
format!("add {}", pathspecs.join(" "))
};
oplog::with_oplog(path, "add", &desc, || {
add_inner(path, pathspecs, all, update)
})
}
fn add_inner(path: &Path, pathspecs: &[String], all: bool, update: bool) -> Result<()> {
let repo = crate::ops::open_repo(path)?;
let mut index = repo.index()?;
if all {
index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?;
index.add_all(
["*"].iter(),
git2::IndexAddOption::DEFAULT | git2::IndexAddOption::CHECK_PATHSPEC,
None,
)?;
} else if update {
index.update_all(["*"].iter(), None)?;
} else if pathspecs.len() == 1 && pathspecs[0] == "." {
index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?;
} else {
for spec in pathspecs {
let full_path = path.join(spec);
if full_path.is_dir() {
index.add_all(
[format!("{}/**", spec), spec.to_string()].iter(),
git2::IndexAddOption::DEFAULT,
None,
)?;
} else {
index.add_path(Path::new(spec))?;
}
}
}
index.write()?;
Ok(())
}
pub fn reset_file(path: &Path, pathspecs: &[String]) -> Result<()> {
oplog::with_oplog(
path,
"reset",
&format!("unstage {}", pathspecs.join(" ")),
|| reset_file_inner(path, pathspecs),
)
}
fn reset_file_inner(path: &Path, pathspecs: &[String]) -> Result<()> {
let repo = crate::ops::open_repo(path)?;
let head = repo.head()?.peel_to_commit()?;
let head_tree = head.tree()?;
let mut index = repo.index()?;
for spec in pathspecs {
if let Ok(entry) = head_tree.get_path(Path::new(spec)) {
let idx_entry = git2::IndexEntry {
ctime: git2::IndexTime::new(0, 0),
mtime: git2::IndexTime::new(0, 0),
dev: 0,
ino: 0,
mode: entry.filemode() as u32,
uid: 0,
gid: 0,
file_size: 0,
id: entry.id(),
flags: 0,
flags_extended: 0,
path: spec.as_bytes().to_vec(),
};
index.add(&idx_entry)?;
} else {
index.remove_path(Path::new(spec))?;
}
}
index.write()?;
Ok(())
}