diff --git a/src/python.rs b/src/python.rs index ecc15fd..c041bad 100644 --- a/src/python.rs +++ b/src/python.rs @@ -57,8 +57,9 @@ fn is_clean(_path: String) -> PyResult { } #[pyfunction] -fn current_branch(_path: String) -> PyResult> { - todo!("repo::current_branch (None if detached)") +fn current_branch(path: String) -> PyResult> { + // soft-fail: any error -> None (API.md) + Ok(crate::repo::current_branch(std::path::Path::new(&path)).unwrap_or(None)) } #[pyfunction] diff --git a/src/repo/current_branch.rs b/src/repo/current_branch.rs new file mode 100644 index 0000000..09630c8 --- /dev/null +++ b/src/repo/current_branch.rs @@ -0,0 +1,38 @@ +use crate::error::GitxtendError; +use crate::repo::Result; +use std::path::Path; + +/// Short name of the current branch (e.g. "main"); `Ok(None)` when HEAD is +/// detached (mirrors `git rev-parse --abbrev-ref HEAD`, which prints "HEAD" +/// when detached — we return None in that case). +pub fn current_branch(path: &Path) -> Result> { + let repo = gix::open(path).map_err(GitxtendError::from_err)?; + let head = repo.head().map_err(GitxtendError::from_err)?; + Ok(head.referent_name().map(|n| n.shorten().to_string())) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::repo::fixtures; + + #[test] + fn test_current_branch_on_main() { + let td = fixtures::repo(); + let p = td.path(); + assert_eq!(current_branch(p).unwrap(), Some("main".into())); + assert_eq!( + current_branch(p).unwrap(), + Some(fixtures::git(p, &["rev-parse", "--abbrev-ref", "HEAD"])) + ); + } + + #[test] + fn test_current_branch_detached_head() { + let td = fixtures::repo(); + let p = td.path(); + let sha = fixtures::git(p, &["rev-parse", "HEAD"]); + fixtures::git(p, &["checkout", "--detach", &sha]); + assert_eq!(current_branch(p).unwrap(), None); + } +} diff --git a/src/repo/mod.rs b/src/repo/mod.rs index 994d8d4..d14428f 100644 --- a/src/repo/mod.rs +++ b/src/repo/mod.rs @@ -26,6 +26,9 @@ pub use is_git_repo::is_git_repo; mod head_sha; pub use head_sha::head_sha; +mod current_branch; +pub use current_branch::current_branch; + /// Temp-dir git fixtures shared by the per-method parity tests. /// /// Fixtures are built with the real `git` CLI, so each parity test asserts