100 lines
3.5 KiB
Rust
100 lines
3.5 KiB
Rust
//! Git handling for `pkh new`: initialize a repository in the scaffolded
|
|
//! tree unless it is already inside one (the `.gitignore`s are written
|
|
//! regardless).
|
|
|
|
use std::path::Path;
|
|
|
|
/// Ensure `dir` has a git repository when it should: when `dir` is already
|
|
/// inside a work tree (its own or a parent's), nothing is initialized and
|
|
/// `Ok(false)` is returned with an info log; otherwise a repository is
|
|
/// initialized in `dir` when `init` is set (the `--no-git` case passes
|
|
/// `init = false`).
|
|
pub fn ensure_repository(dir: &Path, init: bool) -> Result<bool, Box<dyn std::error::Error>> {
|
|
match git2::Repository::discover(dir) {
|
|
Ok(_) => {
|
|
log::info!(
|
|
"Already inside a git repository; skipping git init \
|
|
(the .gitignore files are written anyway)"
|
|
);
|
|
Ok(false)
|
|
}
|
|
Err(_) if init => {
|
|
git2::Repository::init(dir)?;
|
|
log::info!("Initialized empty git repository in {}", dir.display());
|
|
Ok(true)
|
|
}
|
|
// --no-git: gitignores only.
|
|
Err(_) => Ok(false),
|
|
}
|
|
}
|
|
|
|
/// Whether `dir` sits inside a git work tree (its own or a parent's), in the
|
|
/// spirit of `git rev-parse --is-inside-work-tree` (like
|
|
/// [`crate::new::origin::GitOrigin::detect`]). Fail-soft: when `git` cannot
|
|
/// be run or the probe fails, `dir` counts as outside — the caller keeps the
|
|
/// behavior it would have without the probe, and the scaffold-time
|
|
/// repository discovery in [`ensure_repository`] has the final word anyway.
|
|
pub fn inside_work_tree(dir: &Path) -> bool {
|
|
let Ok(output) = std::process::Command::new("git")
|
|
.args(["rev-parse", "--is-inside-work-tree"])
|
|
.current_dir(dir)
|
|
.output()
|
|
else {
|
|
return false;
|
|
};
|
|
output.status.success() && String::from_utf8_lossy(&output.stdout).trim() == "true"
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use tempfile::tempdir;
|
|
|
|
#[test]
|
|
fn init_skipped_with_no_git() {
|
|
let dir = tempdir().unwrap();
|
|
assert!(!ensure_repository(dir.path(), false).unwrap());
|
|
assert!(!dir.path().join(".git").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn init_creates_repository() {
|
|
let dir = tempdir().unwrap();
|
|
assert!(ensure_repository(dir.path(), true).unwrap());
|
|
assert!(dir.path().join(".git").exists());
|
|
|
|
// A second call discovers the fresh repository and skips init.
|
|
assert!(!ensure_repository(dir.path(), true).unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn parent_repository_is_discovered() {
|
|
let dir = tempdir().unwrap();
|
|
let sub = dir.path().join("sub");
|
|
std::fs::create_dir_all(&sub).unwrap();
|
|
git2::Repository::init(dir.path()).unwrap();
|
|
|
|
// The subdirectory is already inside the parent work tree.
|
|
assert!(!ensure_repository(&sub, true).unwrap());
|
|
assert!(!sub.join(".git").exists());
|
|
}
|
|
|
|
/// The probe answers like `git rev-parse --is-inside-work-tree`:
|
|
/// outside any repository it says no, inside one (at any depth, as a
|
|
/// skeleton mode scaffold would probe through its parent directory) it
|
|
/// says yes without touching the tree.
|
|
#[test]
|
|
fn inside_work_tree_follows_the_parent_repository() {
|
|
let dir = tempdir().unwrap();
|
|
assert!(!inside_work_tree(dir.path()));
|
|
|
|
git2::Repository::init(dir.path()).unwrap();
|
|
assert!(inside_work_tree(dir.path()));
|
|
|
|
let sub = dir.path().join("sub");
|
|
std::fs::create_dir(&sub).unwrap();
|
|
assert!(inside_work_tree(&sub));
|
|
assert!(!sub.join(".git").exists());
|
|
}
|
|
}
|