xtask/src/worktree.rs
//! Where this checkout sits relative to the repository's main working tree.
//!
//! One implementation, used by two callers that otherwise could not agree
//! (one implementation, not two): `ci`'s znippy-engine decision, which refuses rather than shrinking its
//! own scope when znippy is beside the MAIN checkout and not this one, and
//! `fmt`, which has the same problem for the same reason — several manifests
//! in this repository reach sibling repositories by relative path, and cargo
//! resolves those relative to the manifest, not to the main checkout.
use std::path::{Path, PathBuf};
use std::process::Command;
/// The main working tree of the repository at `root`, if `root` is a **linked**
/// worktree of one.
///
/// `git rev-parse --git-common-dir` is the only thing that knows: it names the
/// shared `.git` directory, whose parent is the main checkout. `None` when
/// `root` is itself the main checkout (the common dir is its own `.git`), when
/// git cannot be reached, or when the layout is not the ordinary one — every
/// one of which lands on `ZnippyCheckout::AbsentEverywhere`, i.e. exactly the
/// behaviour before this function existed.
pub fn main_worktree(root: &Path) -> Option<PathBuf> {
let out = Command::new("git")
.current_dir(root)
.args(["rev-parse", "--path-format=absolute", "--git-common-dir"])
.output()
.ok()?;
if !out.status.success() {
return None;
}
let common = PathBuf::from(String::from_utf8_lossy(&out.stdout).trim().to_string());
let main = common.parent()?.to_path_buf();
// The main checkout answers with its own `.git`, so its parent is `root`
// again. Comparing canonical paths, because `root` and the git answer can
// spell the same directory differently through a symlink.
let same = std::fs::canonicalize(&main).ok()? == std::fs::canonicalize(root).ok()?;
if same {
None
} else {
Some(main)
}
}