`cargo check` fails because `human-panic` and `sysinfo` require at least
1.88.0.
+++ command cargo check --release --locked --all-targets
error: rustc 1.85.0 is not supported by the following packages:
human-panic@2.0.6 requires rustc 1.88
sysinfo@0.37.2 requires rustc 1.88
Bump MSRV to fix this.
1.88.0 introduced [let chains], which in turn has `clippy` warn about
nested if statements. All of these sites are fixed in this change.
1.87.0 introduced [`is_multiple_of`], which is a more readable version
of `x % y == 0`.
[let chains]: https://blog.rust-lang.org/2025/06/26/Rust-1.88.0/#let-chains
[`is_multiple_of`]: https://doc.rust-lang.org/std/primitive.usize.html#method.is_multiple_of
Canonical references can not be used to model symbolic references.
Relax this restriction by adding another member to the payload, named
"symbolic". Its key-value/name-target pairs then translate directly to
canonical symbolic references.
Care is taken to not allow circular references, and that there always
is a rule that could generate the target of a symbolic reference
(or a chain of symbolic references). Still, a symbolic reference may
dangle (for example when its target reference cannot be computed
because of divergence), but at least it can be prevented that a
symref *always* dangles, because there is no rule that would produce
its target.
Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
The method `set_head` does two things:
1. Compute the canonical head and set the default branch to target.
2. Set the symbolic reference `HEAD` to target the default branch.
Split these two concerns into:
1. `set_default_branch_to_canonical_head`
2. `set_head_to_default_branch`
Instead of passing the signer as an argument to many methods on `Store`,
scope the `Store` itself to a signer.
This further allows to differentiate two different access modes on the
store in `radicle::cob::store::access`: `WriteAs` (which requires signer)
and `ReadOnly` (which does not require a signer).
The caches for issues and patches in `radicle::cob::{issue,patch}::Cache`
are concretised by removing the first type parameter, since it was
specific to issues and patches anyway. This was done in this commit as
it touches very similar usage sites.
Make `Device` less prominent, and instead lean more heavily towards
traits from the `signature` crate, such as `Keypair` and `Verifier`
in addition to `Signer`. Trait bounds regarding `Signer` could be
simplified, but this is left for the future.
In `radicle-cli`, the function `term::cob::patches_mut`, which generates
errors with a hint is used instead of the lower-level `Profile::patches_mut`.
Commands `rad issue cache` and `rad patch cache` now construct a
writeable cache on top of a read-only store.
Many knock-on changes are handled as well, to arrive at a clean state.
To be able to reason about backwards compatibility, downgrade attacks,
and future configurable enforcement of minimum required (security)
features, introduce the concept of a "feature level" in Signed
References.
In this implementation feature levels are assumed to be monotonic, i.e.,
that higher feature levels behave like lower ones, and only add
semantics on top.
Implementing this as an `enum` that is `Ord` flows nicely.
Treatment of the write-side is left for the next commit. Currently,
the write side "promises" `FeatureLevel::Parent`, but is not able
to "keep the promise", since it does not actually inspect the
feature level of the head in case it is unchanged.
CHANGELOG
Make `git2` an *optional* dependency of `radicle-cob`, and refactor
`radicle` to depend on crates that in turn do not depend on `git2`
*non-optionally*
The main offending dependency of `radicle-cob` is `radicle-git-ext`
from the `radicle-git` workspace in repository
(rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt) which *non-optionally* depends on
`git2`.
So, to achieve removal of this dependency:
1. The crate is refactored to depend on the new crates
`radicle-git-ref-format` `radicle-git-metadata`, and
`radicle-oid` introduced in the previous commits, instead of
`radicle-git-ext`.
2. Some code from the `radicle-git-ext` crate in the `radicle-git`
workspace in repository (rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt) is
copied. See `crates/radicle-cob/src/backend/git/commit.rs`.
This cascades to `radicle` and its dependents.
Firstly, the there is an
`impl Deref<Target=git2::Oid> for radicle_git_ext::Oid`. This made
it very convenient to just deref to obtain the wrapped `git2::Oid`,
so there are many expressions of the shape `*oid` in `radicle` and
its dependents. However `radicle-oid` does not provide
`impl Deref<Target=git2::Oid> for radicle_oid::Oid`, as notably,
`Target` is an associated type, and not a type parameter, so an
implementation of `Deref` would tie the new `Oid` too tightly to a
particular implementation.
Instead, work with `impl From<radicle_oid::Oid> for git2::Oid`
(which can be enabled using the feature flag `radicle-oid/git2`).
This explains the changes from `*oid` to `oid.into()` at every
transition to "`git2` land".
Secondly, `radicle` and its dependents are refactored to also depend
on `radicle-git-ref-format` and (much less prominently)
`radicle-git-metadata` instead of `radicle-git-ext`
This is to avoid pulling in `git2` via these dependencies.
Thirdly, as the re-exports in `crates/radicle/src/git.rs` change,
they are at the same time also cleaned up. Notably, the types from
`radicle-git-ref-format` are re-exported under `fmt` only, not "twice".
While overall this obviously is very much a breaking change, these
changes should mostly amount to changing from `Deref` to `Into`, i.e.,
`*oid` → `oid.into()` and rewriting imports for `radicle::fmt`.
This is indicated by the mostly mechanical nature of the changes to
`crates/radicle-{cli,node,remote-helper}`.
A refactoring internal to the `radicle` crate, with the goal of
making dependency on `git2` clearer and more controlled.
`radicle::raw` is changed from a complete re-export of `git2`
to a module that selectively re-exports (many) members of `git2`.
required to build the workspace (potentially breaking dependents
outside the workspace, but given just how many types are
re-exported, this seems unlikely).
In the future, no more `use git2::…` statements should be added
outside of `crates/radicle/src/git/raw.rs`. This is in an effort to
decrease dependence on `git2` in the future.
When using Jujutsu and a non-colocated Git repository, the detection
using `git2` directly fails (just as `git` in a shell would fail)
because there is no `.git` directory found by traversing up the
filesystem hierarchy.
Add an invocation of `jj git root` as a fallback.
Microsoft Windows has Universal Naming Convention (UNC) paths, see
<https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#unc-paths>
The Rust standard library normalizes paths to this form when calling
`std::fs::canonicalize`, see
<https://doc.rust-lang.org/std/fs/fn.canonicalize.html>
However, some programs, do not parse these paths correctly, including
our ally `git`:
$ git clone \\?\C:\Users\lorenz\tmp
Cloning into 'tmp'...
ssh: Could not resolve hostname \\\\?\\c: No such host is known.
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
The `dunce` crate solves the problem of having to deal with
normalization and avoiding weird UNC paths for us. Note that on
non-Windows platforms, it *behaves exactly like*
`std::fs::canonicalize`!