Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: use canonical head for patch target
Alexis Sellier committed 3 years ago
commit 34a2d1a9dc92b9fe74d31b590d7f74d738c31d73
parent aacacf872fceb6b051a940341a4ce71de10be0e8
4 files changed +23 -84
modified radicle-cli/examples/rad-patch.md
@@ -29,7 +29,7 @@ $ rad patch open --message "Define power requirements" --message "See details."
✓ Pushing HEAD to storage...
✓ Analyzing remotes...

-
z6MknSL…StBU8Vi/master (f2de534) <- z6MknSL…StBU8Vi/flux-capacitor-power (3e674d1)
+
master (f2de534) <- z6MknSL…StBU8Vi/flux-capacitor-power (3e674d1)

1 commit(s) ahead, 0 commit(s) behind

modified radicle-cli/src/commands/patch/common.rs
@@ -6,9 +6,7 @@ use radicle::cob::patch::{Clock, MergeTarget, Patch, PatchId, Patches};
use radicle::git;
use radicle::git::raw::Oid;
use radicle::prelude::*;
-
use radicle::storage;
use radicle::storage::git::Repository;
-
use radicle::storage::Remote;

use crate::terminal as term;
use crate::terminal::args::Error;
@@ -33,68 +31,32 @@ pub fn branch_oid(branch: &git::raw::Branch) -> anyhow::Result<git::Oid> {
    Ok(oid.into())
}

-
/// List of merge targets.
-
#[derive(Debug, Default)]
-
pub struct MergeTargets {
-
    /// Merge targets that have already merged the patch.
-
    pub merged: Vec<Remote>,
-
    /// Merge targets that haven't merged the patch.
-
    pub not_merged: Vec<(Remote, git::Oid)>,
-
}
-

-
/// Find potential merge targets for the given head.
-
pub fn find_merge_targets(
-
    head: &Oid,
-
    branch: &git::RefStr,
-
    storage: &Repository,
-
) -> anyhow::Result<MergeTargets> {
-
    let mut targets = MergeTargets::default();
-

-
    for remote in storage.remotes()? {
-
        let (_, remote) = remote?;
-
        let Some(target_oid) = remote.refs.head(branch) else {
-
            continue;
-
        };
-

-
        if is_merged(storage.raw(), target_oid.into(), *head)? {
-
            targets.merged.push(remote);
-
        } else {
-
            targets.not_merged.push((remote, target_oid));
-
        }
-
    }
-
    Ok(targets)
+
#[inline]
+
fn get_branch(git_ref: git::Qualified) -> git::RefString {
+
    let (_, _, head, tail) = git_ref.non_empty_components();
+
    std::iter::once(head).chain(tail).collect()
}

/// Determine the merge target for this patch. This can ben any tracked remote's "default"
/// branch, as well as your own (eg. `rad/master`).
pub fn get_merge_target(
-
    project: &Project,
    storage: &Repository,
    head_branch: &git::raw::Branch,
-
) -> anyhow::Result<(storage::Remote, git::Oid)> {
-
    let head_oid = head_branch
-
        .get()
-
        .target()
-
        .ok_or(anyhow!("invalid HEAD ref; aborting"))?;
-
    let mut spinner = term::spinner("Analyzing remotes...");
-
    let targets = find_merge_targets(&head_oid, project.default_branch().as_refstr(), storage)?;
+
) -> anyhow::Result<(git::RefString, git::Oid)> {
+
    let spinner = term::spinner("Analyzing remotes...");
+
    let (qualified_ref, target_oid) = storage.canonical_head()?;
+
    let head_oid = branch_oid(head_branch)?;
+
    let merge_base = storage.raw().merge_base(*head_oid, *target_oid)?;
+

+
    if head_oid == merge_base.into() {
+
        anyhow::bail!("commits are already included in the target branch; nothing to do");
+
    }

-
    // eg. `refs/namespaces/<peer>/refs/heads/master`
-
    let (target_peer, target_oid) = match targets.not_merged.as_slice() {
-
        [] => {
-
            spinner.message("All tracked peers are up to date.");
-
            todo!("handle case without target");
-
        }
-
        [target] => target,
-
        _ => {
-
            // TODO: Let user select which branch to use as a target.
-
            todo!();
-
        }
-
    };
    // TODO: Tell user how many peers don't have this change.
    spinner.finish();

-
    Ok((target_peer.clone(), *target_oid))
+
    let branch = get_branch(qualified_ref);
+
    Ok((branch, (*target_oid).into()))
}

/// Return the [`Oid`] of the merge target.
@@ -265,12 +227,3 @@ pub fn patch_commits<'a>(
    }
    Ok(commits)
}
-

