Skip to main content

worktree_io/git/
branch.rs

1use anyhow::{bail, Context, Result};
2use std::path::Path;
3
4/// Detect the default branch of a bare remote repository.
5///
6/// Tries `symbolic-ref refs/remotes/origin/HEAD`, then `git remote show origin`,
7/// then falls back to checking `main`, `master`, and `develop` in that order.
8///
9/// # Errors
10///
11/// Returns an error if any git command fails to spawn or if the default branch
12/// cannot be determined.
13pub fn detect_default_branch(bare: &Path) -> Result<String> {
14    let output = super::git_cmd()
15        .args(["-C"])
16        .arg(bare)
17        .args(["symbolic-ref", "refs/remotes/origin/HEAD"])
18        .output()
19        .context("Failed to run `git symbolic-ref`")?;
20
21    if output.status.success() {
22        let full = String::from_utf8_lossy(&output.stdout);
23        if let Some(branch) = full.trim().strip_prefix("refs/remotes/origin/") {
24            return Ok(branch.to_string());
25        }
26    }
27
28    let output = super::git_cmd()
29        .args(["-C"])
30        .arg(bare)
31        .args(["remote", "show", "origin"])
32        .output()
33        .context("Failed to run `git remote show origin`")?;
34
35    if output.status.success() {
36        let text = String::from_utf8_lossy(&output.stdout);
37        for line in text.lines() {
38            let line = line.trim();
39            if let Some(branch) = line.strip_prefix("HEAD branch: ") {
40                return Ok(branch.to_string()); // LLVM_COV_EXCL_LINE
41            }
42        }
43    }
44
45    for candidate in ["main", "master", "develop"] {
46        let output = super::git_cmd()
47            .args(["-C"])
48            .arg(bare)
49            .args([
50                "rev-parse",
51                "--verify",
52                &format!("refs/remotes/origin/{candidate}"),
53            ])
54            .output()
55            .context("Failed to run `git rev-parse`")?;
56        if output.status.success() {
57            return Ok(candidate.to_string()); // LLVM_COV_EXCL_LINE
58        }
59    }
60
61    bail!("Could not detect default branch for the repository"); // LLVM_COV_EXCL_LINE
62}
63
64/// Return `true` if `branch` exists as a remote-tracking ref in the bare clone.
65#[must_use]
66pub fn branch_exists_remote(bare: &Path, branch: &str) -> bool {
67    super::git_cmd()
68        .args(["-C"])
69        .arg(bare)
70        .args([
71            "rev-parse",
72            "--verify",
73            &format!("refs/remotes/origin/{branch}"),
74        ])
75        .output()
76        .map(|o| o.status.success())
77        .unwrap_or(false)
78}