From c3669921a8ab3fd4953f0ac8b8d50827c4c80191 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 4 Sep 2022 12:35:38 +0530 Subject: new post: curing a case of git-ux --- docs/index.html | 16 +- docs/index.xml | 184 +++++++++++++++ docs/posts/curing_a_case_of_git-UX/index.html | 253 ++++++++++++++++++++ docs/posts/index.html | 17 ++ posts/curing_a_case_of_git-UX.md | 320 ++++++++++++++++++++++++++ 5 files changed, 782 insertions(+), 8 deletions(-) create mode 100644 docs/posts/curing_a_case_of_git-UX/index.html create mode 100644 posts/curing_a_case_of_git-UX.md diff --git a/docs/index.html b/docs/index.html index 3973034..b3d6de9 100644 --- a/docs/index.html +++ b/docs/index.html @@ -42,15 +42,15 @@
- 28/08 — 2022 + 03/09 — 2022
- - Programming On 34 Keys + + Curing A Case Of Git-UX - 6.2 + 9.5 min @@ -59,15 +59,15 @@
- 02/08 — 2022 + 28/08 — 2022
- - A Reference Counted Afterlife + + Programming On 34 Keys - 1.6 + 6.2 min diff --git a/docs/index.xml b/docs/index.xml index 62c8871..5586abd 100644 --- a/docs/index.xml +++ b/docs/index.xml @@ -12,6 +12,190 @@ en-us Creative Commons BY-NC-SA 4.0 +Curing A Case Of Git-UX +<p>Git worktrees are great, but they fall behind the venerable <code>git checkout</code> sometimes. I attempted to fix that with <a href="https://github.com/junegunn/fzf">fzf</a> and a bit of bash.</p> +<p><a href="https://asciinema.org/a/D297ztKRzpE4gAHbPTPmkqYps"><img src="https://asciinema.org/a/D297ztKRzpE4gAHbPTPmkqYps.svg" /></a></p> +<p>Fear not if you haven’t heard of “worktrees”, I have included a primer here.<br /> +<a href="#what-makes-them-clunky">Skip the primer -&gt;</a>.</p> +<h3 id="why-worktrees">Why Worktrees?</h3> +<p>Picture this. You are whacking away on a feature branch. Halfway there, in fact. Your friend asks you fix something urgently. You proceed to do one of three things:</p> +<ul> +<li>create a temporary branch, make a WIP commit, begin working on the fix</li> +<li>stash away your changes, begin working on the fix</li> +<li>unfriend said friend for disturbing your flow</li> +</ul> +<p>All of these options are … subpar. With the temporary branch, you are forced to create a partial, non-working commit, and then reset said commit once done with the fix. With the stash approach, you are required to now keep a mental model of the stash, be aware of untracked files that don’t get stashed by default, etc. Why won’t git just let you work on two things at the same time without <em>thinking</em> so much?</p> +<p>That is exactly what worktrees let you do. Worktrees let you have more than one checkout at a time, each checkout in a separate directory. Like creating a new clone, but safer (it disallows checking out the same branch twice) and a lot more space efficient (the new working tree is “linked” to the “main” worktree, and a good amount of stuff is shared). When your friend asks you to make the fix, you proceed like so:</p> +<ol type="1"> +<li>Create a new working tree with:</li> +</ol> +<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># git worktree add -b &lt;branch-name&gt; &lt;path&gt; &lt;from&gt;</span></span> +<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="fu">git</span> worktree add <span class="at">-b</span> fix-stuff /path/to/tree master</span></code></pre></div> +<ol start="2" type="1"> +<li><code>cd</code> into <code>/path/to/tree</code></li> +<li>Fix, test, commit, push, party</li> +<li>Go back to your work, <code>cd -</code></li> +</ol> +<p>Easy as cake. You didn’t have to settle for a partially working commit, you didn’t to deal with this “stash” thing, <em>and</em> you didn’t have to unfriend your friend. Treating each branch as a directory just <em>feels</em> more intuitive, more UNIX-y.</p> +<p>A few weeks later, you find yourself singing in praise of worktrees, working on several things simultaneously. And at the same time, cursing them for being a little … clunky.</p> +<h3 id="what-makes-them-clunky">What makes them clunky?</h3> +<p>Worktrees are great at what they claim to do. They stay out of the way when you need a checkout posthaste. However, as you start using them regularly, you realize they are not as flexible as <code>git checkout</code> or <code>git switch</code>.</p> +<h4 id="branch-hopping">Branch-hopping</h4> +<p>You can <code>git checkout &lt;branch&gt;</code> from anywhere within a git repository. You can’t “jump” to a worktree in the same fashion. The closest you can get, is to run <code>git worktree list</code>, copy the path corresponding to your branch, and <code>cd</code> into it.</p> +<p>Branch-hopping with the good ol’ git-checkout:</p> +<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># anywhere, anytime</span></span> +<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> git checkout feature/is-ascii-octdigit</span></code></pre></div> +<p>Meanwhile, in worktree world:</p> +<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co"># keeping these paths in your head is hard</span></span> +<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> git worktree list</span> +<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="ex">~/worktrees/rustc/master</span> eac6c33bc63 [master]</span> +<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="ex">~/worktrees/rustc/improve-std-char-docs</span> 94cba88553e [improve-std-char-docs]</span> +<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="ex">~/worktrees/rustc/is-ascii-octdigit</span> bc57be3af7a [feature/is-ascii-octdigit]</span> +<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="ex">~/my/other/path/oh/god</span> op57or3ns7n [fix/some-error]</span> +<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> cd ~/worktrees/rustc/is-ascii-octdigit</span></code></pre></div> +<h4 id="branch-previewing">Branch-previewing</h4> +<p>You can “preview” branches with <code>git branch -v</code>. However, to get an idea of what “recent activity” on a worktree looks like, you might need some juggling. You can’t glean much info about a worktree in a jiffy.</p> +<p>Branch-previewing with the good ol’ git-branch:</p> +<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> git branch <span class="at">-v</span></span> +<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="ex">+</span> feature/is-ascii-octdigit bc57be3af7a introduce {char, u8}::is_ ...</span> +<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="ex">+</span> improve-std-char-docs 94cba88553e add whitespace in assert ...</span> +<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="ex">*</span> master eac6c33bc63 Auto merge of <span class="co">#100869 - n ...</span></span></code></pre></div> +<p>Meanwhile in worktree wonderland:</p> +<pre><code>λ git worktree list +~/worktrees/rustc/master eac6c33bc63 [master] +~/worktrees/rustc/improve-std-char-docs 94cba88553e [improve-std-char-docs] +~/worktrees/rustc/is-ascii-octdigit bc57be3af7a [feature/is-ascii-octdigit] + +# aha, so ../is-ascii-octdigit corresponds to `feature/is-ascii-octdigit` +λ git log feature/is-ascii-octdigit +bc57be3af7a introduce {char, u8}::is_ascii_octdigit +eac6c33bc63 Auto merge of #100869 - nnethercote:repl ... +b32223fec10 Auto merge of #100707 - dzvon:fix-typo, ... +aa857eb953e Auto merge of #100537 - petrochenkov:pic ... + +# extra work to make the branch &lt;-&gt; worktree correspondence</code></pre> +<h4 id="shell-completions">Shell completions</h4> +<p>Lastly, you can bank on shell completions to fill in your branch whilst using <code>git checkout</code>. Worktrees have no such conveniences.</p> +<p>We can mend these minor faults with fzf.</p> +<h3 id="unclunkifying-worktrees">Unclunkifying worktrees</h3> +<p>I’d suggest looking up <a href="https://github.com/junegunn/fzf">fzf</a> (or <a href="https://github.com/lotabout/skim">skim</a> or <a href="https://github.com/jhawthorn/fzy">fzy</a>). These things make it cake-easy to add interactivity to your shell. Onto fixing the first minor fault, the inability to “jump” to a worktree from anywhere within a git repository.</p> +<p>I have a little function called <code>gwj</code> which stands for “git worktree jump”. The idea is to list all the worktrees, select one with fzf, and <code>cd</code> to it upon selection:</p> +<div class="sourceCode" id="cb6"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="fu">gwj ()</span> <span class="kw">{</span></span> +<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="bu">local</span> <span class="va">out</span></span> +<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="va">out</span><span class="op">=</span><span class="va">$(</span><span class="fu">git</span> worktree list <span class="kw">|</span> <span class="ex">fzf</span> <span class="kw">|</span> <span class="fu">awk</span> <span class="st">&#39;{print $1}&#39;</span><span class="va">)</span></span> +<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="bu">cd</span> <span class="va">$out</span></span> +<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span></code></pre></div> +<p>That is all of it really. Head into a git repository:</p> +<div class="sourceCode" id="cb7"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co"># here, &quot;master&quot; is a directory, which contains my main</span></span> +<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="co"># worktree: a checkout of the master branch on rust-lang/rust </span></span> +<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> cd ~/worktrees/rustc/master/library/core/src</span> +<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> <span class="co"># hack away</span></span></code></pre></div> +<p>Preferably one with a few worktrees:</p> +<div class="sourceCode" id="cb8"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> git worktree list</span> +<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="ex">~/worktrees/rustc/master</span> eac6c33bc63 [master]</span> +<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="ex">~/worktrees/rustc/improve-std-char-docs</span> 94cba88553e [improve-std-char-docs]</span> +<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="ex">~/worktrees/rustc/is-ascii-octdigit</span> bc57be3af7a [feature/is-ascii-octdigit]</span></code></pre></div> +<p>And hit <code>gwj</code> (pretend that the pipe, |, is your cursor):</p> +<div class="sourceCode" id="cb9"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> gwj</span> +<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span> <span class="kw">|</span></span> +<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="ex">4/4</span></span> +<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span> ~/worktrees/rustc/master <span class="ex">eac6c33bc63</span> [master]</span> +<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="ex">~/worktrees/rustc/improve-std-char-docs</span> 94cba88553e [improve-std-char-docs]</span> +<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> <span class="ex">~/worktrees/rustc/is-ascii-octdigit</span> bc57be3af7a [feature/is-ascii-octdigit]</span></code></pre></div> +<p>Approximately type in your branch of choice:</p> +<div class="sourceCode" id="cb10"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> gwj</span> +<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span> docs<span class="kw">|</span></span> +<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="ex">4/4</span></span> +<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span> ~/worktrees/rustc/improve-std-char-docs <span class="ex">94cba88553e</span> [improve-std-char-docs]</span></code></pre></div> +<p>And hit enter. You should find yourself in the selected worktree.</p> +<p>Onward, to the next fault, lack of preview-bility. We can utilize fzf’s aptly named <code>--preview</code> flag, to, well, preview our worktree before performing a selection:</p> +<div class="sourceCode" id="cb11"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="fu">gwj ()</span> <span class="kw">{</span></span> +<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="bu">local</span> <span class="va">out</span></span> +<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> <span class="va">out</span><span class="op">=</span><span class="va">$(</span></span> +<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">git</span> worktree list <span class="kw">|</span></span> +<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> <span class="ex">fzf</span> <span class="at">--preview</span><span class="op">=</span><span class="st">&#39;git log --oneline -n10 {2}&#39;</span> <span class="kw">|</span></span> +<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> <span class="fu">awk</span> <span class="st">&#39;{print $1}&#39;</span></span> +<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> <span class="va">)</span></span> +<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> <span class="bu">cd</span> <span class="va">$out</span></span> +<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span></code></pre></div> +<p>Once again, hit <code>gwj</code> inside a git repository with linked worktrees:</p> +<div class="sourceCode" id="cb12"><pre 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> gwj</span> +<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="ex">╭─────────────────────────────────────────────────────────╮</span></span> +<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> eac6c33bc63 Auto merge of 100869 nnethercote:replace... │</span> +<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> b32223fec10 Auto merge of 100707 dzvon:fix-typo, r=d... │</span> +<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> aa857eb953e Auto merge of 100537 petrochenkov:picche... │</span> +<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> 3892b7074da Auto merge of 100210 mystor:proc_macro_d... │</span> +<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> db00199d999 Auto merge of 101249 matthiaskrgr:rollup... │</span> +<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> 14d216d33ba Rollup merge of 101240 JohnTitor:JohnTit... │</span> +<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> 3da66f03531 Rollup merge of 101236 thomcc:winfs-noze... │</span> +<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> 0620f6e90af Rollup merge of 101230 davidtwco:transla... │</span> +<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> c30c42ee299 Rollup merge of 101229 mgeisler:link-try... │</span> +<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> e5356712b9e Rollup merge of 101165 ldm0:drain_to_ite... │</span> +<span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a><span class="ex">╰─────────────────────────────────────────────────────────╯</span></span> +<span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span></span> +<span id="cb12-15"><a href="#cb12-15" aria-hidden="true" tabindex="-1"></a> <span class="ex">4/4</span></span> +<span id="cb12-16"><a href="#cb12-16" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span> /home/np/worktrees/compiler/master <span class="ex">eac6c...</span></span> +<span id="cb12-17"><a href="#cb12-17" aria-hidden="true" tabindex="-1"></a> <span class="ex">/home/np/worktrees/compiler/improve-std-char-docs</span> 94cba...</span> +<span id="cb12-18"><a href="#cb12-18" aria-hidden="true" tabindex="-1"></a> <span class="ex">/home/np/worktrees/compiler/is-ascii-octdigit</span> bc57b...</span></code></pre></div> +<p>A fancy preview of the last 10 commits on the branch that the selected worktree corresponds to. In other words, sight for sore eyes. Our little script is already shaping up to be useful, you hit <code>gwj</code>, browse through your worktrees, preview each one and automatically <code>cd</code> to your selection. But we are not done yet.</p> +<p>The last fault was lack shell completions. A quick review of what a shell completion really does:</p> +<div class="sourceCode" id="cb13"><pre 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> git checkout f<span class="op">&lt;</span>tab<span class="op">&gt;</span></span> +<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="ex">feature/is-ascii-octdigit</span></span> +<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="ex">fix/some-error</span></span> +<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="ex">format-doc-tests</span></span> +<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> git checkout feat<span class="op">&lt;</span>tab<span class="op">&gt;</span></span> +<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> git checkout feature/is-ascii-octdigit</span></code></pre></div> +<p>Each time you hit “tab”, the shell produces a few “completion candidates”, and once you have just a single candidate left, the shell inserts that for you directly into your edit line. Of course, this process varies from shell to shell.</p> +<p>fzf narrows down your options as you type into the prompt, but you still have to:</p> +<ol type="1"> +<li>Type <code>gwj</code></li> +<li>Hit enter</li> +<li>Type out a query and narrow down your search</li> +<li>Hit enter</li> +</ol> +<p>We can speed that up a bit, have fzf narrow down the candidates on startup, just like our shell does:</p> +<div class="sourceCode" id="cb14"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="fu">gwj ()</span> <span class="kw">{</span></span> +<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> <span class="bu">local</span> <span class="va">out</span> <span class="va">query</span></span> +<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> <span class="va">query</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${1</span><span class="op">:-</span> <span class="va">}</span><span class="st">&quot;</span></span> +<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a> <span class="va">out</span><span class="op">=</span><span class="va">$(</span></span> +<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">git</span> worktree list <span class="kw">|</span></span> +<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a> <span class="ex">fzf</span> <span class="at">--preview</span><span class="op">=</span><span class="st">&#39;git log --oneline -n10 {2}&#39;</span> <span class="at">--query</span> <span class="st">&quot;</span><span class="va">$query</span><span class="st">&quot;</span> <span class="at">-1</span> <span class="kw">|</span></span> +<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">awk</span> <span class="st">&#39;{print $1}&#39;</span></span> +<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> <span class="va">)</span></span> +<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a> <span class="bu">cd</span> <span class="va">$out</span></span> +<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span></code></pre></div> +<p>The change is extremely tiny, blink-and-you’ll-miss-it kinda tiny. We added a little <code>--query</code> flag, that allows you to prefill the prompt, and the <code>-1</code> flag, that avoids the interactive finder if only one match exists on startup:</p> +<div class="sourceCode" id="cb15"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="co"># skip through the fzf prompt:</span></span> +<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> gwj master</span> +<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="co"># cd -- ~/worktrees/rustc/master</span></span> +<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a><span class="co"># more than one option, we end up in the interactive finder</span></span> +<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a><span class="ex">λ</span> gwj improve</span> +<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a><span class="ex">╭─────────────────────────────────────────────────────────╮</span></span> +<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> eac6c33bc63 Auto merge of 100869 nnethercote:replace... │</span> +<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> b32223fec10 Auto merge of 100707 dzvon:fix-typo, r=d... │</span> +<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a><span class="ex">│</span> aa857eb953e Auto merge of 100537 petrochenkov:picche... │</span> +<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a><span class="ex">╰─────────────────────────────────────────────────────────╯</span></span> +<span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span> improve</span> +<span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a> <span class="ex">2/2</span></span> +<span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span> /home/np/worktrees/compiler/improve-const-perf <span class="ex">eac6c...</span></span> +<span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a> <span class="ex">/home/np/worktrees/compiler/improve-std-char-docs</span> 94cba...</span></code></pre></div> +<p>Throw some error handling in there, hook up a similar script to improve the UX of <code>git worktree remove</code>, go wild. A few more helpers I’ve got:</p> +<div class="sourceCode" id="cb16"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="co"># gwa /path/to/branch-name</span></span> +<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="co"># creates a new branch and &quot;switches&quot; to it</span></span> +<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span><span class="fu"> gwa ()</span> <span class="kw">{</span></span> +<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">git</span> worktree add <span class="st">&quot;</span><span class="va">$1</span><span class="st">&quot;</span> <span class="kw">&amp;&amp;</span> <span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$1</span><span class="st">&quot;</span></span> +<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span> +<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="bu">alias</span> gwls=<span class="st">&quot;git worktree list&quot;</span></span></code></pre></div> +https://peppe.rs/posts/curing_a_case_of_git-UX/ +Sat, 03 Sep 2022 03:18:00 +0000 +https://peppe.rs/posts/curing_a_case_of_git-UX/ + + Programming On 34 Keys <p>Minimizing your keyboard layout is a slippery slope. A few months ago, I built the <a href="https://github.com/icyphox/ferricy">Ferricy</a>, a 34-key-split-ortho-ergo keyboard. The Ferricy is a fork of the <a href="https://github.com/davidphilipbarr/Sweep/tree/main/Sweep%20Bling%20MX">Ferris Sweep MX Bling</a>.</p> <figure> diff --git a/docs/posts/curing_a_case_of_git-UX/index.html b/docs/posts/curing_a_case_of_git-UX/index.html new file mode 100644 index 0000000..9c4761c --- /dev/null +++ b/docs/posts/curing_a_case_of_git-UX/index.html @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + Curing A Case Of Git-UX · peppe.rs + +
+
+ Home + / + Posts + / + Curing A Case Of Git-UX + View Raw +
+
+ 03/09 — 2022 +
+ + 127.87 + + cm +   + + 9.5 + + min +
+
+

