rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwood84ef0cdfe09720748aca7e4e56e732720ac33fed
{
"request": "trigger",
"version": 1,
"event_type": "patch",
"repository": {
"id": "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
"name": "heartwood",
"description": "Radicle Heartwood Protocol & Stack",
"private": false,
"default_branch": "master",
"delegates": [
"did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT",
"did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz"
]
},
"action": "Created",
"patch": {
"id": "d528d439497a9a3c477736e92c0ba8212b72bba7",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"title": "fetch: allow fetch of annotated tags",
"state": {
"status": "merged",
"conflicts": []
},
"before": "b0fbbeed6b42b1846dbd9b2a2d45bc9c2b25e881",
"after": "84ef0cdfe09720748aca7e4e56e732720ac33fed",
"commits": [
"84ef0cdfe09720748aca7e4e56e732720ac33fed"
],
"target": "3fc92f83fe089263c470b328414d923227cf0e4a",
"labels": [],
"assignees": [],
"revisions": [
{
"id": "d528d439497a9a3c477736e92c0ba8212b72bba7",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"description": "If an ancestry check of an annotated tag was triggered then the fetch\nfrom that peer would fail. This is due to `graph_ahead_behind` only\nworking for commit-ish objects.\n\nTo allow for annotated tags, and other object that peel to commits,\nthe check now peels the two objects to commits before getting their\n`Oid`s. The call to `graph_ahead_behind` can take these two `Oid`s\ninstead.\n\nA test for annotated tags is included as part of the `examples/git`\nsuite in the CLI tests.",
"base": "b0fbbeed6b42b1846dbd9b2a2d45bc9c2b25e881",
"oid": "84ef0cdfe09720748aca7e4e56e732720ac33fed",
"timestamp": 1708947980
},
{
"id": "70955eac70a9ced6702aaa55a748061f31ea1490",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"description": "Rebase.",
"base": "d1e2e3b61e203ec223f5955b7f1b8d6f54be350d",
"oid": "87d1cb50a0cb59cc0428425d1afb0ccfd889b36e",
"timestamp": 1710152016
}
]
}
}
{
"response": "triggered",
"run_id": {
"id": "94cfa47f-92b0-4461-9cc5-cf537d6c9ab4"
},
"info_url": "https://cci.rad.levitte.org//94cfa47f-92b0-4461-9cc5-cf537d6c9ab4.html"
}
Started at: 2026-09-08 03:56:22.424430+02:00
Commands:
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 .
✓ Creating checkout in ./...
✓ Remote cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT added
✓ Remote-tracking branch cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/master created for z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT
✓ Remote fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM added
✓ Remote-tracking branch fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM/master created for z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM
✓ Remote lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz added
✓ Remote-tracking branch lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz/master created for z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz
✓ Repository successfully cloned under /opt/radcis/ci.rad.levitte.org/cci/state/94cfa47f-92b0-4461-9cc5-cf537d6c9ab4/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 189 issues · 38 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ rad patch checkout d528d439497a9a3c477736e92c0ba8212b72bba7
✓ Switched to branch patch/d528d43 at revision d528d43
✓ Branch patch/d528d43 setup to track rad/patches/d528d439497a9a3c477736e92c0ba8212b72bba7
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout 84ef0cdfe09720748aca7e4e56e732720ac33fed
HEAD is now at 84ef0cdf fetch: allow fetch of annotated tags
Exit code: 0
$ rad patch show d528d439497a9a3c477736e92c0ba8212b72bba7 -p
╭────────────────────────────────────────────────────────────────────────────────╮
│ Title fetch: allow fetch of annotated tags │
│ Patch d528d439497a9a3c477736e92c0ba8212b72bba7 │
│ Author fintohaps z6Mkire…SQZ3voM │
│ Head 84ef0cdfe09720748aca7e4e56e732720ac33fed │
│ Base b0fbbeed6b42b1846dbd9b2a2d45bc9c2b25e881 │
│ Target master │
│ Branches patch/d528d43 │
│ Commits ahead 1, behind 1589 │
│ Status merged │
│ │
│ If an ancestry check of an annotated tag was triggered then the fetch │
│ from that peer would fail. This is due to `graph_ahead_behind` only │
│ working for commit-ish objects. │
│ │
│ To allow for annotated tags, and other object that peel to commits, │
│ the check now peels the two objects to commits before getting their │
│ `Oid`s. The call to `graph_ahead_behind` can take these two `Oid`s │
│ instead. │
│ │
│ A test for annotated tags is included as part of the `examples/git` │
│ suite in the CLI tests. │
├────────────────────────────────────────────────────────────────────────────────┤
│ 84ef0cd fetch: allow fetch of annotated tags │
├────────────────────────────────────────────────────────────────────────────────┤
│ ● Revision d528d43 @ b0fbbee..84ef0cd by fintohaps z6Mkire…SQZ3voM 2 years ago │
│ ↑ Revision 70955ea @ d1e2e3b..87d1cb5 by cloudhead z6MksFq…bS9wzpT 2 years ago │
│ └─ ✓ merged by cloudhead z6MksFq…bS9wzpT │
╰────────────────────────────────────────────────────────────────────────────────╯
commit 84ef0cdfe09720748aca7e4e56e732720ac33fed
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Wed Feb 21 10:46:34 2024 +0000
fetch: allow fetch of annotated tags
If an ancestry check of an annotated tag was triggered then the fetch
from that peer would fail. This is due to `graph_ahead_behind` only
working for commit-ish objects.
To allow for annotated tags, and other object that peel to commits,
the check now peels the two objects to commits before getting their
`Oid`s. The call to `graph_ahead_behind` can take these two `Oid`s
instead.
A test for annotated tags is included as part of the `examples/git`
suite in the CLI tests.
Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
diff --git a/radicle-cli/examples/git/git-tag.md b/radicle-cli/examples/git/git-tag.md
new file mode 100644
index 000000000..f9b4dd79d
--- /dev/null
+++ b/radicle-cli/examples/git/git-tag.md
@@ -0,0 +1,70 @@
+Alice creates an annotated tag and pushed to her `rad` remote:
+
+``` ~alice
+$ touch LICENSE
+$ git add LICENSE
+$ git commit -am "Add LICENSE"
+[master 62d19fd] Add LICENSE
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+ create mode 100644 LICENSE
+$ git tag v1.0 -a -m "Release v1.0"
+```
+
+``` ~alice (stderr)
+$ git push rad v1.0 --tags
+✓ Synced with 1 node(s)
+To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ * [new tag] v1.0 -> v1.0
+```
+
+Bob fetches the tag from Alice, by adding her as a remote:
+
+``` ~bob
+$ cd heartwood
+$ rad remote add z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --name alice
+✓ Follow policy updated for z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
+✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
+✓ Remote alice added
+✓ Remote-tracking branch alice/master created for z6MknSL…StBU8Vi
+```
+
+Bob is able to fetch Alice's tag into his working copy by using the
+`--tags` flag:
+
+``` ~bob (stderr)
+$ git fetch alice --tags
+From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ * [new tag] v1.0 -> v1.0
+```
+
+Alice forcefully creates a new version of the tag (let's say she made
+a mistake):
+
+``` ~alice
+$ git commit --allow-empty -m "Release: v1.0"
+[master 8260c04] Release: v1.0
+$ git tag v1.0 -f -a -m "Release v1.0"
+Updated tag 'v1.0' (was be18ed6)
+```
+
+``` ~alice (stderr)
+$ git push rad v1.0 -f
+✓ Synced with 1 node(s)
+To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ + be18ed6...9dbdebc v1.0 -> v1.0 (forced update)
+```
+
+We ensure that Bob is still able to fetch from Alice and get the new
+update of the tag:
+
+``` ~bob
+$ rad sync -f
+✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
+✓ Fetched repository from 1 seed(s)
+```
+
+``` ~bob (stderr)
+$ git fetch alice --tags -f
+From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ t [tag update] v1.0 -> v1.0
+```
diff --git a/radicle-cli/tests/commands.rs b/radicle-cli/tests/commands.rs
index c33e7caf5..568a4cf14 100644
--- a/radicle-cli/tests/commands.rs
+++ b/radicle-cli/tests/commands.rs
@@ -2262,6 +2262,51 @@ fn git_push_and_fetch() {
.unwrap();
}
+#[test]
+fn git_tag() {
+ let mut environment = Environment::new();
+ let alice = environment.node(Config::test(Alias::new("alice")));
+ let bob = environment.node(Config::test(Alias::new("bob")));
+ let working = environment.tmp().join("working");
+
+ fixtures::repository(working.join("alice"));
+
+ test(
+ "examples/rad-init.md",
+ working.join("alice"),
+ Some(&alice.home),
+ [],
+ )
+ .unwrap();
+
+ let alice = alice.spawn();
+ let mut bob = bob.spawn();
+
+ bob.connect(&alice).converge([&alice]);
+
+ test(
+ "examples/rad-clone.md",
+ &working.join("bob"),
+ Some(&bob.home),
+ [],
+ )
+ .unwrap();
+ formula(&environment.tmp(), "examples/git/git-tag.md")
+ .unwrap()
+ .home(
+ "alice",
+ working.join("alice"),
+ [("RAD_HOME", alice.home.path().display())],
+ )
+ .home(
+ "bob",
+ working.join("bob"),
+ [("RAD_HOME", bob.home.path().display())],
+ )
+ .run()
+ .unwrap();
+}
+
#[test]
fn rad_workflow() {
let mut environment = Environment::new();
diff --git a/radicle-fetch/src/git/repository.rs b/radicle-fetch/src/git/repository.rs
index a219a8208..5d57ae225 100644
--- a/radicle-fetch/src/git/repository.rs
+++ b/radicle-fetch/src/git/repository.rs
@@ -1,7 +1,7 @@
pub mod error;
use either::Either;
-use radicle::git::{Namespaced, Oid, Qualified};
+use radicle::git::{self, Namespaced, Oid, Qualified};
use radicle::storage::git::Repository;
use super::refs::{Applied, Policy, RefUpdate, Update};
@@ -38,10 +38,29 @@ pub fn contains(repo: &Repository, oid: Oid) -> Result<bool, error::Contains> {
.map_err(error::Contains)
}
-pub fn ancestry(repo: &Repository, old: Oid, new: Oid) -> Result<Ancestry, error::Ancestry> {
- if !contains(repo, old)? || !contains(repo, new)? {
- return Err(error::Ancestry::Missing { a: old, b: new });
+/// Find the object identified by `oid` and peel it to its associated
+/// commit `Oid`.
+///
+/// # Errors
+///
+/// - The object was not found
+/// - The object does not peel to a commit
+/// - Attempting to find the object fails
+fn find_and_peel(repo: &Repository, oid: Oid) -> Result<Oid, error::Ancestry> {
+ match repo.backend.find_object(*oid, None) {
+ Ok(object) => Ok(object
+ .peel(git::raw::ObjectType::Commit)
+ .map_err(|err| error::Ancestry::Peel { oid, err })?
+ .id()
+ .into()),
+ Err(e) if git::is_not_found_err(&e) => Err(error::Ancestry::Missing { oid }),
+ Err(err) => Err(error::Ancestry::Object { oid, err }),
}
+}
+
+pub fn ancestry(repo: &Repository, old: Oid, new: Oid) -> Result<Ancestry, error::Ancestry> {
+ let old = find_and_peel(repo, old)?;
+ let new = find_and_peel(repo, new)?;
if old == new {
return Ok(Ancestry::Equal);
@@ -49,7 +68,7 @@ pub fn ancestry(repo: &Repository, old: Oid, new: Oid) -> Result<Ancestry, error
let (ahead, behind) = repo
.backend
- .graph_ahead_behind(new.into(), old.into())
+ .graph_ahead_behind(*new, *old)
.map_err(|err| error::Ancestry::Check { old, new, err })?;
if ahead > 0 && behind == 0 {
diff --git a/radicle-fetch/src/git/repository/error.rs b/radicle-fetch/src/git/repository/error.rs
index 12525ce39..6c2d546c7 100644
--- a/radicle-fetch/src/git/repository/error.rs
+++ b/radicle-fetch/src/git/repository/error.rs
@@ -7,17 +7,27 @@ pub struct Contains(#[source] pub raw::Error);
#[derive(Debug, Error)]
pub enum Ancestry {
- #[error("missing one of {a} or {b} while checking ancestry")]
- Missing { a: Oid, b: Oid },
- #[error(transparent)]
- Contains(#[from] Contains),
- #[error("failed to check ancestry for {old} and {new}")]
+ #[error("missing {oid} while checking ancestry")]
+ Missing { oid: Oid },
+ #[error("failed to check ancestry for {old} and {new}: {err}")]
Check {
old: Oid,
new: Oid,
#[source]
err: raw::Error,
},
+ #[error("failed to peel object to commit {oid}: {err}")]
+ Peel {
+ oid: Oid,
+ #[source]
+ err: raw::Error,
+ },
+ #[error("failed to find object {oid}: {err}")]
+ Object {
+ oid: Oid,
+ #[source]
+ err: raw::Error,
+ },
}
#[derive(Debug, Error)]
Exit code: 0
shell: 'cargo --version rustc --version cargo fmt --check cargo clippy --all-targets --workspace -- --deny clippy::all cargo build --all-targets --workspace cargo doc --workspace cargo test --workspace --no-fail-fast '
Commands:
$ podman run --name 94cfa47f-92b0-4461-9cc5-cf537d6c9ab4 -v /opt/radcis/ci.rad.levitte.org/cci/state/94cfa47f-92b0-4461-9cc5-cf537d6c9ab4/s:/94cfa47f-92b0-4461-9cc5-cf537d6c9ab4/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/94cfa47f-92b0-4461-9cc5-cf537d6c9ab4/w:/94cfa47f-92b0-4461-9cc5-cf537d6c9ab4/w -w /94cfa47f-92b0-4461-9cc5-cf537d6c9ab4/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:trixie bash /94cfa47f-92b0-4461-9cc5-cf537d6c9ab4/s/script.sh
+ cargo --version
info: syncing channel updates for '1.74-x86_64-unknown-linux-gnu'
info: latest update on 2023-12-07, rust version 1.74.1 (a28077b28 2023-12-04)
info: downloading component 'cargo'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-std'
info: installing component 'rustc'
cargo 1.74.1 (ecb9851af 2023-10-18)
+ rustc --version
rustc 1.74.1 (a28077b28 2023-12-04)
+ cargo fmt --check
error: 'cargo-fmt' is not installed for the toolchain '1.74-x86_64-unknown-linux-gnu'.
To install, run `rustup component add --toolchain 1.74-x86_64-unknown-linux-gnu rustfmt`
Exit code: 1
{
"response": "finished",
"result": "failure"
}