Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
remote-helper: Print `git range-diff` Invocation
Merged lorenz opened 1 year ago

When a patch is updated to a new revision (but not merged), print the corresponding git range-diff invocation.

13 files changed +139 -6 6dcd5627 f13afe49
modified radicle-cli/examples/rad-patch-checkout-force.md
@@ -46,6 +46,10 @@ $ git commit --message "Add README, just for the fun"
``` ~alice (stderr)
$ git push rad -o patch.message="Add README, just for the fun"
✓ Patch aa45913 updated to revision 3156bed9d64d4675d6cf56612d217fc5f4e8a53a
+
To compare against your previous revision aa45913, run:
+

+
   git range-diff f2de534[..] 3e674d1[..] 27857ec[..]
+

✓ Synced with 1 node(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
   3e674d1..27857ec  flux-capacitor-power -> patches/aa45913e757cacd46972733bddee5472c78fa32a
modified radicle-cli/examples/rad-patch-checkout.md
@@ -60,6 +60,10 @@ We can now finish off the update:
``` (stderr)
$ git push rad -o patch.message="Add README, just for the fun"
✓ Patch aa45913 updated to revision 3156bed9d64d4675d6cf56612d217fc5f4e8a53a
+
To compare against your previous revision aa45913, run:
+

+
   git range-diff f2de534[..] 3e674d1[..] 27857ec[..]
+

To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
   3e674d1..27857ec  patch/aa45913 -> patches/aa45913e757cacd46972733bddee5472c78fa32a
```
modified radicle-cli/examples/rad-patch-delete.md
@@ -69,6 +69,10 @@ $ git commit -am "Add MIT License"
``` ~alice (stderr)
$ git push -f
✓ Patch 6c61ef1 updated to revision 93915b9afa94a9dc4f52f12cdf077d4613ea3eb3
+
To compare against your previous revision 6c61ef1, run:
+

+
   git range-diff f2de534[..] 717c900[..] 1cc8cd9[..]
+