+ Curing A Case Of Git-UX +

+
+

Git worktrees are great, but they fall behind the venerable git checkout sometimes. I attempted to fix that with fzf and a bit of bash.

+

+

Fear not if you haven’t heard of “worktrees”, I have included a primer here.
+Skip the primer ->.

+

Why Worktrees?

+

Picture this. You are whacking away on a feature branch. Halfway there, in fact. Your friend asks you fix something urgently. You proceed to do one of three things:

+
    +
  • create a temporary branch, make a WIP commit, begin working on the fix
  • +
  • stash away your changes, begin working on the fix
  • +
  • unfriend said friend for disturbing your flow
  • +
+

All of these options are … subpar. With the temporary branch, you are forced to create a partial, non-working commit, and then reset said commit once done with the fix. With the stash approach, you are required to now keep a mental model of the stash, be aware of untracked files that don’t get stashed by default, etc. Why won’t git just let you work on two things at the same time without thinking so much?

+

That is exactly what worktrees let you do. Worktrees let you have more than one checkout at a time, each checkout in a separate directory. Like creating a new clone, but safer (it disallows checking out the same branch twice) and a lot more space efficient (the new working tree is “linked” to the “main” worktree, and a good amount of stuff is shared). When your friend asks you to make the fix, you proceed like so:

