<?xml version="1.0" encoding="utf-8"?>
  <feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jeff Kreeftmeijer</title>
  <link href="https://jeffkreeftmeijer.com/feed.xml" rel="self"/>
  <link href="https://jeffkreeftmeijer.com/"/>
  <updated>2021-08-16T20:39:11.383Z</updated>
  <id>http://jeffkreeftmeijer.com/</id>
  <author>
    <name>Jeff Kreeftmeijer</name>
  </author>
  <entry>
    <title type="html">Emacs package management with straight.el and use-package</title>
    <link href="https://jeffkreeftmeijer.com/emacs-straight-use-package/"/>
    <id>https://jeffkreeftmeijer.com/emacs-straight-use-package/</id>
    <published>2021-08-16T00:00:00.000Z</published>
    <updated>2021-08-16T20:34:44.000Z</updated>
    <summary>Use straight.el and use-package for reproducable, performant and cleanly-configured package management in Emacs</summary>
    <content type="html"><![CDATA[
<p>
Emacs includes a package manager named <code>package.el</code>, which installs packages from the official Emacs Lisp Package Archive, named <a href="https://elpa.gnu.org">GNU ELPA.</a>
GNU ELPA hosts a selection of packages, but most are available on <a href="https://melpa.org">MELPA</a>, which is an unofficial package archive that implements the ELPA specification. To use MELPA, it has to be <a href="https://melpa.org/#/getting-started">installed</a> by adding it to the list of <code>package.el</code> package archives.
</p>
<p>
The built-in package manager installs packages through the <code>package-install</code> function. For example, to install the “evil-commentary” package from MELPA, call <code>package-install</code> inside Emacs:
</p>
<p>
<code>M-x</code> <code>package-install</code> <code>&lt;RET&gt;</code> <code>evil-commentary</code> <code>&lt;RET&gt;</code>
</p>
<h2 id="org6ff474a">Straight.el</h2>
<p>
<a href="https://github.com/raxod502/straight.el">Straight.el</a> is an alternative package manager that installs packages through Git checkouts instead of downloading tarballs from one of the package archives. Doing so allows installing forked packages, altering local package checkouts, and locking packages to exact versions for reproducable setups.
</p>
<h3 id="orge5091e1">Installation</h3>
<p>
The <a href="https://github.com/raxod502/straight.el#getting-started">Getting started</a> section in the straight.el README provides the bootstrap code to place inside <code>~/.emacs.d/init.el</code> in order to install it:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 1: </span><code>init.el</code>, installing straight.el</label><pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Install straight.el</span>
(<span class="org-keyword">defvar</span> <span class="org-variable-name">bootstrap-version</span>)
(<span class="org-keyword">let</span> ((bootstrap-file
       (expand-file-name <span class="org-string">"straight/repos/straight.el/bootstrap.el"</span> user-emacs-directory))
      (bootstrap-version 5))
  (<span class="org-keyword">unless</span> (file-exists-p bootstrap-file)
    (<span class="org-keyword">with-current-buffer</span>
        (url-retrieve-synchronously
         <span class="org-string">"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"</span>
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))
</pre>
</div>
<p>
Straight.el uses package archives like GNU ELPA as registries to find the linked repositories to clone from. Since these are checked automatically, there’s no need to add them to the list of package archives.
</p>
<p>
While package.el loads all installed packages on startup, straight.el only loads packages that are referenced in the init file. This allows for installing packages temporarily without slowing down Emacs’ startup time on subsequent startups.
</p>
<p>
To create a truly reproducable setup, disable package.el in favor of straight.el by turning off <code>package-enable-at-startup</code>. Because this step needs to happen before package.el gets a chance to load packages, it this configuration needs to be set in the early init file:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 2: </span><code>early-init.el</code>, disabling package.el</label><pre class="src src-emacs-lisp" id="org6f8d1b6"><span class="org-comment-delimiter">;; </span><span class="org-comment">Disable package.el in favor of straight.el</span>
(<span class="org-keyword">setq</span> package-enable-at-startup nil)
</pre>
</div>
<p>
With this configuration set, Emacs will only load the packages installed through straight.el.
</p>
<h3 id="orge87d2e6">Usage</h3>
<p>
To use straight.el to install a package for the current session, execute the <code>straight-use-package</code> command:
</p>
<p>
<code>M-x</code> <code>straight-use-package</code> <code>&lt;RET&gt;</code> <code>evil-commentary</code> <code>&lt;RET&gt;</code>
</p>
<p>
To continue using the package in future sessions, add the <code>straight-use-package</code> call to <code>~/.emacs/init.el</code>:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(straight-use-package 'evil-commentary)
</pre>
</div>
<p>
To update an installed package, execute the <code>straight-pull-package</code> command:
</p>
<p>
<code>M-x</code> <code>straight-pull-package</code> <code>&lt;RET&gt;</code> <code>evil-commentary</code> <code>&lt;RET&gt;</code>
</p>
<p>
To update the version lockfile, which is used to target the exact version to check out when installing, run <code>straight-freeze-versions</code>:
</p>
<p>
<code>M-x</code> <code>straight-freeze-versions</code> <code>&lt;RET&gt;</code>
</p>
<h2 id="org9d3d0f3">Use-package</h2>
<p>
<a href="https://github.com/jwiegley/use-package">Use-package</a> is a macro to configure and load packages in Emacs configurations. It interfaces with package managers like package.el or straight.el to install packages, but is not a package manager by itself.
</p>
<p>
For example, when using straight.el without use-package, installing and starting evil-commentary requires installing the package and starting it as two separate steps:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(straight-use-package 'evil-commentary)
(evil-commentary-mode)
</pre>
</div>
<p>
<a href="#orgb8dadd0">Combined with use-package</a>, the installation and configuration are unified into a single call to <code>use-package</code>:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(use-package evil-commentary
             <span class="org-builtin">:config</span> (evil-commentary-mode))
</pre>
</div>
<p>
Aside from keeping configuration files tidy, having package configuration contained within a single call allows for more advanced package setups. For example, packages can be lazy-loaded, keeping their configuration code from executing until the package they configure is needed.
</p>
<h3 id="orgc7325ca">Installation</h3>
<p>
To install use-package with straight.el, use <code>straight-use-package</code>:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 3: </span><code>init.el</code>, installing use-package</label><pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Install use-package</span>
(straight-use-package 'use-package)
</pre>
</div>
<h3 id="orge8966b0">Using straight.el with use-package</h3>
<p>
By default, use-package uses package.el to install packages. To use straight.el instead of package.el, pass the <code>:straight</code> option:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(use-package evil-commentary
             <span class="org-builtin">:straight</span> t)
</pre>
</div>
<p>
To configure use-package to always use straight.el, use <code>use-package</code> to configure straight.el to turn on <code>straight-use-package-by-default</code><sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 4: </span><code>init.el</code>, configuring straight.el</label><pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Configure use-package to use straight.el by default</span>
(use-package straight
             <span class="org-builtin">:custom</span> (straight-use-package-by-default t))
</pre>
</div>
<p>
Now, installing any package using use-package uses straight.el, even when omitting the <code>:straight.el</code> option.
</p>
<p>
Having both straight.el and use-package installed and configured to work together, the <code>straight-use-package</code> function isn’t used anymore. Instead, all packages are installed and configured through use-package.
</p>
<h3 id="org95d5b38">Usage</h3>
<p>
Use the <code>use-package</code> macro to load a package. If the package is not installed yet, it is installed automatically:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(use-package evil-commentary)
</pre>
</div>
<p>
Use-package provides keywords to add configuration, key bindings and variables. Although there are <a href="https://github.com/jwiegley/use-package#getting-started/">many more options</a>, some examples include <code>:config</code>, <code>:init</code>, <code>:bind</code>, and <code>:custom</code>:
</p>
<dl class="org-dl">
<dt><code>:config</code> and <code>:init</code></dt><dd><p>
The <code>:config</code> and <code>:init</code> configuration keywords define code that’s run right after, or right before a package is loaded, respectively.
</p>
<p>
For example, call <code>evil-mode</code> from the <code>:config</code> keyword to start Evil after loading its package. To <a href="/emacs-evil-org-tab/">turn off <code>evil-want-C-i-jump</code></a> right before evil is loaded (instead of adding it to the early init file), configure it in the <code>:init</code> keyword:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(use-package evil
             <span class="org-builtin">:init</span> (<span class="org-keyword">setq</span> evil-want-C-i-jump nil)
             <span class="org-builtin">:config</span> (evil-mode))
</pre>
</div></dd>
<dt><code>:bind</code></dt><dd><p>
Adds key bindings after a module is loaded. For example, to use <code>consult-buffer</code> instead of the built-in <code>switch-to-buffer</code> after loading the consult package, add a binding through the <code>:bind</code> keyword:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(use-package consult
             <span class="org-builtin">:bind</span> (<span class="org-string">"C-x b"</span> . consult-buffer)
</pre>
</div></dd>
<dt><code>:custom</code></dt><dd><p>
Sets customizable variables. The variables set through use-package are not saved in Emacs’ custom file. Instead, all custom variables are expected to be set through use-package. In an example from before, the <code>:custom</code> keyword is used to set the <code>straight-use-package-by-default</code> configuration option after loading straight.el:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(use-package straight
             <span class="org-builtin">:custom</span> (straight-use-package-by-default t))
</pre>
</div></dd>
</dl>
<h2 id="org06cc077">Summary</h2>
<p>
The resulting <code>~/.emacs.d/init.el</code> file installs straight.el and use-package, and configures straight.el as the package manager for use-package to use:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 5: </span><code>init.el</code>, the package management section</label><pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Install straight.el</span>
(<span class="org-keyword">defvar</span> <span class="org-variable-name">bootstrap-version</span>)
(<span class="org-keyword">let</span> ((bootstrap-file
       (expand-file-name <span class="org-string">"straight/repos/straight.el/bootstrap.el"</span> user-emacs-directory))
      (bootstrap-version 5))
  (<span class="org-keyword">unless</span> (file-exists-p bootstrap-file)
    (<span class="org-keyword">with-current-buffer</span>
        (url-retrieve-synchronously
         <span class="org-string">"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"</span>
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

<span class="org-comment-delimiter">;; </span><span class="org-comment">Install use-package</span>
(straight-use-package 'use-package)

<span class="org-comment-delimiter">;; </span><span class="org-comment">Configure use-package to use straight.el by default</span>
(use-package straight
             <span class="org-builtin">:custom</span> (straight-use-package-by-default t))
</pre>
</div>
<p>
The <code>~/.emacs.d/early-init.el</code> file disables package.el to disable its auto-loading, causing all packages to be loaded through straight.el in the init file:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 6: </span><code>early-init.el</code>, disabling package.el</label><pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Disable package.el in favor of straight.el</span>
(<span class="org-keyword">setq</span> package-enable-at-startup nil)
</pre>
</div>
<p>
This is the only configuration set in the early init file. All other packages are installed and configured through use-package, which makes sure to load configuration options before packages are loaded, if configured with the <code>:init</code> keyword.
</p>
<hr>
<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
Calling <code>use-package</code> would normally install straight.el, but since it’s already installed, the installation is skipped and the configuration is set. Here, the call to <code>use-package</code> is only used to configure straight.el, by setting the <code>straight-use-package-by-default</code> option.
</p></div></div>
]]></content>
  </entry>
  <entry>
    <title type="html">Combine Git repositories with unrelated histories</title>
    <link href="https://jeffkreeftmeijer.com/git-combine/"/>
    <id>https://jeffkreeftmeijer.com/git-combine/</id>
    <published>2021-08-09T00:00:00.000Z</published>
    <updated>2021-08-09T06:18:32.000Z</updated>
    <summary>To combine two separate Git repositories into one, merge while using the --allow-unrelated-histories command line option.</summary>
    <content type="html"><![CDATA[
<p>
To combine two separate Git repositories into one, add the repository to merge <i>in</i> as a remote to the repository to merge <i>into</i>. Then, combine their histories by merging while using the <code>--allow-unrelated-histories</code> command line option.
</p>
<p>
As an example, there are two repositories that both have a single root commit. Running <code>git log</code> produces the log for the first repository, as that repository’s directory is the current directory:
</p>
<div class="org-src-container">
<pre class="src src-shell">git log
</pre>
</div>
<pre class="example">
commit 733ed008d41505623d6f3b5dbee7d1841a959c34
Author: Alice &lt;alice@example.com&gt;
Date:   Sun Aug 8 20:12:38 2021 +0200

    Add one.txt
</pre>
<p>
To get the logs for the second repository, pass a path to that repository’s <code>.git</code> directory to the <code>git log</code> command via the <code>--git-dir</code> command line option:
</p>
<div class="org-src-container">
<pre class="src src-shell">git --git-dir=../two/.git log
</pre>
</div>
<pre class="example">
commit 43d2970b4dafa21477e1a5a86cad45bc5e798696
Author: Bob &lt;bob@example.com&gt;
Date:   Sun Aug 8 20:12:44 2021 +0200

    Add two.txt
</pre>
<p>
To combine the two repositories, first add the second repository as a remote to the first. Then, run <code>git fetch</code> to fetch its branch information:
</p>
<div class="org-src-container">
<pre class="src src-shell">git remote add two ../two
git fetch two
</pre>
</div>
<p>
Then merge, with the remote set up, merge the second repository’s history into the first by using the <code>--allow-unrelated-histories</code> flag:
</p>
<div class="org-src-container">
<pre class="src src-shell">git merge two/main --allow-unrelated-histories
</pre>
</div>
<pre class="example">
Merge made by the 'recursive' strategy.
 two.txt | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 two.txt
</pre>
<p>
Unlike <a href="https://jeffkreeftmeijer.com/git-extract/">extracting part of a repository</a>&#x2014;which rewrites history by definition&#x2014;combining repositories is done by <i>merging</i> their two histories. This means the commits from the second repository are appended to the ones from the first, retaining history:
</p>
<div class="org-src-container">
<pre class="src src-shell">git log
</pre>
</div>
<pre class="example">
commit 0bd5727a597077b5a5db9590b3f6aaf70eb10b60
Merge: 733ed00 43d2970
Author: Alice &lt;alice@example.com&gt;
Date:   Sun Aug 8 20:13:01 2021 +0200

    Merge remote-tracking branch 'two/main' into main

commit 43d2970b4dafa21477e1a5a86cad45bc5e798696
Author: Bob &lt;bob@example.com&gt;
Date:   Sun Aug 8 20:12:44 2021 +0200

    Add two.txt

commit 733ed008d41505623d6f3b5dbee7d1841a959c34
Author: Alice &lt;alice@example.com&gt;
Date:   Sun Aug 8 20:12:38 2021 +0200

    Add one.txt
</pre>
<h2 id="references">References</h2>
<ul class="org-ul">
<li><p>
<a href="https://git-scm.com/docs/git-merge">https://git-scm.com/docs/git-merge</a>:
</p>
<blockquote>
<p>
By default, <code>git merge</code> command refuses to merge histories that do not share a common ancestor. [The <code>--allow-unrelated-histories</code>​] option can be used to override this safety when merging histories of two projects that started their lives independently.
</p>
</blockquote></li>
</ul>
]]></content>
  </entry>
  <entry>
    <title type="html">Convert documents from AsciiDoc to Org</title>
    <link href="https://jeffkreeftmeijer.com/asciidoc-to-org/"/>
    <id>https://jeffkreeftmeijer.com/asciidoc-to-org/</id>
    <published>2021-08-06T00:00:00.000Z</published>
    <updated>2021-08-06T17:04:35.000Z</updated>
    <summary>To convert an AsciiDoc document to an Org document, convert to Docbook as an intermediate step, then send the Docbook file to Pandoc to convert it to Org.</summary>
    <content type="html"><![CDATA[
<p>
To convert an AsciiDoc document to an Org document, convert to Docbook as an intermediate step, then send the Docbook file to Pandoc to convert it to Org:
</p>
<div class="org-src-container">
<pre class="src src-shell">asciidoctor <span class="org-sh-escaped-newline">\</span>
    --backend docbook <span class="org-sh-escaped-newline">\</span>
    --out-file - <span class="org-sh-escaped-newline">\</span>
    hello.adoc | <span class="org-sh-escaped-newline">\</span>
    pandoc <span class="org-sh-escaped-newline">\</span>
        --from docbook <span class="org-sh-escaped-newline">\</span>
        --wrap=preserve <span class="org-sh-escaped-newline">\</span>
        --standalone <span class="org-sh-escaped-newline">\</span>
        --output hello.org
</pre>
</div>
<p>
This command pipes two commands together to produce the desired result:
</p>
<dl class="org-dl">
<dt><code>asciidoctor --backend docbook --out-file - hello.adoc</code></dt><dd>Take the <code>hello.adoc</code> file and convert it to Docbook. Pass <code>-</code> as the <code>--out-file</code> to return the output to standard output</dd>
<dt><code>pandoc --from-docbook --wrap=preserve --standalone --output hello.org</code></dt><dd>Take the input file and convert it from Dobook to Org. Preserve the line wrapping instead of wrapping each line to 80 characters, and create a “standalone” document including document headers, as opposed to a document “fragment”.</dd>
</dl>
<p>
Given an AsciiDoc file:
</p>
<div class="org-src-container">
<pre class="src src-asciidoc">= Hello, World!
Alice
2021-08-06

Hello, World!

[source,ruby]
----
  puts "Hello, World!"
----
</pre>
</div>
<p>
The command above produces an Org file including the document headers, contents, and code blocks:
</p>
<pre class="example">
#+title: Hello, World!

#+author: Alice
#+date: 2021-08-06

Hello, World!

#+begin_src ruby
    puts Hello, World!
#+end_src
</pre>
<hr>
<ul class="org-ul">
<li><p>
<a href="https://lists.gnu.org/archive/html/emacs-orgmode/2018-02/msg00295.html">https://lists.gnu.org/archive/html/emacs-orgmode/2018-02/msg00295.html</a>:
</p>
<blockquote>
<p>
I have a large document (a book) written in AsciiDoc, and I’ve been thinking of converting it to org-mode, which I find eminently more readable. The method I’ve come up with is:
</p>
<ol class="org-ol">
<li>AsciiDoc -&gt; Docbook using asciidoc or asciidoctor</li>
<li>Docbook -&gt; org using pandoc</li>
</ol>
</blockquote></li>
<li><p>
<a href="https://pandoc.org/MANUAL.html#using-pandoc">https://pandoc.org/MANUAL.html#using-pandoc</a>:
</p>
<blockquote>
<p>
By default, pandoc produces a document fragment. To produce a standalone document (e.g. a valid HTML file including <code>&lt;head&gt;</code> and <code>&lt;body&gt;</code>), use the <code>-s</code> or <code>--standalone</code> flag:
</p>
<div class="org-src-container">
<pre class="src src-shell">pandoc -s -o output.html input.txt
</pre>
</div>
</blockquote></li>
</ul>
]]></content>
  </entry>
  <entry>
    <title type="html">Org-mode code blocks with long-running processes</title>
    <link href="https://jeffkreeftmeijer.com/org-code-timeout/"/>
    <id>https://jeffkreeftmeijer.com/org-code-timeout/</id>
    <published>2021-08-05T00:00:00.000Z</published>
    <updated>2021-08-05T21:14:48.000Z</updated>
    <summary>Use the timeout utility to include long-running process output in Org code blocks.</summary>
    <content type="html"><![CDATA[
<p>
When exporting an Org document to HTML, Org-mode’s exporter can run shell commands and print their output to the exported page.
</p>
<p>
For example, when writing an article about Ruby, it might be useful to show how to start a web server and display the output printed to the command line:
</p>
<div class="org-src-container">
<pre class="src src-shell">ruby server.rb
</pre>
</div>
<pre class="example">
Server running on http://localhost:4567...
</pre>
<p>
In the Org file, that code block might look like this:
</p>
<div class="org-src-container">
<pre class="src src-org">Finally, start the server by running <span class="org-org-verbatim">=ruby server.rb=</span>:

<span class="org-org-meta-line">#+header: :exports both</span>
<span class="org-org-meta-line">#+header: :results output</span>
<span class="org-org-block-begin-line">#+begin_src shell</span>
<span class="org-org-block">  ruby server.rb</span>
<span class="org-org-block-end-line">#+end_src</span>
</pre>
</div>
<p>
This example includes the command to start the server (<code>ruby server.rb</code>) and sets the <code>:exports</code> header to export both the input command and the results of running it.
</p>
<p>
However, because the server is a long-running process, exporting this document causes the exporter to hang. It’s waiting for a result from the command which never comes because the server remains open to connections until it’s quit.<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>
</p>
<p>
To start a long-running process and quit it after some time has passed on the command line, use the <code>timeout</code> utility to automatically kill the process after a predefined delay:
</p>
<div class="org-src-container">
<pre class="src src-shell">timeout 1 ruby server.rb; <span class="org-builtin">echo</span> <span class="org-string">"Done!"</span>
</pre>
</div>
<pre class="example">
Server running on http://localhost:4567...
Done!
</pre>
<p>
To translate this to an Org code block, use a <a href="/org-code-setup-teardown/"><code>:prologue</code> and <code>:epilogue</code></a> to add the timeout without showing it in the exported file. The <code>:prologue</code> prefixes the command with the call to the <code>timeout</code> utility and the <code>:epilogue</code> appends <code>:</code> to the command to make sure the block returns a zero exit code:
</p>
<div class="org-src-container">
<pre class="src src-org">Finally, start the server by running <span class="org-org-verbatim">=ruby server.rb=</span>:

<span class="org-org-meta-line">#+header: :exports both</span>
<span class="org-org-meta-line">#+header: :results output</span>
<span class="org-org-meta-line">#+header: :prologue timeout 1</span>
<span class="org-org-meta-line">#+header: :epilogue ":"</span>
<span class="org-org-block-begin-line">#+begin_src shell</span>
<span class="org-org-block">  ruby server.rb</span>
<span class="org-org-block-end-line">#+end_src</span>
</pre>
</div>
<p>
Exporting this example starts the server, then waits for a second before quitting the process. Then, both the input block&#x2014;which shows the command to start the server&#x2014;and the output block&#x2014;showing the server’s output&#x2014;are exported to the HTML document:
</p>
<div class="org-src-container">
<pre class="src src-shell">ruby server.rb
</pre>
</div>
<pre class="example">
Server running on http://localhost:4567...
</pre>
<hr>
<ul class="org-ul">
<li><a href="https://unix.stackexchange.com/a/483903">https://unix.stackexchange.com/a/483903</a></li>
</ul>
<hr>
<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
To stop a stuck foreground process in Emacs (like the exporter that accidentally called a slow or continuous program), run <code>C-g</code> to send an exit signal to the code that’s being executed.
</p></div></div>
]]></content>
  </entry>
  <entry>
    <title type="html">Org-mode code block setup and teardown</title>
    <link href="https://jeffkreeftmeijer.com/org-code-setup-teardown/"/>
    <id>https://jeffkreeftmeijer.com/org-code-setup-teardown/</id>
    <published>2021-08-04T00:00:00.000Z</published>
    <updated>2021-08-04T21:49:28.000Z</updated>
    <summary>Use Org-mode's :prologue and :epilogue header arguments for code block setup and teardown.</summary>
    <content type="html"><![CDATA[
<p>
Org-mode helps keep code samples up to date by actually running them during exporting. Instead of copying a command with its output from the terminal to the document, include only the command. Then, capture its actual output by running the command during the conversion to an output format like HTML.
</p>
<p>
For example, to show the output of the <code>cat</code> command, use a <code>shell</code> code block<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>:
</p>
<div class="org-src-container">
<pre class="src src-org"><span class="org-org-block-begin-line">#+begin_src shell :exports both :results output</span>
<span class="org-org-block">  cat hello.txt</span>
<span class="org-org-block-end-line">#+end_src</span>
</pre>
</div>
<p>
Exporting this example to HTML produces a document with two code blocks in <code>&lt;pre&gt;</code> tags. The first shows the command from the input code block (<code>cat hello.txt</code>), and the second shows the results from running the command:
</p>
<div class="org-src-container">
<pre class="src src-shell">cat hello.txt
</pre>
</div>
<pre class="example">
Hello, World!
</pre>
<h2 id="orgf1a5b9b">Setup and teardown</h2>
<p>
Use the <code>:prologue</code> and <code>:epilogue</code> header arguments to prepare for code blocks to be run, without printing the setup and teardown commands to the exported file. For example, if the file to be read doesn’t exist, create it right before executing the code in the code block, and remove it after<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>:
</p>
<div class="org-src-container">
<pre class="src src-org"><span class="org-org-meta-line">#+header: :exports both</span>
<span class="org-org-meta-line">#+header: :results output</span>
<span class="org-org-meta-line">#+header: :prologue "echo 'Hello, new file!' &gt;&gt; new.txt"</span>
<span class="org-org-meta-line">#+header: :epilogue "rm new.txt"</span>
<span class="org-org-block-begin-line">#+begin_src shell </span>
<span class="org-org-block">  cat new.txt</span>
<span class="org-org-block-end-line">#+end_src</span>
</pre>
</div>
<p>
Like before, this produces the both the input source block, and the results block with the contents of the file:
</p>
<div class="org-src-container">
<pre class="src src-shell">cat new.txt
</pre>
</div>
<pre class="example">
Hello, new file!
</pre>
<h2 id="org4e4c89a">Results with errors</h2>
<p>
Showing that a command fails by printing its error message is sometimes useful to illustrate a point.
</p>
<p>
Org’s exporter captures the command’s standard output stream to print to the output block, but it doesn’t capture standard error. For example, if the passed file does not exist, <code>cat</code> returns an error. The exporter will not print the output block for the following example:
</p>
<div class="org-src-container">
<pre class="src src-org"><span class="org-org-meta-line">#+header: :exports both</span>
<span class="org-org-meta-line">#+header: :results output</span>
<span class="org-org-block-begin-line">#+begin_src shell</span>
<span class="org-org-block">  cat ghost.txt</span>
<span class="org-org-block-end-line">#+end_src</span>
</pre>
</div>
<p>
Since <code>ghost.txt</code> does not exist, the exporter outputs a warning message while exporting and does not include the results block in the HTML result:
</p>
<div class="org-src-container">
<pre class="src src-shell">cat ghost.txt
</pre>
</div>
<p>
To remedy this, make sure the output sent to standard error is redirected to standard output and that the code sample returns a zero exit code. The former is achieved by calling <code>exec 2&gt;&amp;1</code> as the prologue to redirect the command’s output to standard output. To satisfy the latter, the command’s exit code is replaced with a zero by calling <code>:</code> as the epilogue (which is a no-op that returns a zero exit code):
</p>
<div class="org-src-container">
<pre class="src src-org"><span class="org-org-meta-line">#+header: :exports both</span>
<span class="org-org-meta-line">#+header: :results output</span>
<span class="org-org-meta-line">#+header: :prologue exec 2&gt;&amp;1</span>
<span class="org-org-meta-line">#+header: :epilogue ":"</span>
<span class="org-org-block-begin-line">#+begin_src shell</span>
<span class="org-org-block"> cat ghost.txt</span>
<span class="org-org-block-end-line">#+end_src</span>
</pre>
</div>
<p>
This example produces both the input and output code block in the HTML export:
</p>
<div class="org-src-container">
<pre class="src src-shell">cat ghost.txt
</pre>
</div>
<pre class="example">
cat: ghost.txt: No such file or directory
</pre>
<hr>
<ul class="org-ul">
<li><a href="https://orgmode.org/manual/Environment-of-a-Code-Block.html">https://orgmode.org/manual/Environment-of-a-Code-Block.html</a></li>
<li><a href="https://orgmode.org/manual/Exporting-Code-Blocks.html">https://orgmode.org/manual/Exporting-Code-Blocks.html</a></li>
<li><a href="https://orgmode.org/manual/Results-of-Evaluation.html">https://orgmode.org/manual/Results-of-Evaluation.html</a></li>
<li><a href="https://orgmode.org/manual/Using-Header-Arguments.html">https://orgmode.org/manual/Using-Header-Arguments.html</a></li>
<li><a href="https://emacs.stackexchange.com/a/59879">https://emacs.stackexchange.com/a/59879</a></li>
</ul>
<hr>
<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
The <code>:exports</code> and <code>:results</code> header arguments indicate the exporter should export both the code block and its results, and that it should print output of the code block as text results, respectively.
</p></div></div>
<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
This example uses <code>#+header</code> lines to split the header arguments over multiple lines, and is equivalent to placing all header arguments in the <code>#+begin_src</code> line:
</p>
<div class="org-src-container">
<pre class="src src-org"><span class="org-org-block-begin-line">#+begin_src shell :exports both :results output :prologue "echo 'Hello, World!' &gt;&gt; hello.txt" :epilogue "rm hello.txt"</span>
<span class="org-org-block">  cat hello.txt</span>
<span class="org-org-block-end-line">#+end_src</span>
</pre>
</div></div></div>
]]></content>
  </entry>
  <entry>
    <title type="html">Web analytics from CloudFront access logs with GoAccess</title>
    <link href="https://jeffkreeftmeijer.com/goaccess-cloudfront/"/>
    <id>https://jeffkreeftmeijer.com/goaccess-cloudfront/</id>
    <published>2021-07-30T00:00:00.000Z</published>
    <updated>2021-08-08T10:52:57.000Z</updated>
    <summary>Set up GoAccess to parse CloudFront access logs and generate web analytics for static sites hosted on AWS</summary>
    <content type="html"><![CDATA[
<p>
First, turn on <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesLoggingOnOff">turn on logging</a> in your CloudFront Distribution settings to get logs written to an S3 bucket. Then, use the <a href="https://aws.amazon.com/cli/">AWS command line interface</a><sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> to sync the logs to a local directory:<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 1: </span><code>sync.sh</code></label><pre class="src src-shell">/usr/local/bin/aws s3 sync s3://jeffkreeftmeijer.com-log-cf logs
</pre>
</div>
<p>
With the logs downloaded, generate a report by passing the logs to <a href="https://goaccess.io">GoAccess</a> to produce a 28-day HTML<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup> report:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 2: </span><code>html.sh</code></label><pre class="src src-shell">find logs -name <span class="org-string">"*.gz"</span> | <span class="org-sh-escaped-newline">\</span>
    xargs gzcat | <span class="org-sh-escaped-newline">\</span>
    grep --invert-match --file=exclude.txt | <span class="org-sh-escaped-newline">\</span>
    /usr/local/bin/goaccess <span class="org-sh-escaped-newline">\</span>
        --log-format CLOUDFRONT <span class="org-sh-escaped-newline">\</span>
        --date-format CLOUDFRONT <span class="org-sh-escaped-newline">\</span>
        --time-format CLOUDFRONT <span class="org-sh-escaped-newline">\</span>
        --ignore-crawlers <span class="org-sh-escaped-newline">\</span>
        --ignore-status=301 <span class="org-sh-escaped-newline">\</span>
        --ignore-status=302 <span class="org-sh-escaped-newline">\</span>
        --keep-last=28 <span class="org-sh-escaped-newline">\</span>
        --output index.html
</pre>
</div>
<p>
This command consists of four commands piped together:
</p>
<dl class="org-dl">
<dt><code>find logs -name "*.gz"</code></dt><dd><p>
Produce a list of all files in the <code>logs</code> directory. Because of the number of files in the logs directory, passing a directory glob to <code>gunzip</code> directly would result in an “argument list too long” error because the list of filenames exceeds the <code>ARG_MAX</code> configuration:
</p>
<div class="org-src-container">
<pre class="src src-shell">gunzip logs/*.gz
</pre>
</div>
<pre class="example">
zsh: argument list too long: gunzip
</pre></dd>
<dt><code>xargs gzcat</code></dt><dd><p>
<code>xargs</code> takes the output from <code>find</code>&#x2014;which outputs a stream of filenames delimited by newlines&#x2014;and calls the <code>gzcat</code> utility for every line by appending it to the passed command. Essentially, this runs the <code>gzcat</code> command for every file in the <code>logs</code> directory.
</p>
<p>
<code>gzcat</code> is an alias for <code>gzip --decompress --stdout</code>, which decompresses gzipped files and prints the output to the standard output stream.
</p></dd>
<dt><code>grep --invert-match --file=exclude.txt</code></dt><dd><code>grep</code> takes the input stream and filters out all log lines that match a line in the exclude file (<code>exclude.txt</code>). The exclude file is a list of words that are ignored when producing the report<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup>.</dd>
</dl>
<dl class="org-dl">
<dt><code>goaccess --log-format CLOUDFRONT --date-format CLOUDFRONT --time-format CLOUDFRONT --ignore-crawlers --ignore-status=301 --ignore-status=302 --keep-last=28 --output index.html</code></dt><dd><code>goaccess</code> reads the decompressed logs to generate a report with the following options:
<dl class="org-dl">
<dt><code>--log-format CLOUDFRONT --date-format CLOUDFRONT --time-format CLOUDFRONT</code></dt><dd>Use CloudFront’s log, date and time formats to parse the log lines.</dd>
<dt><code>--ignore-crawlers --ignore-status=301 --ignore-status=302</code></dt><dd>Ignore crawlers and redirects.</dd>
<dt><code>--keep-last=28</code></dt><dd>Use the last 28 days to build the report.</dd>
<dt><code>--output=index.html</code></dt><dd>Output an HTML report to a file named <code>index.html</code>.</dd>
</dl></dd>
</dl>
<p>
To sync the logs and generate a new report, run the <code>sync.sh</code> and <code>html.sh</code> scripts in a cron job every night at midnight:
</p>
<div class="org-src-container">
<pre class="src src-shell"><span class="org-builtin">echo</span> <span class="org-string">'0 0 * * * ~/stats/sync.sh &amp;&amp; ~/stats/html.sh'</span> | crontab
</pre>
</div>
<hr>
<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
On a mac, use Homebrew:
</p>
<div class="org-src-container">
<pre class="src src-shell">brew install awscli
</pre>
</div></div></div>
<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
Running the <code>aws s3 sync</code> command on an empty local directory took me two hours and produced a 2.1 GB directory of <code>.gz</code> files for roughly 3 years of logs. Updating the logs by running the same command takes about five minutes.
</p>
<p class="footpara">
Since I’m only interested in the stats for the last 28 days, it would make sense to only download the last 28 days of logs to generate the report. However, AWS’s command line tool doesn’t support filters like that.
</p>
<p class="footpara">
One thing that does work is using both the <code>--exclude</code> and <code>--include</code> options to include only the logs for the current month:
</p>
<div class="org-src-container">
<pre class="src src-shell">/usr/local/bin/aws s3 sync --exclude <span class="org-string">"*"</span> --include <span class="org-string">"*2021-07-*"</span> s3://jeffkreeftmeijer.com-log-cf ~/stats/logs
</pre>
</div>
<p class="footpara">
While this still loops over all files, it won’t download anything outside of the selected month.
</p>
<p class="footpara">
The command accepts the <code>--include=</code> option multiple times, so it’s possible to select multiple months like this. One could, theoretically, write a script that finds the current year and month, then downloads that stats matching that month and the month before it to produce a 28-day report.
</p></div></div>
<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
GoAccess generates JSON and CSV files when passing a filename with a <code>.json</code> or <code>.csv</code> extension, respectively. To generate the 28-day report in CSV format:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 3: </span><code>csv.sh</code></label><pre class="src src-shell">find logs -name <span class="org-string">"*.gz"</span> | <span class="org-sh-escaped-newline">\</span>
    xargs gzcat | <span class="org-sh-escaped-newline">\</span>
    grep --invert-match --file=exclude.txt | <span class="org-sh-escaped-newline">\</span>
    /usr/local/bin/goaccess <span class="org-sh-escaped-newline">\</span>
        --log-format CLOUDFRONT <span class="org-sh-escaped-newline">\</span>
        --date-format CLOUDFRONT <span class="org-sh-escaped-newline">\</span>
        --time-format CLOUDFRONT <span class="org-sh-escaped-newline">\</span>
        --ignore-crawlers <span class="org-sh-escaped-newline">\</span>
        --ignore-status=301 <span class="org-sh-escaped-newline">\</span>
        --ignore-status=302 <span class="org-sh-escaped-newline">\</span>
        --keep-last=28 <span class="org-sh-escaped-newline">\</span>
        --output stats.csv
</pre>
</div></div></div>
<div class="footdef"><sup><a id="fn.4" class="footnum" href="#fnr.4" role="doc-backlink">4</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
My <code>exclude.txt</code> currently consists of the <code>HEAD</code> HTTP request type and the path to the feed file:
</p>
<pre class="example">
HEAD
feed.xml
</pre></div></div>
]]></content>
  </entry>
  <entry>
    <title type="html">“Fix” the tab key for visibility cycling in Org and Evil mode</title>
    <link href="https://jeffkreeftmeijer.com/emacs-evil-org-tab/"/>
    <id>https://jeffkreeftmeijer.com/emacs-evil-org-tab/</id>
    <published>2021-07-29T00:00:00.000Z</published>
    <updated>2021-08-16T14:46:40.000Z</updated>
    <summary>To fix the tab key's functionality in Org mode, sacrifice Evil's backward jumping by turning it off with the evil-want-C-i-jump option.</summary>
    <content type="html"><![CDATA[
<p>
Emacs’ Org mode uses the <code>TAB</code> key to call <code>org-cycle</code>, which cycles visibility for headers. Every <code>TAB</code> press on a headline cycles through a different function<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>:
</p>
<ol class="org-ol">
<li>The first press folds the headline’s subtree, showing only the headline itself</li>
<li>The scond press shows the headline and its direct descendants, but keeps them folded</li>
<li>The third press shows the headline’s complete subtree</li>
</ol>
<p>
However, running Emacs with Evil mode in a terminal breaks the <code>TAB</code> key for cycling through header visibility in Org mode.
</p>
<p>
Most terminals map both <code>TAB</code> and <code>C-i</code> to <code>U+0009 (Character Tabulation)</code> for historical reasons, meaning they’re recognised as the same keypress. Because of this, there is no way to map different functions to them inside Emacs.
</p>
<p>
Evil remaps <code>C-i</code> to <code>evil-jump-forward</code> to emulate Vim’s jump lists feature<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>, which overwrites the default mapping for the <code>TAB</code> key in Org mode.
</p>
<p>
To fix the tab key’s functionality in Org mode, sacrifice Evil’s <code>C-i</code> backward jumping by turning it off in your configuration with the <code>evil-want-C-i-jump</code> option.
</p>
<p>
This option needs to be set <i>before</i> Evil is loaded to take effect, so put it in the early init file:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 1: </span><code>~/emacs.d/early-init.el</code></label><pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Disable C-i to jump forward to restore TAB functionality in Org mode.</span>
(<span class="org-keyword">setq</span> evil-want-C-i-jump nil)
</pre>
</div>
<p>
With use-package, set the <code>evil-want-C-i-jump</code> before the package is loaded by using the <code>:init</code> keyword:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 2: </span><code>~/emacs.d/init.el</code></label><pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Install Evil and disable C-i to jump forward to restore TAB functionality in Org mode.</span>
(use-package evil
             <span class="org-builtin">:init</span> (<span class="org-keyword">setq</span> evil-want-C-i-jump nil)
             <span class="org-builtin">:config</span> (evil-mode))
</pre>
</div>
<hr>
<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
Vim and Evil don’t have a direct equivalent to fold cycling, but they have three different commands that achieve the same result:
</p>
<ol class="org-ol">
<li><code>zf</code> folds the headline’s subtree</li>
<li><code>zo</code> opens the headline’s subtree to show its direct descendants</li>
<li><code>zO</code> opens the complete subtree</li>
</ol></div></div>
<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
Vim and Evil remember jumps between lines and files in the “jump list”. Because the jump locations are stored, you can use <code>C-o</code> to jump to a previous location, and <code>C-i</code> to jump back. For example:
</p>
<ol class="org-ol">
<li>To move to the next empty line, press <code>}</code> in normal mode</li>
<li>To jump back to where you came from, press <code>C-o</code></li>
<li>To jump back to that empty line, press <code>C-i</code></li>
</ol></div></div>
]]></content>
  </entry>
  <entry>
    <title type="html">Set up Eglot with elixir-ls</title>
    <link href="https://jeffkreeftmeijer.com/emacs-eglot-elixir-ls/"/>
    <id>https://jeffkreeftmeijer.com/emacs-eglot-elixir-ls//</id>
    <published>2021-07-28T00:00:00.000Z</published>
    <updated>2021-07-28T00:00:00.000Z</updated>
    <summary>Set up Eglot by checking out elixir-ls and building it locally</summary>
    <content type="html"><![CDATA[
<p>
To set up <a href="https://github.com/joaotavora/eglot">Eglot</a> with <a href="https://github.com/elixir-lsp/elixir-ls">elixir-ls</a>, first install both <a href="https://github.com/elixir-editors/emacs-elixir">elixir-mode</a> and Eglot using your preferred Emacs package manager. With <a href="https://github.com/raxod502/straight.el">straight.el</a>:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 1: </span><code>~/emacs.d/init.el</code></label><pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Elixir: elixir-mode</span>
(straight-use-package 'elixir-mode)

<span class="org-comment-delimiter">;; </span><span class="org-comment">Language server client: Eglot</span>
(straight-use-package 'eglot)
</pre>
</div>
<p>
Check out the repository for elixir-ls inside the <code>~/.emacs.d/</code> directory, and build<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> it using <code>mix elixir_ls.release</code>:
</p>
<div class="org-src-container">
<pre class="src src-shell">git clone git@github.com:elixir-lsp/elixir-ls.git ~/.emacs.d/elixir-ls
<span class="org-builtin">cd</span> ~/.emacs.d/elixir-ls
mix deps.get
mix elixir_ls.release
</pre>
</div>
<p>
In <code>~/.emacs.d/init.el</code>, add the path to the <code>language_server.sh</code> file to the server programs list:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 2: </span><code>~/emacs.d/init.el</code></label><pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Add elixir-ls to Eglot's server programs list</span>
(add-to-list 'eglot-server-programs '(elixir-mode <span class="org-string">"~/.emacs.d/elixir-ls/release/language_server.sh"</span>))
</pre>
</div>
<p>
Evaluate the <code>init.el</code> file by running <code>M-x eval-buffer</code> (or by restarting Emacs) to reload the configuration.
</p>
<p>
Now, run <code>M-x eglot</code> in any Elixir file to start the language server.
</p>
<hr>
<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
Because elixir-ls in a local repository, pull in changes through Git and rebuild the language server to update it in the future:
</p>
<div class="org-src-container">
<pre class="src src-shell"><span class="org-builtin">cd</span> ~/.emacs.d/elixir-ls
git pull
mix deps.get
mix elixir_ls.release
</pre>
</div></div></div>
]]></content>
  </entry>
  <entry>
    <title type="html">Purge and minify internal CSS stylesheets with Node.js</title>
    <link href="https://jeffkreeftmeijer.com/nodejs-purge-minify-css/"/>
    <id>https://jeffkreeftmeijer.com/nodejs-purge-minify-css/</id>
    <published>2021-07-26T00:00:00.000Z</published>
    <updated>2021-07-26T00:00:00.000Z</updated>
    <summary>Use PurgeCSS and cssnano to purge and minify an internal CSS stylesheet</summary>
    <content type="html"><![CDATA[
<p>
For an HTML file with an internal CSS stylesheet, I’d like to remove all unused selectors and minify the result.
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 1: </span>The input file</label><pre class="src src-html">&lt;<span class="org-function-name">html</span>&gt;
  &lt;<span class="org-function-name">head</span>&gt;
    &lt;<span class="org-function-name">style</span>&gt;
      body{
        font: 22px/1.6 system-ui, sans-serif;
        margin: auto;
        max-width: 35em;
        padding: 0 1em;
      }

      img, video{
        max-width: 100%;
        height: auto;
      }
    &lt;/<span class="org-function-name">style</span>&gt;
  &lt;/<span class="org-function-name">head</span>&gt;
  &lt;<span class="org-function-name">body</span>&gt;
    &lt;<span class="org-function-name">h1</span>&gt;<span class="org-underline"><span class="org-bold">Hello world!</span></span>&lt;/<span class="org-function-name">h1</span>&gt;
  &lt;/<span class="org-function-name">body</span>&gt;
&lt;/<span class="org-function-name">html</span>&gt;
</pre>
</div>
<p>
This output example purges the <code>img</code> and <code>video</code> selectors and minifies the stylesheet by removing newlines:
</p>
<pre class="example">
&lt;html&gt;
  &lt;head&gt;
    &lt;style&gt;body{font:22px/1.6 system-ui,sans-serif;margin:auto;max-width:35em;padding:0 1em}&lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Hello world!&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;

</pre>
<p>
The most popular tools to do this seem to be <a href="https://purgecss.com">PurgeCSS</a> for purging and <a href="https://cssnano.co">cssnano</a> for minifying, which are both <a href="https://postcss.org">PostCSS</a> plugins.
</p>
<p>
We can’t use their command-line interfaces, as neither of these has a command-line option to take the internal stylesheet out of a page to purge and minify, as both expect the CSS and HTML to be in separate files. Instead, we’ll write a Node.js program that takes HTML files as input and prints them back out after minifying the internal stylesheets.
</p>
<p>
The program takes an HTML page through <i>standard input</i> with <code>fs.readFileSync("/dev/stdin")</code>, which it then passes to a function named <code>crush</code>:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 2: </span><code>bin/crush.js</code></label><pre class="src src-js"><span class="org-comment">#! /usr/bin/env node</span>
<span class="org-keyword">const</span> <span class="org-variable-name">fs</span> = require(<span class="org-string">"fs"</span>);
<span class="org-keyword">const</span> <span class="org-variable-name">crush</span> = require(<span class="org-string">".."</span>);
<span class="org-keyword">let</span> <span class="org-variable-name">input</span> = fs.readFileSync(<span class="org-string">"/dev/stdin"</span>).toString();

crush.crush(input).then(console.log);
</pre>
</div>
<p>
The <code>crush</code> function takes the input HTML string and finds the stylesheet through a regular expression that captures anything within <code>&lt;style&gt;</code> tags. For each stylesheet, it passes the match from the regular expression along with the whole input file to a function called <code>process</code>. The promises it returns are collected and placed back into the <code>&lt;style&gt;</code> tags in the HTML file:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 3: </span>The <code>crush</code> function in <code>index.js</code></label><pre class="src src-js" id="org9bb9332"><span class="org-keyword">const</span> <span class="org-variable-name">regex</span> = <span class="org-string">/&lt;style&gt;(.*?)&lt;\/style&gt;/</span>gs;

exports.crush = <span class="org-keyword">async</span> <span class="org-keyword">function</span> (<span class="org-variable-name">input</span>) {
  <span class="org-keyword">let</span> <span class="org-variable-name">promises</span> = [...input.matchAll(regex)].map((match) =&gt; {
    <span class="org-keyword">return</span> process(match, input);
  });
  <span class="org-keyword">let</span> <span class="org-variable-name">replacements</span> = <span class="org-keyword">await</span> Promise.all(promises);

  <span class="org-keyword">return</span> input.replace(regex, () =&gt; replacements.shift());
};
</pre>
</div>
<p>
The <code>process</code> function handles processing the extracted stylesheet through PostCSS, initialized with the PurgeCSS<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> and cssnano plugins. When the promise is fulfilled, the result&#x2014;which is the purged and minified style sheet&#x2014;is placed back into the <code>&lt;style&gt;</code> tag:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 4: </span>The <code>process</code> function in <code>index.js</code></label><pre class="src src-js" id="org72a515f"><span class="org-keyword">function</span> <span class="org-function-name">process</span>(<span class="org-variable-name">match</span>, <span class="org-variable-name">html</span>) {
  <span class="org-keyword">return</span> postcss([
    purgecss({ content: [{ raw: html.replace(match[1], <span class="org-string">""</span>) }] }),
    cssnano(),
  ])
    .process(match[1])
    .then((result) =&gt; {
      <span class="org-keyword">return</span> match[0].replace(match[1], result.css);
    });
}
</pre>
</div>
<p>
In the end, the <code>index.js</code> file looks like this:
</p>
<div class="org-src-container">
<label class="org-src-name"><span class="listing-number">Listing 5: </span><code>index.js</code></label><pre class="src src-js"><span class="org-keyword">const</span> <span class="org-variable-name">postcss</span> = require(<span class="org-string">"postcss"</span>);
<span class="org-keyword">const</span> <span class="org-variable-name">cssnano</span> = require(<span class="org-string">"cssnano"</span>);
<span class="org-keyword">const</span> <span class="org-variable-name">purgecss</span> = require(<span class="org-string">"@fullhuman/postcss-purgecss"</span>);
<span class="org-keyword">const</span> <span class="org-variable-name">regex</span> = <span class="org-string">/&lt;style&gt;(.*?)&lt;\/style&gt;/</span>gs;

exports.crush = <span class="org-keyword">async</span> <span class="org-keyword">function</span> (<span class="org-variable-name">input</span>) {
  <span class="org-keyword">let</span> <span class="org-variable-name">promises</span> = [...input.matchAll(regex)].map((match) =&gt; {
    <span class="org-keyword">return</span> process(match, input);
  });
  <span class="org-keyword">let</span> <span class="org-variable-name">replacements</span> = <span class="org-keyword">await</span> Promise.all(promises);

  <span class="org-keyword">return</span> input.replace(regex, () =&gt; replacements.shift());
};

<span class="org-keyword">function</span> <span class="org-function-name">process</span>(<span class="org-variable-name">match</span>, <span class="org-variable-name">html</span>) {
  <span class="org-keyword">return</span> postcss([
    purgecss({ content: [{ raw: html.replace(match[1], <span class="org-string">""</span>) }] }),
    cssnano(),
  ])
    .process(match[1])
    .then((result) =&gt; {
      <span class="org-keyword">return</span> match[0].replace(match[1], result.css);
    });
}
</pre>
</div>
<p>
The program now takes an HTML document as a string through standard input and minifies the internal stylesheet<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup>:
</p>
<div class="org-src-container">
<pre class="src src-shell" id="org9c69be1">cat input.html | ./bin/crush.js
</pre>
</div>
<pre class="example">
&lt;html&gt;
  &lt;head&gt;
    &lt;style&gt;body{font:22px/1.6 system-ui,sans-serif;margin:auto;max-width:35em;padding:0 1em}&lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Hello world!&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
<hr>
<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
When passing the stylesheet through PurgeCSS, the HTML page to check against is passed via the <code>content</code> option:
</p>
<div class="org-src-container">
<pre class="src src-js">purgecss({content: [{raw: html.replace(match[1], <span class="org-string">""</span>)}]})
</pre>
</div>
<p class="footpara">
We need to make sure the stylesheet itself isn’t included in <code>content</code>, as that would prevent PurgeCSS from removing the tags.
</p>
<p class="footpara">
As an example, consider this input HTML file, which has styling for a <code>&lt;div&gt;</code> which isn’t there:
</p>
<div class="org-src-container">
<pre class="src src-html">&lt;<span class="org-function-name">style</span>&gt;div { color: red }&lt;/<span class="org-function-name">style</span>&gt;
</pre>
</div>
<p class="footpara">
PurgeCSS should purge that CSS selector, because there are no <code>&lt;div&gt;</code> tags on the page. However, if we pass the input file as a <code>content</code> as-is, PurgeCSS will see “div” in the stylesheet and assume there’s a <code>&lt;div&gt;</code> tag on the page.<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>
</p></div></div>
<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
Upon closer inspection; PurgeCSS will also recognise the following document as having a <code>&lt;div&gt;</code> tag for mentioning the word “div” in another tag:
</p>
<div class="org-src-container">
<pre class="src src-html">&lt;<span class="org-function-name">h1</span>&gt;<span class="org-underline"><span class="org-bold">An article about the div tag</span></span>&lt;/<span class="org-function-name">h1</span>&gt;
</pre>
</div></div></div>
<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
<a href="https://github.com/jeffkreeftmeijer/crush">Crush</a>’s source code and tests are on GitHub, and is installed via Git:
</p>
<div class="org-src-container">
<pre class="src src-shell">npm install jeffkreeftmeijer/crush
</pre>
</div></div></div>
]]></content>
  </entry>
  <entry>
    <title type="html">Run a shell command on every revision in a Git repository</title>
    <link href="https://jeffkreeftmeijer.com/git-rebase-exec/"/>
    <id>https://jeffkreeftmeijer.com/git-rebase-exec/</id>
    <published>2021-07-22T00:00:00.000Z</published>
    <updated>2021-07-27T00:00:00.000Z</updated>
    <summary>Git's rebase command takes an --exec option to run a shell command on every revision in the rebase.</summary>
    <content type="html"><![CDATA[
<p>
Git’s rebase command takes an <code>--exec</code> option to run a shell command on every revision in the rebase. For example, to run a formatter like Prettier<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> over each file in your repository for every past revision<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>:
</p>
<div class="org-src-container">
<pre class="src src-shell">git rebase -i --exec <span class="org-string">'prettier --write {**/*,*}.js'</span> ffcfe45
</pre>
</div>
<p>
Being an interactive rebase, Git opens up your <code>$EDITOR</code> to show the actions that are about to be executed. Although this rebase spans nine commits, there are eighteen actions as the commands are run as separate steps:
</p>
<pre class="example">
pick 22b042f npm install --save-dev mocha
exec prettier --write {**/*}.js
pick 76995a2 echo node_modules &gt;&gt; .gitignore
exec prettier --write {**/*}.js
pick 85d9d77 Use mocha as test script
exec prettier --write {**/*}.js
pick cfa165c Add failing test for crush.crush()
exec prettier --write {**/*}.js
pick 93cfd2a Minify with cssnano
exec prettier --write {**/*}.js
pick c9dcfb4 Add failing test for purging
exec prettier --write {**/*}.js
pick 1703abd npm install --save-dev @fullhuman/postcss-purgecss postcss
exec prettier --write {**/*}.js
pick 5faa328 Purge css with purgecss
exec prettier --write {**/*}.js
pick bc787ae Add bin/crush.js
exec prettier --write {**/*}.js

# Rebase ffcfe45..bc787ae onto 5faa328 (18 commands)
#
# Commands:
# p, pick &lt;commit&gt; = use commit
# r, reword &lt;commit&gt; = use commit, but edit the commit message
# e, edit &lt;commit&gt; = use commit, but stop for amending
# s, squash &lt;commit&gt; = use commit, but meld into previous commit
# f, fixup &lt;commit&gt; = like "squash", but discard this commit's log message
# x, exec &lt;command&gt; = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop &lt;commit&gt; = remove commit
# l, label &lt;label&gt; = label current HEAD with a name
# t, reset &lt;label&gt; = reset HEAD to a label
# m, merge [-C &lt;commit&gt; | -c &lt;commit&gt;] &lt;label&gt; [# &lt;oneline&gt;]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c &lt;commit&gt; to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
</pre>
<p>
When running the rebase, Git halts whenever the command returns a non-zero exit code to allow you to check what happened. In this example, the first couple of commits don’t have any JavaScript files to format, which are skipped with <code>git rebase --continue</code>:
</p>
<pre class="example">
Executing: prettier --write {**/*,*}.js
[error] No files matching the pattern were found: "**/*.js".
[error] No files matching the pattern were found: "*.js".
warning: execution failed: prettier --write {**/*,*}.js
You can fix the problem, and then run

  git rebase --continue

</pre>
<p>
Each commit gets picked before the execution happens, which can cause conflicts between formatted and still-unformatted code. In that case, remember that the next <code>exec</code> step reformats the code and amends it to the commit, so you can pick the unformatted version and have the formatter do the formatting.
</p>
<hr>
<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
: Examples with different formatters:
</p>
<dl class="org-dl">
<dt><code>mix format</code> (Elixir)</dt><dd><code>git rebase -i --exec 'mix format' main</code></dd>
<dt>Rubocop (Ruby)</dt><dd><code>git rebase -i --exec 'rubocop --auto-correct' main</code></dd>
<dt>Prettier (Node.js)</dt><dd><code>git rebase -i --exec 'prettier --write {**/*,*}.js' main</code></dd>
</dl></div></div>
<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
: <code>ffcfe45</code> is the commit that adds prettier to the project. I injected that commit after the others were already written to be able to the formatter retroactively to clean up history.
</p></div></div>
]]></content>
  </entry>
  <entry>
    <title type="html">Git log formats</title>
    <link href="https://jeffkreeftmeijer.com/git-log-formats/"/>
    <id>https://jeffkreeftmeijer.com/git-log-formats/</id>
    <published>2021-07-17T00:00:00.000Z</published>
    <updated>2021-07-25T00:00:00.000Z</updated>
    <summary>Examples of output produced by Git log's different default formatting options.</summary>
    <content type="html"><![CDATA[
<p>
Given a repository with a single commit:
</p>
<div class="org-src-container">
<pre class="src src-shell">git init
touch <span class="org-string">"file.txt"</span>
git add file.txt
git commit -m <span class="org-string">"Subject"</span> -m <span class="org-string">"First paragraph"</span> -m <span class="org-string">"Second paragraph"</span>
</pre>
</div>
<h2 id="orgfc1a708"><code>git log --format=medium</code></h2>
<p>
The “medium” format is the default when passing no <code>--format</code> option:
</p>
<div class="org-src-container">
<pre class="src src-shell">git log
</pre>
</div>
<pre class="example">
commit f142f6d3327fbc17ff3f9a7f6c4157d70da7083f
Author: Alice &lt;alice@example.com&gt;
Date:   Thu Jul 22 07:45:25 2021 +0200

    Subject

    First paragraph

    Second paragraph
</pre>
<h2 id="org93b964f"><code>git log --format=oneline</code></h2>
<div class="org-src-container">
<pre class="src src-shell">git log --format=oneline
</pre>
</div>
<pre class="example">
f142f6d3327fbc17ff3f9a7f6c4157d70da7083f Subject
</pre>
<h2 id="org3a740ad"><code>git log --format=short</code></h2>
<div class="org-src-container">
<pre class="src src-shell">git log --format=short
</pre>
</div>
<pre class="example">
commit f142f6d3327fbc17ff3f9a7f6c4157d70da7083f
Author: Alice &lt;alice@example.com&gt;

    Subject
</pre>
<h2 id="org5179822"><code>git log --format=full</code></h2>
<div class="org-src-container">
<pre class="src src-shell">git log --format=full
</pre>
</div>
<pre class="example">
commit f142f6d3327fbc17ff3f9a7f6c4157d70da7083f
Author: Alice &lt;alice@example.com&gt;
Commit: Bob &lt;bob@example.com&gt;

    Subject

    First paragraph

    Second paragraph
</pre>
<h2 id="org4ccb7cc"><code>git log --format=fuller</code></h2>
<div class="org-src-container">
<pre class="src src-shell">git log --format=fuller
</pre>
</div>
<pre class="example">
commit f142f6d3327fbc17ff3f9a7f6c4157d70da7083f
Author:     Alice &lt;alice@example.com&gt;
AuthorDate: Thu Jul 22 07:45:25 2021 +0200
Commit:     Bob &lt;bob@example.com&gt;
CommitDate: Thu Jul 22 07:45:25 2021 +0200

    Subject

    First paragraph

    Second paragraph
</pre>
<h2 id="org1cbb9ee"><code>git log --format=email</code></h2>
<div class="org-src-container">
<pre class="src src-shell">git log --format=email
</pre>
</div>
<pre class="example">
From f142f6d3327fbc17ff3f9a7f6c4157d70da7083f Mon Sep 17 00:00:00 2001
From: Alice &lt;alice@example.com&gt;
Date: Thu, 22 Jul 2021 07:45:25 +0200
Subject: [PATCH] Subject

First paragraph

Second paragraph
</pre>
<h2 id="org7d5d686"><code>git log --format=raw</code></h2>
<div class="org-src-container">
<pre class="src src-shell">git log --format=raw
</pre>
</div>
<pre class="example">
commit f142f6d3327fbc17ff3f9a7f6c4157d70da7083f
tree bdd68b0120ca91384c1606468b4ca81b8f67c728
author Alice &lt;alice@example.com&gt; 1626932725 +0200
committer Bob &lt;bob@example.com&gt; 1626932725 +0200

    Subject

    First paragraph

    Second paragraph
</pre>
]]></content>
  </entry>
  <entry>
    <title type="html">Multi-paragraph Git commit messages from the command line</title>
    <link href="https://jeffkreeftmeijer.com/git-m-m-m/"/>
    <id>https://jeffkreeftmeijer.com/git-m-m-m/</id>
    <published>2021-07-13T00:00:00.000Z</published>
    <updated>2021-07-25T00:00:00.000Z</updated>
    <summary>To write a multi-paragraph Git commit message from the command line, use the --message or -m option multiple times.</summary>
    <content type="html"><![CDATA[
<p>
To write a multi-paragraph Git commit message from the command line, use the <code>--message</code> or <code>-m</code> option multiple times:
</p>
<div class="org-src-container">
<pre class="src src-shell">git commit -m <span class="org-string">"Subject"</span> -m <span class="org-string">"First paragraph"</span> -m <span class="org-string">"Second paragraph"</span>
</pre>
</div>
<p>
The first message is the subject line, and every subsequent one becomes a paragraph:
</p>
<div class="org-src-container">
<pre class="src src-shell">git log
</pre>
</div>
<pre class="example">
commit 3c2a65bd0e77bc323b77463ced0feb7c02f3acac
Author: Alice &lt;alice@example.com&gt;
Date:   Sat Jul 17 13:05:58 2021 +0200

    Subject

    First paragraph

    Second paragraph
</pre>
]]></content>
  </entry>
  <entry>
    <title type="html">Extract a subdirectory or single file from a Git repository</title>
    <link href="https://jeffkreeftmeijer.com/git-extract/"/>
    <id>https://jeffkreeftmeijer.com/git-extract/</id>
    <published>2021-07-13T00:00:00.000Z</published>
    <updated>2021-07-25T00:00:00.000Z</updated>
    <summary>The git-filter-repo library supports many ways of file filtering and history rewriting, but extracting directories and files are two I’ve needed in the past.</summary>
    <content type="html"><![CDATA[
<p>
The <code>git-filter-repo</code> library supports many ways of file filtering and history rewriting<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>, but extracting directories and files are two I’ve needed in the past.
</p>
<h2 id="org5b9e098">Cheat sheet</h2>
<dl class="org-dl">
<dt><a href="#subdirectory">Extracting a subdirectory</a> from a Git repository</dt><dd><p>
To extract the directory named <code>path/to/dir</code>, move all files in <code>path/to/dir</code> to the repository root:
</p>
<div class="org-src-container">
<pre class="src src-shell">git filter-repo --subdirectory-filter path/to/dir
</pre>
</div></dd>
<dt><a href="#single-file">Extracting a single file</a> from a Git repository</dt><dd><p>
To extract a single file named <code>path/to/file.txt</code>, move all files in <code>path/to</code> to the repository root:
</p>
<div class="org-src-container">
<pre class="src src-shell">git filter-repo --subdirectory-filter path/to
</pre>
</div>
<p>
Then, remove all files except <code>file.txt</code>:
</p>
<div class="org-src-container">
<pre class="src src-shell">git filter-repo --path file.txt
</pre>
</div></dd>
</dl>
<h2 id="orgf302894">Extracting files</h2>
<p>
Git comes with a file named <a href="https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh"><code>git-prompt.sh</code></a>, which exposes <code>__git_ps1</code>&#x2014;a function that returns the current Git branch name&#x2014;to be used in a terminal command prompt<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>.
</p>
<p>
The instructions in the file encourage copying the file somewhere like <code>~/</code>, and then using it in your prompt. However, I prefer being able to pull in changes by keeping it in a Git repository. Keeping a local checkout of Git’s source proved a bit too bulky, so I decided to extract the file I needed into a separate repository<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup>.
</p>
<p>
Extracting a single file from a repository requires extracting a directory and then removing any unwanted files. First, we clone Git&rsquo;s repository:
</p>
<div class="org-src-container">
<pre class="src src-shell">git clone https://github.com/git/git.git
</pre>
</div>
<pre class="example">
Cloning into 'git'...
[...]
Updating files: 100% (3956/3956), done.
</pre>
<p>
Then, switch directories to end up inside:
</p>
<div class="org-src-container">
<pre class="src src-shell"><span class="org-builtin">cd</span> git
</pre>
</div>
<h2 id="orgc015ec3">A note on <code>git-filter-branch</code></h2>
<p>
An often-recommended approach is using <code>git filter-branch</code>. To extract the <code>contrib/completion</code> directory, we pass the directory name through the <code>--subdirectory</code> option:
</p>
<div class="org-src-container">
<pre class="src src-shell">git filter-branch --subdirectory-filter contrib/completion
</pre>
</div>
<p>
The command halts, refuses to continue and displays us the following warning:
</p>
<pre class="example">
WARNING: git-filter-branch has a glut of gotchas generating mangled history
         rewrites.  Hit Ctrl-C before proceeding to abort, then use an
         alternative filtering tool such as 'git filter-repo'
         (https://github.com/newren/git-filter-repo/) instead.  See the
         filter-branch manual page for more details; to squelch this warning,
         set FILTER_BRANCH_SQUELCH_WARNING=1.
</pre>
<p>
Aside from the risk of corrupting your repository, running <code>git-filter-branch</code> on this repository takes well over a minute if we were to run it with the <code>FILTER_BRANCH_SQUELCH_WARNING</code> environment variable set<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup>.
</p>
<p>
It’s generally a bad idea to use <code>git-filter-branch</code>, so we’ll have to find another way.
</p>
<h2 id="orgcae9fe9">Using <code>git-filter-repo</code></h2>
<p>
As instructed, we’ll use <a href="https://github.com/newren/git-filter-repo/"><code>git-filter-repo</code></a> instead. The <code>git-filter-repo</code> subcommand isn’t bundled with Git, but it’s installable through most package managers<sup><a id="fnr.5" class="footref" href="#fn.5" role="doc-backlink">5</a></sup>. On a Mac, use Homebrew:
</p>
<div class="org-src-container">
<pre class="src src-shell">brew install git-filter-repo
</pre>
</div>
<pre class="example">
==&gt; Downloading https://ghcr.io/v2/homebrew/core/git-filter-repo/manifests/2.32.0-1
[...]
/usr/local/Cellar/git-filter-repo/2.32.0: 8 files, 278.2KB
</pre>
<h3 id="subdirectory">Extracting a subdirectory</h3>
<p>
The command to extract a subdirectory is a direct translation from the one in <code>git-filter-repo</code>. Again, we pass the directory we’d like to extract as the <code>--subdirectory-filter</code>:
</p>
<div class="org-src-container">
<pre class="src src-shell">git filter-repo --subdirectory-filter contrib/completion
</pre>
</div>
<pre class="example">
Parsed 65913 commits
HEAD is now at 5a63a42a9e Merge branch 'fw/complete-cmd-idx-fix'

New history written in 13.23 seconds; now repacking/cleaning...
Repacking your repo and cleaning out old unneeded objects
Completely finished after 15.47 seconds.
</pre>
<p>
Voilà! That was at least five times as fast. We’ve extracted all files in the <code>contrib/completion</code> directory, while retaining their history:
</p>
<div class="org-src-container">
<pre class="src src-shell">ls
</pre>
</div>
<pre class="example">
git-completion.bash
git-completion.tcsh
git-completion.zsh
git-prompt.sh
</pre>
<h3 id="single-file">Extracting a single file</h3>
<p>
The previous example removed most of git’s source code from our checkout, but we’re still left with the completion files in the directory. In this specific cace, we only really need <code>contrib/completion/git-prompt.sh</code>.
</p>
<p>
To extract a single file from a git repository, first extract the subdirectory like we’ve just done, then use the <code>--path</code> option to filter out all files except the selected one:
</p>
<div class="org-src-container">
<pre class="src src-shell">git filter-repo --path <span class="org-string">'git-prompt.sh'</span>
</pre>
</div>
<pre class="example">
Parsed 1240 commits
HEAD is now at 9db4940 git-prompt: work under set -u

New history written in 1.86 seconds; now repacking/cleaning...
Repacking your repo and cleaning out old unneeded objects
Completely finished after 2.62 seconds.
</pre>
<p>
Now we’re left with a repository holding a single file.
</p>
<div class="org-src-container">
<pre class="src src-shell">ls
</pre>
</div>
<pre class="example">
git-prompt.sh
</pre>
<p>
You can pass the <code>--path</code> option multiple times to take out more than one, use <code>--path-blog</code> or <code>--path-regex</code> to match multiple files with a pattern, or combine any of these with the <code>--invert-paths</code> option to invert the selection to remove the matching files instead of keeping them.
</p>
<hr>
<div class="footdef"><sup><a id="fn.1" class="footnum" href="#fnr.1" role="doc-backlink">1</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
Outside of extracting subdirectories and files, <code>git-filter-branch</code> can move files between directories, remove files from repositories, move a while repository into a subdirectory, rewrite commit messages, change author names, and more.
</p></div></div>
<div class="footdef"><sup><a id="fn.2" class="footnum" href="#fnr.2" role="doc-backlink">2</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
I extracted the <code>git-prompt.sh</code> file out of Git’s repository back in 2016 as <a href="https://github.com/jeffkreeftmeijer/git-prompt.sh">git-prompt.sh</a>, and I’ve been using this prompt ever since:
</p>
<div class="org-src-container">
<pre class="src src-zsh">source ~/.config/git-prompt.sh/git-prompt.sh

export PROMPT='%~ $(__git_ps1 "(%s) ")$ '
</pre>
</div>
<p class="footpara">
While in a Git repository, my prompt shows the current branch name:
</p>
<div class="org-src-container">
<pre class="src src-zsh">$ ~/.config/git-prompt.sh (main)
</pre>
</div></div></div>
<div class="footdef"><sup><a id="fn.3" class="footnum" href="#fnr.3" role="doc-backlink">3</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
Assuming I’m the only one using this extraction, I must admit there’s a problem with this approach. While it seems like it would save some time because it’s quick to pull a new version of the file, updating it involves extracting the file, then rebasing the license and readme onto Git’s upstream main branch before I get the ease of pulling in changes through Git. If no such extraction existed (I’m comitted to it now), it would have been quicker to download the file directly through cURL:
</p>
<div class="org-src-container">
<pre class="src src-zsh">curl https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh --output ~/.config/git-prompt
</pre>
</div>
<p class="footpara">
A mirror that automatically updates and extracts the file would solve this. Until then, it’s a nice example to show how to extract files from Git repositories.
</p></div></div>
<div class="footdef"><sup><a id="fn.4" class="footnum" href="#fnr.4" role="doc-backlink">4</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
The <a href="https://git-scm.com/docs/git-filter-branch">documentation page for <code>git-filter-branch</code></a> reiterates that using this command is <a href="https://git-scm.com/docs/git-filter-branch#PERFORMANCE">“glacially slow”</a>, that it <a href="https://git-scm.com/docs/git-filter-branch#SAFETY">“easily corrupts repos”</a>, and urges us once more to use <a href="https://github.com/newren/git-filter-repo/">git-filter-repo</a>.
</p></div></div>
<div class="footdef"><sup><a id="fn.5" class="footnum" href="#fnr.5" role="doc-backlink">5</a></sup><div class="footpara" role="doc-footnote"><p class="footpara">
Check out the <a href="https://github.com/newren/git-filter-repo/blob/main/INSTALL.md">documentation</a> for installation instructions.
</p></div></div>
]]></content>
  </entry>
</feed>
