Skip to main content

nb_api/
git.rs

1//! Git repository detection helpers.
2//!
3//! Consumed by `nb-api` directly and by downstream tools (e.g.,
4//! `nb-mcp-server`'s `paths.rs`) via the published crate.
5
6use std::path::PathBuf;
7
8/// Derive the notebook name from the current Git repository.
9///
10/// Returns the directory name of the master repository (not the worktree).
11/// Used as a fallback when no explicit notebook name is configured.
12pub fn derive_git_notebook_name() -> Option<String> {
13    let current_root = git_rev_parse(&["--show-toplevel"])?;
14    let git_common_dir = git_rev_parse(&["--git-common-dir"])?;
15    let git_common_dir = if git_common_dir.is_relative() {
16        current_root.join(&git_common_dir)
17    } else {
18        git_common_dir
19    };
20    let git_common_dir = git_common_dir.canonicalize().ok()?;
21    let master_root = if git_common_dir.file_name().is_some_and(|n| n == ".git") {
22        git_common_dir.parent()?.to_path_buf()
23    } else {
24        return None;
25    };
26    master_root
27        .file_name()
28        .and_then(|name| name.to_str())
29        .map(|name| name.to_string())
30}
31
32/// Run `git rev-parse` with the given arguments and return the output as a path.
33///
34/// Returns `None` if git is not available, the command fails, or the output is empty.
35pub fn git_rev_parse(args: &[&str]) -> Option<PathBuf> {
36    let output = std::process::Command::new("git")
37        .args(["rev-parse"])
38        .args(args)
39        .output()
40        .ok()?;
41    if !output.status.success() {
42        return None;
43    }
44    let stdout = String::from_utf8(output.stdout).ok()?;
45    let value = stdout.trim();
46    if value.is_empty() {
47        return None;
48    }
49    Some(PathBuf::from(value))
50}