mirror of
https://seed.radicle.dev/rad:z371PVmDHdjJucejRoRYJcDEvD5pp.git
synced 2026-10-18 19:10:34 +08:00
blog: Jujutsu + Radicle = ❤️
This commit is contained in:
committed by
Lorenz Leutgeb
parent
38d6dd2e2c
commit
9e985935c9
@@ -0,0 +1,574 @@
|
||||
---
|
||||
title: "Jujutsu + Radicle = ❤️"
|
||||
subtitle: "How I use Jujutsu in tandem with Radicle"
|
||||
author: "fintohaps"
|
||||
authorUrl: "https://app.radicle.xyz/nodes/seed.radicle.garden/users/did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM"
|
||||
layout: "blog"
|
||||
---
|
||||
|
||||
Roughly a year ago at the first ever [Local First Conference], a friend and
|
||||
previous colleague – [Alex Good] – told me about this tool called
|
||||
`jj` ([Jujutsu][jj]). We did the usual thing and I sat down beside him as he
|
||||
explained it to me. My brain did the usual thing and took in some of the
|
||||
information but not enough of it, and so I didn't touch `jj` for quite some time
|
||||
after that – but what's good enough for Alex Good is good enough for me.
|
||||
|
||||
After that, I feel like I saw a post about `jj` once every couple of months on
|
||||
Hacker News – confirmation bias anyone? It was a constant talking point during
|
||||
Git Merge 2024, and now it's a third Git tool that uses the concept of change
|
||||
identifiers, so it's a talking point on the [Git mailing list][git-list-change-id-topic].
|
||||
|
||||
So, fast-forward a year or so, and I've been using `jj` for quite some time
|
||||
while contributing to and maintaining the `heartwood` repository – the home of
|
||||
the Radicle protocol – as well as some others. Did I have to convince my whole
|
||||
team that `jj` should be used by all of us and we all switch to this new
|
||||
workflow? No. The first piece of "magic" of `jj` is that it is essentially a
|
||||
version control system that has a transparent layer on top of Git itself. A
|
||||
change in `jj` will always point to a Git commit. The beauty of its
|
||||
implementation is that the underlying commit can change as much as it wants,
|
||||
while the `jj` change remains the same. This unlocks a lot of nice flows for
|
||||
managing changes using `jj`.
|
||||
|
||||
So, you must be wondering by now, "How do I blend Radicle with `jj`?" Well,
|
||||
let's dance between the three worlds of `jj`, Git, and Radicle, to see how they
|
||||
have melted together to form a beautiful (_almost_) branch-less workflow.
|
||||
|
||||
### Radicle and Git
|
||||
|
||||
I won't spend too much time here, but if you don't know by now, Radicle works on
|
||||
top of Git to allow people to use this ubiquitous tool, while we benefit from
|
||||
its storage and protocol. When you start a Radicle repository, it's essentially
|
||||
a Git repository where we use some special references and extension points of
|
||||
Git to cryptographically secure your commits, and store all your
|
||||
[social, collaborative artifacts][guide-user-cobs]. If you haven't yet, go
|
||||
[download] Radicle and try it yourself using our [guides].
|
||||
|
||||
Note that if you're already familiar with `jj` this might not be that
|
||||
interesting for you, and you can skip to [User Config](#user-config).
|
||||
|
||||
#### My `.git/config`
|
||||
|
||||
As a maintainer of a few repositories using Radicle, I naturally need to push to
|
||||
and fetch from the repository in Radicle [storage][rip-storage]. This means
|
||||
that I'll need a remote – this is set up for you when you run `rad init` or `rad
|
||||
clone`. This looks like:
|
||||
|
||||
```ini
|
||||
[remote "rad"]
|
||||
url = rad://z371PVmDHdjJucejRoRYJcDEvD5pp
|
||||
fetch = +refs/heads/*:refs/remotes/rad/*
|
||||
fetch = +refs/tags/*:refs/remotes/rad/tags/*
|
||||
pushurl = rad://z371PVmDHdjJucejRoRYJcDEvD5pp/z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM
|
||||
[branch "master"]
|
||||
remote = rad
|
||||
merge = refs/heads/master
|
||||
```
|
||||
|
||||
The `rad://` URL tells `git` which [remote helper][git-remote-helpers] to use
|
||||
by trying to find `git-remote-rad`. This will handle fetching/pushing from/to
|
||||
the repository identified by `z371PVmDHdjJucejRoRYJcDEvD5pp`. The string
|
||||
`z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM` is my Node ID, and
|
||||
identifies my machine
|
||||
which makes sure that when I push, my references get stored under that
|
||||
[namespace][rip-storage-namespace]. Then when have the usual upstream branch setup
|
||||
for `master` for the `rad` remote – you may be familiar with this Git config entry
|
||||
when you have your `origin` set up for another Git forge.
|
||||
|
||||
There's one last piece of the puzzle in config that is an alias for easily
|
||||
creating a Radicle [patch][guide-user-patch].
|
||||
|
||||
```ini
|
||||
[alias]
|
||||
patch = push rad HEAD:refs/patches
|
||||
```
|
||||
|
||||
When you push to the special reference `refs/patches`, the remote helper will
|
||||
catch this and create a new patch for you, and in this case it will use whatever
|
||||
`HEAD` is for the head of the patch. Note that it will use whatever `rad/master`
|
||||
is as the base of the patch – that is to say, whatever commits are between
|
||||
`rad/master` and `HEAD` (including `HEAD`) are the commits being proposed by the
|
||||
patch. So, whenever I'm ready to make a patch, I use `git patch` and my
|
||||
`$EDITOR` pops open to make my well-crafted message describing what changes I'm
|
||||
making.
|
||||
|
||||
#### `git fetch rad` and `git push rad`
|
||||
|
||||
This is going to be brief. All I do with `git` now is `git fetch rad` (or my
|
||||
peer's remotes) to fetch any new work in Radicle storage. For pushing I will use
|
||||
`git push rad` to create or update patches (coming up), update my version of
|
||||
`master`, and, on the rare occasion, push a branch. That's it! No more `commit`,
|
||||
no more `rebase`, no more `merge` – ok I still use `git log` – but that's pretty
|
||||
much it. So how did I ditch all of these commands? Let's take a look `jj`.
|
||||
|
||||
### Jujutsu and Git
|
||||
|
||||
Let's see how I'm using `jj` by visiting several of its commands and seeing how
|
||||
I can use them in different scenarios.
|
||||
|
||||
#### `jj new`
|
||||
|
||||
It's only natural to start off with `jj new`. This command creates a new change
|
||||
in `jj`, as well as creating a new, empty commit for that change. Whenever I'm
|
||||
going to make a new change that's based on the `master` branch, I run:
|
||||
|
||||
<pre class="wide">
|
||||
$ jj new master@rad
|
||||
Working copy (@) now at: <span style="font-weight:bold;"></span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:purple;">qx</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">uvyurn</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:blue;">8e</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">711a87</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(empty)</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(no description set)</span>
|
||||
Parent commit (@-) : <span style="font-weight:bold;"></span><span style="font-weight:bold;color:purple;">xsl</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">qmmsl</span> <span style="font-weight:bold;"></span><span style="font-weight:bold;color:blue;">62</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">cdaf6d</span> <span style="color:purple;">master@rad</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;"> | </span>deployment: Vercel → Cloudflare Workers
|
||||
Added 0 files, modified 0 files, removed 1 files
|
||||
</pre>
|
||||
|
||||
You'll notice that `jj` spits out a Change ID and a Commit ID. You may also
|
||||
notice that a prefix is highlighted – this is the unique prefix for the change
|
||||
and the commit at this time! Which means that I can use `qx` or `8e` to refer to
|
||||
this particular change or commit without any ambiguity; an amazing UX, if you
|
||||
ask me.
|
||||
|
||||
At this point, I might know what I'm going to be working on so I use `jj
|
||||
describe` to give this change a message.
|
||||
|
||||
<pre class="wide">
|
||||
$ jj describe -m <span style="color:green;">"blog: Radicle and JJ"</span>
|
||||
Working copy (@) now at: <span style="font-weight:bold;"></span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:purple;">qx</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">uvyurn</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:blue;">40</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">8133a5</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(empty)</span><span style="font-weight:bold;"> blog: Radicle and JJ</span>
|
||||
Parent commit (@-) : <span style="font-weight:bold;"></span><span style="font-weight:bold;color:purple;">xsl</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">qmmsl</span> <span style="font-weight:bold;"></span><span style="font-weight:bold;color:blue;">62</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">cdaf6d</span> <span style="color:purple;">master@rad</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;"> | </span>deployment: Vercel → Cloudflare Workers
|
||||
</pre>
|
||||
|
||||
I've now changed the description so that it no longer says `(no description
|
||||
yet)`, and it now reads `blog: Radicle and JJ`.
|
||||
|
||||
So let's see what we have here:
|
||||
|
||||
<pre class="wide">
|
||||
$ jj show qx
|
||||
Commit ID: <span style="color:blue;">408133a5e54c80d2398be0c78cccabbd6063902d</span>
|
||||
Change ID: <span style="color:purple;">qxuvyurnqsvupzlpzsvzzpqlmlqvoxwq</span>
|
||||
Author : <span style="color:olive;">Fintan Halpenny</span> <<span style="color:olive;">fintan.halpenny@radicle.xyz</span>> (<span style="color:teal;">2025-06-10 07:52:34</span>)
|
||||
Committer: <span style="color:olive;">Fintan Halpenny</span> <<span style="color:olive;">fintan.halpenny@radicle.xyz</span>> (<span style="color:teal;">2025-06-10 07:52:34</span>)
|
||||
|
||||
blog: Radicle and JJ
|
||||
</pre>
|
||||
|
||||
We can see that it looks similar to a Git commit, which we can also inspect
|
||||
using:
|
||||
|
||||
<pre class="wide">
|
||||
$ git show 408133a5e54c80d2398be0c78cccabbd6063902d
|
||||
<span style="color:olive;">commit 408133a5e54c80d2398be0c78cccabbd6063902d</span>
|
||||
Author: Fintan Halpenny <fintan.halpenny@radicle.xyz>
|
||||
Date: 2025-06-10 07:52:34 +0200
|
||||
|
||||
blog: Radicle and JJ
|
||||
</pre>
|
||||
|
||||
This leaves us in a position to do our usual changes within our working copy of
|
||||
the Git repository.
|
||||
|
||||
At any point where I'm looking to separate changes, I can use `jj new` again,
|
||||
specifying any change to make a new change after the given change:
|
||||
|
||||
<pre class="wide">
|
||||
$ jj new qx
|
||||
Working copy (@) now at: <span style="font-weight:bold;"></span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:purple;">w</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">msmovxx</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:blue;">c5</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">0301c1</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(empty)</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(no description set)</span>
|
||||
Parent commit (@-) : <span style="font-weight:bold;"></span><span style="font-weight:bold;color:purple;">qx</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">uvyurn</span> <span style="font-weight:bold;"></span><span style="font-weight:bold;color:blue;">40</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">8133a5</span> blog: Radicle and JJ
|
||||
</pre>
|
||||
|
||||
<pre class="wide">
|
||||
$ jj describe -m <span style="color:green;">"blog: Radicle an JJ - add body"</span>
|
||||
Working copy (@) now at: <span style="font-weight:bold;"></span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:purple;">w</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">msmovxx</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:blue;">a3</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">d195ad</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(empty)</span><span style="font-weight:bold;"> blog: Radicle and JJ – add body</span>
|
||||
Parent commit (@-) : <span style="font-weight:bold;"></span><span style="font-weight:bold;color:purple;">qx</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">uvyurn</span> <span style="font-weight:bold;"></span><span style="font-weight:bold;color:blue;">40</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">8133a5</span> blog: Radicle and JJ
|
||||
</pre>
|
||||
|
||||
If I ever think I'm about to make some changes before the change I'm on, then I
|
||||
can use the `-B` option:
|
||||
|
||||
<pre class="wide">
|
||||
$ jj new -B @
|
||||
Rebased 1 descendant commits
|
||||
Working copy (@) now at: <span style="font-weight:bold;"></span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:purple;">zv</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">rmpyox</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:blue;">f0</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">635336</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(empty)</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(no description set)</span>
|
||||
Parent commit (@-) : <span style="font-weight:bold;"></span><span style="font-weight:bold;color:purple;">xsl</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">qmmsl</span> <span style="font-weight:bold;"></span><span style="font-weight:bold;color:blue;">62</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">cdaf6d</span> <span style="color:purple;">master@rad</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;"> | </span>deployment: Vercel → Cloudflare Workers
|
||||
Added 0 files, modified 0 files, removed 1 files
|
||||
</pre>
|
||||
|
||||
#### `jj edit`
|
||||
|
||||
At any point in time, I can also decide to go back to an old change and edit it,
|
||||
specifying the change that I want to edit:
|
||||
|
||||
<pre class="wide">
|
||||
$ jj edit qx
|
||||
Working copy (@) now at: <span style="font-weight:bold;"></span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:purple;">qx</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">uvyurn</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:blue;">40</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">8133a5</span><span style="font-weight:bold;"> blog: Radicle and JJ</span>
|
||||
Parent commit (@-) : <span style="font-weight:bold;"></span><span style="font-weight:bold;color:purple;">xsl</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">qmmsl</span> <span style="font-weight:bold;"></span><span style="font-weight:bold;color:blue;">62</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">cdaf6d</span> <span style="color:purple;">master@rad</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;"> | </span>deployment: Vercel → Cloudflare Workers
|
||||
</pre>
|
||||
|
||||
You can now forget about all those `fixup!` commits you were making to add
|
||||
changes into previous commits. No longer are you at the mercy of making a commit
|
||||
that is ahead of some other changes and you need to reorder it using `git
|
||||
rebase`. You taste that? It tastes like victory...
|
||||
|
||||
#### `jj squash`
|
||||
|
||||
Ok, so you've made some changes that are not related to the current change? This
|
||||
happens, or at least it does to me – I'm not perfect, (un)fortunately. I can use
|
||||
the power of `jj new`, whether after or before the current change, and combine
|
||||
it with `jj squash`:
|
||||
|
||||
<pre class="wide">
|
||||
$ jj squash -u --from w --to qx
|
||||
Rebased 1 descendant commits
|
||||
Working copy (@) now at: <span style="font-weight:bold;"></span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:purple;">qx</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">uvyurn</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:blue;">1e</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">2b0ccc</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(empty)</span><span style="font-weight:bold;"> blog: Radicle and JJ</span>
|
||||
Parent commit (@-) : <span style="font-weight:bold;"></span><span style="font-weight:bold;color:purple;">xsl</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">qmmsl</span> <span style="font-weight:bold;"></span><span style="font-weight:bold;color:blue;">62</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">cdaf6d</span> <span style="color:purple;">master@rad</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;"> | </span>deployment: Vercel → Cloudflare Workers
|
||||
</pre>
|
||||
|
||||
This says that I'm squashing the changes from the change identified by `r` into
|
||||
the change `xn`, and I want to keep the description of `xn` and drop the
|
||||
description of `r` (the `-u` option).
|
||||
|
||||
For extra points, `jj` even includes the beautiful `-i` option for _choosing_
|
||||
which changes you're taking from the source change – via a TUI. I cannot
|
||||
begin to describe how useful this is for moving around file changes and keeping
|
||||
my history clean and linear.
|
||||
|
||||
#### `jj rebase`
|
||||
|
||||
The final piece of the puzzle, at least for my workflow, is `jj rebase`. I can
|
||||
move around changes and put them on top of a destination change:
|
||||
|
||||
<pre class="wide">
|
||||
$ jj rebase -d qx -r sm
|
||||
Working copy (@) now at: <span style="font-weight:bold;"></span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:purple;">sm</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">vvuqzo</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:blue;">42</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">0180e8</span> blog: relevant blog material
|
||||
Parent commit (@-) : <span style="font-weight:bold;"></span><span style="font-weight:bold;color:purple;">qx</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">uvyurn</span> <span style="font-weight:bold;"></span><span style="font-weight:bold;color:blue;">1e</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">2b0ccc</span> blog: Radicle and JJ
|
||||
Added 1 files, modified 0 files, removed 0 files
|
||||
</pre>
|
||||
|
||||
This rebases the change `sm` onto the change `qx`. In fact, the `-r` can take a
|
||||
set of changes (see [revsets][jj-revsets]) and graft them all on top of the
|
||||
destination.
|
||||
|
||||
#### My `.jj/config`
|
||||
|
||||
The final part I'll touch on is my `jj` config, which can be split into the user
|
||||
and repo config. Thanks to Bruno, who wrote a lot of this on Zulip, and I
|
||||
cribbed it from him.
|
||||
|
||||
##### User Config
|
||||
|
||||
Here is my user config, and we'll discuss a couple of the entries, and I'll
|
||||
leave the rest as homework.
|
||||
|
||||
```toml
|
||||
[aliases]
|
||||
dlog = ["log", "-r"]
|
||||
l = ["log", "-r", "(trunk()..@):: | (trunk()..@)-"]
|
||||
fresh = ["new", "trunk()"]
|
||||
tug = [
|
||||
"bookmark",
|
||||
"move",
|
||||
"--from",
|
||||
"closest_bookmark(@)",
|
||||
"--to",
|
||||
"closest_pushable(@)",
|
||||
]
|
||||
|
||||
[revset-aliases]
|
||||
"closest_bookmark(to)" = "heads(::to & bookmarks())"
|
||||
"closest_pushable(to)" = "heads(::to & mutable() & ~description(exact:\"\") & (~empty() | merges()))"
|
||||
"desc(x)" = "description(x)"
|
||||
"pending()" = ".. ~ ::tags() ~ ::remote_bookmarks() ~ @ ~ private()"
|
||||
"private()" = "description(glob:'wip:*') | \
|
||||
description(glob:'private:*') | \
|
||||
description(glob:'WIP:*') | \
|
||||
description(glob:'PRIVATE:*') | \
|
||||
conflicts() | \
|
||||
(empty() ~ merges()) | \
|
||||
description('substring-i:\"DO NOT MAIL\"')"
|
||||
```
|
||||
|
||||
- `fresh`: this allows me to have an alias for `jj new master@rad` and use `jj
|
||||
fresh`.
|
||||
- `tug`: this allows me to tug the closest [bookmark][jj-bookmarks] to a change
|
||||
that can be pushed – we'll see an example of this later.
|
||||
|
||||
##### Repository Config
|
||||
|
||||
And here is my repository config, which we'll discuss a bit more in detail.
|
||||
|
||||
```toml
|
||||
[revset-aliases]
|
||||
"trunk()" = "master@rad"
|
||||
"immutable_heads()" = "present(trunk()) | \
|
||||
tags() | \
|
||||
( \
|
||||
untracked_remote_bookmarks() ~ \
|
||||
untracked_remote_bookmarks(remote='rad') ~ \
|
||||
untracked_remote_bookmarks(regex:'^patch(es)/',remote='rad') \
|
||||
)"
|
||||
|
||||
[git]
|
||||
write-change-id-header = true
|
||||
```
|
||||
|
||||
We want to change the `trunk()` alias from its default in `jj` so that it points
|
||||
to `master@rad`, the default branch in this particular Radicle repository. The
|
||||
`trunk()` revset is used in a few place, for example, we saw it above in
|
||||
`fresh`, but it is also in the next revset alias.
|
||||
|
||||
Some changes in `jj` will be marked as [immutable][jj-immutables-heads]. `jj`
|
||||
will prevent you from changing certain changes if they are marked as immutable,
|
||||
and its default value for this can be very restrictive, so instead we change it
|
||||
here. First we mark changes that are `present` in `trunk()` or `tags()` as
|
||||
immutable. Then we have untracked remote bookmarks with the set difference
|
||||
operator `~`. What are not marking as immutable are bookmarks that are in `rad`
|
||||
or that `patch`/`patches`. That is, if the changes are ours or from patches,
|
||||
then they're safe to edit. You might think, "Why are patches safe?"" Well, let's
|
||||
finally get into Radicle and Jujutsu.
|
||||
|
||||
### Radicle and Jujutsu
|
||||
|
||||
So here we are, a lot of build up to get to the point where I can describe how I
|
||||
can avoid using branches (as much as possible).
|
||||
|
||||
#### Contributing Patches
|
||||
|
||||
We will first dive into contributing a new patch using Radicle. As described in
|
||||
[Jujutsu and Git](#jujutsu-and-git), I can start making a set of changes using
|
||||
`jj new`, editing them just how I like using `jj edit`, and ordering them just
|
||||
the way I want with `jj rebase` and `jj squash`. During this whole time, I'm in
|
||||
that, initially scary, [detached HEAD state][detached-head]. Here it comes,
|
||||
we're going to make a patch!
|
||||
|
||||
##### Creating a New Patch
|
||||
|
||||
```
|
||||
git patch
|
||||
```
|
||||
|
||||
That's it. Well, the `$EDITOR` opens and I write a title and a body describing
|
||||
my wonderful changes, and when I'm done, the remote helper will create the patch
|
||||
and announce it to the network.
|
||||
|
||||
<pre class="wide">
|
||||
<span style="color:green;">✓</span> Patch <span style="color:teal;">e5f0a5a5adaa33c3b931235967e4930ece9bb617</span> opened
|
||||
<span style="color:green;">✓</span> Synced with <span style="color:green;">8</span> node(s)
|
||||
|
||||
To rad://z3cyotNHuasWowQ2h4yF9c3tFFdvc/z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C
|
||||
* [new reference] HEAD -> refs/patches
|
||||
</pre>
|
||||
|
||||
|
||||
##### Updating a Patch
|
||||
|
||||
Let's be honest though, my wonderful changes are rarely wonderful from the
|
||||
get-go. They need some polishing, and my peers always have great suggestions
|
||||
that I should integrate into the patch.
|
||||
|
||||
From here, I can find the patch using `rad patch`:
|
||||
|
||||
<pre class="wide">
|
||||
$ rad patch
|
||||
<span style="color:#2a2a2a;">╭</span><span style="color:#2a2a2a;">───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><span style="color:#2a2a2a;">╮</span>
|
||||
<span style="color:#2a2a2a;">│ </span>● <span style="font-weight:bold;">ID</span> <span style="font-weight:bold;">Title</span> <span style="font-weight:bold;">Author</span> <span style="font-weight:bold;">Reviews</span> <span style="font-weight:bold;">Head</span> <span style="font-weight:bold;">+</span> <span style="font-weight:bold;">-</span> <span style="font-weight:bold;">Updated</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">├</span><span style="color:#2a2a2a;">───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><span style="color:#2a2a2a;">┤</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">18a71ad</span> radicle-cli: Warn when using old names of nodes <span style="color:purple;">self</span> <span style="font-style:italic;color:purple;">(you)</span> - - - - - <span style="color:blue;">552f4af</span> <span style="color:green;">+146</span> <span style="color:red;">-3</span> <span style="font-style:italic;">4 days ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">ed450c9</span> node, profile, ssh: Make key location configurable <span style="color:purple;">self</span> <span style="font-style:italic;color:purple;">(you)</span> - - - - - <span style="color:blue;">d2f7b89</span> <span style="color:green;">+376</span> <span style="color:red;">-74</span> <span style="font-style:italic;">1 month ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">12bc851</span> node, cli: Refactor test environment <span style="color:purple;">self</span> <span style="font-style:italic;color:purple;">(you)</span> - - - - - <span style="color:blue;">d059957</span> <span style="color:green;">+826</span> <span style="color:red;">-1214</span> <span style="font-style:italic;">1 month ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">3219ef8</span> Remove predefined bootstrap nodes <span style="color:purple;">istankovic</span> <span style="color:purple;">z6MkmiJ…mkTV5sS</span> - - - - - <span style="color:blue;">7322e3a</span> <span style="color:green;">+138</span> <span style="color:red;">-108</span> <span style="font-style:italic;">2 days ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">058586b</span> Suggest the git configured default branch during init <span style="color:purple;">stemporus</span> <span style="color:purple;">z6MkqLa…jr8xo5K</span> - - - - - <span style="color:blue;">6a1147f</span> <span style="color:green;">+16</span> <span style="color:red;">-8</span> <span style="font-style:italic;">2 weeks ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">1015e51</span> build: Rewrite tagging script <span style="color:purple;">fintohaps</span> <span style="color:purple;">z6Mkire…SQZ3voM</span> - - - - - <span style="color:blue;">149de0b</span> <span style="color:green;">+24</span> <span style="color:red;">-12</span> <span style="font-style:italic;">3 weeks ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">e85ff9a</span> node: clean up `UploadError` <span style="color:purple;">fintohaps</span> <span style="color:purple;">z6Mkire…SQZ3voM</span> - - - - - <span style="color:blue;">b408e44</span> <span style="color:green;">+15</span> <span style="color:red;">-13</span> <span style="font-style:italic;">3 weeks ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">c54883e</span> Canonical References <span style="color:purple;">fintohaps</span> <span style="color:purple;">z6Mkire…SQZ3voM</span> - - - - - <span style="color:blue;">34014a6</span> <span style="color:green;">+4642</span> <span style="color:red;">-1575</span> <span style="font-style:italic;">1 month ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">e500399</span> radicle: improve inline comments <span style="color:purple;">fintohaps</span> <span style="color:purple;">z6Mkire…SQZ3voM</span> - - - - - <span style="color:blue;">e7cab63</span> <span style="color:green;">+924</span> <span style="color:red;">-244</span> <span style="font-style:italic;">1 month ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">6080c3c</span> Add issue instructions <span style="color:purple;">yorgos-laptop</span> <span style="color:purple;">z6MkrnX…CPFSFS3</span> - - - - - <span style="color:blue;">1877285</span> <span style="color:green;">+32</span> <span style="color:red;">-15</span> <span style="font-style:italic;">1 month ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">40a8d72</span> radicle: introduce COB stream <span style="color:purple;">fintohaps</span> <span style="color:purple;">z6Mkire…SQZ3voM</span> - - - - - <span style="color:blue;">ec00acb</span> <span style="color:green;">+1178</span> <span style="color:red;">-9</span> <span style="font-style:italic;">4 months ago</span><span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> <span style="color:teal;">8ab3f9c</span> Add document on how to implement a new COB type <span style="color:purple;">liw</span> <span style="color:purple;">z6MkgEM…1b2w2FV</span> - - - - - <span style="color:blue;">5a3b095</span> <span style="color:green;">+314</span> <span style="color:red;">-0</span> <span style="font-style:italic;">1 year ago</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">╰</span><span style="color:#2a2a2a;">───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><span style="color:#2a2a2a;">╯</span>
|
||||
</pre>
|
||||
|
||||
Let's say I received feedback on my `Canonical References` patch, I can use its
|
||||
`ID`, the shortened version above, to inspect it:
|
||||
|
||||
<pre class="wide">
|
||||
$ rad patch show c54883e
|
||||
<span style="color:#2a2a2a;">╭</span><span style="color:#2a2a2a;">──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><span style="color:#2a2a2a;">╮</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">Title</span> <span style="font-weight:bold;">Canonical References</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">Patch</span> c54883e5ffb1f8a99f432e3ac79c0b728cd0dab3 <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">Author</span> <span style="color:purple;">fintohaps</span> <span style="color:purple;">z6Mkire…SQZ3voM</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">Head</span> <span style="color:blue;">34014a67b0ddc859d95e17ffc71c1ae61aff5758</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">Branches</span> <span style="color:olive;">patch/c54883e, sync-goal</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">Commits</span> ahead <span style="color:green;">6</span>, behind <span style="color:red;">49</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">Status</span> <span style="color:green;">open</span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>See RIP-0004[^0] for the specification. <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>This patch is an implementation of RIP-0004. It implements the rules mechanism <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>within the `rules` module. This is interplays with the existing `canonical` <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>mechanisms, already defined (but slightly refactored). <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>The `rules` are then used in pushing and fetching references. A test is added to <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>illustrate the canonical references in action via tags. <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>There were some incidental changes that were made to ensure the tags use case is <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>easy for users. The first change was to add a tags refspec to remotes in order <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>to easily fetch tags from peers -- as well ensuring those tags do not pollute <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>the `refs/tags` namespace in the working copy. <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>This had a knock on change where there was a bug `libgit2` that didn't allow for <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>deleting `multivar` entries, which the new remote setup fell under. This was <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>fixed and so we update to `git2-0.19`. <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>As well this, the `rad id update` command would error if the payload identifier <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>was not the project identifier. This would stop adding new payloads to extend <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>the identity -- which was needed for introducing canonical references. <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span> <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>[^0]: <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span>https://app.radicle.xyz/nodes/seed.radicle.garden/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/patches/1d1ce874f7c39ecdcd8c318bbae46ffd02fe1ea8?tab=changes<span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">├</span><span style="color:#2a2a2a;">──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><span style="color:#2a2a2a;">┤</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:blue;">34014a6</span> radicle: refactor rule matching <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:blue;">0e0b77e</span> radicle: add canonical refs to identity <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:blue;">bbe019c</span> radicle: canonical reference rules <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:blue;">b3ad6f2</span> radicle: refactor Canonical <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:blue;">04277b4</span> radicle: store threshold in Canonical <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:blue;">312c6a4</span> meta: relax radicle-git dependencies <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">├</span><span style="color:#2a2a2a;">──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><span style="color:#2a2a2a;">┤</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:green;">●</span> opened by <span style="color:purple;">fintohaps</span> <span style="color:purple;">z6Mkire…SQZ3voM</span> <span style="color:blue;">(3e97837)</span> 10 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to c1a2cc5787f44c0a835c1deae375be04c399dd7e <span style="color:blue;">(58e932c)</span> 9 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to c55494efc2e780cd6c91a1f90efdae8a3eb1c7ef <span style="color:blue;">(1b07774)</span> 8 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to 583e6b3366c36cc7e67910c29a66750397a60484 <span style="color:blue;">(fdd5277)</span> 7 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to d54ddef216909bdd3e54e33e4f82c45df79c00d3 <span style="color:blue;">(f24f9d8)</span> 7 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to ac48ae6e75d4eaa13daed657eed24dfeabb9be94 <span style="color:blue;">(7d8e461)</span> 7 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to 2b31e460db7451376dc3e346ee02b5fd597fa5c6 <span style="color:blue;">(040cfb7)</span> 7 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to e1c360a1311a0a215bed6eb42e4b0c8c5c44e611 <span style="color:blue;">(f0dec88)</span> 6 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to 492cfbafd31e4bac85ee73af519ddc6254b47f82 <span style="color:blue;">(f9cb27f)</span> 4 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to fbdf18d7683bdac7a76149777eed5cf9bbbf5bd5 <span style="color:blue;">(2a64755)</span> 4 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to 4baf32afd65f2c4b374d8f21fed6877aa804a003 <span style="color:blue;">(0cecae6)</span> 4 months ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span> └─ ⋄ reviewed by <span style="color:purple;">self</span> <span style="font-style:italic;color:purple;">(you)</span> 1 month ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to d2ebc70caca54a8ba508d72862c1e1c79d718129 <span style="color:blue;">(4515d45)</span> 1 month ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to 13e9ba641c624db26b6bfe85870daf064f90e9ab <span style="color:blue;">(045e465)</span> 1 month ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to 47495c408ccf5eec49b61c7bdb339e5f2d695a30 <span style="color:blue;">(a6be355)</span> 1 month ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to e3bdb65d3adb94360dd3449744792f6ecb1f451f <span style="color:blue;">(8d08215)</span> 1 month ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span> └─ ⋄ reviewed by <span style="color:purple;">erikli</span> <span style="color:purple;">z6MkgFq…FGAnBGz</span> 1 month ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to 9f779028704b4c022cbe25c0e4a9bb46dc8463ba <span style="color:blue;">(49fcea7)</span> 1 month ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to 86ebfcaaf986edba5e77ede1be4d3c4ce33bd27c <span style="color:blue;">(2df7cd9)</span> 1 month ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">│ </span><span style="color:teal;">↑</span> updated to fa9bdff35d76903f72cf24f1cccca812ae26e98c <span style="color:blue;">(34014a6)</span> 1 month ago <span style="color:#2a2a2a;"> │</span>
|
||||
<span style="color:#2a2a2a;">╰</span><span style="color:#2a2a2a;">──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span><span style="color:#2a2a2a;">╯</span>
|
||||
</pre>
|
||||
|
||||
You can see here how non-perfect my changes are, I'm being vulnerable here.
|
||||
|
||||
I can now grab the value `Head` in the above table, and use it in `jj`, by
|
||||
running `jj new 34014a67b0ddc859d95e17ffc71c1ae61aff5758`. This will drop me
|
||||
onto a new change after `34014a67b0ddc859d95e17ffc71c1ae61aff5758`, and then I
|
||||
can use `jj log -r ::@` to see all the previous changes.
|
||||
|
||||
Again, I use the wonderful `jj edit` command, or perhaps I make new changes that
|
||||
I then `jj squash` into the relevant changes – it all depends on the scope of
|
||||
the change!
|
||||
|
||||
Once I'm done, I push `HEAD` to another special [refspec][git-refspec], using
|
||||
the patch's full identifier:
|
||||
|
||||
```sh
|
||||
git push rad HEAD:patches/c54883e5ffb1f8a99f432e3ac79c0b728cd0dab3 -f
|
||||
```
|
||||
|
||||
We use `-f` if we are editing the changes since this will change the underlying
|
||||
commits and `git` will reject this. Once again, this will open my `$EDITOR` and
|
||||
I will add a message about the changes that were made in this update.
|
||||
|
||||
This creates a new "revision" for the patch, preserving the older revisions.
|
||||
So essentially, patches in Radicle are append-only. This makes it safe for us to
|
||||
make edits to changes, marking them as mutable – the Git history will be
|
||||
preserved!
|
||||
|
||||
#### Maintaining Patches
|
||||
|
||||
From the maintaining perspective, the flow starts off similar to updating, where
|
||||
I would look up the patch that I want to merge. If I made the patch, things are
|
||||
a bit easier because the Git objects are easily accessible and I can do `jj new`
|
||||
using the commit. If I attempt to do this with a patch that came from another
|
||||
contributor, then I may run into this issue:
|
||||
|
||||
<pre class="wide">
|
||||
$ jj new 7322e3ac61669ba6dbde16bb0f7d30edf1ee85ce
|
||||
<span style="font-weight:bold;"></span><span style="font-weight:bold;color:red;">Error: </span><span style="font-weight:bold;">Revision `7322e3ac61669ba6dbde16bb0f7d30edf1ee85ce` doesn't exist</span>
|
||||
</pre>
|
||||
|
||||
The way to do this instead, is to use the remote syntax and the special
|
||||
`patches` reference:
|
||||
|
||||
<pre class="wide">
|
||||
$ jj new patches/3219ef871dd44c7ef51693f4aeba4c2c5c0c5c7b@rad
|
||||
Working copy (@) now at: <span style="font-weight:bold;"></span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:purple;">oo</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">xzsqoy</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:blue;">eb</span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:dimgray;">9e0803</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(empty)</span><span style="font-weight:bold;"> </span><span style="font-weight:bold;filter: contrast(70%) brightness(190%);color:green;">(no description set)</span>
|
||||
Parent commit (@-) : <span style="font-weight:bold;"></span><span style="font-weight:bold;color:purple;">s</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">wpyssrk</span> <span style="font-weight:bold;"></span><span style="font-weight:bold;color:blue;">73</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;">22e3ac</span> <span style="color:purple;">patches/3219ef871dd44c7ef51693f4aeba4c2c5c0c5c7b patches/3219ef871dd44c7ef51693f4aeba4c2c5c0c5c7b@rad</span><span style="filter: contrast(70%) brightness(190%);color:dimgray;"> | </span>node, cli: remove predefined bootstrap nodes
|
||||
</pre>
|
||||
|
||||
At this point, I can also look at what commits are in the patch via `rad patch
|
||||
show`, or by using `jj log -r ::@`. If they're already on top `master@rad`, then
|
||||
to merge the patch I can simply `git push rad master` – and the remote helper
|
||||
marks the patch as merged if the canonical reference of `master` is update (a
|
||||
topic for another time).
|
||||
|
||||
If the patch isn't on top of `master@rad` then I can rebase the changes using
|
||||
`jj rebase -d master@rad -r <base>::<head>` to get the series of changes on top
|
||||
of our latest. It's then necessary to push a new revision to the patch so that
|
||||
the patch can know it is being merged with the new commits – remember that I
|
||||
rebased, so this changes the underlying commits.
|
||||
|
||||
We should update our `master` bookmark, and this is where the `tug` alias comes
|
||||
in. When I run `jj tug`, it figures out that `master` is the closest bookmark
|
||||
and pulls it up to the latest change that can be pushed. I can then push to
|
||||
update the patch:
|
||||
|
||||
```sh
|
||||
git push rad master:patches/3219ef871dd44c7ef51693f4aeba4c2c5c0c5c7b -f
|
||||
```
|
||||
|
||||
Here I'm using `master` instead of `HEAD` – this gets around a little issue I've
|
||||
been seeing for patches that I do not own, where the remote helper rejects the
|
||||
push because it cannot resolve `HEAD` (a mystery left for another day).
|
||||
|
||||
Once the patch has been rebased, I can do the usual `git push rad master` to
|
||||
update the canonical reference and have the patch marked as merged.
|
||||
|
||||
## Conclusion
|
||||
|
||||
And our adventure ends here. We dived into how Radicle works with Git, how
|
||||
Jujutsu works Git, and how I use Jujutsu to have a branch-less flow in Radicle.
|
||||
This is has been a dream to work with. This type of tooling feels like it
|
||||
enables me a lot more when managing my changes and keeping a clean history. I
|
||||
was *able* to do this with `git rebase`, but it felt like it got in the way more
|
||||
than it enabled me – and I haven't even touched on how [conflicts][jj-conflicts]
|
||||
are easy in Jujutsu!
|
||||
|
||||
There is plenty of room for improvements here, some things on my list are:
|
||||
- Keeping track of Jujutsu change IDs in Radicle data, which is already being
|
||||
looked at!
|
||||
- Not needing to use `rad patch show` to get metadata for managing patches, and
|
||||
perhaps even bookmarking patch identifiers automatically.
|
||||
|
||||
Come help in discussion on our [Zulip], and enjoy being Radicle 🌱👾
|
||||
|
||||
<!-- Other people and events. --->
|
||||
[Alex Good]: https://patternist.xyz
|
||||
[Local First Conference]: https://www.localfirstconf.com/
|
||||
[detached-head]: https://wizardzines.com/comics/detached-head-state/
|
||||
|
||||
<!-- Jujutsu -->
|
||||
[jj]: https://jj-vcs.github.io/jj/v0.30.0/
|
||||
[jj-bookmarks]: https://jj-vcs.github.io/jj/v0.30.0/bookmarks
|
||||
[jj-conflicts]: https://jj-vcs.github.io/jj/v0.30.0/conflicts
|
||||
[jj-immutables-heads]: https://jj-vcs.github.io/jj/v0.30.0/config/#set-of-immutable-commits
|
||||
[jj-revsets]: https://jj-vcs.github.io/jj/v0.30.0/revsets
|
||||
|
||||
<!-- Git -->
|
||||
[git-list-change-id-topic]: https://lore.kernel.org/git/CAESOdVAspxUJKGAA58i0tvks4ZOfoGf1Aa5gPr0FXzdcywqUUw@mail.gmail.com/
|
||||
[git-refspec]: https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
|
||||
[git-remote-helpers]: https://git-scm.com/docs/gitremote-helpers
|
||||
|
||||
<!-- Radicle -->
|
||||
[Zulip]: https://radicle.zulipchat.com
|
||||
[download]: https://radicle.xyz/download
|
||||
[guide-user-cobs]: https://radicle.xyz/guides/user#2-collaborating-the-radicle-way
|
||||
[guide-user-patch]: https://radicle.xyz/guides/user#working-with-patches
|
||||
[guides]: https://radicle.xyz/guides
|
||||
[rip-storage]: https://app.radicle.xyz/nodes/ash.radicle.garden/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/0003-storage-layout.md
|
||||
[rip-storage-namespace]: https://app.radicle.xyz/nodes/ash.radicle.garden/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/0003-storage-layout.md#layout
|
||||
@@ -132,6 +132,7 @@ updated, join our community on 💬 [Zulip][zulip], or <a href="{{ site.feed.pat
|
||||
|
||||
## Blog
|
||||
|
||||
- 14.08.2025 [Jujutsu + Radicle = ❤️](/2025/08/14/jj-with-radicle.html)
|
||||
- 12.08.2025 [Canonical References](/2025/08/12/canonical-references.html) released. ✨
|
||||
- 23.07.2025 [Using Radicle CI for Development](/2025/07/23/using-radicle-ci-for-development.html)
|
||||
- 30.05.2025 [How we used Radicle with GitHub Actions](/2025/05/30/radicle-with-github-actions.html)
|
||||
|
||||
Reference in New Issue
Block a user