tutorial_radicle_patch_workflow.org
1 #+title: Tutorial: Radicle Patch Workflow — From Issue to Merge 2 #+author: Xavier Brinon 3 #+date: [2026-04-06 Mon] 4 #+startup: indent 5 #+options: toc:t num:t ^:{} 6 7 * What You Will Learn 8 9 By the end of this tutorial you will have completed the full lifecycle of a 10 Radicle issue: 11 12 - Created an issue to track a unit of work 13 - Branched, coded, and committed a fix 14 - Opened a patch (Radicle's equivalent of a pull request) 15 - Reviewed and validated the patch 16 - Merged the patch and closed the issue 17 18 This tutorial uses *MC-003: Configure Project Structure* as a worked example. 19 The steps apply to any issue in any Radicle repository. 20 21 * Prerequisites 22 23 - A Radicle-initialized repository (=rad init= already run) 24 - The =rad= CLI installed and working 25 - A =rad= remote configured (=git remote -v= shows a =rad://= URL) 26 - Basic familiarity with Git branching and merging 27 28 * Step 1: Create an Issue 29 30 Open a new issue describing the work to be done. 31 32 #+begin_src shell 33 rad issue open --title "MC-003: Configure Project Structure" \ 34 --description "Create src directory structure for types, components, machines, stores, and server." 35 #+end_src 36 37 Radicle returns the issue ID — a short hex string like =3085700=. Note it down; 38 you will reference it throughout. 39 40 Verify the issue exists: 41 42 #+begin_src shell 43 rad issue list 44 #+end_src 45 46 You should see your issue in the list with status *open*. 47 48 #+description: Tip Add labels to categorize the issue 49 #+begin_src shell 50 rad issue label 3085700 --add "chore" --add "phase-0" 51 #+end_src 52 53 * Step 2: Create a Working Branch 54 55 Create a branch for your work. A descriptive name helps others understand what 56 the branch is for. 57 58 #+begin_src shell 59 git switch -c MC003/configure-project-structure 60 #+end_src 61 62 * Step 3: Do the Work 63 64 Make your changes. For MC-003 this means creating the directory structure and 65 adding a barrel export file. 66 67 #+begin_src shell 68 mkdir -p mission-control/src/{types,components,machines,stores,server} 69 #+end_src 70 71 Create =src/types/index.ts= as a barrel export (this also ensures Git tracks the 72 directory — Git does not track empty directories): 73 74 #+begin_src javascript 75 // src/types/index.ts 76 // Barrel export for shared types 77 #+end_src 78 79 Stage and commit: 80 81 #+begin_src shell 82 git add mission-control/src/types/index.ts 83 git commit -m "chore: create directory structure for ADR-001 state management" 84 #+end_src 85 86 * Step 4: Open a Patch 87 88 Push your branch to Radicle's special =refs/patches= reference. This creates a 89 *patch* — Radicle's collaborative code review object. 90 91 #+begin_src shell 92 git push rad HEAD:refs/patches 93 #+end_src 94 95 An editor opens for the patch title and description. Write: 96 97 #+begin_example 98 MC-003: Configure project structure 99 100 Create src directory structure for types, components, machines, 101 stores, and server. Add barrel export for types/. 102 #+end_example 103 104 Save and close the editor. Radicle prints the patch ID: 105 106 #+begin_example 107 ✓ Patch 730cd2e opened 108 #+end_example 109 110 ** What just happened? 111 Radicle created a /Collaborative Object/ (COB) — a Git object that stores the 112 patch metadata, title, description, and revision history. Unlike GitHub PRs 113 which live on a server, this object lives in your local Git repository and syncs 114 to seeds when available. You can work offline. 115 116 * Step 5: Review the Patch 117 118 List open patches to confirm yours appears: 119 120 #+begin_src shell 121 rad patch 122 #+end_src 123 124 Inspect the patch details: 125 126 #+begin_src shell 127 rad patch show 730cd2e 128 #+end_src 129 130 This shows the title, author, status, base branch, and commits. Verify the patch 131 targets =main= and contains the expected commit(s). 132 133 Review the diff without checking it out: 134 135 #+begin_src shell 136 rad patch diff 730cd2e 137 #+end_src 138 139 * Step 6: Validate the Patch 140 141 Check out the patch to test it locally: 142 143 #+begin_src shell 144 rad patch checkout 730cd2e 145 #+end_src 146 147 This creates a local branch =patch/730cd2e= at the patch's latest revision. Now 148 validate: 149 150 #+begin_src shell 151 # Verify directories exist 152 ls mission-control/src/types mission-control/src/components \ 153 mission-control/src/machines mission-control/src/stores \ 154 mission-control/src/server 155 156 # Verify no TypeScript errors 157 cd mission-control && npx tsc --noEmit 158 159 # Verify dev server starts 160 npm run dev 161 #+end_src 162 163 If everything passes, the patch is ready to merge. 164 165 ** If changes are needed: 166 Go back to your working branch, fix the issue, amend, and force-push. Each push 167 creates an immutable /revision/ — the full review history is preserved. 168 169 #+begin_src shell 170 git checkout MC003/configure-project-structure 171 # ... make fixes ... 172 git commit --amend 173 git push --force 174 #+end_src 175 176 Optionally include a revision message explaining the update: 177 178 #+begin_src shell 179 git push -o patch.message="fix: added missing server directory" --force 180 #+end_src 181 182 * Step 7: Merge the Patch 183 184 Switch to =main= and merge. You can merge either the patch branch or your 185 original working branch — the effect is the same. 186 187 #+begin_src shell 188 git switch main 189 git merge patch/730cd2e 190 #+end_src 191 192 Or equivalently: 193 194 #+begin_src shell 195 git switch main 196 git merge MC003/configure-project-structure 197 #+end_src 198 199 Then push =main= to the Radicle remote. This marks the patch as *merged* and 200 syncs the canonical branch with the network. 201 202 #+begin_src shell 203 git push rad main 204 #+end_src 205 206 Verify the patch shows as merged: 207 208 #+begin_src shell 209 rad patch --merged 210 #+end_src 211 212 ** Key point: 213 There is no "merge button." You merge locally with standard Git commands, then 214 =git push rad main= tells the Radicle network the patch is done. This keeps the 215 canonical branch entirely under your control. 216 217 * Step 8: Close the Issue 218 219 Transition the issue state to closed: 220 221 #+begin_src shell 222 rad issue state --closed 3085700 223 #+end_src 224 225 Verify: 226 227 #+begin_src shell 228 rad issue show 3085700 229 #+end_src 230 231 The status should now read *closed*. 232 233 * Step 9: Clean Up 234 235 Delete the local working branch and the patch branch: 236 237 #+begin_src shell 238 git branch -d MC003/configure-project-structure 239 git branch -d patch/730cd2e 240 #+end_src 241 242 * Using Magit for the Patch Workflow 243 244 Because you use Emacs with Magit (obviously), most of the Git steps map directly 245 to Magit commands. Radicle-specific commands (=rad issue=, =rad patch=) still 246 run in a shell — either =M-x shell-command= or =M-x eshell= — but the Git 247 operations are smoother through Magit. 248 249 ** Branch, Stage, and Commit (Steps 2-3) 250 251 - =b c= (=magit-branch-and-checkout=) :: create and switch to working branch 252 - Stage files in the Magit status buffer with =s= (stage file) or =S= (stage 253 all) 254 - =c c= (=magit-commit-create=) :: write your commit message in the dedicated 255 buffer 256 257 ** Open a Patch (Step 4) 258 259 Magit does not know about =refs/patches=, so use a manual push: 260 261 - =P= to open the push transient 262 - =e= (=magit-push-elsewhere=) — set the remote to =rad= and the refspec to 263 =HEAD:refs/patches= 264 265 Or run from the Magit status buffer: 266 267 =M-!= then type =git push rad HEAD:refs/patches= 268 269 The editor for the patch title/description opens in your configured =$EDITOR=. 270 If that is =emacsclient= (because of course it is), it opens in Emacs as a 271 commit-message-style buffer. 272 273 ** Review the Diff (Step 5) 274 275 From a shell (=M-!=): 276 277 #+begin_src shell 278 rad patch diff 730cd2e 279 #+end_src 280 281 Or check out the patch branch and use Magit's diff viewer: 282 283 - =M-!= then =rad patch checkout 730cd2e= 284 - =d d= (=magit-diff-dwim=) in the Magit status buffer to see changes against 285 =main= 286 287 ** Merge (Step 7) 288 289 - =b b= (=magit-checkout=) — switch to =main= 290 - =m m= (=magit-merge=) — select =patch/730cd2e= or your working branch 291 - =P p= (=magit-push-current-to-pushremote=) — push =main= to =rad= 292 293 If your push remote is not set to =rad=, use =P e= and specify =rad= as the 294 remote. 295 296 ** Clean Up (Step 9) 297 298 - =b k= (=magit-branch-delete=) — select branches to delete 299 300 ** Radicle Commands from Emacs 301 302 For =rad= commands that have no Magit equivalent, use: 303 304 - =M-!= (=shell-command=) for one-off commands like =rad issue state --closed 3085700= 305 - =M-x eshell= for an interactive session when you need to run several =rad= 306 commands in sequence 307 308 ** Quick Reference 309 310 | Step | Magit | Shell fallback | 311 |----------------+--------------------------------+----------------------------------| 312 | Create branch | =b c= | | 313 | Stage + commit | =s= / =S= then =c c= | | 314 | Push patch | =P e= (rad, HEAD:refs/patches) | =git push rad HEAD:refs/patches= | 315 | Review diff | =d d= | =rad patch diff <id>= | 316 | Checkout patch | | =rad patch checkout <id>= | 317 | Merge | =m m= | | 318 | Push main | =P p= or =P e= | | 319 | Close issue | | =rad issue state --closed <id>= | 320 | Delete branch | =b k= | | 321 322 * Summary 323 324 | Step | Command | What it does | 325 |------+-----------------------------------+---------------------------------| 326 | 1 | =rad issue open= | Create a trackable unit of work | 327 | 2 | =git checkout -b <branch>= | Start a working branch | 328 | 3 | =git commit= | Record your changes | 329 | 4 | =git push rad HEAD:refs/patches= | Open a patch for review | 330 | 5 | =rad patch show / diff= | Review the code | 331 | 6 | =rad patch checkout= | Test locally | 332 | 7 | =git merge= + =git push rad main= | Merge and announce | 333 | 8 | =rad issue state <id> --closed= | Close the issue | 334 | 9 | =git branch -d= | Clean up local branches | 335 336 * What to Read Next 337 338 - [[file:../explanation/explanation_realtime_data_patterns.org][Real-Time Data Patterns]] — context for the data transport work tracked by 339 these issues 340 - [[file:../howto/howto_connect_opensky.org][Connect to OpenSky Network]] — the next piece of work after project setup