the ugly ocaml monstrosity powering my site oppi.li
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

write about jujutsu

+1084 -17
+17 -17
docs/index.html
··· 35 35 <tr> 36 36 <td class=table-post> 37 37 <div class="date"> 38 + 23/05 — 2025 39 + </div> 40 + <a href="/posts/configuring_jujutsu" class="post-link"> 41 + <span class="post-link">Configuring Jujutsu</span> 42 + </a> 43 + </td> 44 + <td class=table-stats> 45 + <span class="stats-number"> 46 + 9.5 47 + </span> 48 + <span class=stats-unit>min</span> 49 + </td> 50 + </tr> 51 + 52 + <tr> 53 + <td class=table-post> 54 + <div class="date"> 38 55 21/05 — 2025 39 56 </div> 40 57 <a href="/posts/tales_from_mainframe_modernization" class="post-link"> ··· 61 78 <td class=table-stats> 62 79 <span class="stats-number"> 63 80 1.9 64 - </span> 65 - <span class=stats-unit>min</span> 66 - </td> 67 - </tr> 68 - 69 - <tr> 70 - <td class=table-post> 71 - <div class="date"> 72 - 01/08 — 2024 73 - </div> 74 - <a href="/posts/introducing_tablespoon" class="post-link"> 75 - <span class="post-link">Introducing Tablespoon</span> 76 - </a> 77 - </td> 78 - <td class=table-stats> 79 - <span class="stats-number"> 80 - 4.5 81 81 </span> 82 82 <span class=stats-unit>min</span> 83 83 </td>
+297
docs/index.xml
··· 12 12 <language>en-us</language> 13 13 <copyright>Creative Commons BY-NC-SA 4.0</copyright> 14 14 <item> 15 + <title>Configuring Jujutsu</title> 16 + <description>&lt;p&gt;There are a lot of reasons to use jujutsu, but this post is not about 17 + that. I have this terrible habit of turning every knob on a tool before 18 + I even fully absorb how it works; and boy does &lt;code&gt;jj&lt;/code&gt; have a 19 + lot of knobs.&lt;/p&gt; 20 + &lt;h3 id="templates"&gt;Templates&lt;/h3&gt; 21 + &lt;p&gt;&lt;code&gt;jj&lt;/code&gt; let you tweak nearly every single character of its 22 + output. In fact, &lt;code&gt;jj&lt;/code&gt; includes a full-blown templating 23 + language to do so. Lets start from the basics:&lt;/p&gt; 24 + &lt;p&gt;Format all timestamps in relative fashion, like “5 hours ago”:&lt;/p&gt; 25 + &lt;div class="sourceCode" id="cb1"&gt;&lt;pre 26 + class="sourceCode toml"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[template-aliases]&lt;/span&gt;&lt;/span&gt; 27 + &lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;&amp;quot;format_timestamp(timestamp)&amp;quot;&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;timestamp.ago()&amp;quot;&lt;/span&gt;&lt;span class="er"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 28 + &lt;p&gt;The default nodes in the log graph are a bit large for my taste, we 29 + can modify the &lt;code&gt;log_node&lt;/code&gt; template to fix that:&lt;/p&gt; 30 + &lt;div class="sourceCode" id="cb2"&gt;&lt;pre 31 + class="sourceCode toml"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[templates]&lt;/span&gt;&lt;/span&gt; 32 + &lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;log_node&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;/span&gt; 33 + &lt;span id="cb2-3"&gt;&lt;a href="#cb2-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; label(&amp;quot;node&amp;quot;,&lt;/span&gt;&lt;/span&gt; 34 + &lt;span id="cb2-4"&gt;&lt;a href="#cb2-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; coalesce(&lt;/span&gt;&lt;/span&gt; 35 + &lt;span id="cb2-5"&gt;&lt;a href="#cb2-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; if(!self, label(&amp;quot;elided&amp;quot;, &amp;quot;~&amp;quot;)),&lt;/span&gt;&lt;/span&gt; 36 + &lt;span id="cb2-6"&gt;&lt;a href="#cb2-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; if(current_working_copy, label(&amp;quot;working_copy&amp;quot;, &amp;quot;@&amp;quot;)),&lt;/span&gt;&lt;/span&gt; 37 + &lt;span id="cb2-7"&gt;&lt;a href="#cb2-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; if(conflict, label(&amp;quot;conflict&amp;quot;, &amp;quot;×&amp;quot;)),&lt;/span&gt;&lt;/span&gt; 38 + &lt;span id="cb2-8"&gt;&lt;a href="#cb2-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; if(immutable, label(&amp;quot;immutable&amp;quot;, &amp;quot;*&amp;quot;)),&lt;/span&gt;&lt;/span&gt; 39 + &lt;span id="cb2-9"&gt;&lt;a href="#cb2-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; label(&amp;quot;normal&amp;quot;, &amp;quot;·&amp;quot;)&lt;/span&gt;&lt;/span&gt; 40 + &lt;span id="cb2-10"&gt;&lt;a href="#cb2-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; )&lt;/span&gt;&lt;/span&gt; 41 + &lt;span id="cb2-11"&gt;&lt;a href="#cb2-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; )&lt;/span&gt;&lt;/span&gt; 42 + &lt;span id="cb2-12"&gt;&lt;a href="#cb2-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="st"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 43 + &lt;p&gt;This uses smaller iconography in &lt;code&gt;jj log&lt;/code&gt; (these icons are 44 + also more widely available in fonts, in my experience):&lt;/p&gt; 45 + &lt;pre&gt;&lt;code&gt;@ wuuownsw me@oppi.li 21 minutes ago 30d1bd12 46 + │ (no description set) 47 + · plmznxvy me@oppi.li 5 hours ago push-plmznxvyqrqw git_head() 051c142e 48 + │ appview: pulls: bump sourceRev for stacks without causing resubmits 49 + │ * towvwqxk x@icyphox.sh 4 hours ago master a588f625 50 + │ │ appview: pages/markup: don&amp;#39;t double camo in post process 51 + │ * ryzqnyvs me@oppi.li 4 hours ago ea4b520a 52 + │ │ appview: fix stack merging 53 + │ * kqvutzxr me@oppi.li 4 hours ago ff73ca23 54 + ├─╯ appview: rework RepoLanguages 55 + * vwpqwmms jeynesbrook@gmail.com 8 hours ago d759587b 56 + │ appview: repo: inject language percentage into repo index 57 + ~&lt;/code&gt;&lt;/pre&gt; 58 + &lt;p&gt;Much nicer! And speaking of &lt;code&gt;log&lt;/code&gt;, I find it hard to parse 59 + the output when every change occupies two lines instead of just one 60 + line, we can remedy that quickly; in fact, &lt;code&gt;jj&lt;/code&gt; has a builtin 61 + template for single-line log outputs:&lt;/p&gt; 62 + &lt;pre&gt;&lt;code&gt;λ jj log -T builtin_log_oneline 63 + @ wuuownsw me@oppi.li 22 minutes ago 30d1bd12 (no description set) 64 + · plmznxvy me@oppi.li 5 hours ago push-plmznxvyqrqw git_head() 051c142e appview: pulls: bump sourceRev for stacks without causing resubmits 65 + │ * towvwqxk x@icyphox.sh 4 hours ago master a588f625 appview: pages/markup: don&amp;#39;t double camo in post process 66 + │ * ryzqnyvs me@oppi.li 4 hours ago ea4b520a appview: fix stack merging 67 + │ * kqvutzxr me@oppi.li 4 hours ago ff73ca23 appview: rework RepoLanguages 68 + ├─╯ 69 + * vwpqwmms jeynesbrook 8 hours ago d759587b appview: repo: inject language percentage into repo index 70 + 71 + ~&lt;/code&gt;&lt;/pre&gt; 72 + &lt;p&gt;Bit too long! I personally do not always need author and time 73 + information when quickly scrolling through the log, so I created my own 74 + template based on &lt;code&gt;builtin_log_oneline&lt;/code&gt;:&lt;/p&gt; 75 + &lt;div class="sourceCode" id="cb5"&gt;&lt;pre 76 + class="sourceCode toml"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb5-1"&gt;&lt;a href="#cb5-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[templates]&lt;/span&gt;&lt;/span&gt; 77 + &lt;span id="cb5-2"&gt;&lt;a href="#cb5-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;log&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;/span&gt; 78 + &lt;span id="cb5-3"&gt;&lt;a href="#cb5-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; if(root,&lt;/span&gt;&lt;/span&gt; 79 + &lt;span id="cb5-4"&gt;&lt;a href="#cb5-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; format_root_commit(self),&lt;/span&gt;&lt;/span&gt; 80 + &lt;span id="cb5-5"&gt;&lt;a href="#cb5-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; label(if(current_working_copy, &amp;quot;working_copy&amp;quot;),&lt;/span&gt;&lt;/span&gt; 81 + &lt;span id="cb5-6"&gt;&lt;a href="#cb5-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; concat(&lt;/span&gt;&lt;/span&gt; 82 + &lt;span id="cb5-7"&gt;&lt;a href="#cb5-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; separate(&amp;quot; &amp;quot;,&lt;/span&gt;&lt;/span&gt; 83 + &lt;span id="cb5-8"&gt;&lt;a href="#cb5-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; format_short_change_id_with_hidden_and_divergent_info(self),&lt;/span&gt;&lt;/span&gt; 84 + &lt;span id="cb5-9"&gt;&lt;a href="#cb5-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; if(empty, label(&amp;quot;empty&amp;quot;, &amp;quot;(empty)&amp;quot;)),&lt;/span&gt;&lt;/span&gt; 85 + &lt;span id="cb5-10"&gt;&lt;a href="#cb5-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; if(description,&lt;/span&gt;&lt;/span&gt; 86 + &lt;span id="cb5-11"&gt;&lt;a href="#cb5-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; description.first_line(),&lt;/span&gt;&lt;/span&gt; 87 + &lt;span id="cb5-12"&gt;&lt;a href="#cb5-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; label(if(empty, &amp;quot;empty&amp;quot;), description_placeholder),&lt;/span&gt;&lt;/span&gt; 88 + &lt;span id="cb5-13"&gt;&lt;a href="#cb5-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; ),&lt;/span&gt;&lt;/span&gt; 89 + &lt;span id="cb5-14"&gt;&lt;a href="#cb5-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; bookmarks,&lt;/span&gt;&lt;/span&gt; 90 + &lt;span id="cb5-15"&gt;&lt;a href="#cb5-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; tags,&lt;/span&gt;&lt;/span&gt; 91 + &lt;span id="cb5-16"&gt;&lt;a href="#cb5-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; working_copies,&lt;/span&gt;&lt;/span&gt; 92 + &lt;span id="cb5-17"&gt;&lt;a href="#cb5-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; if(git_head, label(&amp;quot;git_head&amp;quot;, &amp;quot;HEAD&amp;quot;)),&lt;/span&gt;&lt;/span&gt; 93 + &lt;span id="cb5-18"&gt;&lt;a href="#cb5-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; if(conflict, label(&amp;quot;conflict&amp;quot;, &amp;quot;conflict&amp;quot;)),&lt;/span&gt;&lt;/span&gt; 94 + &lt;span id="cb5-19"&gt;&lt;a href="#cb5-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; if(config(&amp;quot;ui.show-cryptographic-signatures&amp;quot;).as_boolean(),&lt;/span&gt;&lt;/span&gt; 95 + &lt;span id="cb5-20"&gt;&lt;a href="#cb5-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; format_short_cryptographic_signature(signature)),&lt;/span&gt;&lt;/span&gt; 96 + &lt;span id="cb5-21"&gt;&lt;a href="#cb5-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; ) ++ &amp;quot;\n&amp;quot;,&lt;/span&gt;&lt;/span&gt; 97 + &lt;span id="cb5-22"&gt;&lt;a href="#cb5-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; ),&lt;/span&gt;&lt;/span&gt; 98 + &lt;span id="cb5-23"&gt;&lt;a href="#cb5-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; )&lt;/span&gt;&lt;/span&gt; 99 + &lt;span id="cb5-24"&gt;&lt;a href="#cb5-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; )&lt;/span&gt;&lt;/span&gt; 100 + &lt;span id="cb5-25"&gt;&lt;a href="#cb5-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="st"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 101 + &lt;p&gt;Which produces:&lt;/p&gt; 102 + &lt;pre&gt;&lt;code&gt;@ wuuownsw (no description set) 103 + · plmznxvy appview: pulls: bump sourceRev for stacks without causing resubmits push-plmznxvyqrqw HEAD 104 + │ * towvwqxk appview: pages/markup: don&amp;#39;t double camo in post process master 105 + │ * ryzqnyvs appview: fix stack merging 106 + │ * kqvutzxr appview: rework RepoLanguages 107 + ├─╯ 108 + * vwpqwmms appview: repo: inject language percentage into repo index 109 + 110 + ~&lt;/code&gt;&lt;/pre&gt; 111 + &lt;p&gt;Sweet! To get a more detailed log, you can always use a different 112 + template for the output: 113 + &lt;code&gt;jj log -T builtin_log_detailed&lt;/code&gt;.&lt;/p&gt; 114 + &lt;p&gt;With git, I set &lt;code&gt;commit.verbose&lt;/code&gt; to true, this lets me 115 + view the diff when composing a commit messaege in my 116 + &lt;code&gt;$EDITOR&lt;/code&gt;:&lt;/p&gt; 117 + &lt;div class="sourceCode" id="cb7"&gt;&lt;pre 118 + class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb7-1"&gt;&lt;a href="#cb7-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git commit&lt;/span&gt; 119 + &lt;span id="cb7-2"&gt;&lt;a href="#cb7-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# no message passed, opens $EDITOR to compose message&lt;/span&gt;&lt;/span&gt; 120 + &lt;span id="cb7-3"&gt;&lt;a href="#cb7-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 121 + &lt;span id="cb7-4"&gt;&lt;a href="#cb7-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# -- inside vim --&lt;/span&gt;&lt;/span&gt; 122 + &lt;span id="cb7-5"&gt;&lt;a href="#cb7-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 123 + &lt;span id="cb7-6"&gt;&lt;a href="#cb7-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# Please enter the commit message for your changes. Lines starting&lt;/span&gt;&lt;/span&gt; 124 + &lt;span id="cb7-7"&gt;&lt;a href="#cb7-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# with &amp;#39;#&amp;#39; will be ignored, and an empty message aborts the commit.&lt;/span&gt;&lt;/span&gt; 125 + &lt;span id="cb7-8"&gt;&lt;a href="#cb7-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;#&lt;/span&gt;&lt;/span&gt; 126 + &lt;span id="cb7-9"&gt;&lt;a href="#cb7-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# On branch trunk&lt;/span&gt;&lt;/span&gt; 127 + &lt;span id="cb7-10"&gt;&lt;a href="#cb7-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# Changes to be committed:&lt;/span&gt;&lt;/span&gt; 128 + &lt;span id="cb7-11"&gt;&lt;a href="#cb7-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# new file: scripts/handle.js&lt;/span&gt;&lt;/span&gt; 129 + &lt;span id="cb7-12"&gt;&lt;a href="#cb7-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;#&lt;/span&gt;&lt;/span&gt; 130 + &lt;span id="cb7-13"&gt;&lt;a href="#cb7-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# ------------------------ &amp;gt;8 ------------------------&lt;/span&gt;&lt;/span&gt; 131 + &lt;span id="cb7-14"&gt;&lt;a href="#cb7-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# Do not modify or remove the line above.&lt;/span&gt;&lt;/span&gt; 132 + &lt;span id="cb7-15"&gt;&lt;a href="#cb7-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# Everything below it will be ignored.&lt;/span&gt;&lt;/span&gt; 133 + &lt;span id="cb7-16"&gt;&lt;a href="#cb7-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;diff&lt;/span&gt; &lt;span class="at"&gt;--git&lt;/span&gt; a/scripts/handle.js b/scripts/handle.js&lt;/span&gt; 134 + &lt;span id="cb7-17"&gt;&lt;a href="#cb7-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;new&lt;/span&gt; file mode 100644&lt;/span&gt; 135 + &lt;span id="cb7-18"&gt;&lt;a href="#cb7-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;index&lt;/span&gt; 0000000..d8a07f3&lt;/span&gt; 136 + &lt;span id="cb7-19"&gt;&lt;a href="#cb7-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;---&lt;/span&gt; /dev/null&lt;/span&gt; 137 + &lt;span id="cb7-20"&gt;&lt;a href="#cb7-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+++&lt;/span&gt; b/scripts/handle.js&lt;/span&gt; 138 + &lt;span id="cb7-21"&gt;&lt;a href="#cb7-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;@@&lt;/span&gt; &lt;span class="at"&gt;-0,0&lt;/span&gt; +1,104 @@&lt;/span&gt; 139 + &lt;span id="cb7-22"&gt;&lt;a href="#cb7-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+//&lt;/span&gt; Run using node handle.js&lt;/span&gt; 140 + &lt;span id="cb7-23"&gt;&lt;a href="#cb7-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+//&lt;/span&gt; Install axios using npm install axios&lt;/span&gt; 141 + &lt;span id="cb7-24"&gt;&lt;a href="#cb7-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+//&lt;/span&gt; nodejs v18+&lt;/span&gt; 142 + &lt;span id="cb7-25"&gt;&lt;a href="#cb7-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+const&lt;/span&gt; axios = require&lt;span class="er"&gt;(&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;axios&amp;quot;&lt;/span&gt;&lt;span class="kw"&gt;);&lt;/span&gt;&lt;/span&gt; 143 + &lt;span id="cb7-26"&gt;&lt;a href="#cb7-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;.&lt;/span&gt;&lt;/span&gt; 144 + &lt;span id="cb7-27"&gt;&lt;a href="#cb7-27" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;.&lt;/span&gt;&lt;/span&gt; 145 + &lt;span id="cb7-28"&gt;&lt;a href="#cb7-28" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 146 + &lt;p&gt;The equivalent in &lt;code&gt;jj&lt;/code&gt; requires modifying the 147 + &lt;code&gt;draft_commit_description&lt;/code&gt; template:&lt;/p&gt; 148 + &lt;div class="sourceCode" id="cb8"&gt;&lt;pre 149 + class="sourceCode toml"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb8-1"&gt;&lt;a href="#cb8-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[templates]&lt;/span&gt;&lt;/span&gt; 150 + &lt;span id="cb8-2"&gt;&lt;a href="#cb8-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;draft_commit_description&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;/span&gt; 151 + &lt;span id="cb8-3"&gt;&lt;a href="#cb8-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; concat(&lt;/span&gt;&lt;/span&gt; 152 + &lt;span id="cb8-4"&gt;&lt;a href="#cb8-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; coalesce(description, default_commit_description, &amp;quot;\n&amp;quot;),&lt;/span&gt;&lt;/span&gt; 153 + &lt;span id="cb8-5"&gt;&lt;a href="#cb8-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; surround(&lt;/span&gt;&lt;/span&gt; 154 + &lt;span id="cb8-6"&gt;&lt;a href="#cb8-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; &amp;quot;\nJJ: This commit contains the following changes:\n&amp;quot;, &amp;quot;&amp;quot;,&lt;/span&gt;&lt;/span&gt; 155 + &lt;span id="cb8-7"&gt;&lt;a href="#cb8-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; indent(&amp;quot;JJ: &amp;quot;, diff.stat(72)),&lt;/span&gt;&lt;/span&gt; 156 + &lt;span id="cb8-8"&gt;&lt;a href="#cb8-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; ),&lt;/span&gt;&lt;/span&gt; 157 + &lt;span id="cb8-9"&gt;&lt;a href="#cb8-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; &amp;quot;\nJJ: ignore-rest\n&amp;quot;,&lt;/span&gt;&lt;/span&gt; 158 + &lt;span id="cb8-10"&gt;&lt;a href="#cb8-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; diff.git(),&lt;/span&gt;&lt;/span&gt; 159 + &lt;span id="cb8-11"&gt;&lt;a href="#cb8-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="vs"&gt; )&lt;/span&gt;&lt;/span&gt; 160 + &lt;span id="cb8-12"&gt;&lt;a href="#cb8-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="st"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 161 + &lt;h3 id="revsets"&gt;Revsets&lt;/h3&gt; 162 + &lt;p&gt;As a not-so-power-user, I presently only use revsets to improve my 163 + &lt;code&gt;jj log&lt;/code&gt; experience. In the context of &lt;code&gt;jj log&lt;/code&gt;, 164 + giving it a revset means “here is a bag of revisions, graph them for me 165 + neatly”.&lt;/p&gt; 166 + &lt;p&gt;I find myself using the “rebase” flow quite often:&lt;/p&gt; 167 + &lt;ul&gt; 168 + &lt;li&gt;I hack on a stack of changes&lt;/li&gt; 169 + &lt;li&gt;&lt;code&gt;trunk&lt;/code&gt; is updated by my fellow collaborators&lt;/li&gt; 170 + &lt;li&gt;I rebase my stack using 171 + &lt;code&gt;jj rebase -s &amp;lt;mine&amp;gt; -d &amp;lt;trunk&amp;gt;&lt;/code&gt;&lt;/li&gt; 172 + &lt;/ul&gt; 173 + &lt;p&gt;And I scrub through the log output to roughly figure out how much 174 + work it would be to rebase, what I need for this is:&lt;/p&gt; 175 + &lt;ul&gt; 176 + &lt;li&gt;the changes added to &lt;code&gt;trunk&lt;/code&gt; since I last diverged from 177 + it&lt;/li&gt; 178 + &lt;li&gt;the changes add to my stack since I last diverged from 179 + &lt;code&gt;trunk&lt;/code&gt;&lt;/li&gt; 180 + &lt;/ul&gt; 181 + &lt;pre&gt;&lt;code&gt; X--Y--Z my stack 182 + / 183 + F--A--B--C--D trunk&lt;/code&gt;&lt;/pre&gt; 184 + &lt;p&gt;If you want &lt;code&gt;jj log&lt;/code&gt; to print this (and by “this”, I mean, 185 + the graph above, exactly as presented), you have to supply it a 186 + &lt;code&gt;revset&lt;/code&gt; argument that grabs X, Y, Z, A, B, C, D and F. Some 187 + examples of revsets are:&lt;/p&gt; 188 + &lt;pre&gt;&lt;code&gt;@ # the rev marking the working copy 189 + x # the rev identified by shorthand `x` 190 + x | y # the set of two revs, [x, y] 191 + @ | trunk() # the set of two revs, [@, trunk()] 192 + .. # everything in this repository 193 + all() # also everything in this repository 194 + fork_point(@ | trunk()) # the rev where @ and trunk() forked off&lt;/code&gt;&lt;/pre&gt; 195 + &lt;p&gt;And the revset that captures “ahead-behind” style output is:&lt;/p&gt; 196 + &lt;pre&gt;&lt;code&gt;trunk()..@ | @..trunk() | trunk() | @:: | fork_point(trunk() | @)&lt;/code&gt;&lt;/pre&gt; 197 + &lt;p&gt;Which includes:&lt;/p&gt; 198 + &lt;ul&gt; 199 + &lt;li&gt;&lt;code&gt;trunk()..@&lt;/code&gt;: the new changes that my collaborators have 200 + added&lt;/li&gt; 201 + &lt;li&gt;&lt;code&gt;@..trunk()&lt;/code&gt;: the new changes that I have added&lt;/li&gt; 202 + &lt;li&gt;&lt;code&gt;trunk()&lt;/code&gt;: the trunk rev itself&lt;/li&gt; 203 + &lt;li&gt;&lt;code&gt;@::&lt;/code&gt;: all descendants of &lt;code&gt;@&lt;/code&gt;&lt;/li&gt; 204 + &lt;li&gt;&lt;code&gt;fork_point(trunk() | @)&lt;/code&gt;: the rev from which the two 205 + streams of work diverged&lt;/li&gt; 206 + &lt;/ul&gt; 207 + &lt;p&gt;Rougly, when working on a stack, this is what I can see by supplying 208 + the above revset expression (simplified output):&lt;/p&gt; 209 + &lt;div class="sourceCode" id="cb12"&gt;&lt;pre 210 + class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb12-1"&gt;&lt;a href="#cb12-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; jj log &lt;span class="at"&gt;-r&lt;/span&gt; &lt;span class="st"&gt;&amp;#39;trunk()..@ | @..trunk() | trunk() | @:: | fork_point(trunk() | @)&amp;#39;&lt;/span&gt;&lt;/span&gt; 211 + &lt;span id="cb12-2"&gt;&lt;a href="#cb12-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;@&lt;/span&gt; xdihgmke &lt;span class="op"&gt;&amp;lt;&lt;/span&gt;-- me&lt;/span&gt; 212 + &lt;span id="cb12-3"&gt;&lt;a href="#cb12-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; vurstull&lt;/span&gt; 213 + &lt;span id="cb12-4"&gt;&lt;a href="#cb12-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; plmznxvy&lt;/span&gt; 214 + &lt;span id="cb12-5"&gt;&lt;a href="#cb12-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; ihefghyy&lt;/span&gt; 215 + &lt;span id="cb12-6"&gt;&lt;a href="#cb12-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt; &lt;span class="pp"&gt;*&lt;/span&gt; towvwqxk trunk &lt;span class="op"&gt;&amp;lt;&lt;/span&gt;-- trunk&lt;/span&gt; 216 + &lt;span id="cb12-7"&gt;&lt;a href="#cb12-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt; &lt;span class="pp"&gt;*&lt;/span&gt; ryzqnyvs&lt;/span&gt; 217 + &lt;span id="cb12-8"&gt;&lt;a href="#cb12-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt; &lt;span class="pp"&gt;*&lt;/span&gt; kqvutzxr&lt;/span&gt; 218 + &lt;span id="cb12-9"&gt;&lt;a href="#cb12-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├─╯&lt;/span&gt;&lt;/span&gt; 219 + &lt;span id="cb12-10"&gt;&lt;a href="#cb12-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;*&lt;/span&gt; vwpqwmms &lt;span class="op"&gt;&amp;lt;&lt;/span&gt;-- fork-point&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 220 + &lt;p&gt;And sometimes, when I am editing older parts of my stack 221 + (&lt;code&gt;@::&lt;/code&gt; helps us grab &lt;code&gt;xdihgmke&lt;/code&gt; and 222 + &lt;code&gt;vurstull&lt;/code&gt;):&lt;/p&gt; 223 + &lt;div class="sourceCode" id="cb13"&gt;&lt;pre 224 + class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb13-1"&gt;&lt;a href="#cb13-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; jj log &lt;span class="at"&gt;-r&lt;/span&gt; &lt;span class="st"&gt;&amp;#39;trunk()..@ | @..trunk() | trunk() | @:: | fork_point(trunk() | @)&amp;#39;&lt;/span&gt;&lt;/span&gt; 225 + &lt;span id="cb13-2"&gt;&lt;a href="#cb13-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; xdihgmke&lt;/span&gt; 226 + &lt;span id="cb13-3"&gt;&lt;a href="#cb13-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; vurstull&lt;/span&gt; 227 + &lt;span id="cb13-4"&gt;&lt;a href="#cb13-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;@&lt;/span&gt; plmznxvy &lt;span class="op"&gt;&amp;lt;&lt;/span&gt;-- me&lt;/span&gt; 228 + &lt;span id="cb13-5"&gt;&lt;a href="#cb13-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; ihefghyy&lt;/span&gt; 229 + &lt;span id="cb13-6"&gt;&lt;a href="#cb13-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt; &lt;span class="pp"&gt;*&lt;/span&gt; towvwqxk trunk &lt;span class="op"&gt;&amp;lt;&lt;/span&gt;-- trunk&lt;/span&gt; 230 + &lt;span id="cb13-7"&gt;&lt;a href="#cb13-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt; &lt;span class="pp"&gt;*&lt;/span&gt; ryzqnyvs&lt;/span&gt; 231 + &lt;span id="cb13-8"&gt;&lt;a href="#cb13-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt; &lt;span class="pp"&gt;*&lt;/span&gt; kqvutzxr&lt;/span&gt; 232 + &lt;span id="cb13-9"&gt;&lt;a href="#cb13-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├─╯&lt;/span&gt;&lt;/span&gt; 233 + &lt;span id="cb13-10"&gt;&lt;a href="#cb13-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;*&lt;/span&gt; vwpqwmms &lt;span class="op"&gt;&amp;lt;&lt;/span&gt;-- fork-point&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 234 + &lt;p&gt;To rebase, I would run:&lt;/p&gt; 235 + &lt;pre&gt;&lt;code&gt;jj rebase -s ihefghyy -d towvwqxk&lt;/code&gt;&lt;/pre&gt; 236 + &lt;h3 id="aliases"&gt;Aliases&lt;/h3&gt; 237 + &lt;p&gt;I imagine that most git power users are already familiar with 238 + aliases. The only alias I see myself using often is:&lt;/p&gt; 239 + &lt;div class="sourceCode" id="cb15"&gt;&lt;pre 240 + class="sourceCode toml"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb15-1"&gt;&lt;a href="#cb15-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[aliases]&lt;/span&gt;&lt;/span&gt; 241 + &lt;span id="cb15-2"&gt;&lt;a href="#cb15-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;tug&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;[&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;bookmark&amp;quot;&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;move&amp;quot;&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;--from&amp;quot;&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;heads(::@- &amp;amp; bookmarks())&amp;quot;&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;--to&amp;quot;&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;@-&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;]&lt;/span&gt;&lt;span class="er"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 242 + &lt;p&gt;In action:&lt;/p&gt; 243 + &lt;div class="sourceCode" id="cb16"&gt;&lt;pre 244 + class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb16-1"&gt;&lt;a href="#cb16-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# ugh my bookmark is way behind&lt;/span&gt;&lt;/span&gt; 245 + &lt;span id="cb16-2"&gt;&lt;a href="#cb16-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; jj log&lt;/span&gt; 246 + &lt;span id="cb16-3"&gt;&lt;a href="#cb16-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;@&lt;/span&gt; xdihgmke&lt;/span&gt; 247 + &lt;span id="cb16-4"&gt;&lt;a href="#cb16-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; vurstull&lt;/span&gt; 248 + &lt;span id="cb16-5"&gt;&lt;a href="#cb16-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; cyzmakil&lt;/span&gt; 249 + &lt;span id="cb16-6"&gt;&lt;a href="#cb16-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; plmznxvy&lt;/span&gt; 250 + &lt;span id="cb16-7"&gt;&lt;a href="#cb16-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; ihefghyy some-bookmark&lt;/span&gt; 251 + &lt;span id="cb16-8"&gt;&lt;a href="#cb16-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt;&lt;/span&gt; 252 + &lt;span id="cb16-9"&gt;&lt;a href="#cb16-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~&lt;/span&gt;&lt;/span&gt; 253 + &lt;span id="cb16-10"&gt;&lt;a href="#cb16-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 254 + &lt;span id="cb16-11"&gt;&lt;a href="#cb16-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; jj tug&lt;/span&gt; 255 + &lt;span id="cb16-12"&gt;&lt;a href="#cb16-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 256 + &lt;span id="cb16-13"&gt;&lt;a href="#cb16-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# ready to push!&lt;/span&gt;&lt;/span&gt; 257 + &lt;span id="cb16-14"&gt;&lt;a href="#cb16-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; jj log&lt;/span&gt; 258 + &lt;span id="cb16-15"&gt;&lt;a href="#cb16-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;@&lt;/span&gt; xdihgmke&lt;/span&gt; 259 + &lt;span id="cb16-16"&gt;&lt;a href="#cb16-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; vurstull some-bookmark&lt;span class="pp"&gt;*&lt;/span&gt;&lt;/span&gt; 260 + &lt;span id="cb16-17"&gt;&lt;a href="#cb16-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; cyzmakil&lt;/span&gt; 261 + &lt;span id="cb16-18"&gt;&lt;a href="#cb16-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; plmznxvy&lt;/span&gt; 262 + &lt;span id="cb16-19"&gt;&lt;a href="#cb16-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;·&lt;/span&gt; ihefghyy&lt;/span&gt; 263 + &lt;span id="cb16-20"&gt;&lt;a href="#cb16-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt;&lt;/span&gt; 264 + &lt;span id="cb16-21"&gt;&lt;a href="#cb16-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 265 + &lt;p&gt;This alias was stolen from this &lt;a 266 + href="https://gist.github.com/thoughtpolice/8f2fd36ae17cd11b8e7bd93a70e31ad6#file-jjconfig-toml"&gt;wonderful 267 + gist&lt;/a&gt; by &lt;a 268 + href="https://github.com/jj-vcs/jj/blob/main/docs/testimonials.md?plain=1#L120"&gt;Austin 269 + Seipp&lt;/a&gt;.&lt;/p&gt; 270 + &lt;h3 id="experimental-features"&gt;Experimental features&lt;/h3&gt; 271 + &lt;p&gt;I use one experimental feature, that is only available on some of the 272 + newer versions of jujutsu:&lt;/p&gt; 273 + &lt;div class="sourceCode" id="cb17"&gt;&lt;pre 274 + class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb17-1"&gt;&lt;a href="#cb17-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# built from master&lt;/span&gt;&lt;/span&gt; 275 + &lt;span id="cb17-2"&gt;&lt;a href="#cb17-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; jj version&lt;/span&gt; 276 + &lt;span id="cb17-3"&gt;&lt;a href="#cb17-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;jj&lt;/span&gt; 0.29.0-8c7ca30074767257d75e3842581b61e764d022cf&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 277 + &lt;div class="sourceCode" id="cb18"&gt;&lt;pre 278 + class="sourceCode toml"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb18-1"&gt;&lt;a href="#cb18-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[git]&lt;/span&gt;&lt;/span&gt; 279 + &lt;span id="cb18-2"&gt;&lt;a href="#cb18-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;write-change-id-header&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="cn"&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 280 + &lt;p&gt;This writes an extra commit-header (not to be confused with 281 + commit-trailer, which goes directly in the commit body) with the jujutsu 282 + change-id, as seen by inspecting the commit object:&lt;/p&gt; 283 + &lt;div class="sourceCode" id="cb19"&gt;&lt;pre 284 + class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb19-1"&gt;&lt;a href="#cb19-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git cat-file commit 4edebe96159bf81c3be662d0a2b4c7b08a062968&lt;/span&gt; 285 + &lt;span id="cb19-2"&gt;&lt;a href="#cb19-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;tree&lt;/span&gt; a9b7e9e3eed8a22c35829bf586d7560ec8396124&lt;/span&gt; 286 + &lt;span id="cb19-3"&gt;&lt;a href="#cb19-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;parent&lt;/span&gt; edc0d2750d4848bc05cfd3255ee1ca916bea9156&lt;/span&gt; 287 + &lt;span id="cb19-4"&gt;&lt;a href="#cb19-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;author&lt;/span&gt; oppiliappan &lt;span class="op"&gt;&amp;lt;&lt;/span&gt;me@oppi.li&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; 1747919102 +0100&lt;/span&gt; 288 + &lt;span id="cb19-5"&gt;&lt;a href="#cb19-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;committer&lt;/span&gt; oppiliappan &lt;span class="op"&gt;&amp;lt;&lt;/span&gt;me@oppi.li&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; 1747919102 +0100&lt;/span&gt; 289 + &lt;span id="cb19-6"&gt;&lt;a href="#cb19-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;change-id&lt;/span&gt; skrrxvvxlpzrqzpxlxksvryrykpxkvon &lt;span class="op"&gt;&amp;lt;&lt;/span&gt;-- this&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 290 + &lt;p&gt;This allows code forges to extract change-ids from commits, and most 291 + crucially: &lt;strong&gt;allows tracking changes across force pushes&lt;/strong&gt;. 292 + This enables the &lt;a 293 + href="https://gist.github.com/thoughtpolice/9c45287550a56b2047c6311fbadebed2"&gt;interdiff 294 + format&lt;/a&gt; of code-review. Stacking, commit-wise-review, and interdiff 295 + are all supported on &lt;a href="https://tangled.sh"&gt;tangled&lt;/a&gt; (you can 296 + read more &lt;a 297 + href="https://bsky.app/profile/tangled.sh/post/3lptwcb47kc2u"&gt;here&lt;/a&gt;), 298 + if you submit a branch with this experimental feature enabled.&lt;/p&gt; 299 + &lt;h3 id="misc"&gt;Misc&lt;/h3&gt; 300 + &lt;p&gt;Couple of other quality of life additions:&lt;/p&gt; 301 + &lt;div class="sourceCode" id="cb20"&gt;&lt;pre 302 + class="sourceCode toml"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb20-1"&gt;&lt;a href="#cb20-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[ui]&lt;/span&gt;&lt;/span&gt; 303 + &lt;span id="cb20-2"&gt;&lt;a href="#cb20-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;default-command&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;/span&gt; 304 + &lt;span id="cb20-3"&gt;&lt;a href="#cb20-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;pager&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;delta&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 305 + &lt;p&gt;I also use nix and home-manager to configure jj, you can find my 306 + configuration &lt;a href="https://plonk.li/r/YQ"&gt;here&lt;/a&gt;.&lt;/p&gt;</description> 307 + <link>https://oppi.li/posts/configuring_jujutsu/</link> 308 + <pubDate>Fri, 23 May 2025 16:18:00 +0000</pubDate> 309 + <guid>https://oppi.li/posts/configuring_jujutsu/</guid> 310 + </item> 311 + <item> 15 312 <title>Tales From Mainframe Modernization</title> 16 313 <description>&lt;p&gt;At my last workplace, I wrote transpilers (or just &lt;a 17 314 href="https://people.csail.mit.edu/rachit/post/transpiler/"&gt;compilers&lt;/a&gt;
+359
docs/posts/configuring_jujutsu/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <link rel="stylesheet" href="/style.css"> 5 + <link rel="stylesheet" href="/syntax.css"> 6 + <meta charset="UTF-8"> 7 + <meta name="viewport" content="initial-scale=1"> 8 + <meta content="#ffffff" name="theme-color"> 9 + <meta name="HandheldFriendly" content="true"> 10 + <meta property="og:title" content="Configuring Jujutsu"> 11 + <meta property="og:type" content="website"> 12 + <meta property="og:description" content="a static site {for, by, about} me "> 13 + <meta property="og:url" content="https://oppi.li"> 14 + <link rel="icon" type="image/x-icon" href="/favicon.png"> 15 + <title>Configuring Jujutsu · oppi.li</title> 16 + <body> 17 + <div class="posts"> 18 + <div class="post"> 19 + <a href="/" class="post-end-link">Home</a> 20 + <span>/</span> 21 + <a href="/posts" class="post-end-link">Posts</a> 22 + <span>/</span> 23 + <a class="post-end-link">Configuring Jujutsu</a> 24 + <a class="stats post-end-link" href="https://tangled.sh/@oppi.li/site/raw/main/posts/configuring_jujutsu.md 25 + ">View Raw</a> 26 + <div class="separator"></div> 27 + <div class="date"> 28 + 23/05 — 2025 29 + <div class="stats"> 30 + <span class="stats-number"> 31 + 145.45 32 + </span> 33 + <span class="stats-unit">cm</span> 34 + &nbsp 35 + <span class="stats-number"> 36 + 9.5 37 + </span> 38 + <span class="stats-unit">min</span> 39 + </div> 40 + </div> 41 + <h1> 42 + Configuring Jujutsu 43 + </h1> 44 + <div class="post-text"> 45 + <p>There are a lot of reasons to use jujutsu, but this post is not about 46 + that. I have this terrible habit of turning every knob on a tool before 47 + I even fully absorb how it works; and boy does <code>jj</code> have a 48 + lot of knobs.</p> 49 + <h3 id="templates">Templates</h3> 50 + <p><code>jj</code> let you tweak nearly every single character of its 51 + output. In fact, <code>jj</code> includes a full-blown templating 52 + language to do so. Lets start from the basics:</p> 53 + <p>Format all timestamps in relative fashion, like “5 hours ago”:</p> 54 + <div class="sourceCode" id="cb1"><pre 55 + class="sourceCode toml"><code class="sourceCode toml"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">[template-aliases]</span></span> 56 + <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="dt">&quot;format_timestamp(timestamp)&quot;</span> <span class="op">=</span> <span class="st">&quot;timestamp.ago()&quot;</span><span class="er">;</span></span></code></pre></div> 57 + <p>The default nodes in the log graph are a bit large for my taste, we 58 + can modify the <code>log_node</code> template to fix that:</p> 59 + <div class="sourceCode" id="cb2"><pre 60 + class="sourceCode toml"><code class="sourceCode toml"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">[templates]</span></span> 61 + <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="dt">log_node</span> <span class="op">=</span> <span class="st">&#39;&#39;&#39;</span></span> 62 + <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="vs"> label(&quot;node&quot;,</span></span> 63 + <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="vs"> coalesce(</span></span> 64 + <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="vs"> if(!self, label(&quot;elided&quot;, &quot;~&quot;)),</span></span> 65 + <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="vs"> if(current_working_copy, label(&quot;working_copy&quot;, &quot;@&quot;)),</span></span> 66 + <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="vs"> if(conflict, label(&quot;conflict&quot;, &quot;×&quot;)),</span></span> 67 + <span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="vs"> if(immutable, label(&quot;immutable&quot;, &quot;*&quot;)),</span></span> 68 + <span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="vs"> label(&quot;normal&quot;, &quot;·&quot;)</span></span> 69 + <span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="vs"> )</span></span> 70 + <span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="vs"> )</span></span> 71 + <span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="st">&#39;&#39;&#39;</span></span></code></pre></div> 72 + <p>This uses smaller iconography in <code>jj log</code> (these icons are 73 + also more widely available in fonts, in my experience):</p> 74 + <pre><code>@ wuuownsw me@oppi.li 21 minutes ago 30d1bd12 75 + │ (no description set) 76 + · plmznxvy me@oppi.li 5 hours ago push-plmznxvyqrqw git_head() 051c142e 77 + │ appview: pulls: bump sourceRev for stacks without causing resubmits 78 + │ * towvwqxk x@icyphox.sh 4 hours ago master a588f625 79 + │ │ appview: pages/markup: don&#39;t double camo in post process 80 + │ * ryzqnyvs me@oppi.li 4 hours ago ea4b520a 81 + │ │ appview: fix stack merging 82 + │ * kqvutzxr me@oppi.li 4 hours ago ff73ca23 83 + ├─╯ appview: rework RepoLanguages 84 + * vwpqwmms jeynesbrook@gmail.com 8 hours ago d759587b 85 + │ appview: repo: inject language percentage into repo index 86 + ~</code></pre> 87 + <p>Much nicer! And speaking of <code>log</code>, I find it hard to parse 88 + the output when every change occupies two lines instead of just one 89 + line, we can remedy that quickly; in fact, <code>jj</code> has a builtin 90 + template for single-line log outputs:</p> 91 + <pre><code>λ jj log -T builtin_log_oneline 92 + @ wuuownsw me@oppi.li 22 minutes ago 30d1bd12 (no description set) 93 + · plmznxvy me@oppi.li 5 hours ago push-plmznxvyqrqw git_head() 051c142e appview: pulls: bump sourceRev for stacks without causing resubmits 94 + │ * towvwqxk x@icyphox.sh 4 hours ago master a588f625 appview: pages/markup: don&#39;t double camo in post process 95 + │ * ryzqnyvs me@oppi.li 4 hours ago ea4b520a appview: fix stack merging 96 + │ * kqvutzxr me@oppi.li 4 hours ago ff73ca23 appview: rework RepoLanguages 97 + ├─╯ 98 + * vwpqwmms jeynesbrook 8 hours ago d759587b appview: repo: inject language percentage into repo index 99 + 100 + ~</code></pre> 101 + <p>Bit too long! I personally do not always need author and time 102 + information when quickly scrolling through the log, so I created my own 103 + template based on <code>builtin_log_oneline</code>:</p> 104 + <div class="sourceCode" id="cb5"><pre 105 + class="sourceCode toml"><code class="sourceCode toml"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">[templates]</span></span> 106 + <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="dt">log</span> <span class="op">=</span> <span class="st">&#39;&#39;&#39;</span></span> 107 + <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="vs"> if(root,</span></span> 108 + <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="vs"> format_root_commit(self),</span></span> 109 + <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="vs"> label(if(current_working_copy, &quot;working_copy&quot;),</span></span> 110 + <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="vs"> concat(</span></span> 111 + <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="vs"> separate(&quot; &quot;,</span></span> 112 + <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="vs"> format_short_change_id_with_hidden_and_divergent_info(self),</span></span> 113 + <span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="vs"> if(empty, label(&quot;empty&quot;, &quot;(empty)&quot;)),</span></span> 114 + <span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="vs"> if(description,</span></span> 115 + <span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="vs"> description.first_line(),</span></span> 116 + <span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="vs"> label(if(empty, &quot;empty&quot;), description_placeholder),</span></span> 117 + <span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="vs"> ),</span></span> 118 + <span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="vs"> bookmarks,</span></span> 119 + <span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a><span class="vs"> tags,</span></span> 120 + <span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a><span class="vs"> working_copies,</span></span> 121 + <span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a><span class="vs"> if(git_head, label(&quot;git_head&quot;, &quot;HEAD&quot;)),</span></span> 122 + <span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a><span class="vs"> if(conflict, label(&quot;conflict&quot;, &quot;conflict&quot;)),</span></span> 123 + <span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a><span class="vs"> if(config(&quot;ui.show-cryptographic-signatures&quot;).as_boolean(),</span></span> 124 + <span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a><span class="vs"> format_short_cryptographic_signature(signature)),</span></span> 125 + <span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a><span class="vs"> ) ++ &quot;\n&quot;,</span></span> 126 + <span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a><span class="vs"> ),</span></span> 127 + <span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a><span class="vs"> )</span></span> 128 + <span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a><span class="vs"> )</span></span> 129 + <span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a><span class="st">&#39;&#39;&#39;</span></span></code></pre></div> 130 + <p>Which produces:</p> 131 + <pre><code>@ wuuownsw (no description set) 132 + · plmznxvy appview: pulls: bump sourceRev for stacks without causing resubmits push-plmznxvyqrqw HEAD 133 + │ * towvwqxk appview: pages/markup: don&#39;t double camo in post process master 134 + │ * ryzqnyvs appview: fix stack merging 135 + │ * kqvutzxr appview: rework RepoLanguages 136 + ├─╯ 137 + * vwpqwmms appview: repo: inject language percentage into repo index 138 + 139 + ~</code></pre> 140 + <p>Sweet! To get a more detailed log, you can always use a different 141 + template for the output: 142 + <code>jj log -T builtin_log_detailed</code>.</p> 143 + <p>With git, I set <code>commit.verbose</code> to true, this lets me 144 + view the diff when composing a commit messaege in my 145 + <code>$EDITOR</code>:</p> 146 + <div class="sourceCode" id="cb7"><pre 147 + class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> git commit</span> 148 + <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="co"># no message passed, opens $EDITOR to compose message</span></span> 149 + <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a></span> 150 + <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="co"># -- inside vim --</span></span> 151 + <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a></span> 152 + <span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Please enter the commit message for your changes. Lines starting</span></span> 153 + <span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="co"># with &#39;#&#39; will be ignored, and an empty message aborts the commit.</span></span> 154 + <span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span> 155 + <span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="co"># On branch trunk</span></span> 156 + <span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a><span class="co"># Changes to be committed:</span></span> 157 + <span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a><span class="co"># new file: scripts/handle.js</span></span> 158 + <span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span> 159 + <span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a><span class="co"># ------------------------ &gt;8 ------------------------</span></span> 160 + <span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a><span class="co"># Do not modify or remove the line above.</span></span> 161 + <span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a><span class="co"># Everything below it will be ignored.</span></span> 162 + <span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a><span class="fu">diff</span> <span class="at">--git</span> a/scripts/handle.js b/scripts/handle.js</span> 163 + <span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a><span class="ex">new</span> file mode 100644</span> 164 + <span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a><span class="ex">index</span> 0000000..d8a07f3</span> 165 + <span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a><span class="ex">---</span> /dev/null</span> 166 + <span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a><span class="ex">+++</span> b/scripts/handle.js</span> 167 + <span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a><span class="ex">@@</span> <span class="at">-0,0</span> +1,104 @@</span> 168 + <span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a><span class="ex">+//</span> Run using node handle.js</span> 169 + <span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a><span class="ex">+//</span> Install axios using npm install axios</span> 170 + <span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a><span class="ex">+//</span> nodejs v18+</span> 171 + <span id="cb7-25"><a href="#cb7-25" aria-hidden="true" tabindex="-1"></a><span class="ex">+const</span> axios = require<span class="er">(</span><span class="st">&quot;axios&quot;</span><span class="kw">);</span></span> 172 + <span id="cb7-26"><a href="#cb7-26" aria-hidden="true" tabindex="-1"></a><span class="bu">.</span></span> 173 + <span id="cb7-27"><a href="#cb7-27" aria-hidden="true" tabindex="-1"></a><span class="bu">.</span></span> 174 + <span id="cb7-28"><a href="#cb7-28" aria-hidden="true" tabindex="-1"></a><span class="bu">.</span></span></code></pre></div> 175 + <p>The equivalent in <code>jj</code> requires modifying the 176 + <code>draft_commit_description</code> template:</p> 177 + <div class="sourceCode" id="cb8"><pre 178 + class="sourceCode toml"><code class="sourceCode toml"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">[templates]</span></span> 179 + <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="dt">draft_commit_description</span> <span class="op">=</span><span class="st">&#39;&#39;&#39;</span></span> 180 + <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="vs"> concat(</span></span> 181 + <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="vs"> coalesce(description, default_commit_description, &quot;\n&quot;),</span></span> 182 + <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="vs"> surround(</span></span> 183 + <span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="vs"> &quot;\nJJ: This commit contains the following changes:\n&quot;, &quot;&quot;,</span></span> 184 + <span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="vs"> indent(&quot;JJ: &quot;, diff.stat(72)),</span></span> 185 + <span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a><span class="vs"> ),</span></span> 186 + <span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a><span class="vs"> &quot;\nJJ: ignore-rest\n&quot;,</span></span> 187 + <span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="vs"> diff.git(),</span></span> 188 + <span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a><span class="vs"> )</span></span> 189 + <span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a><span class="st">&#39;&#39;&#39;</span></span></code></pre></div> 190 + <h3 id="revsets">Revsets</h3> 191 + <p>As a not-so-power-user, I presently only use revsets to improve my 192 + <code>jj log</code> experience. In the context of <code>jj log</code>, 193 + giving it a revset means “here is a bag of revisions, graph them for me 194 + neatly”.</p> 195 + <p>I find myself using the “rebase” flow quite often:</p> 196 + <ul> 197 + <li>I hack on a stack of changes</li> 198 + <li><code>trunk</code> is updated by my fellow collaborators</li> 199 + <li>I rebase my stack using 200 + <code>jj rebase -s &lt;mine&gt; -d &lt;trunk&gt;</code></li> 201 + </ul> 202 + <p>And I scrub through the log output to roughly figure out how much 203 + work it would be to rebase, what I need for this is:</p> 204 + <ul> 205 + <li>the changes added to <code>trunk</code> since I last diverged from 206 + it</li> 207 + <li>the changes add to my stack since I last diverged from 208 + <code>trunk</code></li> 209 + </ul> 210 + <pre><code> X--Y--Z my stack 211 + / 212 + F--A--B--C--D trunk</code></pre> 213 + <p>If you want <code>jj log</code> to print this (and by “this”, I mean, 214 + the graph above, exactly as presented), you have to supply it a 215 + <code>revset</code> argument that grabs X, Y, Z, A, B, C, D and F. Some 216 + examples of revsets are:</p> 217 + <pre><code>@ # the rev marking the working copy 218 + x # the rev identified by shorthand `x` 219 + x | y # the set of two revs, [x, y] 220 + @ | trunk() # the set of two revs, [@, trunk()] 221 + .. # everything in this repository 222 + all() # also everything in this repository 223 + fork_point(@ | trunk()) # the rev where @ and trunk() forked off</code></pre> 224 + <p>And the revset that captures “ahead-behind” style output is:</p> 225 + <pre><code>trunk()..@ | @..trunk() | trunk() | @:: | fork_point(trunk() | @)</code></pre> 226 + <p>Which includes:</p> 227 + <ul> 228 + <li><code>trunk()..@</code>: the new changes that my collaborators have 229 + added</li> 230 + <li><code>@..trunk()</code>: the new changes that I have added</li> 231 + <li><code>trunk()</code>: the trunk rev itself</li> 232 + <li><code>@::</code>: all descendants of <code>@</code></li> 233 + <li><code>fork_point(trunk() | @)</code>: the rev from which the two 234 + streams of work diverged</li> 235 + </ul> 236 + <p>Rougly, when working on a stack, this is what I can see by supplying 237 + the above revset expression (simplified output):</p> 238 + <div class="sourceCode" id="cb12"><pre 239 + class="sourceCode bash"><code class="sourceCode bash"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> jj log <span class="at">-r</span> <span class="st">&#39;trunk()..@ | @..trunk() | trunk() | @:: | fork_point(trunk() | @)&#39;</span></span> 240 + <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="ex">@</span> xdihgmke <span class="op">&lt;</span>-- me</span> 241 + <span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> vurstull</span> 242 + <span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> plmznxvy</span> 243 + <span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> ihefghyy</span> 244 + <span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> <span class="pp">*</span> towvwqxk trunk <span class="op">&lt;</span>-- trunk</span> 245 + <span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> <span class="pp">*</span> ryzqnyvs</span> 246 + <span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> <span class="pp">*</span> kqvutzxr</span> 247 + <span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a><span class="ex">├─╯</span></span> 248 + <span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a><span class="ex">*</span> vwpqwmms <span class="op">&lt;</span>-- fork-point</span></code></pre></div> 249 + <p>And sometimes, when I am editing older parts of my stack 250 + (<code>@::</code> helps us grab <code>xdihgmke</code> and 251 + <code>vurstull</code>):</p> 252 + <div class="sourceCode" id="cb13"><pre 253 + class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> jj log <span class="at">-r</span> <span class="st">&#39;trunk()..@ | @..trunk() | trunk() | @:: | fork_point(trunk() | @)&#39;</span></span> 254 + <span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> xdihgmke</span> 255 + <span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> vurstull</span> 256 + <span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="ex">@</span> plmznxvy <span class="op">&lt;</span>-- me</span> 257 + <span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> ihefghyy</span> 258 + <span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> <span class="pp">*</span> towvwqxk trunk <span class="op">&lt;</span>-- trunk</span> 259 + <span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> <span class="pp">*</span> ryzqnyvs</span> 260 + <span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> <span class="pp">*</span> kqvutzxr</span> 261 + <span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a><span class="ex">├─╯</span></span> 262 + <span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a><span class="ex">*</span> vwpqwmms <span class="op">&lt;</span>-- fork-point</span></code></pre></div> 263 + <p>To rebase, I would run:</p> 264 + <pre><code>jj rebase -s ihefghyy -d towvwqxk</code></pre> 265 + <h3 id="aliases">Aliases</h3> 266 + <p>I imagine that most git power users are already familiar with 267 + aliases. The only alias I see myself using often is:</p> 268 + <div class="sourceCode" id="cb15"><pre 269 + class="sourceCode toml"><code class="sourceCode toml"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">[aliases]</span></span> 270 + <span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="dt">tug</span> <span class="op">=</span> <span class="op">[</span><span class="st">&quot;bookmark&quot;</span> <span class="st">&quot;move&quot;</span> <span class="st">&quot;--from&quot;</span> <span class="st">&quot;heads(::@- &amp; bookmarks())&quot;</span> <span class="st">&quot;--to&quot;</span> <span class="st">&quot;@-&quot;</span><span class="op">]</span><span class="er">;</span></span></code></pre></div> 271 + <p>In action:</p> 272 + <div class="sourceCode" id="cb16"><pre 273 + class="sourceCode bash"><code class="sourceCode bash"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="co"># ugh my bookmark is way behind</span></span> 274 + <span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> jj log</span> 275 + <span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a><span class="ex">@</span> xdihgmke</span> 276 + <span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> vurstull</span> 277 + <span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> cyzmakil</span> 278 + <span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> plmznxvy</span> 279 + <span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> ihefghyy some-bookmark</span> 280 + <span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span></span> 281 + <span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a><span class="ex">~</span></span> 282 + <span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a></span> 283 + <span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> jj tug</span> 284 + <span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a></span> 285 + <span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a><span class="co"># ready to push!</span></span> 286 + <span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> jj log</span> 287 + <span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a><span class="ex">@</span> xdihgmke</span> 288 + <span id="cb16-16"><a href="#cb16-16" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> vurstull some-bookmark<span class="pp">*</span></span> 289 + <span id="cb16-17"><a href="#cb16-17" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> cyzmakil</span> 290 + <span id="cb16-18"><a href="#cb16-18" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> plmznxvy</span> 291 + <span id="cb16-19"><a href="#cb16-19" aria-hidden="true" tabindex="-1"></a><span class="ex">·</span> ihefghyy</span> 292 + <span id="cb16-20"><a href="#cb16-20" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span></span> 293 + <span id="cb16-21"><a href="#cb16-21" aria-hidden="true" tabindex="-1"></a><span class="ex">~</span></span></code></pre></div> 294 + <p>This alias was stolen from this <a 295 + href="https://gist.github.com/thoughtpolice/8f2fd36ae17cd11b8e7bd93a70e31ad6#file-jjconfig-toml">wonderful 296 + gist</a> by <a 297 + href="https://github.com/jj-vcs/jj/blob/main/docs/testimonials.md?plain=1#L120">Austin 298 + Seipp</a>.</p> 299 + <h3 id="experimental-features">Experimental features</h3> 300 + <p>I use one experimental feature, that is only available on some of the 301 + newer versions of jujutsu:</p> 302 + <div class="sourceCode" id="cb17"><pre 303 + class="sourceCode bash"><code class="sourceCode bash"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="co"># built from master</span></span> 304 + <span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> jj version</span> 305 + <span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a><span class="ex">jj</span> 0.29.0-8c7ca30074767257d75e3842581b61e764d022cf</span></code></pre></div> 306 + <div class="sourceCode" id="cb18"><pre 307 + class="sourceCode toml"><code class="sourceCode toml"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="kw">[git]</span></span> 308 + <span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="dt">write-change-id-header</span> <span class="op">=</span> <span class="cn">true</span></span></code></pre></div> 309 + <p>This writes an extra commit-header (not to be confused with 310 + commit-trailer, which goes directly in the commit body) with the jujutsu 311 + change-id, as seen by inspecting the commit object:</p> 312 + <div class="sourceCode" id="cb19"><pre 313 + class="sourceCode bash"><code class="sourceCode bash"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> git cat-file commit 4edebe96159bf81c3be662d0a2b4c7b08a062968</span> 314 + <span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="ex">tree</span> a9b7e9e3eed8a22c35829bf586d7560ec8396124</span> 315 + <span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="ex">parent</span> edc0d2750d4848bc05cfd3255ee1ca916bea9156</span> 316 + <span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a><span class="ex">author</span> oppiliappan <span class="op">&lt;</span>me@oppi.li<span class="op">&gt;</span> 1747919102 +0100</span> 317 + <span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="ex">committer</span> oppiliappan <span class="op">&lt;</span>me@oppi.li<span class="op">&gt;</span> 1747919102 +0100</span> 318 + <span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="ex">change-id</span> skrrxvvxlpzrqzpxlxksvryrykpxkvon <span class="op">&lt;</span>-- this</span></code></pre></div> 319 + <p>This allows code forges to extract change-ids from commits, and most 320 + crucially: <strong>allows tracking changes across force pushes</strong>. 321 + This enables the <a 322 + href="https://gist.github.com/thoughtpolice/9c45287550a56b2047c6311fbadebed2">interdiff 323 + format</a> of code-review. Stacking, commit-wise-review, and interdiff 324 + are all supported on <a href="https://tangled.sh">tangled</a> (you can 325 + read more <a 326 + href="https://bsky.app/profile/tangled.sh/post/3lptwcb47kc2u">here</a>), 327 + if you submit a branch with this experimental feature enabled.</p> 328 + <h3 id="misc">Misc</h3> 329 + <p>Couple of other quality of life additions:</p> 330 + <div class="sourceCode" id="cb20"><pre 331 + class="sourceCode toml"><code class="sourceCode toml"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">[ui]</span></span> 332 + <span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="dt">default-command</span> <span class="op">=</span> <span class="st">&quot;status&quot;</span></span> 333 + <span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a><span class="dt">pager</span> <span class="op">=</span> <span class="st">&quot;delta&quot;</span></span></code></pre></div> 334 + <p>I also use nix and home-manager to configure jj, you can find my 335 + configuration <a href="https://plonk.li/r/YQ">here</a>.</p> 336 + 337 + </div> 338 + 339 + <div class="intro"> 340 + Hi. 341 + <div class="hot-links"> 342 + <a href="/index.xml" class="feed-button">Subscribe</a> 343 + </div> 344 + <p>I'm Akshay, programmer, pixel-artist & programming-language enthusiast.</p> 345 + <p>I am currently building <a href="https://tangled.sh">tangled.sh</a> — a decentralized code-collaboration platform.</p> 346 + <p>Reach out at oppili@libera.chat.</p> 347 + </div> 348 + 349 + <a href="/" class="post-end-link">Home</a> 350 + <span>/</span> 351 + <a href="/posts" class="post-end-link">Posts</a> 352 + <span>/</span> 353 + <a class="post-end-link">Configuring Jujutsu</a> 354 + <a class="stats post-end-link" href="https://tangled.sh/@oppi.li/site/raw/main/posts/configuring_jujutsu.md 355 + ">View Raw</a> 356 + </div> 357 + </div> 358 + </body> 359 + </html>
+17
docs/posts/index.html
··· 27 27 <tr> 28 28 <td class=table-post> 29 29 <div class="date"> 30 + 23/05 — 2025 31 + </div> 32 + <a href="/posts/configuring_jujutsu" class="post-link"> 33 + <span class="post-link">Configuring Jujutsu</span> 34 + </a> 35 + </td> 36 + <td class=table-stats> 37 + <span class="stats-number"> 38 + 9.5 39 + </span> 40 + <span class=stats-unit>min</span> 41 + </td> 42 + </tr> 43 + 44 + <tr> 45 + <td class=table-post> 46 + <div class="date"> 30 47 21/05 — 2025 31 48 </div> 32 49 <a href="/posts/tales_from_mainframe_modernization" class="post-link">
+30
drafts/code_review_diagnostics.md
··· 1 + What if you could view code review comments in your editor 2 + as LSP diagnostics? 3 + 4 + Requirements: 5 + 6 + - The forge to support a format like [f3](https://f3.forgefriends.org/) 7 + - Users can write reviews in the browser 8 + 9 + More technically: 10 + 11 + - The "LSP" would expect the forge to support a format like [f3](https://f3.forgefriends.org/) 12 + - Your editor will sync review comments from your codeforge 13 + - Your LSP should know when to show these diagnostics 14 + * in branch-based review, reviews are not really attached 15 + to anything, but based on current git branch perhaps? 16 + * in commit-based review, reviews can be attached to a 17 + change-id (a la jujutsu), and displayed based on current 18 + working copy change-id 19 + 20 + Cons: 21 + 22 + - How do you reply to these? Is there a way to introduce 23 + bidirectional communication? 24 + - Review comments are not always on code, they could be on 25 + commit message or just general architecture, for which an 26 + "inbox" is a better abstraction 27 + - Bidirectional communication eventually becomes a chat app 28 + 29 + This thought was inspired by recent comments on 30 + "offline-first" reviews by [matklad]()
+364
posts/configuring_jujutsu.md
··· 1 + There are a lot of reasons to use jujutsu, but this post is 2 + not about that. I have this terrible habit of turning every 3 + knob on a tool before I even fully absorb how it works; and 4 + boy does `jj` have a lot of knobs. 5 + 6 + ### Templates 7 + 8 + `jj` let you tweak nearly every single character of its 9 + output. In fact, `jj` includes a full-blown templating 10 + language to do so. Lets start from the basics: 11 + 12 + Format all timestamps in relative fashion, like "5 hours 13 + ago": 14 + 15 + ```toml 16 + [template-aliases] 17 + "format_timestamp(timestamp)" = "timestamp.ago()"; 18 + ``` 19 + 20 + The default nodes in the log graph are a bit large for my 21 + taste, we can modify the `log_node` template to fix that: 22 + 23 + ```toml 24 + [templates] 25 + log_node = ''' 26 + label("node", 27 + coalesce( 28 + if(!self, label("elided", "~")), 29 + if(current_working_copy, label("working_copy", "@")), 30 + if(conflict, label("conflict", "×")), 31 + if(immutable, label("immutable", "*")), 32 + label("normal", "·") 33 + ) 34 + ) 35 + ''' 36 + ``` 37 + 38 + This uses smaller iconography in `jj log` (these icons are 39 + also more widely available in fonts, in my experience): 40 + 41 + ``` 42 + @ wuuownsw me@oppi.li 21 minutes ago 30d1bd12 43 + │ (no description set) 44 + · plmznxvy me@oppi.li 5 hours ago push-plmznxvyqrqw git_head() 051c142e 45 + │ appview: pulls: bump sourceRev for stacks without causing resubmits 46 + │ * towvwqxk x@icyphox.sh 4 hours ago master a588f625 47 + │ │ appview: pages/markup: don't double camo in post process 48 + │ * ryzqnyvs me@oppi.li 4 hours ago ea4b520a 49 + │ │ appview: fix stack merging 50 + │ * kqvutzxr me@oppi.li 4 hours ago ff73ca23 51 + ├─╯ appview: rework RepoLanguages 52 + * vwpqwmms jeynesbrook@gmail.com 8 hours ago d759587b 53 + │ appview: repo: inject language percentage into repo index 54 + ~ 55 + ``` 56 + 57 + Much nicer! And speaking of `log`, I find it hard to parse 58 + the output when every change occupies two lines instead of 59 + just one line, we can remedy that quickly; in fact, `jj` has 60 + a builtin template for single-line log outputs: 61 + 62 + ``` 63 + λ jj log -T builtin_log_oneline 64 + @ wuuownsw me@oppi.li 22 minutes ago 30d1bd12 (no description set) 65 + · plmznxvy me@oppi.li 5 hours ago push-plmznxvyqrqw git_head() 051c142e appview: pulls: bump sourceRev for stacks without causing resubmits 66 + │ * towvwqxk x@icyphox.sh 4 hours ago master a588f625 appview: pages/markup: don't double camo in post process 67 + │ * ryzqnyvs me@oppi.li 4 hours ago ea4b520a appview: fix stack merging 68 + │ * kqvutzxr me@oppi.li 4 hours ago ff73ca23 appview: rework RepoLanguages 69 + ├─╯ 70 + * vwpqwmms jeynesbrook 8 hours ago d759587b appview: repo: inject language percentage into repo index 71 + 72 + ~ 73 + ``` 74 + 75 + Bit too long! I personally do not always need author and 76 + time information when quickly scrolling through the log, so 77 + I created my own template based on `builtin_log_oneline`: 78 + 79 + ```toml 80 + [templates] 81 + log = ''' 82 + if(root, 83 + format_root_commit(self), 84 + label(if(current_working_copy, "working_copy"), 85 + concat( 86 + separate(" ", 87 + format_short_change_id_with_hidden_and_divergent_info(self), 88 + if(empty, label("empty", "(empty)")), 89 + if(description, 90 + description.first_line(), 91 + label(if(empty, "empty"), description_placeholder), 92 + ), 93 + bookmarks, 94 + tags, 95 + working_copies, 96 + if(git_head, label("git_head", "HEAD")), 97 + if(conflict, label("conflict", "conflict")), 98 + if(config("ui.show-cryptographic-signatures").as_boolean(), 99 + format_short_cryptographic_signature(signature)), 100 + ) ++ "\n", 101 + ), 102 + ) 103 + ) 104 + ''' 105 + ``` 106 + 107 + Which produces: 108 + 109 + ``` 110 + @ wuuownsw (no description set) 111 + · plmznxvy appview: pulls: bump sourceRev for stacks without causing resubmits push-plmznxvyqrqw HEAD 112 + │ * towvwqxk appview: pages/markup: don't double camo in post process master 113 + │ * ryzqnyvs appview: fix stack merging 114 + │ * kqvutzxr appview: rework RepoLanguages 115 + ├─╯ 116 + * vwpqwmms appview: repo: inject language percentage into repo index 117 + 118 + ~ 119 + ``` 120 + 121 + Sweet! To get a more detailed log, you can always use a 122 + different template for the output: `jj log -T 123 + builtin_log_detailed`. 124 + 125 + With git, I set `commit.verbose` to true, this lets me view 126 + the diff when composing a commit messaege in my `$EDITOR`: 127 + 128 + ```bash 129 + λ git commit 130 + # no message passed, opens $EDITOR to compose message 131 + 132 + # -- inside vim -- 133 + 134 + # Please enter the commit message for your changes. Lines starting 135 + # with '#' will be ignored, and an empty message aborts the commit. 136 + # 137 + # On branch trunk 138 + # Changes to be committed: 139 + # new file: scripts/handle.js 140 + # 141 + # ------------------------ >8 ------------------------ 142 + # Do not modify or remove the line above. 143 + # Everything below it will be ignored. 144 + diff --git a/scripts/handle.js b/scripts/handle.js 145 + new file mode 100644 146 + index 0000000..d8a07f3 147 + --- /dev/null 148 + +++ b/scripts/handle.js 149 + @@ -0,0 +1,104 @@ 150 + +// Run using node handle.js 151 + +// Install axios using npm install axios 152 + +// nodejs v18+ 153 + +const axios = require("axios"); 154 + . 155 + . 156 + . 157 + ``` 158 + 159 + The equivalent in `jj` requires modifying the 160 + `draft_commit_description` template: 161 + 162 + ```toml 163 + [templates] 164 + draft_commit_description =''' 165 + concat( 166 + coalesce(description, default_commit_description, "\n"), 167 + surround( 168 + "\nJJ: This commit contains the following changes:\n", "", 169 + indent("JJ: ", diff.stat(72)), 170 + ), 171 + "\nJJ: ignore-rest\n", 172 + diff.git(), 173 + ) 174 + ''' 175 + ``` 176 + 177 + ### Revsets 178 + 179 + As a not-so-power-user, I presently only use revsets to 180 + improve my `jj log` experience. In the context of `jj log`, 181 + giving it a revset means "here is a bag of revisions, graph 182 + them for me neatly". 183 + 184 + I find myself using the "rebase" flow quite often: 185 + 186 + - I hack on a stack of changes 187 + - `trunk` is updated by my fellow collaborators 188 + - I rebase my stack using `jj rebase -s <mine> -d <trunk>` 189 + 190 + And I scrub through the log output to roughly figure out how 191 + much work it would be to rebase, what I need for this is: 192 + 193 + - the changes added to `trunk` since I last diverged from it 194 + - the changes add to my stack since I last diverged from 195 + `trunk` 196 + 197 + ``` 198 + X--Y--Z my stack 199 + / 200 + F--A--B--C--D trunk 201 + ``` 202 + 203 + If you want `jj log` to print this (and by "this", I mean, 204 + the graph above, exactly as presented), you have to supply 205 + it a `revset` argument that grabs X, Y, Z, A, B, C, D and F. 206 + Some examples of revsets are: 207 + 208 + ``` 209 + @ # the rev marking the working copy 210 + x # the rev identified by shorthand `x` 211 + x | y # the set of two revs, [x, y] 212 + @ | trunk() # the set of two revs, [@, trunk()] 213 + .. # everything in this repository 214 + all() # also everything in this repository 215 + fork_point(@ | trunk()) # the rev where @ and trunk() forked off 216 + ``` 217 + 218 + And the revset that captures "ahead-behind" style output is: 219 + 220 + ``` 221 + trunk()..@ | @..trunk() | trunk() | @:: | fork_point(trunk() | @) 222 + ``` 223 + 224 + Which includes: 225 + 226 + - `trunk()..@`: the new changes that my collaborators have added 227 + - `@..trunk()`: the new changes that I have added 228 + - `trunk()`: the trunk rev itself 229 + - `@::`: all descendants of `@` 230 + - `fork_point(trunk() | @)`: the rev from which the two 231 + streams of work diverged 232 + 233 + Rougly, when working on a stack, this is what I can see by 234 + supplying the above revset expression (simplified output): 235 + 236 + ```bash 237 + λ jj log -r 'trunk()..@ | @..trunk() | trunk() | @:: | fork_point(trunk() | @)' 238 + @ xdihgmke <-- me 239 + · vurstull 240 + · plmznxvy 241 + · ihefghyy 242 + │ * towvwqxk trunk <-- trunk 243 + │ * ryzqnyvs 244 + │ * kqvutzxr 245 + ├─╯ 246 + * vwpqwmms <-- fork-point 247 + ``` 248 + 249 + And sometimes, when I am editing older parts of my stack 250 + (`@::` helps us grab `xdihgmke` and `vurstull`): 251 + 252 + ```bash 253 + λ jj log -r 'trunk()..@ | @..trunk() | trunk() | @:: | fork_point(trunk() | @)' 254 + · xdihgmke 255 + · vurstull 256 + @ plmznxvy <-- me 257 + · ihefghyy 258 + │ * towvwqxk trunk <-- trunk 259 + │ * ryzqnyvs 260 + │ * kqvutzxr 261 + ├─╯ 262 + * vwpqwmms <-- fork-point 263 + ``` 264 + 265 + To rebase, I would run: 266 + 267 + ``` 268 + jj rebase -s ihefghyy -d towvwqxk 269 + ``` 270 + 271 + ### Aliases 272 + 273 + I imagine that most git power users are already familiar 274 + with aliases. The only alias I see myself using often is: 275 + 276 + ```toml 277 + [aliases] 278 + tug = ["bookmark" "move" "--from" "heads(::@- & bookmarks())" "--to" "@-"]; 279 + ``` 280 + 281 + In action: 282 + 283 + ```bash 284 + # ugh my bookmark is way behind 285 + λ jj log 286 + @ xdihgmke 287 + · vurstull 288 + · cyzmakil 289 + · plmznxvy 290 + · ihefghyy some-bookmark 291 + 292 + ~ 293 + 294 + λ jj tug 295 + 296 + # ready to push! 297 + λ jj log 298 + @ xdihgmke 299 + · vurstull some-bookmark* 300 + · cyzmakil 301 + · plmznxvy 302 + · ihefghyy 303 + 304 + ~ 305 + ``` 306 + 307 + This alias was stolen from this [wonderful 308 + gist](https://gist.github.com/thoughtpolice/8f2fd36ae17cd11b8e7bd93a70e31ad6#file-jjconfig-toml) 309 + by [Austin 310 + Seipp](https://github.com/jj-vcs/jj/blob/main/docs/testimonials.md?plain=1#L120). 311 + 312 + ### Experimental features 313 + 314 + I use one experimental feature, that is only available on 315 + some of the newer versions of jujutsu: 316 + 317 + ```bash 318 + # built from master 319 + λ jj version 320 + jj 0.29.0-8c7ca30074767257d75e3842581b61e764d022cf 321 + ``` 322 + 323 + ```toml 324 + [git] 325 + write-change-id-header = true 326 + ``` 327 + 328 + This writes an extra commit-header (not to be confused with 329 + commit-trailer, which goes directly in the commit body) with 330 + the jujutsu change-id, as seen by inspecting the commit 331 + object: 332 + 333 + ```bash 334 + λ git cat-file commit 4edebe96159bf81c3be662d0a2b4c7b08a062968 335 + tree a9b7e9e3eed8a22c35829bf586d7560ec8396124 336 + parent edc0d2750d4848bc05cfd3255ee1ca916bea9156 337 + author oppiliappan <me@oppi.li> 1747919102 +0100 338 + committer oppiliappan <me@oppi.li> 1747919102 +0100 339 + change-id skrrxvvxlpzrqzpxlxksvryrykpxkvon <-- this 340 + ``` 341 + 342 + This allows code forges to extract change-ids from commits, 343 + and most crucially: **allows tracking changes across force 344 + pushes**. This enables the [interdiff 345 + format](https://gist.github.com/thoughtpolice/9c45287550a56b2047c6311fbadebed2) 346 + of code-review. Stacking, commit-wise-review, and interdiff 347 + are all supported on [tangled](https://tangled.sh) (you can 348 + read more 349 + [here](https://bsky.app/profile/tangled.sh/post/3lptwcb47kc2u)), 350 + if you submit a branch with this experimental feature 351 + enabled. 352 + 353 + ### Misc 354 + 355 + Couple of other quality of life additions: 356 + 357 + ```toml 358 + [ui] 359 + default-command = "status" 360 + pager = "delta" 361 + ``` 362 + 363 + I also use nix and home-manager to configure jj, you can 364 + find my configuration [here](https://plonk.li/r/YQ).