+
    +
  1. Create a new working tree with:
  2. +
+
# git worktree add -b <branch-name> <path> <from>
+git worktree add -b fix-stuff /path/to/tree master
+
    +
  1. cd into /path/to/tree
  2. +
  3. Fix, test, commit, push, party
  4. +
  5. Go back to your work, cd -
  6. +
+

Easy as cake. You didn’t have to settle for a partially working commit, you didn’t to deal with this “stash” thing, and you didn’t have to unfriend your friend. Treating each branch as a directory just feels more intuitive, more UNIX-y.

+

A few weeks later, you find yourself singing in praise of worktrees, working on several things simultaneously. And at the same time, cursing them for being a little … clunky.

+

What makes them clunky?

+

Worktrees are great at what they claim to do. They stay out of the way when you need a checkout posthaste. However, as you start using them regularly, you realize they are not as flexible as git checkout or git switch.

+

Branch-hopping

+

You can git checkout <branch> from anywhere within a git repository. You can’t “jump” to a worktree in the same fashion. The closest you can get, is to run git worktree list, copy the path corresponding to your branch, and cd into it.

+

Branch-hopping with the good ol’ git-checkout:

+
# anywhere, anytime
+λ git checkout feature/is-ascii-octdigit
+

Meanwhile, in worktree world:

