Skip to main content

sem_core/git/
jj.rs

1use std::path::Path;
2use std::process::Command;
3
4/// Check if the given repo root is a Jujutsu repository.
5pub fn is_jj_repo(root: &Path) -> bool {
6    root.join(".jj").is_dir()
7}
8
9/// Resolve a jj revset to a git commit SHA using `jj log`.
10/// Returns None if jj is not installed, the revset is invalid, or resolution fails.
11pub fn resolve_jj_revset(revset: &str, root: &Path) -> Option<String> {
12    let output = Command::new("jj")
13        .args(["log", "--no-graph", "-T", "commit_id ++ \"\\n\"", "-r", revset])
14        .current_dir(root)
15        .output()
16        .ok()?;
17
18    if !output.status.success() {
19        return None;
20    }
21
22    let stdout = String::from_utf8_lossy(&output.stdout);
23    let sha = stdout.lines().next()?.trim().to_string();
24
25    // Validate: must be a 40-char hex string
26    if sha.len() == 40 && sha.chars().all(|c| c.is_ascii_hexdigit()) {
27        Some(sha)
28    } else {
29        None
30    }
31}
32
33/// Try to resolve a ref string via jj if we're in a jj repo.
34/// Falls back to the original string if not a jj repo or resolution fails.
35pub fn maybe_resolve_ref(refspec: &str, root: &Path) -> String {
36    if !is_jj_repo(root) {
37        return refspec.to_string();
38    }
39
40    // Skip refs that are already valid hex SHAs (no need to resolve)
41    let trimmed = refspec.trim();
42    if trimmed.len() >= 7
43        && trimmed.len() <= 40
44        && trimmed.chars().all(|c| c.is_ascii_hexdigit())
45    {
46        return refspec.to_string();
47    }
48
49    resolve_jj_revset(refspec, root).unwrap_or_else(|| refspec.to_string())
50}