From cb576cf8c33974e8acd59271f84af5aed3b3aded Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Tue, 13 Jun 2023 13:14:52 +0200 Subject: [PATCH] cli: Allow setting a different patch base on push Using `-o patch.base=`, users can specify a different patch base, when creating or updating a patch. --- .../examples/rad-patch-ahead-behind.md | 36 ++++++++++++++++++- radicle-remote-helper/src/lib.rs | 16 +++++++-- radicle-remote-helper/src/push.rs | 32 +++++++++++------ radicle/src/cob/patch.rs | 7 +--- 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/radicle-cli/examples/rad-patch-ahead-behind.md b/radicle-cli/examples/rad-patch-ahead-behind.md index 87b6f534..224b2324 100644 --- a/radicle-cli/examples/rad-patch-ahead-behind.md +++ b/radicle-cli/examples/rad-patch-ahead-behind.md @@ -115,6 +115,40 @@ $ rad patch show -v 57cb9b2758518e547de324456ac967fda456c6c1 │ 7f63fcb Add Mel │ │ 5c88a79 Add Alan │ ├────────────────────────────────────────────────────────────────────┤ -│ ● opened by (you) (z6MknSL…StBU8Vi) 5 months ago │ +│ ● opened by (you) (z6MknSL…StBU8Vi) [ ... ] │ +╰────────────────────────────────────────────────────────────────────╯ +``` + +If we want to instead create a "stacked" patch, we can do so with the +`patch.base` push option: + +``` (stderr) +$ git push -o patch.message="Add Mel #2" -o patch.base=5c88a79d75f5c2b4cc51ee6f163d2db91ee198d7 rad HEAD:refs/patches +✓ Patch 395221c5b75fa9bc2de7909d03e69dfd606611c6 opened +To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi + * [new reference] HEAD -> refs/patches +``` + +As you'll notice, using the previous patch as the base, we only see commit +`7f63fcb` listed for this new patch. + +However, since the patch is still intended to be merged into `master`, we see +that it is still two commits ahead and one behind from `master`. + +``` +$ rad patch show -v 395221c5b75fa9bc2de7909d03e69dfd606611c6 +╭────────────────────────────────────────────────────────────────────╮ +│ Title Add Mel #2 │ +│ Patch 395221c5b75fa9bc2de7909d03e69dfd606611c6 │ +│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │ +│ Head 7f63fcbcf23fc39eea784c091ad3d20d7e4bd005 │ +│ Base 5c88a79d75f5c2b4cc51ee6f163d2db91ee198d7 │ +│ Branches feature/2 │ +│ Commits ahead 2, behind 1 │ +│ Status open │ +├────────────────────────────────────────────────────────────────────┤ +│ 7f63fcb Add Mel │ +├────────────────────────────────────────────────────────────────────┤ +│ ● opened by (you) (z6MknSL…StBU8Vi) [ ... ] │ ╰────────────────────────────────────────────────────────────────────╯ ``` diff --git a/radicle-remote-helper/src/lib.rs b/radicle-remote-helper/src/lib.rs index 68fb201b..9e1b0ff6 100644 --- a/radicle-remote-helper/src/lib.rs +++ b/radicle-remote-helper/src/lib.rs @@ -60,6 +60,8 @@ pub struct Options { no_sync: bool, /// Open patch in draft mode. draft: bool, + /// Patch base to use, when opening or updating a patch. + base: Option, /// Patch message. message: cli::patch::Message, } @@ -115,8 +117,18 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> { let args = args.join(" "); if let Some((key, val)) = args.split_once('=') { - if key == "patch.message" { - opts.message.append(val); + match key { + "patch.message" => { + opts.message.append(val); + } + "patch.base" => { + let base = val.parse()?; + opts.base = Some(base); + } + _ => { + println!("unsupported"); + continue; + } } } else { println!("unsupported"); diff --git a/radicle-remote-helper/src/push.rs b/radicle-remote-helper/src/push.rs index c1d68ff3..9d9bbe0b 100644 --- a/radicle-remote-helper/src/push.rs +++ b/radicle-remote-helper/src/push.rs @@ -50,6 +50,9 @@ pub enum Error { /// Git error. #[error("git: {0}")] Git(#[from] git::raw::Error), + /// Git extension error. + #[error("git: {0}")] + GitExt(#[from] git::ext::Error), /// Storage error. #[error(transparent)] Storage(#[from] radicle::storage::Error), @@ -261,16 +264,17 @@ fn patch_open( push_ref(src, &dst, false, working, stored.raw())?; let (_, target) = stored.canonical_head()?; - let base = stored.backend.merge_base(*target, commit.id())?; - if base == commit.id() { + let head = commit.id().into(); + let base = if let Some(base) = opts.base { + base + } else { + stored.merge_base(&target, &head)? + }; + if base == head { return Err(Error::EmptyPatch); } - let (title, description) = cli::patch::get_create_message( - opts.message, - &stored.backend, - &base.into(), - &commit.id().into(), - )?; + let (title, description) = + cli::patch::get_create_message(opts.message, &stored.backend, &base, &head)?; let mut patches = patch::Patches::open(stored)?; let patch = if opts.draft { @@ -401,8 +405,16 @@ fn patch_update( )?; let (_, target) = stored.canonical_head()?; - let base = stored.backend.merge_base(*target, commit.id())?; - let revision = patch.update(message, base, commit.id(), signer)?; + let head: git::Oid = commit.id().into(); + let base = if let Some(base) = opts.base { + base + } else { + stored.merge_base(&target, &head)? + }; + if base == head { + return Err(Error::EmptyPatch); + } + let revision = patch.update(message, base, head, signer)?; eprintln!( "{} Patch {} updated to {}", diff --git a/radicle/src/cob/patch.rs b/radicle/src/cob/patch.rs index a12c25aa..e69052f3 100644 --- a/radicle/src/cob/patch.rs +++ b/radicle/src/cob/patch.rs @@ -305,12 +305,7 @@ impl Patch { /// Get the merge base of this patch. pub fn merge_base(&self, repo: &R) -> Result { - let target = self.target().head(repo).map_err(|_| { - git::ext::Error::NotFound(git::ext::NotFound::NoSuchBranch(String::from("HEAD"))) - })?; - let base = repo.merge_base(&target, self.head())?; - - Ok(base) + repo.merge_base(self.base(), self.head()) } /// Get the commit range of this patch.