+
# keeping these paths in your head is hard
+λ git worktree list
+~/worktrees/rustc/master                 eac6c33bc63 [master]
+~/worktrees/rustc/improve-std-char-docs  94cba88553e [improve-std-char-docs]
+~/worktrees/rustc/is-ascii-octdigit      bc57be3af7a [feature/is-ascii-octdigit]
+~/my/other/path/oh/god                   op57or3ns7n [fix/some-error]
+
+λ cd ~/worktrees/rustc/is-ascii-octdigit
+

Branch-previewing

+

You can “preview” branches with git branch -v. However, to get an idea of what “recent activity” on a worktree looks like, you might need some juggling. You can’t glean much info about a worktree in a jiffy.

+

Branch-previewing with the good ol’ git-branch:

+
λ git branch -v
++ feature/is-ascii-octdigit bc57be3af7a introduce {char, u8}::is_ ...
++ improve-std-char-docs     94cba88553e add whitespace in assert ...
+* master                    eac6c33bc63 Auto merge of #100869 - n ...
+

Meanwhile in worktree wonderland:

+
λ git worktree list
+~/worktrees/rustc/master                 eac6c33bc63 [master]
+~/worktrees/rustc/improve-std-char-docs  94cba88553e [improve-std-char-docs]
+~/worktrees/rustc/is-ascii-octdigit      bc57be3af7a [feature/is-ascii-octdigit]
+
+# aha, so ../is-ascii-octdigit corresponds to `feature/is-ascii-octdigit`
+λ git log feature/is-ascii-octdigit
+bc57be3af7a introduce {char, u8}::is_ascii_octdigit
+eac6c33bc63 Auto merge of #100869 - nnethercote:repl ...
+b32223fec10 Auto merge of #100707 - dzvon:fix-typo,  ...
+aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
+
+# extra work to make the branch <-> worktree correspondence
+