-
/// Check whether a commit has been merged into a target branch.
-
pub fn is_merged(repo: &git::raw::Repository, target: Oid, commit: Oid) -> Result<bool, Error> {
-
    if let Ok(base) = repo.merge_base(target, commit) {
-
        Ok(base == commit)
-
    } else {
-
        Ok(false)
-
    }
-
}
modified radicle-cli/src/commands/patch/create.rs
@@ -1,9 +1,8 @@
-
use anyhow::{anyhow, Context};
+
use anyhow::anyhow;

use radicle::cob::patch;
use radicle::git;
use radicle::prelude::*;
-
use radicle::storage;
use radicle::storage::git::Repository;

use crate::terminal as term;
@@ -46,11 +45,10 @@ pub fn handle_patch_message(
}

fn show_patch_commit_info(
-
    project: &Project,
    workdir: &git::raw::Repository,
    node_id: &NodeId,
    head_branch: &git::raw::Branch,
-
    target_peer: &storage::Remote,
+
    target_ref: &git::RefStr,
    target_oid: git::Oid,
) -> anyhow::Result<()> {
    let head_oid = branch_oid(head_branch)?;
@@ -60,9 +58,8 @@ fn show_patch_commit_info(

    term::blank();
    term::info!(
-
        "{}/{} ({}) <- {}/{} ({})",
-
        term::format::dim(term::format::node(&target_peer.id)),
-
        term::format::highlight(project.default_branch().to_string()),
+
        "{} ({}) <- {}/{} ({})",
+
        term::format::highlight(target_ref),
        term::format::secondary(term::format::oid(*target_oid)),
        term::format::dim(term::format::node(node_id)),
        term::format::highlight(branch_name(head_branch)?),
@@ -91,27 +88,16 @@ pub fn run(
    options: Options,
) -> anyhow::Result<()> {
    let mut patches = patch::Patches::open(storage)?;
-
    let project = storage.project_of(&profile.public_key).context(format!(
-
        "couldn't load project {} from local state",
-
        storage.id
-
    ))?;
    let head_branch = try_branch(workdir.head()?)?;
    push_to_storage(storage, &head_branch, &options)?;
-
    let (target_peer, target_oid) = get_merge_target(&project, storage, &head_branch)?;
+
    let (target_ref, target_oid) = get_merge_target(storage, &head_branch)?;

    // TODO: Handle case where `rad/master` isn't up to date with the target.
    // In that case we should warn the user that their master branch is not up
    // to date, and error out, unless the user specifies manually the merge
    // base.

-
    show_patch_commit_info(
-
        &project,
-
        workdir,
-
        profile.id(),
-
        &head_branch,
-
        &target_peer,
-
        target_oid,
-
    )?;
+
    show_patch_commit_info(workdir, profile.id(), &head_branch, &target_ref, target_oid)?;

    term::blank();

modified radicle-cli/src/commands/patch/update.rs
@@ -104,7 +104,7 @@ pub fn run(

    push_to_storage(storage, &head_branch, options)?;

-
    let (_, target_oid) = get_merge_target(&project, storage, &head_branch)?;
+
    let (_, target_oid) = get_merge_target(storage, &head_branch)?;
    let mut patches = patch::Patches::open(storage)?;

    let patch_id = match patch_id {