✓ Synced with 2 node(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
   717c900..1cc8cd9  prepare-license -> patches/6c61ef1716ad8a5c11e04dd7a3fec51e01fba70b
modified radicle-cli/examples/rad-patch-edit.md
@@ -33,6 +33,10 @@ $ git commit -v -m "Define the LICENSE"
``` (stderr)
$ git push -f -o patch.message="Add License"
✓ Patch 89f7afb updated to revision 5d78dd5376453e25df5988ec86951c99cb73742c
+
To compare against your previous revision 89f7afb, run:
+

+
   git range-diff f2de534[..] 03c02af[..] 8945f61[..]
+

To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
   03c02af..8945f61  changes -> patches/89f7afb1511b976482b21f6b2f39aef7f4fb88a2
```
modified radicle-cli/examples/rad-patch-open-explore.md
@@ -19,6 +19,10 @@ If we update the patch, the URL is also output.
$ git commit --amend --allow-empty -q -m "Other changes"
$ git push -f
✓ Patch acab0ec updated to revision f7a830d829d0cdf398f63a32b0d5ee31f08e21ab
+
To compare against your previous revision acab0ec, run:
+

+
   git range-diff f2de534[..] e12525d[..] b2b6432[..]
+

✓ Synced with 1 node(s)

  https://app.radicle.xyz/nodes/[..]/rad:z3yXbb1sR6UG6ixxV2YF9jUP7ABra/patches/acab0ec777a97d013f30be5d5d1aec32562ecb02
modified radicle-cli/examples/rad-patch-pull-update.md
@@ -83,6 +83,10 @@ Bob then updates the patch.
$ git commit --allow-empty -m "Bob's commit #2" -q
$ git push rad -o sync -o patch.message="Updated."
✓ Patch 55b9721 updated to revision f91e056da05b2d9a58af1160c76245bc3debf7a8
+
To compare against your previous revision 55b9721, run:
+

+
   git range-diff f2de534[..] bdcdb30[..] cad2666[..]
+

✓ Synced with 1 node(s)
To rad://zhbMU4DUXrzB8xT6qAJh6yZ7bFMK/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
   bdcdb30..cad2666  bob/feature -> patches/55b9721ed7f6bfec38f43729e9b6631c5dc812fb
modified radicle-cli/examples/rad-patch-via-push.md
@@ -113,6 +113,10 @@ $ git commit -a -m "Improve code" -q --allow-empty
``` (stderr)
$ git push rad
✓ Patch 9580891 updated to revision d7040c6c97629c2b94f86fb639bebbff5de39697
+
To compare against your previous revision 9580891, run:
+

+
   git range-diff f2de534[..] 8b0ea80[..] 02bef3f[..]
+

To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
   8b0ea80..02bef3f  feature/2 -> patches/95808913573cead52ad7b42c7b475260ec45c4b2
```
@@ -201,6 +205,10 @@ use `--force` to force the update.
``` (stderr)
$ git push --force
✓ Patch 9580891 updated to revision 670d02794aa05afd6e0851f4aa848bc87c4712c7
+
To compare against your previous revision d7040c6, run:
+

+
   git range-diff f2de534[..] 02bef3f[..] 9304dbc[..]
+

To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
 + 02bef3f...9304dbc feature/2 -> patches/95808913573cead52ad7b42c7b475260ec45c4b2 (forced update)
```
modified radicle-cli/examples/rad-patch.md
@@ -127,6 +127,10 @@ $ git commit --message "Add README, just for the fun"
``` (stderr)
$ git push rad -o patch.message="Add README, just for the fun"
✓ Patch aa45913 updated to revision 6e5a3b7b2ce27b32e7ccc2f0b3f4594897dde638
+
To compare against your previous revision aa45913, run:
+

+
   git range-diff f2de534[..] 3e674d1[..] 27857ec[..]
+

To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
   3e674d1..27857ec  flux-capacitor-power -> patches/aa45913e757cacd46972733bddee5472c78fa32a
```
modified radicle-cli/examples/rad-push-and-pull-patches.md
@@ -33,6 +33,10 @@ $ git checkout patch/d004b67 -q
$ git commit --allow-empty -m "Changes #2" -q
$ git push
✓ Patch d004b67 updated to revision 2eb705c3da98e05c083df15be5b1bd6856a0bd77
+
To compare against your previous revision d004b67, run:
+

+
   git range-diff f2de534[..] 8d5f1ba[..] c2aaf1c[..]
+

✓ Synced with 1 node(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
 * [new branch]      patch/d004b67 -> patches/d004b67355456c46de10c0d287e4a791ad1a6945
@@ -52,6 +56,10 @@ $ git checkout - -q
$ git commit --allow-empty -m "Changes #3" -q
$ git push
✓ Patch d004b67 updated to revision 7b5015a8dac188bb0d44a334aa68a51298750b07
+
To compare against your previous revision d004b67, run:
+

+
   git range-diff f2de534[..] 8d5f1ba[..] d9f8caf[..]
+

✓ Synced with 1 node(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
   c2aaf1c..d9f8caf  patch/d004b67 -> patches/d004b67355456c46de10c0d287e4a791ad1a6945
modified radicle-cli/examples/workflow/4-patching-contributor.md
@@ -78,6 +78,10 @@ $ git commit --message "Add README, just for the fun"
``` (stderr) RAD_SOCKET=/dev/null
$ git push -o patch.message="Add README, just for the fun"
✓ Patch e4934b6 updated to revision 773b9aab58b11e9fa83d0ed0baca2bea6ff889c9
+
To compare against your previous revision e4934b6, run:
+

+
   git range-diff f2de534[..] 3e674d1[..] 27857ec[..]
+

To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
   3e674d1..27857ec  flux-capacitor-power -> patches/e4934b6d9dbe01ce3c7fbb5b77a80d5f1dacdc46
```
modified radicle-cli/examples/workflow/5-patching-maintainer.md
@@ -63,6 +63,10 @@ $ git commit -m "Use markdown for requirements"
``` (stderr)
$ git push rad -o no-sync -o patch.message="Use markdown for requirements"
✓ Patch e4934b6 updated to revision 9d62420e779e5cfe1dc02c51eddec9a0907aa844
+
To compare against your previous revision 773b9aa, run:
+

+
   git range-diff f2de534[..] 27857ec[..] f567f69[..]
+

To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
 * [new branch]      patch/e4934b6 -> patches/e4934b6d9dbe01ce3c7fbb5b77a80d5f1dacdc46
```
modified radicle-remote-helper/src/push.rs
@@ -97,6 +97,9 @@ pub enum Error {
    /// Patch not found in store.
    #[error("patch `{0}` not found")]
    NotFound(patch::PatchId),
+
    /// Revision not found in store.
+
    #[error("revision `{0}` not found")]
+
    RevisionNotFound(patch::RevisionId),
    /// Patch is empty.
    #[error("patch commits are already included in the base branch")]
    EmptyPatch,
@@ -528,7 +531,7 @@ fn patch_update<G: Signer>(

    push_ref(src, &dst, force, working, stored.raw())?;

-
    let Ok(mut patch) = patches.get_mut(&patch_id) else {
+
    let Ok(Some(patch)) = patches.get(&patch_id) else {
        return Err(Error::NotFound(patch_id));
    };

@@ -536,10 +539,14 @@ fn patch_update<G: Signer>(
    if patch.revisions().any(|(_, r)| *r.head() == commit.id()) {
        return Ok(None);
    }
+

+
    let (latest_id, latest) = patch.latest();
+
    let latest = latest.clone();
+

    let message = term::patch::get_update_message(
        opts.message,
        &stored.backend,
-
        patch.latest().1,
+
        &latest,
        &commit.id().into(),
    )?;

@@ -550,13 +557,18 @@ fn patch_update<G: Signer>(
    } else {
        stored.merge_base(&target, &head)?
    };
-
    let revision = patch.update(message, base, head, signer)?;
+

+
    let mut patch_mut = patch::PatchMut::new(patch_id, patch, &mut patches);
+
    let revision = patch_mut.update(message, base, head, signer)?;
+
    let Some(revision) = patch_mut.revision(&revision).cloned() else {
+
        return Err(Error::RevisionNotFound(revision));
+
    };

    eprintln!(
        "{} Patch {} updated to revision {}",
        term::format::positive("✓"),
        term::format::tertiary(term::format::cob(&patch_id)),
-
        term::format::dim(revision)
+
        term::format::dim(revision.id())
    );

    // In this case, the patch was already merged via git, and pushed to storage.
@@ -565,8 +577,16 @@ fn patch_update<G: Signer>(
    // This can happen if for eg. a patch commit is amended, the patch branch is merged
    // and pushed, but the patch hasn't yet been updated. On push to the patch branch,
    // it'll seem like the patch is "empty", because the changes are already in the base branch.
-
    if base == head && patch.is_open() {
-
        patch_merge(patch, revision, head, working, signer)?;
+
    if base == head && patch_mut.is_open() {
+
        patch_merge(patch_mut, revision.id(), head, working, signer)?;
+
    } else {
+
        eprintln!(
+
            "To compare against your previous revision {}, run:\n\n   {}\n",
+
            term::format::tertiary(term::format::cob(&cob::ObjectId::from(git::Oid::from(
+
                latest_id
+
            )))),
+
            patch::RangeDiff::new(&latest, &revision).to_command()
+
        );
    }

    Ok(Some(ExplorerResource::Patch { id: patch_id }))
modified radicle/src/cob/patch.rs
@@ -2609,6 +2609,67 @@ where
    }
}

+
/// Models a comparison between two commit ranges,
+
/// commonly obtained from two revisions (likely of the same patch).
+
/// This can be used to generate a `git range-diff` command.
+
/// See <https://git-scm.com/docs/git-range-diff>.
+
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
+
pub struct RangeDiff {
+
    old: (git::Oid, git::Oid),
+
    new: (git::Oid, git::Oid),
+
}
+

+
impl RangeDiff {
+
    const COMMAND: &str = "git";
+
    const SUBCOMMAND: &str = "range-diff";
+

+
    pub fn new(old: &Revision, new: &Revision) -> Self {
+
        Self {
+
            old: old.range(),
+
            new: new.range(),
+
        }
+
    }
+

+
    pub fn to_command(&self) -> String {
+
        let range = if self.has_same_base() {
+
            format!("{} {} {}", self.old.0, self.old.1, self.new.1)
+
        } else {
+
            format!(
+
                "{}..{} {}..{}",
+
                self.old.0, self.old.1, self.new.0, self.new.1,
+
            )
+
        };
+

+
        Self::COMMAND.to_string() + " " + Self::SUBCOMMAND + " " + &range
+
    }
+

+
    fn has_same_base(&self) -> bool {
+
        self.old.0 == self.new.0
+
    }
+
}
+

+
impl From<RangeDiff> for std::process::Command {
+
    fn from(range_diff: RangeDiff) -> Self {
+
        let mut command = std::process::Command::new(RangeDiff::COMMAND);
+

+
        command.arg(RangeDiff::SUBCOMMAND);
+

+
        if range_diff.has_same_base() {
+
            command.args([
+
                range_diff.old.0.to_string(),
+
                range_diff.old.1.to_string(),
+
                range_diff.new.1.to_string(),
+
            ]);
+
        } else {
+
            command.args([
+
                format!("{}..{}", range_diff.old.0, range_diff.old.1),
+
                format!("{}..{}", range_diff.new.0, range_diff.new.1),
+
            ]);
+
        }
+
        command
+
    }
+
}
+

/// Helpers for de/serialization of patch data types.
mod ser {
    use std::collections::{BTreeMap, BTreeSet};