Shell completions

+

Lastly, you can bank on shell completions to fill in your branch whilst using git checkout. Worktrees have no such conveniences.

+

We can mend these minor faults with fzf.

+

Unclunkifying worktrees

+

I’d suggest looking up fzf (or skim or fzy). These things make it cake-easy to add interactivity to your shell. Onto fixing the first minor fault, the inability to “jump” to a worktree from anywhere within a git repository.

+

I have a little function called gwj which stands for “git worktree jump”. The idea is to list all the worktrees, select one with fzf, and cd to it upon selection:

+
gwj () {
+  local out
+  out=$(git worktree list | fzf | awk '{print $1}')
+  cd $out
+}
+

That is all of it really. Head into a git repository:

+
# here, "master" is a directory, which contains my main
+# worktree: a checkout of the master branch on rust-lang/rust 
+λ cd ~/worktrees/rustc/master/library/core/src
+λ # hack away
+

Preferably one with a few worktrees:

+
λ git worktree list
+~/worktrees/rustc/master                 eac6c33bc63 [master]
+~/worktrees/rustc/improve-std-char-docs  94cba88553e [improve-std-char-docs]
+~/worktrees/rustc/is-ascii-octdigit      bc57be3af7a [feature/is-ascii-octdigit]
+

And hit gwj (pretend that the pipe, |, is your cursor):

+
λ gwj
+> |
+  4/4
+> ~/worktrees/rustc/master                 eac6c33bc63 [master]
+  ~/worktrees/rustc/improve-std-char-docs  94cba88553e [improve-std-char-docs]
+  ~/worktrees/rustc/is-ascii-octdigit      bc57be3af7a [feature/is-ascii-octdigit]
+

Approximately type in your branch of choice:

+
λ gwj
+> docs|
+  4/4
+> ~/worktrees/rustc/improve-std-char-docs  94cba88553e [improve-std-char-docs]
+

And hit enter. You should find yourself in the selected worktree.

+

Onward, to the next fault, lack of preview-bility. We can utilize fzf’s aptly named --preview flag, to, well, preview our worktree before performing a selection:

+
gwj () {
+  local out
+  out=$(
+    git worktree list |
+    fzf --preview='git log --oneline -n10 {2}' |
+    awk '{print $1}'
+  )
+  cd $out
+}
+

Once again, hit gwj inside a git repository with linked worktrees:

