Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: improve default branch pick
✗ CI failure Sekhat Temporus committed 10 months ago
commit b4e925fcb5d4f75d758555d0110b456a0e5312be
parent 7c4b71ab8205a69a04ee9e9242288c864e6c7c33
1 failed (1 total) View logs
3 files changed +73 -8
modified crates/radicle-cli/examples/rad-init-no-git.md
@@ -16,7 +16,9 @@ Now we try again.

``` (fail)
$ rad init
-
✗ Error: repository head must point to a commit
+
✗ Error: could not determine default branch in repository
+
✗ Hint: perhaps you need to create a branch?
+
✗ Error: aborting `rad init`
```

Looks like we need a commit.
modified crates/radicle-cli/src/commands/init.rs
@@ -12,6 +12,7 @@ use serde_json as json;

use radicle::crypto::ssh;
use radicle::explorer::ExplorerUrl;
+
use radicle::git::raw;
use radicle::git::RefString;
use radicle::identity::project::ProjectName;
use radicle::identity::{Doc, RepoId, Visibility};
@@ -216,11 +217,21 @@ pub fn init(
        .unwrap_or_else(|| repo.path())
        .canonicalize()?;
    let interactive = options.interactive;
-
    let head: String = repo
-
        .head()
-
        .ok()
-
        .and_then(|head| head.shorthand().map(|h| h.to_owned()))
-
        .ok_or_else(|| anyhow!("repository head must point to a commit"))?;
+

+
    let default_branch = match find_default_branch(&repo) {
+
        Err(err @ DefaultBranchError::Head) => {
+
            term::error(err);
+
            term::hint("try `git checkout <default branch>` or set `git config set --local init.defaultBranch <default branch>`");
+
            anyhow::bail!("aborting `rad init`")
+
        }
+
        Err(err @ DefaultBranchError::NoHead) => {
+
            term::error(err);
+
            term::hint("perhaps you need to create a branch?");
+
            anyhow::bail!("aborting `rad init`")
+
        }
+
        Err(err) => anyhow::bail!(err),
+
        Ok(branch) => branch,
+
    };

    term::headline(format!(
        "Initializing{}radicle 👾 repository in {}..",
@@ -252,10 +263,10 @@ pub fn init(
        Some(branch) => branch,
        None if interactive.yes() => term::input(
            "Default branch",
-
            Some(head),
+
            Some(default_branch),
            Some("Please specify an existing branch"),
        )?,
-
        None => head,
+
        None => default_branch,
    };
    let branch = RefString::try_from(branch.clone())
        .map_err(|e| anyhow!("invalid branch name {:?}: {}", branch, e))?;
@@ -671,3 +682,39 @@ pub fn setup_signing(
    }
    Ok(())
}
+

+
#[derive(Debug, thiserror::Error)]
+
enum DefaultBranchError {
+
    #[error("could not determine default branch in repository")]
+
    NoHead,
+
    #[error("in detached HEAD state")]
+
    Head,
+
    #[error("could not determine default branch in repository: {0}")]
+
    Git(raw::Error),
+
}
+

+
fn find_default_branch(repo: &raw::Repository) -> Result<String, DefaultBranchError> {
+
    match find_init_default_branch(repo).ok().flatten() {
+
        Some(refname) => Ok(refname),
+
        None => Ok(find_repository_head(repo)?),
+
    }
+
}
+

+
fn find_init_default_branch(repo: &raw::Repository) -> Result<Option<String>, raw::Error> {
+
    let config = repo.config().and_then(|mut c| c.snapshot())?;
+
    let default_branch = config.get_str("init.defaultbranch")?;
+
    let branch = repo.find_branch(default_branch, raw::BranchType::Local)?;
+
    Ok(branch.into_reference().shorthand().map(ToOwned::to_owned))
+
}
+

+
fn find_repository_head(repo: &raw::Repository) -> Result<String, DefaultBranchError> {
+
    match repo.head() {
+
        Err(e) if e.code() == raw::ErrorCode::UnbornBranch => Err(DefaultBranchError::NoHead),
+
        Err(e) => Err(DefaultBranchError::Git(e)),
+
        Ok(head) => head
+
            .shorthand()
+
            .filter(|refname| *refname != "HEAD")
+
            .ok_or(DefaultBranchError::Head)
+
            .map(|refname| refname.to_owned()),
+
    }
+
}
modified crates/radicle/src/git.rs
@@ -701,6 +701,22 @@ pub fn set_upstream(
    Ok(())
}

+
pub fn init_default_branch(repo: &git2::Repository) -> Result<Option<String>, git2::Error> {
+
    let config = repo.config().and_then(|mut c| c.snapshot())?;
+
    let default_branch = config.get_str("init.defaultbranch")?;
+
    let branch = repo.find_branch(default_branch, git2::BranchType::Local)?;
+
    Ok(branch.into_reference().shorthand().map(ToOwned::to_owned))
+
}
+

+
pub fn head_refname(repo: &git2::Repository) -> Result<Option<String>, git2::Error> {
+
    let head = repo.head()?;
+
    match head.shorthand() {
+
        Some("HEAD") => Ok(None),
+
        Some(refname) => Ok(Some(refname.to_owned())),
+
        None => Ok(None),
+
    }
+
}
+

/// Execute a git command by spawning a child process.
pub fn run<P, S, K, V>(
    repo: P,