+
λ gwj
+╭─────────────────────────────────────────────────────────╮
+ eac6c33bc63 Auto merge of 100869 nnethercote:replace... │
+ b32223fec10 Auto merge of 100707 dzvon:fix-typo, r=d... │
+ aa857eb953e Auto merge of 100537 petrochenkov:picche... │
+ 3892b7074da Auto merge of 100210 mystor:proc_macro_d... │
+ db00199d999 Auto merge of 101249 matthiaskrgr:rollup... │
+ 14d216d33ba Rollup merge of 101240 JohnTitor:JohnTit... │
+ 3da66f03531 Rollup merge of 101236 thomcc:winfs-noze... │
+ 0620f6e90af Rollup merge of 101230 davidtwco:transla... │
+ c30c42ee299 Rollup merge of 101229 mgeisler:link-try... │
+ e5356712b9e Rollup merge of 101165 ldm0:drain_to_ite... │
+╰─────────────────────────────────────────────────────────╯
+>
+  4/4
+> /home/np/worktrees/compiler/master                 eac6c...
+  /home/np/worktrees/compiler/improve-std-char-docs  94cba...
+  /home/np/worktrees/compiler/is-ascii-octdigit      bc57b...
+

A fancy preview of the last 10 commits on the branch that the selected worktree corresponds to. In other words, sight for sore eyes. Our little script is already shaping up to be useful, you hit gwj, browse through your worktrees, preview each one and automatically cd to your selection. But we are not done yet.

+

The last fault was lack shell completions. A quick review of what a shell completion really does:

+
λ git checkout f<tab>
+feature/is-ascii-octdigit
+fix/some-error
+format-doc-tests
+
+λ git checkout feat<tab>
+
+λ git checkout feature/is-ascii-octdigit
+

Each time you hit “tab”, the shell produces a few “completion candidates”, and once you have just a single candidate left, the shell inserts that for you directly into your edit line. Of course, this process varies from shell to shell.

+

fzf narrows down your options as you type into the prompt, but you still have to:

+
    +
  1. Type gwj
  2. +
  3. Hit enter
  4. +
  5. Type out a query and narrow down your search
  6. +
  7. Hit enter
  8. +
+

We can speed that up a bit, have fzf narrow down the candidates on startup, just like our shell does:

+
gwj () {
+  local out query
+  query="${1:- }"
+  out=$(
+    git worktree list |
+    fzf --preview='git log --oneline -n10 {2}' --query "$query" -1 |
+    awk '{print $1}'
+  )
+  cd $out
+}
+

The change is extremely tiny, blink-and-you’ll-miss-it kinda tiny. We added a little --query flag, that allows you to prefill the prompt, and the -1 flag, that avoids the interactive finder if only one match exists on startup:

+
# skip through the fzf prompt:
+λ gwj master
+# cd -- ~/worktrees/rustc/master
+
+# more than one option, we end up in the interactive finder
+λ gwj improve
+╭─────────────────────────────────────────────────────────╮
+ eac6c33bc63 Auto merge of 100869 nnethercote:replace... │
+ b32223fec10 Auto merge of 100707 dzvon:fix-typo, r=d... │
+ aa857eb953e Auto merge of 100537 petrochenkov:picche... │
+╰─────────────────────────────────────────────────────────╯
+> improve
+  2/2
+> /home/np/worktrees/compiler/improve-const-perf     eac6c...
+  /home/np/worktrees/compiler/improve-std-char-docs  94cba...
+

Throw some error handling in there, hook up a similar script to improve the UX of git worktree remove, go wild. A few more helpers I’ve got:

+
# gwa /path/to/branch-name
+# creates a new branch and "switches" to it
+function gwa () {
+  git worktree add "$1" && cd "$1"
+}
+
+alias gwls="git worktree list"
+ +
+ +
+ Hi. + +

I'm Akshay, I go by nerd or nerdypepper on the internet.

+

+ I am a compsci undergrad, Rust programmer and an enthusiastic Vimmer. + I write open-source stuff to pass time. + I also design fonts: + scientifica, + curie. +

+

Send me a mail at nerdy@peppe.rs or a message at nerdypepper@irc.rizon.net.

+
+ + Home + / + Posts + / + Curing A Case Of Git-UX + View Raw +
+
+ + diff --git a/docs/posts/index.html b/docs/posts/index.html index c211d3b..48452c0 100644 --- a/docs/posts/index.html +++ b/docs/posts/index.html @@ -24,6 +24,23 @@
+ + + + +
+
+ 03/09 — 2022 +
+ + Curing A Case Of Git-UX + +
+ + 9.5 + + min +
diff --git a/posts/curing_a_case_of_git-UX.md b/posts/curing_a_case_of_git-UX.md new file mode 100644 index 0000000..556df1b --- /dev/null +++ b/posts/curing_a_case_of_git-UX.md @@ -0,0 +1,320 @@ +Git worktrees are great, but they fall behind the venerable +`git checkout` sometimes. I attempted to fix that with +[fzf](https://github.com/junegunn/fzf) and +a bit of bash. + +[![](https://asciinema.org/a/D297ztKRzpE4gAHbPTPmkqYps.svg)](https://asciinema.org/a/D297ztKRzpE4gAHbPTPmkqYps) + +Fear not if you haven't heard of "worktrees", I have +included a primer here. +[Skip the primer +->](#what-makes-them-clunky). + +### Why Worktrees? + +Picture this. You are whacking away on a feature branch. +Halfway there, in fact. Your friend asks you fix something +urgently. You proceed to do one of three things: + +- create a temporary branch, make a WIP commit, begin + working on the fix +- stash away your changes, begin working on the fix +- unfriend said friend for disturbing your flow + +All of these options are ... subpar. With the temporary +branch, you are forced to create a partial, non-working +commit, and then reset said commit once done with the fix. +With the stash approach, you are required to now keep a +mental model of the stash, be aware of untracked files that +don't get stashed by default, etc. Why won't git just let +you work on two things at the same time without _thinking_ +so much? + +That is exactly what worktrees let you do. Worktrees let you +have more than one checkout at a time, each checkout in a +separate directory. Like creating a new clone, but safer (it +disallows checking out the same branch twice) and a lot more +space efficient (the new working tree is "linked" to the +"main" worktree, and a good amount of stuff is shared). When +your friend asks you to make the fix, you proceed like so: + +1. Create a new working tree with: + ```bash + # git worktree add -b + git worktree add -b fix-stuff /path/to/tree master + ``` +2. `cd` into `/path/to/tree` +3. Fix, test, commit, push, party +4. Go back to your work, `cd -` + +Easy as cake. You didn't have to settle for a partially +working commit, you didn't to deal with this "stash" thing, +_and_ you didn't have to unfriend your friend. Treating each +branch as a directory just _feels_ more intuitive, more +UNIX-y. + +A few weeks later, you find yourself singing in praise of +worktrees, working on several things simultaneously. And at +the same time, cursing them for being a little ... clunky. + +### What makes them clunky? + +Worktrees are great at what they claim to do. They stay out +of the way when you need a checkout posthaste. However, as +you start using them regularly, you realize they are not as +flexible as `git checkout` or `git switch`. + +#### Branch-hopping +You can `git checkout ` from anywhere within a git +repository. You can't "jump" to a worktree in the same +fashion. The closest you can get, is to run `git worktree +list`, copy the path corresponding to your branch, and `cd` +into it. + +Branch-hopping with the good ol' git-checkout: +```bash +# anywhere, anytime +λ git checkout feature/is-ascii-octdigit +``` + +Meanwhile, in worktree world: +```bash +# keeping these paths in your head is hard +λ git worktree list +~/worktrees/rustc/master eac6c33bc63 [master] +~/worktrees/rustc/improve-std-char-docs 94cba88553e [improve-std-char-docs] +~/worktrees/rustc/is-ascii-octdigit bc57be3af7a [feature/is-ascii-octdigit] +~/my/other/path/oh/god op57or3ns7n [fix/some-error] + +λ cd ~/worktrees/rustc/is-ascii-octdigit +``` + +#### Branch-previewing + +You can "preview" branches with `git branch -v`. However, to +get an idea of what "recent activity" on a worktree looks +like, you might need some juggling. You can't glean much +info about a worktree in a jiffy. + +Branch-previewing with the good ol' git-branch: +```bash +λ git branch -v ++ feature/is-ascii-octdigit bc57be3af7a introduce {char, u8}::is_ ... ++ improve-std-char-docs 94cba88553e add whitespace in assert ... +* master eac6c33bc63 Auto merge of #100869 - n ... +``` + +Meanwhile in worktree wonderland: +``` +λ git worktree list +~/worktrees/rustc/master eac6c33bc63 [master] +~/worktrees/rustc/improve-std-char-docs 94cba88553e [improve-std-char-docs] +~/worktrees/rustc/is-ascii-octdigit bc57be3af7a [feature/is-ascii-octdigit] + +# aha, so ../is-ascii-octdigit corresponds to `feature/is-ascii-octdigit` +λ git log feature/is-ascii-octdigit +bc57be3af7a introduce {char, u8}::is_ascii_octdigit +eac6c33bc63 Auto merge of #100869 - nnethercote:repl ... +b32223fec10 Auto merge of #100707 - dzvon:fix-typo, ... +aa857eb953e Auto merge of #100537 - petrochenkov:pic ... + +# extra work to make the branch <-> worktree correspondence +``` + +#### Shell completions + +Lastly, you can bank on shell completions to fill in your +branch whilst using `git checkout`. Worktrees have no such +conveniences. + +We can mend these minor faults with fzf. + +### Unclunkifying worktrees + +I'd suggest looking up +[fzf](https://github.com/junegunn/fzf) (or +[skim](https://github.com/lotabout/skim) or +[fzy](https://github.com/jhawthorn/fzy)). These things make +it cake-easy to add interactivity to your shell. Onto fixing +the first minor fault, the inability to "jump" to a worktree +from anywhere within a git repository. + +I have a little function called `gwj` which stands for "git +worktree jump". The idea is to list all the worktrees, +select one with fzf, and `cd` to it upon selection: + +```bash +gwj () { + local out + out=$(git worktree list | fzf | awk '{print $1}') + cd $out +} +``` + +That is all of it really. Head into a git repository: + +```bash +# here, "master" is a directory, which contains my main +# worktree: a checkout of the master branch on rust-lang/rust +λ cd ~/worktrees/rustc/master/library/core/src +λ # hack away +``` + +Preferably one with a few worktrees: + +```bash +λ git worktree list +~/worktrees/rustc/master eac6c33bc63 [master] +~/worktrees/rustc/improve-std-char-docs 94cba88553e [improve-std-char-docs] +~/worktrees/rustc/is-ascii-octdigit bc57be3af7a [feature/is-ascii-octdigit] +``` + +And hit `gwj` (pretend that the pipe, |, is your cursor): + +```bash +λ gwj +> | + 4/4 +> ~/worktrees/rustc/master eac6c33bc63 [master] + ~/worktrees/rustc/improve-std-char-docs 94cba88553e [improve-std-char-docs] + ~/worktrees/rustc/is-ascii-octdigit bc57be3af7a [feature/is-ascii-octdigit] +``` + +Approximately type in your branch of choice: + +```bash +λ gwj +> docs| + 4/4 +> ~/worktrees/rustc/improve-std-char-docs 94cba88553e [improve-std-char-docs] +``` + +And hit enter. You should find yourself in the selected +worktree. + +Onward, to the next fault, lack of preview-bility. We can +utilize fzf's aptly named `--preview` flag, to, well, +preview our worktree before performing a selection: + +```bash +gwj () { + local out + out=$( + git worktree list | + fzf --preview='git log --oneline -n10 {2}' | + awk '{print $1}' + ) + cd $out +} +``` + +Once again, hit `gwj` inside a git repository with linked worktrees: + +```bash +λ gwj +╭─────────────────────────────────────────────────────────╮ +│ eac6c33bc63 Auto merge of 100869 nnethercote:replace... │ +│ b32223fec10 Auto merge of 100707 dzvon:fix-typo, r=d... │ +│ aa857eb953e Auto merge of 100537 petrochenkov:picche... │ +│ 3892b7074da Auto merge of 100210 mystor:proc_macro_d... │ +│ db00199d999 Auto merge of 101249 matthiaskrgr:rollup... │ +│ 14d216d33ba Rollup merge of 101240 JohnTitor:JohnTit... │ +│ 3da66f03531 Rollup merge of 101236 thomcc:winfs-noze... │ +│ 0620f6e90af Rollup merge of 101230 davidtwco:transla... │ +│ c30c42ee299 Rollup merge of 101229 mgeisler:link-try... │ +│ e5356712b9e Rollup merge of 101165 ldm0:drain_to_ite... │ +╰─────────────────────────────────────────────────────────╯ +> + 4/4 +> /home/np/worktrees/compiler/master eac6c... + /home/np/worktrees/compiler/improve-std-char-docs 94cba... + /home/np/worktrees/compiler/is-ascii-octdigit bc57b... +``` + +A fancy preview of the last 10 commits on the branch that +the selected worktree corresponds to. In other words, sight +for sore eyes. Our little script is already shaping up to be +useful, you hit `gwj`, browse through your worktrees, +preview each one and automatically `cd` to your selection. +But we are not done yet. + +The last fault was lack shell completions. A quick review of +what a shell completion really does: + +```bash +λ git checkout f +feature/is-ascii-octdigit +fix/some-error +format-doc-tests + +λ git checkout feat + +λ git checkout feature/is-ascii-octdigit +``` + +Each time you hit "tab", the shell produces a few +"completion candidates", and once you have just a single +candidate left, the shell inserts that for you directly into +your edit line. Of course, this process varies from shell to +shell. + +fzf narrows down your options as you type into the prompt, +but you still have to: + +1. Type `gwj` +2. Hit enter +3. Type out a query and narrow down your search +4. Hit enter + +We can speed that up a bit, have fzf narrow down the +candidates on startup, just like our shell does: + +```bash +gwj () { + local out query + query="${1:- }" + out=$( + git worktree list | + fzf --preview='git log --oneline -n10 {2}' --query "$query" -1 | + awk '{print $1}' + ) + cd $out +} +``` + +The change is extremely tiny, blink-and-you'll-miss-it kinda +tiny. We added a little `--query` flag, that allows you to +prefill the prompt, and the `-1` flag, that avoids the +interactive finder if only one match exists on startup: + +```bash +# skip through the fzf prompt: +λ gwj master +# cd -- ~/worktrees/rustc/master + +# more than one option, we end up in the interactive finder +λ gwj improve +╭─────────────────────────────────────────────────────────╮ +│ eac6c33bc63 Auto merge of 100869 nnethercote:replace... │ +│ b32223fec10 Auto merge of 100707 dzvon:fix-typo, r=d... │ +│ aa857eb953e Auto merge of 100537 petrochenkov:picche... │ +╰─────────────────────────────────────────────────────────╯ +> improve + 2/2 +> /home/np/worktrees/compiler/improve-const-perf eac6c... + /home/np/worktrees/compiler/improve-std-char-docs 94cba... +``` + +Throw some error handling in there, hook up a similar script +to improve the UX of `git worktree remove`, go wild. A few +more helpers I've got: + +```bash +# gwa /path/to/branch-name +# creates a new branch and "switches" to it +function gwa () { + git worktree add "$1" && cd "$1" +} + +alias gwls="git worktree list" +``` -- cgit v1.2.3