<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Linux Maxxing dot com</title>
    <link>https://linuxmaxxing.com/</link>
    <description>Recent content on Linux Maxxing dot com</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <copyright>No Copyright</copyright>
    <lastBuildDate>Sun, 02 Aug 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://linuxmaxxing.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Actually Learning git</title>
      <link>https://linuxmaxxing.com/posts/12_git/</link>
      <pubDate>Sun, 02 Aug 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/12_git/</guid>
      <description>&lt;p&gt;&#xA;Git has been essential for my &amp;#34;workflow&amp;#34; for a long time.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;In reality git has only served a limited purpose for me:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Project management - keeping related files and stuff together.&lt;/li&gt;&#xA;&lt;li&gt;Backup - this honestly has been the primary purpose.&lt;/li&gt;&#xA;&lt;li&gt;Replication on new machines.&lt;/li&gt;&#xA;&lt;li&gt;Occasional sharing of code&lt;/li&gt;&#xA;&lt;li&gt;Occasional version control / review.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;I have overwhelmingly only used the master branch for my work.&#xA;Most of my commit messages are basically useless, e.g. &amp;#34;update&amp;#34;.&#xA;I am sure none of you have ever done this, or rather still do.&lt;/p&gt;</description>
      <content:encoded><![CDATA[
<p>
Git has been essential for my &#34;workflow&#34; for a long time.</p>
<p>
In reality git has only served a limited purpose for me:</p>
<ol>
<li>Project management - keeping related files and stuff together.</li>
<li>Backup - this honestly has been the primary purpose.</li>
<li>Replication on new machines.</li>
<li>Occasional sharing of code</li>
<li>Occasional version control / review.</li>
</ol>
<p>I have overwhelmingly only used the master branch for my work.
Most of my commit messages are basically useless, e.g. &#34;update&#34;.
I am sure none of you have ever done this, or rather still do.</p>
<p>
As I began toying more and more with my &#34;homelab&#34;, I began to at least use branches, do more frequent commits, and give useful commit messages.
Magit really made this simple as committing hunks (sometimes called partial commits) is very easy.
I think I&#39;m almost at 1000 commits on my emacs config, tons of tiny commits and zero squashing, made quick and easy via magit.</p>
<p>
Inevitably I ran into situations where I pushed updates from one computer, forgot to pull from a second computer prior to doing work, and then ended up with conflict messages when I pushed from the second computer.
Skill/habit issue.
Typically I would blindly copypasta whatever git recommended (e.g. rebase true), which most of the time worked, and if not I knew enough about git to get around to where I needed to be, typically via stashing changes.
This is definitely something I am sure the reader never has done or is currently at this time doing.</p>
<p>
I decided that I wanted to learn git from the ground up as a lot of the material out there doesn&#39;t start with esoteric fundamentals of what git actually is.
From a learning standpoint I learn by doing and watching, and then once I have a grasp on the material I like to read reference manuals.
Starting off with <code>man</code> pages generally isn&#39;t the best way that I learn.</p>
<p>
Like any professional I watched a few yt vidjas which basically said all the basics I had already known.
Then I stumbled upon <a href="https://youtu.be/rH3zE7VlIMs">The Primeagen&#39;s exactly 4-hour and 20-minute git course</a> and figured this would have more nuggets (and humour) than all others.
That has proven to be true.</p>
<p>
I don&#39;t really consume The Primeagen content, not really my style or vibe.
But The Primeagen has <a href="https://pubmed.ncbi.nlm.nih.gov/35947316/">this one simple trick</a> that makes him extremely effective (good use of research dollars btw).</p>
<p>
His course is hosted at <a href="https://boot.dev">boot.dev</a> and is free.
I am not a fan of the website, but it is an interactive course which is the best way that I learn.</p>
<p>
The content itself is absolutely outstanding.
10/10 would recommend.
Linuxmaxxing dot com is not getting any kickback for the advert.</p>
<p>
Perhaps it would have been overwhelming to learn at first, but nevertheless I wish I had at least learned the fundamentals of how git actually works under the hood very early on.</p>
<hr>
<div id="outline-container-headline-1" class="outline-2">
<h2 id="headline-1">
Notes
</h2>
<div id="outline-text-headline-1" class="outline-text-2">
<p>I have added a Notes section to my site.
The purpose of the Notes section is to post notes. In other news water is wet.</p>
<p>
To no surprise the first note in the Notes section are notes taken whilst (re)learning git.
It covers the entirety of the course mentioned above, so if you&#39;re curious maybe skim through these notes to see if there are any gems or nuggets you were unaware of.</p>
<p>
A while back I heard a note-taking strategy of making your notes public, not necessarily to benefit the public, but because it is a forcing function to write good, concise, and accurate notes which you will more than likely reread (and thus reinforce) prior to posting.
Jury is out as to whether I&#39;ll meet that intention. Perhaps I (and perhaps you) will benefit from having them available.
Plus it gives me a known, quick reference available anywhere as opposed to grepping through some less organized files.</p>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Git Notes</title>
      <link>https://linuxmaxxing.com/notes/git/</link>
      <pubDate>Sun, 02 Aug 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/notes/git/</guid>
      <description>&lt;div id=&#34;outline-container-headline-1&#34; class=&#34;outline-2&#34;&gt;&#xA;&lt;h2 id=&#34;headline-1&#34;&gt;&#xA;Git Repository&#xA;&lt;/h2&gt;&#xA;&lt;div id=&#34;outline-text-headline-1&#34; class=&#34;outline-text-2&#34;&gt;&#xA;&lt;div id=&#34;outline-container-headline-2&#34; class=&#34;outline-3&#34;&gt;&#xA;&lt;h3 id=&#34;headline-2&#34;&gt;&#xA;Concepts&#xA;&lt;/h3&gt;&#xA;&lt;div id=&#34;outline-text-headline-2&#34; class=&#34;outline-text-3&#34;&gt;&#xA;&lt;p&gt;File States&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;untracked&lt;/li&gt;&#xA;&lt;li&gt;staged&lt;/li&gt;&#xA;&lt;li&gt;committed&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Commit&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;snapshot of the repository at a given time&lt;/li&gt;&#xA;&lt;li&gt;it is not a diff&lt;/li&gt;&#xA;&lt;li&gt;diffs are calculated&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Commits are just a file&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;first two hash characters = the directory within &lt;code&gt;.git/objects&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;the rest is the filename&lt;/li&gt;&#xA;&lt;li&gt;this is to prevent inode busting from putting all commits under one directory&lt;/li&gt;&#xA;&lt;li&gt;commit stores entire history via pointers&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Hash&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;unique; depends on every aspect of the git operation:&lt;/p&gt;</description>
      <content:encoded><![CDATA[
<div id="outline-container-headline-1" class="outline-2">
<h2 id="headline-1">
Git Repository
</h2>
<div id="outline-text-headline-1" class="outline-text-2">
<div id="outline-container-headline-2" class="outline-3">
<h3 id="headline-2">
Concepts
</h3>
<div id="outline-text-headline-2" class="outline-text-3">
<p>File States</p>
<ol>
<li>untracked</li>
<li>staged</li>
<li>committed</li>
</ol>
<p>Commit</p>
<ul>
<li>snapshot of the repository at a given time</li>
<li>it is not a diff</li>
<li>diffs are calculated</li>
</ul>
<p>Commits are just a file</p>
<ul>
<li>first two hash characters = the directory within <code>.git/objects</code></li>
<li>the rest is the filename</li>
<li>this is to prevent inode busting from putting all commits under one directory</li>
<li>commit stores entire history via pointers</li>
</ul>
<p>Hash</p>
<ul>
<li>
<p>unique; depends on every aspect of the git operation:</p>
<ul>
<li>author name and email</li>
<li>commit</li>
<li>message</li>
<li>date and time</li>
<li>parent commit hashes</li>
</ul>
</li>
</ul>
<p>Git Objects</p>
<ul>
<li>git is made of up objects in the .git/objects directory</li>
<li>a commit is just one type of these objects</li>
<li>these objects will match commits shown in git log</li>
<li><code>cat</code> will show binary nonsense</li>
<li><code>git log</code> will output the object in hexadecimal format</li>
<li>git compresses files in the .git/objects directory</li>
<li>git deduplicates files common across commits</li>
<li>git does not change or overwrite past trees or blobs</li>
</ul>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git init                        <span class="c1"># create git repo in current directory</span>
</span></span><span class="line"><span class="cl">  git status                      <span class="c1"># see file states</span>
</span></span><span class="line"><span class="cl">  git add .                       <span class="c1"># does not recursively add</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git log
</span></span><span class="line"><span class="cl">  git --no-pager log
</span></span><span class="line"><span class="cl">  git --no-pager log -n <span class="m">10</span>    <span class="c1"># prints last 10 commits NOT in the pager (less)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># other options</span>
</span></span><span class="line"><span class="cl">  --oneline <span class="c1"># puts everything on one line</span>
</span></span><span class="line"><span class="cl">  --parents <span class="c1"># shows parent commmits</span>
</span></span><span class="line"><span class="cl">  --graph   <span class="c1"># shows commit/branch lines</span>
</span></span><span class="line"><span class="cl">  --all    <span class="c1"># shows all branches</span>
</span></span><span class="line"><span class="cl">  --decorate <span class="c1"># branch + tag info, can be short, full,no</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git log --decorate<span class="o">=</span>full</span></span></code></pre></div>
</div>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-3" class="outline-2">
<h2 id="headline-3">
git internals
</h2>
<div id="outline-text-headline-3" class="outline-text-2">
<ul>
<li>tree: git&#39;s way of storing a directory</li>
<li>blob: git&#39;s way of storing a file</li>
</ul>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  <span class="c1"># use the first 7 characters of the commit hash (4 might be sufficient)</span>
</span></span><span class="line"><span class="cl">  git cat-file -p &lt;commit hash&gt;   <span class="c1"># this will show the tree hash</span></span></span></code></pre></div>
</div>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git cat-file -p &lt;tree hash&gt;     <span class="c1"># this will show the blob hash</span></span></span></code></pre></div>
</div>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git cat-file -p &lt;blob&gt;          <span class="c1"># this will show the file hash</span></span></span></code></pre></div>
</div>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git cat-file -p &lt;file hash&gt;     <span class="c1"># shows contents of the file</span></span></span></code></pre></div>
</div>
</div>
</div>
<div id="outline-container-headline-4" class="outline-2">
<h2 id="headline-4">
git config
</h2>
<div id="outline-text-headline-4" class="outline-text-2">
<p>You can store any data you want in your git config files</p>
<ul>
<li>git might not use the data, but it can live there</li>
<li>use –global and –local flags after the operation</li>
<li>git config files can also be directly edited</li>
<li>git is case insensitive; defaultBranch and defaultbranch are the same</li>
</ul>
<p>Config files are located in the following places in reverse priority:</p>
<table>
<thead>
<tr>
<th>Location</th>
<th>Config File</th>
</tr>
</thead>
<tbody>
<tr>
<td>System</td>
<td><code>/etc/gitconfig</code></td>
</tr>
<tr>
<td>Global</td>
<td>~/.gitconfig</td>
</tr>
<tr>
<td>Local</td>
<td>.git/config</td>
</tr>
<tr>
<td>Worktree</td>
<td>.git/config.worktree</td>
</tr>
</tbody>
</table>
<p>
Local definitions will take priority over global definitions.</p>
<div id="outline-container-headline-5" class="outline-3">
<h3 id="headline-5">
git config set
</h3>
<div id="outline-text-headline-5" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git config <span class="nb">set</span> --global user.name <span class="s2">&#34;Bill S. Preston, Esq.&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># useless data for git, but allowed</span>
</span></span><span class="line"><span class="cl">  git config <span class="nb">set</span> --local organization.ceo <span class="s2">&#34;Ted&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># --add functionally does the same thing</span>
</span></span><span class="line"><span class="cl">  git config --add --global user.email <span class="s2">&#34;bill@email.com&#34;</span></span></span></code></pre></div>
</div>
</div>
</div>
<div id="outline-container-headline-6" class="outline-3">
<h3 id="headline-6">
git config list
</h3>
<div id="outline-text-headline-6" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git config list --local
</span></span><span class="line"><span class="cl">  git config list --global
</span></span><span class="line"><span class="cl">  cat .git/config                 <span class="c1"># local data</span>
</span></span><span class="line"><span class="cl">  cat ~/.gitconfig                <span class="c1"># global data</span></span></span></code></pre></div>
</div>
</div>
</div>
<div id="outline-container-headline-7" class="outline-3">
<h3 id="headline-7">
git config get
</h3>
<div id="outline-text-headline-7" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git config get &lt;key&gt;              <span class="c1"># &lt;key&gt; made up of section.value</span>
</span></span><span class="line"><span class="cl">  git config get user.name          <span class="c1"># section = user, value = name</span>
</span></span><span class="line"><span class="cl">  git config get --global user.name <span class="c1"># --global goes after get/set</span></span></span></code></pre></div>
</div>
</div>
</div>
<div id="outline-container-headline-8" class="outline-3">
<h3 id="headline-8">
git config unset
</h3>
<div id="outline-text-headline-8" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git config <span class="nb">unset</span> &lt;key&gt;           <span class="c1"># &lt;key&gt; needs a section and value</span>
</span></span><span class="line"><span class="cl">  git config <span class="nb">unset</span> --all &lt;section&gt; <span class="c1"># unset all of the same section</span>
</span></span><span class="line"><span class="cl">  git config <span class="nb">unset</span> user.name
</span></span><span class="line"><span class="cl">  git config <span class="nb">unset</span> user            <span class="c1"># THIS FAILS - needs a key and value</span></span></span></code></pre></div>
</div>
</div>
</div>
<div id="outline-container-headline-9" class="outline-3">
<h3 id="headline-9">
duplicates
</h3>
<div id="outline-text-headline-9" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  <span class="c1"># you can have multiples of a key</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># the last one is what is tracked in git</span>
</span></span><span class="line"><span class="cl">  git config <span class="nb">set</span> --append organization.ceo <span class="s2">&#34;Warren&#34;</span>
</span></span><span class="line"><span class="cl">  git config <span class="nb">set</span> --append organization.ceo <span class="s2">&#34;Carson&#34;</span>
</span></span><span class="line"><span class="cl">  git config <span class="nb">set</span> --append organization.ceo <span class="s2">&#34;Sarah&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># unset all of the same key</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># otherwise git will not allow unsetting that key</span>
</span></span><span class="line"><span class="cl">  git config <span class="nb">unset</span> --all &lt;key&gt;</span></span></code></pre></div>
</div>
</div>
</div>
<div id="outline-container-headline-10" class="outline-3">
<h3 id="headline-10">
Remove a section
</h3>
<div id="outline-text-headline-10" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git config remove-section &lt;section&gt;
</span></span><span class="line"><span class="cl">  git config remove-section user</span></span></code></pre></div>
</div>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-11" class="outline-2">
<h2 id="headline-11">
Branch
</h2>
<div id="outline-text-headline-11" class="outline-text-2">
<ul>
<li>Branch is a named pointer to a specific commit.</li>
<li>When you create 10 branches, you are not creating 10 copies of the project.</li>
</ul>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git branch                      <span class="c1"># shows you which branch you are on</span>
</span></span><span class="line"><span class="cl">  git branch -m oldname newname   <span class="c1"># rename</span>
</span></span><span class="line"><span class="cl">  git config <span class="nb">set</span> --global init.defaultBranch main</span></span></code></pre></div>
</div>
<div id="outline-container-headline-12" class="outline-3">
<h3 id="headline-12">
Create a  branch
</h3>
<div id="outline-text-headline-12" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git branch &lt;branchname&gt;
</span></span><span class="line"><span class="cl">  git switch -c &lt;branchname&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">#   Create a new branch off of a specific commit hash</span>
</span></span><span class="line"><span class="cl">  git switch -c &lt;branchname&gt; &lt;commit hash&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git checkout &lt;branchname&gt;          <span class="c1"># only works if &lt;branchname&gt; exits</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git branch -d &lt;branchname&gt;        <span class="c1"># delete branch</span>
</span></span><span class="line"><span class="cl">  git branch -D &lt;branchname&gt;        <span class="c1"># force delete a branch (unmerged commits)</span></span></span></code></pre></div>
</div>
<p>
When you create a branch:</p>
<ul>
<li>git uses the current commit you are on as the branch base</li>
<li>HEAD moves to that branch</li>
</ul>
<p>After deleting a branch</p>
<ul>
<li>The commits from that branch will no longer be visible in git log</li>
<li>Even with <code>git log --all</code>, you will no longer see any unmerged commits from that branch</li>
<li>Commits will still be visible in reflog</li>
</ul>
</div>
</div>
<div id="outline-container-headline-13" class="outline-3">
<h3 id="headline-13">
HEAD and tip
</h3>
<div id="outline-text-headline-13" class="outline-text-3">
<p>tip = most recent commit in a branch</p>
<p>
HEAD = commit you are working off of</p>
<p>
HEAD usually will be the tip, but does not necessarily have to be.</p>
<p>
The commit hash of HEAD of a branch is stored in <code>.git/refs/heads</code></p>
<p>
HEAD is stored in a file:</p>
<ul>
<li>.git/HEAD</li>
<li>This points to the branch HEAD stored in .git/refs/heads</li>
</ul>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-14" class="outline-2">
<h2 id="headline-14">
Merge
</h2>
<div id="outline-text-headline-14" class="outline-text-2">
<p>Typical Workflow</p>
<ul>
<li>Fetch or pull updated main branch</li>
<li>Create a branch to do stuff</li>
<li>Merge branch to main when sufficient via pull request or git merge on cli</li>
</ul>
<div id="outline-container-headline-15" class="outline-3">
<h3 id="headline-15">
Merge Commit
</h3>
<div id="outline-text-headline-15" class="outline-text-3">
<ul>
<li>Commit which merges two branches with diverging histories.</li>
<li>Only kind of commit with two parent commits.</li>
<li>Relative to Merge Base, aka best common ancestor</li>
</ul>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git merge &lt;feature_branch_name&gt;            <span class="c1"># usually done from main branch</span></span></span></code></pre></div>
</div>
</div>
</div>
<div id="outline-container-headline-16" class="outline-3">
<h3 id="headline-16">
Fast-forward merge
</h3>
<div id="outline-text-headline-16" class="outline-text-3">
<ul>
<li>Simplest</li>
<li>Tip of feature branch becomes the tip of main branch</li>
<li>NO MERGE COMMITS</li>
<li>Feature branch is merged into the main branch prior to additional main branch commits</li>
</ul>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-17" class="outline-2">
<h2 id="headline-17">
Rebase
</h2>
<div id="outline-text-headline-17" class="outline-text-2">
<p>Most misunderstood features of git</p>
<ul>
<li>Potential drawbacks for the unintiated:</li>
<li>e.g. someone changes the public history of a branch which screws up a ton of stuff</li>
</ul>
<p>Ideal scenario:</p>
<ul>
<li>You are working on a feature branch</li>
<li>There have been updates to main</li>
<li>You want to pull those updates from main onto your feature branch</li>
<li>After rebasing, this allows for a fast-forward merge</li>
</ul>
<p>Said otherwise</p>
<ul>
<li>Push the parent commit of the feature branch to the new tip of the main branch</li>
<li>The tip of the feature branch is now a fast-forward merge away from the main branch</li>
</ul>
<p>Allows maintaining a merge-commit-free history</p>
<ul>
<li>Has some benefits</li>
<li>Allows using certain git features if you maintain your history this way</li>
</ul>
<p>When on the feature branch, rebase with main</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git rebase main                <span class="c1"># from the feature branch</span></span></span></code></pre></div>
</div>
<hr>
<div id="outline-container-headline-18" class="outline-3">
<h3 id="headline-18">
WARNING: NEVER REBASE A PUBLIC BRANCH LIKE MAIN OR MASTER
</h3>
<div id="outline-text-headline-18" class="outline-text-3">
<ul>
<li>Out of habit, it is a good idea not to rebase your own private/fork main branches</li>
</ul>
<hr>
</div>
</div>
<div id="outline-container-headline-19" class="outline-3">
<h3 id="headline-19">
Rebase vs Merge
</h3>
<div id="outline-text-headline-19" class="outline-text-3">
<ul>
<li>Rebase will change commit hashes</li>
<li>
<p>Will have to address conflicts if rebasing:</p>
<ul>
<li>If there are conflicts in rebasing there would be conflicts in merging as well</li>
<li>Merging does not have any benefits over rebasing in this regard</li>
<li>Generally if there are no conflicts in a merge, there will be no conflicts in rebase</li>
</ul>
</li>
<li>
<p>Rebase advantage:</p>
<ul>
<li>Reverting commits</li>
<li>Only really done at scale</li>
</ul>
</li>
<li>
<p>Merge advantage:</p>
<ul>
<li>Shows true history</li>
</ul>
</li>
<li>
<p>Merge vs rebase might be enforced by some teams on the main branch</p>
<ul>
<li>For your own branches you can generally do whatever you want.</li>
</ul>
</li>
<li>
<p>An opinionated strategy:</p>
<ul>
<li>Always rebase</li>
<li>All merges then become fast-forward merges</li>
<li>If you are in a situation where you need to revert, it becomes really easy to do.</li>
<li>Squashing becomes easy</li>
</ul>
</li>
</ul>
<p>With merge conflict resolution</p>
<ul>
<li>
<p>you COMMIT the resolution</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git add &lt;file&gt;
</span></span><span class="line"><span class="cl">  git commit <span class="o">[</span>-m <span class="s2">&#34;Message&#34;</span><span class="o">]</span></span></span></code></pre></div>
</div>
</li>
</ul>
<p>With rebase conflict resolution</p>
<ul>
<li>
<p>you CONTINUE the resolution</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git rebase --continue</span></span></code></pre></div>
</div>
</li>
<li>you DO NOT commit rebase resolution</li>
</ul>
<p>If you happen to accidentally commit rebase resolutiton</p>
<ul>
<li>
<p>undo the commit</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git reset --soft HEAD~1</span></span></code></pre></div>
</div>
</li>
<li>then continue the rebase</li>
</ul>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-20" class="outline-2">
<h2 id="headline-20">
Undoing Changes
</h2>
<div id="outline-text-headline-20" class="outline-text-2">
<p>Git fundamentally does 3 things</p>
<ol>
<li>Add</li>
<li>Delete</li>
<li>Modify</li>
</ol>
<p>Conflicts arise on the modify portion</p>
<ul>
<li>If two commits edit the same line, git will throw a conflict</li>
<li>Requires personal editing and deconfliction</li>
<li>Comments in code can cause conflict</li>
</ul>
<div id="outline-container-headline-21" class="outline-3">
<h3 id="headline-21">
git reset –soft
</h3>
<div id="outline-text-headline-21" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git reset --soft &lt;commit hash&gt;
</span></span><span class="line"><span class="cl">  git reset --soft HEAD~1         <span class="c1"># go back one commit from HEAD</span></span></span></code></pre></div>
</div>
<ul>
<li>Undo the last commit and committed changes in the index (staged but not committed)</li>
<li>Commit changes will be uncommited and staged</li>
<li>Uncommited changes will remain staged or unstaged as before</li>
</ul>
</div>
</div>
<div id="outline-container-headline-22" class="outline-3">
<h3 id="headline-22">
git reset –hard
</h3>
<div id="outline-text-headline-22" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git reset --hard &lt;commit hash&gt;</span></span></code></pre></div>
</div>
<ul>
<li>Makes working directory and staging area match the last commit exactly</li>
<li>Disregards local changes</li>
</ul>
<hr>
<div id="outline-container-headline-23" class="outline-4">
<h4 id="headline-23">
DANGER: GIT RESET –HARD IS PERMANENT
</h4>
<div id="outline-text-headline-23" class="outline-text-4">
<ul>
<li>If we simply delete a commited/tracked file using <code>rm -rf filename</code>, we can easily restore</li>
<li>If we use hard reset prior to committing that file, those changes are gone for good</li>
<li>Still recoverable through reflog</li>
</ul>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-24" class="outline-3">
<h3 id="headline-24">
Change Commit Message
</h3>
<div id="outline-text-headline-24" class="outline-text-3">
<p>Rather than soft resetting to change a commit message, you can just do this:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  <span class="c1"># this is a destructive item because it changes the commit hash</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># only do for latest commit which is not merged to main</span>
</span></span><span class="line"><span class="cl">  git commit --amend</span></span></code></pre></div>
</div>
<p>
This is a destructive operation and will change the commit hash.</p>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-25" class="outline-2">
<h2 id="headline-25">
Git Remote
</h2>
<div id="outline-text-headline-25" class="outline-text-2">
<p>
Remotes are just git repos</p>
<ul>
<li>We can have remotes that reside on our local machine.</li>
<li>Origin: If treating the remote as the authoritative source of truth, name it &#34;origin&#34;</li>
<li>
<p>Upstream:</p>
<ul>
<li>Sometimes this will be the authoritative.</li>
<li>Origin becomes what you&#39;re working in (fork of upstream).</li>
<li>You only want upstream when working with a lot of people and/or forking, etc.</li>
</ul>
</li>
</ul>
<div id="outline-container-headline-26" class="outline-3">
<h3 id="headline-26">
git fetch
</h3>
<div id="outline-text-headline-26" class="outline-text-3">
<ul>
<li>This fetches objects and bookeeping information about a repo into <code>.git/objects</code></li>
<li>It does not pull the files into your local branch(es).</li>
</ul>
</div>
</div>
<div id="outline-container-headline-27" class="outline-3">
<h3 id="headline-27">
Remote Commands
</h3>
<div id="outline-text-headline-27" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git remote add &lt;remote&gt; &lt;uri&gt;   <span class="c1"># notice this is uri, can be a file address</span>
</span></span><span class="line"><span class="cl">  git remote add origin ssh://git@forge.site/user/repo.git
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git ls-remote
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git push origin main            <span class="c1"># push the local main branch to origin</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># push a local branch to a remote branch of a differnet name</span>
</span></span><span class="line"><span class="cl">  git push origin &lt;localbranch&gt;:&lt;remotebranch&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git push origin :&lt;remotebranch&gt; <span class="c1"># deletes a remote branch</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git log origin/master --oneline
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git merge &lt;remote&gt;/&lt;branchname&gt;
</span></span><span class="line"><span class="cl">  git merge origin/main           <span class="c1"># merge from the main branch on origin</span></span></span></code></pre></div>
</div>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-28" class="outline-2">
<h2 id="headline-28">
Git Pull
</h2>
<div id="outline-text-headline-28" class="outline-text-2">
<p>Pulls actual filechanges onto your local branch, not just metadata.</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  <span class="c1"># ensures we merge on a pull, may or may not want this</span>
</span></span><span class="line"><span class="cl">  git config <span class="nb">set</span> pull.rebase <span class="nb">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git pull origin main            <span class="c1"># pulls the main branch from origin</span></span></span></code></pre></div>
</div>
<p>
Workflow Example:</p>
<ul>
<li>Remote is truth</li>
<li>
<p>Rebase by default</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git config <span class="nb">set</span> --global pull.rebase true</span></span></code></pre></div>
</div>
</li>
</ul>
<p>Solo work:</p>
<ul>
<li>Everything on a single branch (main)</li>
</ul>
<p>Team work</p>
<ul>
<li>Work on new-branch</li>
<li>Push to origin new-branch</li>
<li>Open PR</li>
<li>Ask for review</li>
<li>After review, hit merge on remote forge</li>
<li>Delete new-branch merging</li>
</ul>
<hr>
<hr>
</div>
</div>
<div id="outline-container-headline-29" class="outline-2">
<h2 id="headline-29">
Gitignore
</h2>
<div id="outline-text-headline-29" class="outline-text-2">
<p>Example:
Adding <code>node-modules</code> to <code>.gitignore</code> will ignore every file that has exactly <code>node-modules</code> in its name.
It will not ignore files or directories that contain <code>node-modules-2</code></p>
<div id="outline-container-headline-30" class="outline-3">
<h3 id="headline-30">
Exclude file
</h3>
<div id="outline-text-headline-30" class="outline-text-3">
<p><code>.git/info/exclude</code></p>
<ul>
<li>Alternative to .gitignore</li>
<li>Allows your personal ignored files to not be part of the repo</li>
<li>Team members cannot see what kind of shady stuff you&#39;re hiding from the repo</li>
</ul>
</div>
</div>
<div id="outline-container-headline-31" class="outline-3">
<h3 id="headline-31">
Nested <code>.gitignore</code>
</h3>
<div id="outline-text-headline-31" class="outline-text-3">
<ul>
<li>.gitignore does not have to be a t the project root</li>
<li>There can be <code>.gitignore</code> in any directory of the project</li>
</ul>
</div>
</div>
<div id="outline-container-headline-32" class="outline-3">
<h3 id="headline-32">
Pattern Recognition
</h3>
<div id="outline-text-headline-32" class="outline-text-3">
<p>Patterns starting with a forward slash are anchored to that directory</p>
<ul>
<li>/main.py would ignore main.py in the root directory but not any other directories</li>
<li>&#34;*&#34; matches any number of characters except forward-slash</li>
<li>&#34;#&#34; comments</li>
<li>
<p>&#34;!&#34; indicates negation: negate specific files that would otherwise be ignored</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  ,*.txt
</span></span><span class="line"><span class="cl">  !/important.txt                 <span class="c1"># negates important.txt despite the *.txt wildcard</span></span></span></code></pre></div>
</div>
</li>
</ul>
<p>Order matters:</p>
<ul>
<li>one entry can override another</li>
<li>
<p>correct:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  temp/*
</span></span><span class="line"><span class="cl">  !temp/instructions.md      <span class="c1"># this ignores all temp/ files except for instructions.md</span></span></span></code></pre></div>
</div>
</li>
<li>
<p>incorrect:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  !temp/instructions.md
</span></span><span class="line"><span class="cl">  temp/*                          <span class="c1"># this overrides the above negation</span></span></span></code></pre></div>
</div>
</li>
</ul>
</div>
</div>
<div id="outline-container-headline-33" class="outline-3">
<h3 id="headline-33">
What to ignore:
</h3>
<div id="outline-text-headline-33" class="outline-text-3">
<p>Ignore things that can be generated or sensitive data</p>
<ul>
<li>compiled code</li>
<li>minified files</li>
<li>dependencies (e.g. node_modules, venv, packages, etc.)</li>
<li>personal or specific preferences (e.g. editor settings)</li>
<li>sensitive or dangerous things (e.g. .env files, passwords, API keys, etc.)</li>
</ul>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-34" class="outline-2">
<h2 id="headline-34">
Fork
</h2>
<div id="outline-text-headline-34" class="outline-text-2">
<ul>
<li>Not a git operation</li>
<li>Provided by git forges</li>
<li>Creates a copy of the original repo that you can edit</li>
</ul>
<div id="outline-container-headline-35" class="outline-3">
<h3 id="headline-35">
Why fork?
</h3>
<div id="outline-text-headline-35" class="outline-text-3">
<ul>
<li>Want to contribute to the project</li>
<li>
<p>Add second remote called upstream which points to original repo</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git remote add upstream &lt;uri&gt;</span></span></code></pre></div>
</div>
<ul>
<li>Can bring in latest changes to your fork</li>
</ul>
</li>
</ul>
</div>
</div>
<div id="outline-container-headline-36" class="outline-3">
<h3 id="headline-36">
PR tips
</h3>
<div id="outline-text-headline-36" class="outline-text-3">
<p>Review code on the actual git forge site</p>
<ul>
<li>Different look than your editor</li>
<li>Removes bias, potentially see stuff differently</li>
</ul>
<p>Make sure maintainers want these changes</p>
<ul>
<li>Look for issues</li>
<li>Consider creating an issue beforehand</li>
</ul>
</div>
</div>
<div id="outline-container-headline-37" class="outline-3">
<h3 id="headline-37">
Add yourself as a contributor
</h3>
<div id="outline-text-headline-37" class="outline-text-3">
<p>In the contributors/ directory, add:</p>
<ul>
<li>A file with your forge username.txt</li>
<li>A link to your forge profile in the text</li>
</ul>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-38" class="outline-2">
<h2 id="headline-38">
Reflog
</h2>
<div id="outline-text-headline-38" class="outline-text-2">
<table>
<thead>
<tr>
<th>Location</th>
<th>Descripion</th>
</tr>
</thead>
<tbody>
<tr>
<td>HEAD@{0}</td>
<td>where HEAD is now</td>
</tr>
<tr>
<td>HEAD@{1}</td>
<td>where HEAD was 1 move ago</td>
</tr>
<tr>
<td>HEAD@{2}</td>
<td>where HEAD was 2 moves ago</td>
</tr>
<tr>
<td>HEAD@{3}</td>
<td>where HEAD was 3 move ago</td>
</tr>
</tbody>
</table>
<ul>
<li>Reflog will show history of all operations, even destructive ones.</li>
<li>Can be used to restore after hard reset or long forgotten commits.</li>
<li>Records tip of branches and HEAD when updated; tracks every last step of HEAD.</li>
</ul>
<hr>
<div id="outline-container-headline-39" class="outline-3">
<h3 id="headline-39">
Restoring a deleted but previously committed file using git cat-file:
</h3>
<div id="outline-text-headline-39" class="outline-text-3">
<ul>
<li>Use git reflog to find the commit hash of the now deleted commit.</li>
<li>
<p>Use <code>git cat-file -p &lt;hash&gt;</code> to get the tree</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git cat-file -p &lt;commithash&gt;    <span class="c1"># gets the tree</span>
</span></span><span class="line"><span class="cl">  git cat-file -p &lt;tree&gt;          <span class="c1"># gets the blob</span>
</span></span><span class="line"><span class="cl">  git cat-file -p &lt;blob&gt;          <span class="c1"># gets the file-hash</span>
</span></span><span class="line"><span class="cl">  git cat-file -p &lt;file-hash&gt;     <span class="c1"># shows contents of file</span></span></span></code></pre></div>
</div>
</li>
</ul>
<hr>
</div>
</div>
<div id="outline-container-headline-40" class="outline-3">
<h3 id="headline-40">
Restoring a deleted file the fast way
</h3>
<div id="outline-text-headline-40" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  <span class="c1"># Instead of everything above, we could have just done:</span>
</span></span><span class="line"><span class="cl">  git merge HEAD@<span class="o">{</span>1<span class="o">}</span></span></span></code></pre></div>
</div>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-41" class="outline-2">
<h2 id="headline-41">
Conflicting Changes
</h2>
<div id="outline-text-headline-41" class="outline-text-2">
<ul>
<li>It is ok when the same line is modified in two separate commits</li>
<li>it Is not ok when the same line is modified in two commits with the same parent</li>
</ul>
<div id="outline-container-headline-42" class="outline-3">
<h3 id="headline-42">
Merge Conflicts
</h3>
<div id="outline-text-headline-42" class="outline-text-3">
<p>The TOP changes are OURS</p>
<ul>
<li>
<p>Should indicating HEAD (which is always local/ours)</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  <span class="o">&lt;&lt;&lt;&lt;&lt;&lt;</span>&lt;&lt; HEAD</span></span></code></pre></div>
</div>
</li>
</ul>
<p>The BOTTOM changes are THEIRS</p>
<ul>
<li>
<p>Should indicate the branch, e.g. main</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; main</span></span></code></pre></div>
</div>
</li>
</ul>
<p>You can manually accept both changes.</p>
<p>
To reset to a specific commit:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git reset --hard &lt;commit hash&gt;</span></span></code></pre></div>
</div>
<hr>
</div>
</div>
<div id="outline-container-headline-43" class="outline-3">
<h3 id="headline-43">
Multi Conflict
</h3>
<div id="outline-text-headline-43" class="outline-text-3">
<p>The git checkout command can checkout the individual changes during a merge conflict using the –theirs or –ours flags.</p>
<ul>
<li>–ours keeps the version of the file from your current branch (the one you&#39;re on before merging)</li>
<li>–theirs uses the version of the file from the branch you&#39;re merging into your current branch</li>
</ul>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git checkout --theirs &lt;path/to/file&gt;
</span></span><span class="line"><span class="cl">  git checkout --ours &lt;path/to/file&gt;</span></span></code></pre></div>
</div>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-44" class="outline-2">
<h2 id="headline-44">
Rebase Conflicts
</h2>
<div id="outline-text-headline-44" class="outline-text-2">
<p>Most of the bad rap rebase gets is around conflicts because it rewrites history.</p>
<p>
Rarely will we have conflicts on our own stuff</p>
<p>
In the &#34;real world,&#34; what happens most often is:</p>
<ul>
<li>You switch to a new branch, say fix_bug, which is a copy of main.</li>
<li>While you&#39;re fixing the bug, someone else merges their changes into main.</li>
<li>You fix the bug, and it so happens that you edited the same files (and lines) that the other person did.</li>
<li>You open a Pull Request to merge (or rebase) fix_bug into main, then git tells you there&#39;s a conflict.</li>
<li>You resolve the conflict on your branch.</li>
<li>You complete the Pull Request with the conflict resolved.</li>
</ul>
<p>REMEMBER YOU SHOULD NEVER REBASE MAIN
MEANING YOU SHOULD NEVER TYPE THE WORDS:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git rebase &lt;feature_branch&gt;     <span class="c1"># NEVER DO THIS</span></span></span></code></pre></div>
</div>
<p>
IF REBASING, YOU SHOULD ONLY TYPE:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git rebase main                 <span class="c1"># rebase onto main</span>
</span></span><span class="line"><span class="cl">  git rebase master               <span class="c1"># rebase onto master</span></span></span></code></pre></div>
</div>
<p>
This is saying &#34;rebasing onto main/master.&#34;</p>
<div id="outline-container-headline-45" class="outline-3">
<h3 id="headline-45">
Detatched HEAD
</h3>
<div id="outline-text-headline-45" class="outline-text-3">
<p>After rebasing main on the feature branch and conflict arises:</p>
<ul>
<li>
<p>Check the branch</p>
<ul>
<li>YOU WILL NOT BE ON ANY BRANCH</li>
<li>your branch will be: (no branch, rebasing &lt;featurebranch&gt;)</li>
</ul>
</li>
<li>
<p>This is called detached HEAD</p>
<ul>
<li>temporary state</li>
<li>allows you to resolve the conflict before proceeding with the rebase</li>
</ul>
</li>
</ul>
</div>
</div>
<div id="outline-container-headline-46" class="outline-3">
<h3 id="headline-46">
Resolving Conflicts
</h3>
<div id="outline-text-headline-46" class="outline-text-3">
<p>Use git checkout –theirs and git checkout –ours
In a merge</p>
<ul>
<li>–ours refers to your current branch</li>
<li>
<p>this is most likely MAIN/MASTER</p>
<ul>
<li>sinced based bros will be rebasing onto a feature branch</li>
<li>and only (fast forward) merging onto main/master</li>
</ul>
</li>
</ul>
<p>In a rebase</p>
<ul>
<li>–ours refers to the branch you&#39;re rebasing onto.</li>
<li>in this case we are rebasing from the feature branch onto a different branch</li>
<li>it is probably main/master</li>
</ul>
<p>&#34;Accept current change&#34; is the same as</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git checkout --ours &lt;filepath&gt;</span></span></code></pre></div>
</div>
<ul>
<li>&lt;&lt;&lt;&lt;&lt;&lt;</li>
<li>top portion of the conflicting file</li>
</ul>
<p>&#34;Accept incoming change&#34; is the same as</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git checkout --theirs &lt;filepath&gt;</span></span></code></pre></div>
</div>
<ul>
<li>&gt;&gt;&gt;&gt;&gt;&gt;</li>
<li>bottom portion of the conflicting file</li>
</ul>
<div id="outline-container-headline-47" class="outline-4">
<h4 id="headline-47">
After resolving conflicts
</h4>
<div id="outline-text-headline-47" class="outline-text-4">
<p>Add the file.</p>
<p>
DO NOT COMMIT: YOU ARE STILL ON THE REBASE BRANCH</p>
<p>
Instead continue the rebase:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git rebase --continue</span></span></code></pre></div>
</div>
<p>
Check the log</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git log --oneline --all</span></span></code></pre></div>
</div>
<p>
NOTE</p>
<ul>
<li>the commit we discarded is gone from git history</li>
<li>still accessible with reflog</li>
</ul>
<hr>
<hr>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-48" class="outline-2">
<h2 id="headline-48">
Repeat Resolution (rerere)
</h2>
<div id="outline-text-headline-48" class="outline-text-2">
<ul>
<li>&#34;Reuse recorded resolution&#34;</li>
<li>It allows you to ask Git to remember how you&#39;ve resolved a hunk conflict.</li>
<li>Next time it sees the same conflict, Git can resolve it for you automatically.</li>
<li>Applies to rebasing and merging</li>
</ul>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git config <span class="nb">set</span> --local rerere.enabled <span class="nb">true</span>
</span></span><span class="line"><span class="cl">  git config <span class="nb">set</span> --local rerere.enabled <span class="nb">false</span> <span class="c1"># disable rerere</span>
</span></span><span class="line"><span class="cl">  rm -rf .git/rr-cache            <span class="c1"># delete any remaining rerere cache</span></span></span></code></pre></div>
</div>
<div id="outline-container-headline-49" class="outline-3">
<h3 id="headline-49">
Conflict After Rebase Example:
</h3>
<div id="outline-text-headline-49" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  wagslane@MacBook-Pro-2 megacorp % git rebase main
</span></span><span class="line"><span class="cl">  Auto-merging customers/favs.md
</span></span><span class="line"><span class="cl">  CONFLICT <span class="o">(</span>add/add<span class="o">)</span>: Merge conflict in customers/favs.md
</span></span><span class="line"><span class="cl">  error: could not apply ad9b194... K
</span></span><span class="line"><span class="cl">  hint: Resolve all conflicts manually, mark them as resolved with
</span></span><span class="line"><span class="cl">  hint: <span class="s2">&#34;git add/rm &lt;conflicted_files&gt;&#34;</span>, <span class="k">then</span> run <span class="s2">&#34;git rebase --continue&#34;</span>.
</span></span><span class="line"><span class="cl">  hint: You can instead skip this commit: run <span class="s2">&#34;git rebase --skip&#34;</span>.
</span></span><span class="line"><span class="cl">  hint: To abort and get back to the state before <span class="s2">&#34;git rebase&#34;</span>, run <span class="s2">&#34;git rebase --abort&#34;</span>.
</span></span><span class="line"><span class="cl">  Recorded preimage <span class="k">for</span> <span class="s1">&#39;customers/favs.md&#39;</span> <span class="c1"># &lt;--- THIS LINE HERE</span>
</span></span><span class="line"><span class="cl">  Could not apply ad9b194... K</span></span></code></pre></div>
</div>
<p>
NOTICE:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  Recorded preimage <span class="k">for</span> <span class="s1">&#39;customers/favs.md&#39;</span> <span class="c1"># &lt;--- THIS LINE HERE</span></span></span></code></pre></div>
</div>
<p>
Rerere is now recording how to resolve the conflict</p>
<ul>
<li>Either accept –ours (main) or –theirs (favs branch)</li>
<li>Or accept both by manually editing the file</li>
</ul>
<p>If we recreate the same conflict from another (third) branch (favs2)</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  wagslane@MacBook-Pro-2 megacorp % git rebase main
</span></span><span class="line"><span class="cl">  Auto-merging customers/favs.md
</span></span><span class="line"><span class="cl">  CONFLICT <span class="o">(</span>add/add<span class="o">)</span>: Merge conflict in customers/favs.md
</span></span><span class="line"><span class="cl">  error: could not apply ad9b194... K
</span></span><span class="line"><span class="cl">  hint: Resolve all conflicts manually, mark them as resolved with
</span></span><span class="line"><span class="cl">  hint: <span class="s2">&#34;git add/rm &lt;conflicted_files&gt;&#34;</span>, <span class="k">then</span> run <span class="s2">&#34;git rebase --continue&#34;</span>.
</span></span><span class="line"><span class="cl">  hint: You can instead skip this commit: run <span class="s2">&#34;git rebase --skip&#34;</span>.
</span></span><span class="line"><span class="cl">  hint: To abort and get back to the state before <span class="s2">&#34;git rebase&#34;</span>, run <span class="s2">&#34;git rebase --abort&#34;</span>.
</span></span><span class="line"><span class="cl">  Resolved <span class="s1">&#39;customers/favs.md&#39;</span> using previous resolution. <span class="c1"># &lt;--- THIS LINE HERE</span>
</span></span><span class="line"><span class="cl">  Could not apply ad9b194... K</span></span></code></pre></div>
</div>
<p>
NOTICE</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  Resolved <span class="s1">&#39;customers/favs.md&#39;</span> using previous resolution. <span class="c1"># &lt;--- THIS LINE HERE</span></span></span></code></pre></div>
</div>
<ul>
<li>if you open the conflicting file, the changes have already been made to match the first one</li>
</ul>
<p>Continue the rebase with</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git rebase --continue</span></span></code></pre></div>
</div>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-50" class="outline-2">
<h2 id="headline-50">
Squashing
</h2>
<div id="outline-text-headline-50" class="outline-text-2">
<p>Squashing groups a series of sequential, small commits into one commit. This is preferred by some teams</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git rebase -i HEAD~n            <span class="c1"># n is number of commits we want to squash</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># HEAD is current commit, ~n is n previous commits</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># -i creates an interactive rebase</span></span></span></code></pre></div>
</div>
<ol>
<li>
<p>git will open editor with a list of commits</p>
<ul>
<li>-i flag is interactive</li>
<li>Allows us to edit commit history</li>
</ul>
</li>
<li>
<p>Change the word &#34;pick&#34; to &#34;squash&#34; for all but the first commit</p>
<ul>
<li>First commit will be at the top of the editor prompt</li>
</ul>
</li>
<li>Save and close the editor</li>
</ol>
<hr>
<div id="outline-container-headline-51" class="outline-3">
<h3 id="headline-51">
Squashing Best practices
</h3>
<div id="outline-text-headline-51" class="outline-text-3">
<ol>
<li>
<p>Identify the main branch remote</p>
<ul>
<li>Might have to reset this</li>
<li>Most likely origin</li>
</ul>
</li>
<li>
<p>Create a temporary branch</p>
<ul>
<li>Rebase is destructive</li>
</ul>
</li>
<li>
<p>Perform the rebase on temp branch</p>
<ul>
<li>Emacs git-rebase-mode has its own keybindings; do not need to directly edit, just press &#34;s&#34; on all lines that should be changed from &#34;pick&#34; to &#34;squash&#34;</li>
</ul>
</li>
<li>
<p>Delete the main branch</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git branch -d main</span></span></code></pre></div>
</div>
</li>
<li>
<p>Rename temp branch to main</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git branch -m temp_main main</span></span></code></pre></div>
</div>
</li>
<li>
<p>Push and reset the main branch remote</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git push -u origin main
</span></span><span class="line"><span class="cl">  <span class="c1"># git might suggest:</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># git push --set-upstream origin main</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># which does the same thing</span></span></span></code></pre></div>
</div>
</li>
</ol>
<hr>
</div>
</div>
<div id="outline-container-headline-52" class="outline-3">
<h3 id="headline-52">
Force push
</h3>
<div id="outline-text-headline-52" class="outline-text-3">
<p>Force push required because this is a destructive operation on the remote main</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git push origin main --force</span></span></code></pre></div>
</div>
<p>
Your git history is rewritten, but full history is still accessible with reflog.</p>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-53" class="outline-2">
<h2 id="headline-53">
Stash
</h2>
<div id="outline-text-headline-53" class="outline-text-2">
<p>The git stash command:</p>
<ul>
<li>Records the current state of your working directory and the index (staging area).</li>
<li>Like your computer&#39;s copy/paste clipboard.</li>
<li>It records those changes in a safe place</li>
<li>Reverts the working directory to match the HEAD commit</li>
</ul>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git stash
</span></span><span class="line"><span class="cl">  git stash -m <span class="s2">&#34;jdsl v0 almost working&#34;</span> <span class="c1"># adds a message</span>
</span></span><span class="line"><span class="cl">  git stash list
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git stash pop                   <span class="c1"># applies the first stash on the stack</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># applies the first stash on the stack but keeps it in the stash list</span>
</span></span><span class="line"><span class="cl">  git stash apply
</span></span><span class="line"><span class="cl">  git stash apply stash@<span class="o">{</span>2<span class="o">}</span>       <span class="c1"># Apply the third (0, 1, 2) most recent stash</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git stash drop                  <span class="c1"># removes a stash without applying</span>
</span></span><span class="line"><span class="cl">  git stash drop stash@<span class="o">{</span>2<span class="o">}</span>        <span class="c1"># Remove the third most recent stash</span></span></span></code></pre></div>
</div>
<p>
The pop command will (by default):</p>
<ul>
<li>Apply your most recent stash entry to your working directory</li>
<li>Remove it from the stash list.</li>
<li>It effectively undoes the git stash command. It gets you back to where you were.</li>
</ul>
<div id="outline-container-headline-54" class="outline-3">
<h3 id="headline-54">
Stash is a Stack Data structure
</h3>
<div id="outline-text-headline-54" class="outline-text-3">
<p>The <code>git stash</code> command stores your changes in a stack (LIFO) data structure.
That means that when you retrieve your changes from the stash:</p>
<ul>
<li>You&#39;ll always get the most recent changes first.</li>
<li>Stores index and worktree.</li>
</ul>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-55" class="outline-2">
<h2 id="headline-55">
Revert
</h2>
<div id="outline-text-headline-55" class="outline-text-2">
<p>To revert a commit, you need to know the commit hash of the commit you want to revert (git log)</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git revert &lt;commit-hash&gt;</span></span></code></pre></div>
</div>
<div id="outline-container-headline-56" class="outline-3">
<h3 id="headline-56">
Revert vs Reset
</h3>
<div id="outline-text-headline-56" class="outline-text-3">
<p><code>git reset --soft</code>: Undo commits but keep changes staged</p>
<p>
<code>git reset --hard</code>: Undo commits and discard changes</p>
<p>
<code>git revert</code>: Create a new commit that undoes a previous commit</p>
<p>
When to Reset?:</p>
<ul>
<li>Working on your own branch,</li>
<li>Just undoing something you&#39;ve already committed,</li>
<li>E.g cleaning everything up so you can open a pull request</li>
</ul>
<p>When to Revert?:</p>
<ul>
<li>Undo a change that&#39;s already on a shared/public branch (especially if it&#39;s an older change)</li>
<li>It won&#39;t rewrite any history, and therefore won&#39;t step on your coworkers&#39; toes.</li>
</ul>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-57" class="outline-2">
<h2 id="headline-57">
Diff
</h2>
<div id="outline-text-headline-57" class="outline-text-2">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  <span class="c1"># show the changes between the working tree and the last commit</span>
</span></span><span class="line"><span class="cl">  git diff
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># show the differences between the previous commit and the current state, including the last commit and uncommitted changes</span>
</span></span><span class="line"><span class="cl">  git diff HEAD~1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># show the change between two commits</span>
</span></span><span class="line"><span class="cl">  git diff COMMIT_HASH_1 COMMIT_HASH_2</span></span></code></pre></div>
</div>
<hr>
<hr>
</div>
</div>
<div id="outline-container-headline-58" class="outline-2">
<h2 id="headline-58">
Cherry-pick
</h2>
<div id="outline-text-headline-58" class="outline-text-2">
<p>Useful if you want one change from a branch but do not want the entire branch history.</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git cherry-pick &lt;commit-hash&gt;</span></span></code></pre></div>
</div>
<ul>
<li>You need a clean working tree (no uncommitted changes).</li>
<li>Identify the commit you want to cherry-pick, typically by git loging the branch it&#39;s on.</li>
</ul>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git cherry-pick &lt;commit-hash&gt;</span></span></code></pre></div>
</div>
<hr>
<hr>
</div>
</div>
<div id="outline-container-headline-59" class="outline-2">
<h2 id="headline-59">
Bisect
</h2>
<div id="outline-text-headline-59" class="outline-text-2">
<p>Git bisect is a binary search between known good and known bad commits.</p>
<ul>
<li>Finds the middle commit between a good and bad commit.</li>
<li>You check to see if that bug is present in that middle commit.</li>
<li>If not present, identify that commit as good. It then searches the middle of that and the bad commit.</li>
<li>Repeat until the bug is found.</li>
<li>Drastically cuts down time to find bugs.</li>
</ul>
<table>
<thead>
<tr>
<th class="align-right">Commits to Check</th>
<th class="align-right">Max Checks to Find</th>
</tr>
</thead>
<tbody>
<tr>
<td class="align-right">1</td>
<td class="align-right">1</td>
</tr>
<tr>
<td class="align-right">2</td>
<td class="align-right">1</td>
</tr>
<tr>
<td class="align-right">10</td>
<td class="align-right">4</td>
</tr>
<tr>
<td class="align-right">100</td>
<td class="align-right">7</td>
</tr>
<tr>
<td class="align-right">1000</td>
<td class="align-right">10</td>
</tr>
<tr>
<td class="align-right">10000</td>
<td class="align-right">14</td>
</tr>
</tbody>
</table>
<ol>
<li>
<p>Start the bisect with</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git bisect start</span></span></code></pre></div>
</div>
</li>
<li>
<p>Select a &#34;good&#34; commit with</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git bisect good &lt;commit hash&gt;     <span class="c1"># (a commit where you&#39;re sure the bug wasn&#39;t present)</span></span></span></code></pre></div>
</div>
</li>
<li>
<p>Select a &#34;bad&#34; commit with</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git bisect bad &lt;commit hash&gt;      <span class="c1"># (a commit where you&#39;re sure the bug was present)</span></span></span></code></pre></div>
</div>
</li>
<li>
<p>Git will checkout a commit between the good and bad commits. You must test to see if the bug is present in that commit.</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git show HEAD</span></span></code></pre></div>
</div>
</li>
<li>Execute <code>git bisect good</code> or <code>git bisect bad</code> to identify the current commit as good or bad.</li>
<li>Loop back to step 4 (until git bisect completes).</li>
<li>
<p>Exit the bisect mode with git bisect reset.</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git bisect reset</span></span></code></pre></div>
</div>
</li>
</ol>
<p>Then, take action to address the commit:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  <span class="c1"># you want to revert the commit that introduced the bug - first &#34;bad commit&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># you do not want to revert the last good commit</span>
</span></span><span class="line"><span class="cl">  git revert &lt;bad_commit_hash&gt;</span></span></code></pre></div>
</div>
<p>
You can refer to commits as HEAD</p>
<ul>
<li>After the first bisect, you are really just saying HEAD is good or bad</li>
<li>
<p>For each iteration you simply manually check the file for the bug</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git show HASH
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  git show HEAD                   <span class="c1"># most likely</span></span></span></code></pre></div>
</div>
</li>
<li>
<p>git has no way of knowing what is the bug</p>
<ul>
<li>You don&#39;t &#34;tell&#34; git what the bug is and/or what to search for.</li>
<li>All git is doing is finding the middle commit.</li>
</ul>
</li>
</ul>
<hr>
<div id="outline-container-headline-60" class="outline-3">
<h3 id="headline-60">
Automating git bisect
</h3>
<div id="outline-text-headline-60" class="outline-text-3">
<p>From the git bisect man page:</p>
<blockquote>
<p>Bisect run
If you have a script that can tell if the current source code is good or bad, you can
bisect by issuing the command:</p>
<p>
$ git bisect run my_script arguments</p>
<p>
Note that the script (my_script in the above example) should exit with code 0 if the
current source code is good/old, and exit with a code between 1 and 127 (inclusive),
except 125, if the current source code is bad/new.</p>
</blockquote>
<p>
This could be accomplished by a script that:</p>
<ul>
<li>searches a file for whatever the problem is</li>
<li>returns 0 if the source is good</li>
<li>returns 1+ if the source is bad</li>
</ul>
<p>For example:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  <span class="c1">#!/bin/sh</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># bisect-script.sh</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> grep -q <span class="s2">&#34;SCANNING&#34;</span> <span class="s2">&#34;scripts/scan.sh&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">      <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span></span></span></code></pre></div>
</div>
<ul>
<li>save and mark this script as executable somewhere in the repo</li>
<li>mark your good and bad initial commits</li>
</ul>
<p>Then run:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git besect run ./bisect-script.sh</span></span></code></pre></div>
</div>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-61" class="outline-2">
<h2 id="headline-61">
Worktree
</h2>
<div id="outline-text-headline-61" class="outline-text-2">
<p>A worktree (or &#34;working tree&#34; or &#34;working directory&#34;):</p>
<ul>
<li>The directory on the local filesystem where code tracked in Git lives.</li>
<li>Usually just the root of your Git repo (where the .git directory is).</li>
<li>
<p>Contains:</p>
<ul>
<li>Tracked files - files that Git knows about</li>
<li>Untracked files - files that Git doesn&#39;t know about</li>
<li>Modified files - files that Git knows about that have been changed since the last commit</li>
</ul>
</li>
</ul>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git worktree list</span></span></code></pre></div>
</div>
<p>
Stash is still useful for small and short-lived changes.
Worktrees are better for long-lived changes.</p>
<p>
Worktrees accomplish a similar goal as stash, branch, clone, etc:</p>
<ul>
<li>Allow you to work on different changes without losing work.</li>
</ul>
<p>Linked worktrees but are particularly useful when you want to:</p>
<ul>
<li>Switch back and forth between the two change sets without having to run a bunch of git commands (not branches or stash).</li>
<li>Keep a light footprint on your machine that&#39;s still connected to the main repo (not a clone).</li>
</ul>
<div id="outline-container-headline-62" class="outline-4">
<h4 id="headline-62">
The Main Worktree
</h4>
<div id="outline-text-headline-62" class="outline-text-4">
<ul>
<li>Contains the .git directory with the entire state of the repo</li>
<li>Heavy (lots of data)</li>
<li>A new main working tree requires a git clone or git init.</li>
</ul>
</div>
</div>
<div id="outline-container-headline-63" class="outline-4">
<h4 id="headline-63">
Linked Worktree
</h4>
<div id="outline-text-headline-63" class="outline-text-4">
<ul>
<li>Contains a .git file with a path to the main working tree</li>
<li>Light (essentially no data), about as light as a branch.</li>
<li>Can be complicated to work with when it comes to env files and secrets.</li>
</ul>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git worktree add &lt;path&gt; &lt;branch&gt; <span class="c1"># create a new linked worktree</span></span></span></code></pre></div>
</div>
</div>
</div>
<div id="outline-container-headline-64" class="outline-3">
<h3 id="headline-64">
No Duplicate Branches
</h3>
<div id="outline-text-headline-64" class="outline-text-3">
<p>Linked worktrees behave just like a &#34;normal&#34; git repo. You can:</p>
<ul>
<li>create new branches</li>
<li>switch branches</li>
<li>delete branches</li>
<li>create tags</li>
<li>etc</li>
</ul>
<p>BUT there is one thing you cannot do:</p>
<ul>
<li>you cannot work on a branch that is currently checked out by any other working tree (main or linked worktree).</li>
</ul>
<p>Example: try to switch to main/master from a linked worktree.</p>
<ul>
<li>tldr; you can&#39;t</li>
</ul>
</div>
</div>
<div id="outline-container-headline-65" class="outline-3">
<h3 id="headline-65">
Upstream
</h3>
<div id="outline-text-headline-65" class="outline-text-3">
<p>Linked Worktrees are tracked in the <code>.git/worktrees</code> directory</p>
<p>
When you make a commit in a linked worktree, that commit is automatically reflected in the main worktree
Why this makes sense:</p>
<ul>
<li>The linked worktree doesn&#39;t have a .git directory</li>
<li>It is not a separate repository.</li>
<li>It is just a different view of the same repository.</li>
</ul>
<p>You can almost think of a linked worktree as just another branch in the same repo but with its own space on the filesystem.</p>
</div>
</div>
<div id="outline-container-headline-66" class="outline-3">
<h3 id="headline-66">
Delete Worktrees
</h3>
<div id="outline-text-headline-66" class="outline-text-3">
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git worktree remove &lt;worktree name&gt;</span></span></code></pre></div>
</div>
<p>
NOTE</p>
<ul>
<li>This deletes the worktree: it removes the worktree directory from the filesystem</li>
<li>It does not remove the branch.</li>
<li>Run <code>git branch</code> in the main git repo and you will see the branch without the &#34;+&#34; prefix which a worktree will have.</li>
</ul>
<p>Alternatively:</p>
<ul>
<li>Delete the worktree directory manually</li>
<li>
<p>Then prune all worktrees</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git worktree prune</span></span></code></pre></div>
</div>
</li>
</ul>
<hr>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-67" class="outline-2">
<h2 id="headline-67">
Tags
</h2>
<div id="outline-text-headline-67" class="outline-text-2">
<p>A tag is a name linked to a commit that does not move between commits, unlike a branch.</p>
<p>
Tags can be created and deleted, but not modified.</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git tag                         <span class="c1"># lists all tags</span>
</span></span><span class="line"><span class="cl">  git tag -a <span class="s2">&#34;tag name&#34;</span> -m <span class="s2">&#34;tag message&#34;</span> <span class="c1"># create a tag on the current commit</span></span></span></code></pre></div>
</div>
<div id="outline-container-headline-68" class="outline-3">
<h3 id="headline-68">
Semver -  Semantic Versioning
</h3>
<div id="outline-text-headline-68" class="outline-text-3">
<p>Naming convention for software - vMajor.Minor.Patch</p>
<ul>
<li>Major: breaking changes</li>
<li>Minor: safe features</li>
<li>Patch: safe bug fixes</li>
</ul>
<p>It has two primary purposes:</p>
<ol>
<li>To give us a standard convention for versioning software</li>
<li>To help us understand the impact of a version change: if it&#39;s safe (how hard it will be) to upgrade to</li>
</ol>
</div>
</div>
<div id="outline-container-headline-69" class="outline-3">
<h3 id="headline-69">
Conventional Tags
</h3>
<div id="outline-text-headline-69" class="outline-text-3">
<p>Tags are used for all sorts of reasons. Sometimes they&#39;re used to denote releases. Tags that follow semver are common.</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">  git tag -a v3.10.2 -m <span class="s2">&#34;Fixed a lil bug&#34;</span></span></span></code></pre></div>
</div>
</div>
</div>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Virgin Tailscale vs Chad NetBird</title>
      <link>https://linuxmaxxing.com/posts/11_netbird/</link>
      <pubDate>Fri, 24 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/11_netbird/</guid>
      <description>&lt;p&gt;&#xA;Ever since I entered the rank of homelabber, I have been a Tailscale enjoyer.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;As most of you know Tailscale is a wireguard-based private mesh vpn network which can be used to link devices.&#xA;For home use this is a fantastic way to access your services from anywhere without port-forwarding or even messing with firewall rules (for the most part),&#xA;or to share services or devices with others without posting them on the public internet.&lt;/p&gt;</description>
      <content:encoded><![CDATA[
<p>
Ever since I entered the rank of homelabber, I have been a Tailscale enjoyer.</p>
<p>
As most of you know Tailscale is a wireguard-based private mesh vpn network which can be used to link devices.
For home use this is a fantastic way to access your services from anywhere without port-forwarding or even messing with firewall rules (for the most part),
or to share services or devices with others without posting them on the public internet.</p>
<p>
This is significantly more secure than port-forwarding and exposing your services (and ip) to the public web,
and is more secure than using a better but still flawed tunneling service like that offered by Clownflare.
Tailscale gives you a way to share specific devices or services to other users, be they on your same Tailscale network (tailnet),
separate accounts, and even offers a tunnelling service to expose your service - but not your ip - publicly.</p>
<p>
Tailscale really is a fantastic product which significantly improves the tech tinkering hobby we&#39;re all into.
It is geared towards the enterprise but is free for personal use.
Previously personal use was limited to a few users and up to 100 devices - which let&#39;s be honest is more than enough for any serious personal or small-business use-case.
Those limits were recently increased to up to 6 users and <em>UNLIMITED</em> devices.
Impressive.
Talk about something I would never need to pay for.</p>
<p>
But the question often comes up: how on earth is Tailscale able to offer this amount of product for free?</p>
<hr>
<div id="outline-container-headline-1" class="outline-2">
<h2 id="headline-1">
Tailscale
</h2>
<div id="outline-text-headline-1" class="outline-text-2">
<p>
Facts:</p>
<ul>
<li>Tailscale is a for-profit <strong>Canadian</strong> company (standby for tariffs on Tailscale by Orange Boomer).</li>
<li>Tailscale was founded in 2019 by former Google engineers (sus).</li>
<li>Tailscale has been VC-funded since 2020.</li>
<li>Tailscale is <em>not</em> Free Software.</li>
<li>Tailscale is <em>not</em> primarily self-hosted.</li>
<li>Tailscale is <em>not</em> <a href="https://Tailscale.com/blog/Tailscale-privacy-anonymity">anonymous</a>.</li>
<li>Tailscale requires cloud authentication provider (Google, Microsoft, github, etc) for account creation.</li>
</ul>
<div id="outline-container-headline-2" class="outline-3">
<h3 id="headline-2">
Tailscale is a Tech Company
</h3>
<div id="outline-text-headline-2" class="outline-text-3">
<p>
I am not saying this is bad in and of itself.
Tailscale was a small tech startup that resembles the so many others in this space in how it is founded, operated, and funded.
Bitwarden is a similar company that comes to mind that is also beloved by many of us.</p>
<p>
Tailscale will do what it needs to do to survive and be profitable.
Right now that means they are offering an <em>incredible</em> amount of product to us to consoom for free so they can increase their user-base and brand recognition.
They know that if we are enthusiastic about using their product at home then we will likewise be enthusiastic about their product in the workplace.</p>
<p>
I don&#39;t fault them for this at all, it&#39;s a fantastic strategy.
I have no problem with Tailscale being a for-profit company.
NetBird also is.
It is just important to remember when we consider where Tailscale could go in the future and more importantly the future revenue opportunities they could exploit in the future.
This is especially true considering the obvious problem with Tailscale: it is proprietary.</p>
</div>
</div>
<div id="outline-container-headline-3" class="outline-3">
<h3 id="headline-3">
Tailscale is neither Free Software nor Open Source
</h3>
<div id="outline-text-headline-3" class="outline-text-3">
<p>
Tailscale&#39;s clients are all open source software licensed under the BSD-3 clause license.
This is an acceptable, GPL-compatible license per <a href="https://www.gnu.org/licenses/license-list.en.html#ModifiedBSD">GNU and FSF</a>, but obviously not preferred to GPL variants for a <a href="https://lukesmith.xyz/articles/why-i-use-the-gpl-and-not-cuck-licenses/">number of reasons</a></p>
<p>
Tailscale&#39;s back-end software, however, is proprietary.
We don&#39;t exactly know what is running on their servers.
As such we cannot call Tailscale Open Source as typically advertised. This is similar to Telegram.</p>
</div>
</div>
<div id="outline-container-headline-4" class="outline-3">
<h3 id="headline-4">
Tailscale can be Self-Hosted, sort-of
</h3>
<div id="outline-text-headline-4" class="outline-text-3">
<p>
Tailscale is compatible with a self-hosted option called <a href="https://headscale.net/stable/">Headscale</a>, but this is not a one-for-one version of the Tailscale control server.
It is basically a reverse-engineered Tailscale server, like the Nouveau drivers on GNU/Linux.</p>
<p>
Headscale gives us a decent idea of how Tailscale servers are running.
It is a couple versions behind in terms of features and is not officially a Tailscale project,
though there are Tailscale employees who contribute to the project and Headscale is mentioned in Tailscale documentation.
Tailscale isn&#39;t disallowing Headscale, which is good.</p>
<p>
Headscale is Open Source, so it is an improvement. It is also something Tailscale could pull the plug on in the future.</p>
</div>
</div>
<div id="outline-container-headline-5" class="outline-3">
<h3 id="headline-5">
Tailscale is not Anonymous
</h3>
<div id="outline-text-headline-5" class="outline-text-3">
<p>
Details about Tailscale&#39;s anonymity/privacy can be found in <a href="https://Tailscale.com/blog/Tailscale-privacy-anonymity">this Tailscale blog post</a>.</p>
<p>
In short, your Tailnet is private in that only you can access it and your traffic is end-to-end encrypted.
It is not anonymous in that the location of your devices and where they are communicating is known to Tailscale.
And they do collect what they describe as necessary telemetry.</p>
<p>
To be fair, this is exactly as I would have expected and is probably a decent trade-off this for certain devices/services I need remote access to.
I appreciate Tailscale being straightforward about this.</p>
<p>
That being said, I can&#39;t help but wonder what they could be doing with this data now or in the future, particularly when there is a greater need for &#34;line go up&#34; by the VC.</p>
<p>
Tailscale also requires some kind of slop account for authentication, be it github, Google, Microsoft, Apple, etc.
There is no ability to just have an email account with Tailscale and 2FA from an authenticator app of your choosing.</p>
<p>
This was the first red flag I experienced with Tailscale, but something I could accept considering what I got in return.
Which is generally how anyone continues to use these kinds of slop services:</p>
<ul>
<li>&#34;I use github because everyone is on github and I get the most traction there&#34;</li>
<li>&#34;I use google because the product and ui are so convenient&#34;</li>
<li>&#34;I use apple so I can flex on the poors of the world. I also have a Tesla on lease.&#34;</li>
</ul>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-6" class="outline-2">
<h2 id="headline-6">
NetBird
</h2>
<div id="outline-text-headline-6" class="outline-text-2">
<p>
Facts:</p>
<ol>
<li>
<p>Founded in 2022 to address the two major shortcomings of Tailscale</p>
<ul>
<li>Proprietary back-end</li>
<li>Cannot be self-hosted</li>
</ul>
</li>
<li>For-profit <strong>German</strong> company.</li>
<li>Outside-funded, probably VC, just to a far less degree (for now).</li>
<li>Fully Open Source</li>
<li>Primarily self-hosted</li>
<li>Does not require cloud authentication providers</li>
</ol>
<div id="outline-container-headline-7" class="outline-3">
<h3 id="headline-7">
Also a Tech Company
</h3>
<div id="outline-text-headline-7" class="outline-text-3">
<p>
NetBird is still a for-profit company. Any issues you might have with Tailscale for that reason you will also have with NetBird.</p>
</div>
</div>
<div id="outline-container-headline-8" class="outline-3">
<h3 id="headline-8">
Also (likely) Not Anonymous
</h3>
<div id="outline-text-headline-8" class="outline-text-3">
<p>
I have no reason to believe NetBird cannot see the same metadata that Tailscale can see.
I haven&#39;t checked the source code, but that&#39;s the key difference - I <em>can</em> check the source code for their servers (control plane), assuming that is actually what their severs are running.</p>
</div>
</div>
<div id="outline-container-headline-9" class="outline-3">
<h3 id="headline-9">
Also BSD-3 Clause Licensed.
</h3>
<div id="outline-text-headline-9" class="outline-text-3">
<p>
The clients and server are licensed under the same license as the Tailscale clients.</p>
</div>
</div>
<div id="outline-container-headline-10" class="outline-3">
<h3 id="headline-10">
Also Free for Personal Use
</h3>
<div id="outline-text-headline-10" class="outline-text-3">
<p>
NetBird offers their service for free for personal use with up to 5 users and 100 devices.
I wonder what pressured Tailscale to increase their personal-tier device limit?…</p>
</div>
</div>
<div id="outline-container-headline-11" class="outline-3">
<h3 id="headline-11">
Key Differences
</h3>
<div id="outline-text-headline-11" class="outline-text-3">
<p>
NetBird has always been fully open-source and primarily self-hosted. Historically NetBird&#39;s public servers lagged in features compared a self-hosted NetBird server.</p>
<p>
This is the fundamental difference that drastically changes the long-term viability and acceptability of NetBird compared to Tailscale.</p>
<p>
Regardless of feature parity or lack thereof, NetBird&#39;s model is <em>much</em> better than that of Tailscale.
It is much more in-line with the various corporate GNU/Linux companies - Red Hat, Canonical, SUSE, etc.
Now I am no fan of corporate GNU/Linux compared to community projects, but that is because of the behaviour of the companies themselves and not how they or their projects are structured.
I am no fan of SystemD but it is <em>hecking-based</em> free software licensed under the GPL.</p>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-12" class="outline-2">
<h2 id="headline-12">
Feature Comparison
</h2>
<div id="outline-text-headline-12" class="outline-text-2">
<p>
In my use of NetBird I&#39;ve found the features are by-and-large the same. I would rate it somewhere around 90% feature parity.
There are things NetBird does better than Tailscale, there are things Tailscale does better than NetBird.</p>
<p>
For now I am not self-hosting NetBird and have not been using Headscale.
These comparisons are based on cloud-provided NetBird and Tailscale which I assume most users are or will be using.</p>
<div id="outline-container-headline-13" class="outline-3">
<h3 id="headline-13">
Performance
</h3>
<div id="outline-text-headline-13" class="outline-text-3">
<p>
No difference in terms of speed or latency. NetBird put out a <a href="https://youtu.be/McCluatv9lw">video</a> demonstrating this, assuming the data are accurate.</p>
<p>
The only thing I notice is my android client takes a few seconds to connect to NetBird, whereas it was pretty instant with Tailscale.</p>
</div>
</div>
<div id="outline-container-headline-14" class="outline-3">
<h3 id="headline-14">
GNU/Linux Clients
</h3>
<div id="outline-text-headline-14" class="outline-text-3">
<div id="outline-container-headline-15" class="outline-4">
<h4 id="headline-15">
Installation
</h4>
<div id="outline-text-headline-15" class="outline-text-4">
<p>The official method of installing both Tailscale and NetBird is by the classic <code>curl | sudo sh</code> method.
My favourite for security.</p>
</div>
</div>
<div id="outline-container-headline-16" class="outline-4">
<h4 id="headline-16">
Distributions
</h4>
<div id="outline-text-headline-16" class="outline-text-4">
<p>Fortunately the Tailscale client is found in most distribution package managers so you can get around running arbitrary scripts as root.
The Tailscale repository can be added via the <a href="https://pkginfo.devuan.org/cgi-bin/package-query.html?c=package&amp;q=extrepo=0.15&amp;eXtra=66.177.10.151">extrepo</a> (external repository) package in Debian and derivatives.
NetBird additionally provides instructions for adding their repository to Ubuntu, and this works on any Debian-based distribution.</p>
<p>
NetBird is in <a href="https://pkgs.alpinelinux.org/package/edge/community/x86_64/NetBird">Alpine&#39;s Community repository</a>, and an (of course) out-of-date package is in <a href="https://search.nixos.org/packages?channel=26.05&amp;query=NetBird#show=NetBird">nix</a>.</p>
<p>
Recently the incredibly popular <a href="https://codeberg.org/joshuablais/renaissance">renaissance guix channel</a> added NetBird. NetBird-ui is still a work-in-progress.</p>
</div>
</div>
<div id="outline-container-headline-17" class="outline-4">
<h4 id="headline-17">
Init Systems
</h4>
<div id="outline-text-headline-17" class="outline-text-4">
<p>
Tailscale officially supports SystemD but runit and openrc service files can be found on Gentoo, alpine, and artix.
I had to roll my own sysvinit Tailscale service on Devuan.</p>
<p>
NetBird supports openrc, sysvinit, and I assume runit.
Big W for NetBird in this regard, not that service files are difficult to write (contrary to what the Big SystemD lobby might lead you to believe).</p>
<p>
NetBird has a GUI program with system tray, Tailscale does not.
Not a huge deal and is not something I care much about.
I believe the GUI app is just a web wrapper anyway.</p>
</div>
</div>
<div id="outline-container-headline-18" class="outline-4">
<h4 id="headline-18">
CLI
</h4>
<div id="outline-text-headline-18" class="outline-text-4">
<p>NetBird&#39;s status command only offers information about the device you are on, whereas Tailscale&#39;s status command provides information about all the clients on your tailnet.
I prefer Tailscale here, but it is not a deal breaker.</p>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-19" class="outline-3">
<h3 id="headline-19">
Management
</h3>
<div id="outline-text-headline-19" class="outline-text-3">
<p>
Basically feature parity for management via the web ui.</p>
<p>
Both services allow you to pool devices/services into groups and set policies and routing rules based on group, device, and user.
Essentially both have their own built-in firewalls that are easy to figure out.</p>
<p>
NetBird has additional features and is more intuitive in my opinion (minding any recency bias).
NetBird gives you a cool routing graph so you have a nice visual of allowed and disallowed routes on you mesh network.
I provide a few more reasons why NetBird&#39;s management interface is better throughout this post.</p>
</div>
</div>
<div id="outline-container-headline-20" class="outline-3">
<h3 id="headline-20">
DNS
</h3>
<div id="outline-text-headline-20" class="outline-text-3">
<p>
Both offer the ability to customize DNS settings.
You can use their own DNS servers or specify your own within the respective management interfaces.</p>
<p>
Both services offer ip addresses in the 100.x.x.x range.</p>
<p>
Both client services will overwrite <code>/etc/resolv.conf/</code> with their own DNS values.</p>
<p>
Tailscale has a feature called MagicDNS which can be used with their DNS servers.
This allows you use the machine name (e.g. <code>alpine-vm</code>) instead of the Tailscale ip (<code>100.x.x.x</code>).
NetBird also has this feature, it just isn&#39;t named anything.</p>
<p>
Both services allow you to use your own domain name on the respective VPN mesh network.
As of now on NetBird this is only available if self-hosting, but I anticipate that changing in the future.
NetBird&#39;s is a bit round about. NetBird&#39;s Reverse Proxy is used for exposing services to the web like a tunnel and requires your own domain.
You can limit that exposure to just your NetBird network if your control plane is self-hosted, whereas you cannot (as of now) if using NetBird&#39;s cloud servers.</p>
<p>
Tailscale goes a step further and issues you a <code>ts.net</code> address for your tailnet which can be used to access each service with valid https certs via Let&#39;s Encrypt.</p>
<p>
Further you can change this <code>ts.net</code> address to be a random pairing of words.
I cycled through a bunch of combos until I got two words that were short and easy to remember, and then accessed any of my services with <code>https://servicename.word1-word2.ts.net</code>.
There was a bit of setup with via <code>Tailscale serve</code>, but worked extremely well once set up.
This is probably the nicest feature you don&#39;t get with NetBird unless you self-host and purchase a domain (for now).
NetBird does allow you to access your devices with the FQDN <code>devicename.NetBird.cloud</code>, but cannot be done via https and still requires ports specification.</p>
<p>
Tailscale also has the ability to completely turn off MagicDNS so that your service or machine is only getting DNS from your LAN or provider of your choosing.
Basically Tailscale will no longer overwrite <code>/etc/resolv.conf</code>.
This means you can still access the service or device from the Tailscale ip, but the device will not be able to access other devices on the tailnet since it has no way of looking up tailnet ip addresses.
This was really useful on my two proxmox hosts which often lost DNS due to Tailscale&#39;s overwriting of <code>/etc/resolv.conf</code>, and I basically made this the standard on all my physical servers.
I had zero DNS issues on vms or containers with Tailscale DNS.</p>
<p>
NetBird does not have this option that I know of. BUT in my testing I have not had any DNS issues on any of my hosts.</p>
<p>
For both Tailscale and NetBird I set custom DNS addresses to point to the mesh and LAN ip of my adguard instances, basically ensuring I am using my adguard instances wherever I am.
NetBird takes this a step further in that you can set DNS servers on a per-group basis.
So assuming I had DNS issues on my proxmox hosts, I would just set the proxmox group to use quad-9 or some other public DNS instead of or in addition to adguard to ensure I am maintaining DNS on these devices.
These devices are (hopefully) are not reaching out to the web for anything other than updates anyway, so it doesn&#39;t really matter.
Whereas I might always want to use DNS with ad-blocking and/or unbound on a laptop or desktop.</p>
<p>
From what I could figure out, Tailscale only allows DNS servers to be set for the entire tailnet, which is why I disabled MagicDNS on certain problematic devices.</p>
</div>
</div>
<div id="outline-container-headline-21" class="outline-3">
<h3 id="headline-21">
Exit Nodes
</h3>
<div id="outline-text-headline-21" class="outline-text-3">
<p>
Both Tailscale and NetBird allow you to set up an exit node: a device through which mesh network traffic can pass through.
For home users this gives you the ability to access your actual LAN from the outside, rather than just the devices on your LAN.
You&#39;ll even get a LAN ip.</p>
<p>
This is great for accessing devices which cannot have a NetBird/Tailscale client, or when you&#39;re at the devicemaxxing level and have &gt; 100 clients and still want to access stuff remotely.</p>
<p>
NetBird allows you to toggle the use of an exit node through their UI app.
NetBird also allows you specify exit node routes, for instance you can establish a route to a specific device on your LAN, rather than your entire LAN.
You can additionally have a group of exit nodes.</p>
<p>
Tailscale does not have this amount of refinement and ease of setup (all through NetBird webgui), and to be honest I could never get exit nodes to properly work in Tailscale.
It was literally point, click, and verify in NetBird.</p>
</div>
</div>
<div id="outline-container-headline-22" class="outline-3">
<h3 id="headline-22">
Exposing Services to the Web
</h3>
<div id="outline-text-headline-22" class="outline-text-3">
<p>
This is where NetBird starts to break away from Tailscale (or will in the future).
NetBird has Reverse Proxy (beta) and authentication services built in to the control plane which basically turns NetBird potentially into a one-stop shop for hosting.
In fact you could use your self-hosted NetBird control plane on a VPS to be the reverse proxy and authentication engine for services running on the same VPS.</p>
<p>
For exposing something like a personal gaming server at home, this seems like a no-brainer short of using another service which specializes in this sort of stuff.</p>
<p>
There are some caveats to this (e.g. requires Traefik, mixed opinions on that) and is in beta.
Additionally I&#39;ve seen some comments about very high CPU usage compared to similar services like Pangolin.
But it is clear they are offering much more than a Clownflare Tunnel clone.</p>
<p>
Tailscale offers a service called Tailscale Funnel which to nobody&#39;s surprise is a clone of Clownflare&#39;s Tunnel. It works as intended.</p>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-23" class="outline-2">
<h2 id="headline-23">
Conclusion
</h2>
<div id="outline-text-headline-23" class="outline-text-2">
<p>
I don&#39;t hate Tailscale and it has worked perfectly fine for me for a long time.
Tailscale hasn&#39;t wronged me from a user standpoint and I have no reason to believe they are doing anything nefarious, despite their servers being proprietary.
If it were my only option I&#39;d probably still use it until I get on the elite <a href="https://yggdrasil-network.github.io/">ygggggggggdrasil</a> level.</p>
<p>
But given the foundational benefits of NetBird, there is no reason for me to use Tailscale over it.</p>
<p>
Additionally NetBird has tangible benefits over Tailscale:</p>
<ul>
<li>Management UI</li>
<li>DNS settings</li>
<li>Exit Nodes</li>
<li>Exposing Services</li>
</ul>
<p>But I still find Tailscale to be superior in the some areas:</p>
<ul>
<li>Device limit</li>
<li>Device support (GNU/Linux distro availability)</li>
<li>Client performance (startup time and cli)</li>
<li>Tailnet domain name w/ TLS certs</li>
<li>Maturity</li>
</ul>
<p>Inertia is going to be the biggest driver here as swapping clients on each device is no small task particularly with the potential of downtime.
This can be streamlined to an great with access keys on NetBird, but each device has to be added before ssh can be enabled (from my testing).
This can of course all be automated via shell scripts or Ansible, but it another thing that can go awry and require development and troubleshooting.</p>
<p>
Tailscale and NetBird <em>can</em> run in parallel, but I bet you can guess the main issue you&#39;ll have - DNS.
You need to set up a way for one service to start first, most easily done with service dependencies, or starting one on boot and another some time after boot via cron or timer.
I still had occasional DNS issues when running both in parallel (competing <code>/etc/resolv.conf</code> changes).</p>
<p>
After only doing a bit of testing, I decided to the rip the bandaid off, enabled NetBird and disabled Tailscale on all devices, and hoped for the best.
So far so good.</p>
<p>
If you&#39;ve been using Tailscale or have been resorting to less-secure means for accessing your devices or services, give NetBird a try!
I will be giving NetBird a deep-dive over the coming months and will update the site with my findings.</p>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Guix Part 2: Desktop Setup</title>
      <link>https://linuxmaxxing.com/posts/10_guix/</link>
      <pubDate>Wed, 01 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/10_guix/</guid>
      <description>&lt;div id=&#34;outline-container-headline-1&#34; class=&#34;outline-2&#34;&gt;&#xA;&lt;h2 id=&#34;headline-1&#34;&gt;&#xA;Correction&#xA;&lt;/h2&gt;&#xA;&lt;div id=&#34;outline-text-headline-1&#34; class=&#34;outline-text-2&#34;&gt;&#xA;&lt;p&gt;&#xA;One of the best features of any GNU project is the Reference Manual&#xA;which typically ships in the texinfo format, commonly called info pages.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;Prior to using Emacs I absolutely hated info pages.&#xA;The terminal info viewer is basically a stripped-down version of emacs similar to &lt;code&gt;mg&lt;/code&gt; and does not provide a great reading experience compared to &lt;code&gt;man&lt;/code&gt; pages.&lt;/p&gt;&#xA;&lt;p&gt;&#xA;But within Emacs info pages are fantastic, especially when you&#xA;leverage Emacs features like bookmarks. So as I cracked open the Guix&#xA;Info Page I found this blurb:&lt;/p&gt;</description>
      <content:encoded><![CDATA[
<div id="outline-container-headline-1" class="outline-2">
<h2 id="headline-1">
Correction
</h2>
<div id="outline-text-headline-1" class="outline-text-2">
<p>
One of the best features of any GNU project is the Reference Manual
which typically ships in the texinfo format, commonly called info pages.</p>
<p>
Prior to using Emacs I absolutely hated info pages.
The terminal info viewer is basically a stripped-down version of emacs similar to <code>mg</code> and does not provide a great reading experience compared to <code>man</code> pages.</p>
<p>
But within Emacs info pages are fantastic, especially when you
leverage Emacs features like bookmarks. So as I cracked open the Guix
Info Page I found this blurb:</p>
<blockquote>
<p>(2) We used to refer to Guix System as “Guix System Distribution” or
“GuixSD”.  We now consider it makes more sense to group everything under
the “Guix” banner since, after all, Guix System is readily available
through the ‘guix system’ command, even if you’re using a different
distro underneath!</p>
</blockquote>
<p>
This must have been a recent change, I did not realize Guix System and GuixSD just became Guix. I referred to Guix (the distro) as Guix System in my last post.
There are other parts of the manual that still refer to Guix System when differentiating from Guix the package manager on &#34;foreign distros,&#34; so perhaps the change has not propagated.</p>
<hr>
<hr>
</div>
</div>
<div id="outline-container-headline-2" class="outline-2">
<h2 id="headline-2">
Default Programs
</h2>
<div id="outline-text-headline-2" class="outline-text-2">
<p>I failed to mention this in my first post, but Guix ships with a couple nice packages.
The first is <code>mg</code>, also known as microemacs, as mentioned above.
As an Emaxxer I am mildly hampered by the fact that your default out-of-the-box editor is almost always <code>vi</code>.
Sometimes nano is installed as well.</p>
<p>
<code>mg</code> is to Emacs what <code>vi</code> is to vim. It is an emacs-like environment with vanilla bindings and basic features (e.g. dired) maintained by the OpenBSD team.</p>
<p>
It reminds me a ton of the default <code>info</code> page reader (which is why most do not like reading <code>info</code> pages).
However the editing experience is much closer to emacs than <code>vi</code> is to vim and I have zero issues using it when on a server or container and don&#39;t want the full Emacs package, or when already in a terminal and don&#39;t want to fire up Emacs.
It is not surprising that Guix ships with it considering guix is basically a convoluted way of Emaxxing an entire distro.</p>
<p>
Guix ships with the <code>findutils</code> package which contains the <code>locate</code> and <code>updatedb</code> commands. On other distros this is usually found in the <code>plocate</code> package.
I prefer <code>plocate</code> for speed reasons, but having the same functionality out of the box is nice since it is pretty much a must-have on guix unless you are a <code>find</code> command wizard.</p>
<p>
In order to use <code>plocate</code> on guix it appears the <code>plocate</code> group is required.
Additionally there is an <code>updatedb</code> cron/herd timer service, so with that in mind I will just
stick with the default <code>findutils</code> version rather than bloating up my system with a single extra group and package</p>
<hr>
</div>
</div>
<div id="outline-container-headline-3" class="outline-2">
<h2 id="headline-3">
Dollar Path
</h2>
<div id="outline-text-headline-3" class="outline-text-2">
<div id="outline-container-headline-4" class="outline-3">
<h3 id="headline-4">
Shebangs
</h3>
<div id="outline-text-headline-4" class="outline-text-3">
<p>I have followed <a href="https://blog.daveeddy.com/">Dave Eddy</a> for a while and love his bash content. One
thing he is adamant about is not calling bash with
<code>#!/bin/bash</code> and instead using <code>#!/usr/bin/env bash</code>. This is because the
<code>bash</code> executable is not located in <code>/bin</code> in macOS and it potentially renders the script unable to call locally-installed versions
of bash depending on the installation path, so <code>#!/usr/bin/env bash</code>
should be used instead as a catch-all that will always work.
I basically said &#34;whatever bro&#34; to this advice as I will never use macOS.
However in Guix and I presume Nix there is no <code>/bin/bash</code> symlink.
So yeah, Dave was right, make your bash shebangs <code>#!/usr/bin/env bash</code>.</p>
</div>
</div>
<div id="outline-container-headline-5" class="outline-3">
<h3 id="headline-5">
echo $PATH
</h3>
<div id="outline-text-headline-5" class="outline-text-3">
<p>All your system executable files are ultimately symlinked to <code>/gnu/store</code>. By default you should have the following directories or something similar in your <code>PATH</code>:</p>
<ul>
<li>$HOME/.config/guix/current/bin</li>
<li>$HOME/.guix-profile/bin</li>
<li>/run/setuid-programs</li>
<li>$HOME/.config/guix/current/bin</li>
<li>$HOME/.guix-profile/bin</li>
<li>/run/current-system/profile/bin</li>
<li>/run/current-system/profile/sbin</li>
</ul>
<p>If you use guix-home, you&#39;ll have the following added to your path from the <code>~/.profile</code> symlinked file:</p>
<ul>
<li>$HOME/.guix-home/profile/bin</li>
<li>$HOME/.guix-home/profile/sbin</li>
</ul>
<p>If you follow the <a href="https://guix.gnu.org/manual/1.5.0/en/html_node/Miscellaneous-Services.html#:~:text=Nix%20service">Nix service on Guix</a> instructions, you&#39;ll end up adding:</p>
<ul>
<li>$HOME/.nix-profile/bin</li>
</ul>
<p>On most distros users typically add <code>~/.local/bin</code> to <code>PATH</code> via .bash_profile (or similar) and stick custom executable files in that directory.
You can still do this with Guix since your $HOME directory is still fair game.
You cannot create <code>/usr/local/bin</code> however.</p>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-6" class="outline-2">
<h2 id="headline-6">
Dotfiles
</h2>
<div id="outline-text-headline-6" class="outline-text-2">
<p>Previously I used <code>~/.profile</code> for most environmental variables and other stuff, but <code>~/.profile</code> is a
symlink used by guix-home to set the <code>HOME_ENVIRONMENT</code> variable.
If you choose not to use guix-home, this won&#39;t be an issue, but guix-home is one of the biggest selling points of guix.</p>
<p>
Instead I use the default profile file, <code>~/.bash_profile</code>, as a launching point. All environmental
variables are declared in <code>~/.config/shell/profile</code>, which is the same
directory all my aliases file. Then all Guix and Nix sourcing happens
in <code>~/.bash_profile</code>. I don&#39;t use <code>zsh</code> because I am a <a href="https://youtu.be/5Wz9BkzU1zY">GNUTard</a>.</p>
<p>
Here&#39;s my <code>~/.bash_profile</code> which has conditionals for Guix system, Guix package manager on a foreign distro, and Nix package manager.
It works on any distro.
The <code>XDG_DATA_DIRS</code> environment variable is also modified to include .desktop files provided by both nix and flatpak:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/bin/sh
</span></span></span><span class="line"><span class="cl"><span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.profile&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> . <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.profile&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/shell/profile&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> . <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/shell/profile&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.bashrc&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> . <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.bashrc&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">####### GUIX SYSTEM</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Merge search-paths from multiple profiles. Order matters.</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -e /run/current-system/configuration.scm <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>guix package --search-paths <span class="se">\
</span></span></span><span class="line"><span class="cl">-p <span class="nv">$HOME</span>/.config/guix/current <span class="se">\
</span></span></span><span class="line"><span class="cl">-p <span class="nv">$HOME</span>/.guix-home/profile <span class="se">\
</span></span></span><span class="line"><span class="cl">-p <span class="nv">$HOME</span>/.guix-profile <span class="se">\
</span></span></span><span class="line"><span class="cl">-p /run/current-system/profile<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Prepend setuid programs.</span>
</span></span><span class="line"><span class="cl">    <span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span>/run/setuid-programs:<span class="nv">$PATH</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">####### GUIX PACKAGE MANAGER</span>
</span></span><span class="line"><span class="cl"><span class="nv">GUIX_PROFILE</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.guix-profile&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$GUIX_PROFILE</span><span class="s2">/etc/profile&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    . <span class="s2">&#34;</span><span class="nv">$GUIX_PROFILE</span><span class="s2">/etc/profile&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">unset</span> GUIX_PROFILE
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">GUIX_PROFILE</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/guix/current&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$GUIX_PROFILE</span><span class="s2">/etc/profile&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    . <span class="s2">&#34;</span><span class="nv">$GUIX_PROFILE</span><span class="s2">/etc/profile&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">unset</span> GUIX_PROFILE
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">####### GUIX HOME</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.guix-home/setup-environment&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> . <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.guix-home/setup-environment&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">####### NIX PACKAGE MANAGER</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -d <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.nix-profile/bin&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.nix-profile/bin:</span><span class="nv">$PATH</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -d <span class="s2">&#34;/nix/var/nix/profiles/default/bin&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">&#34;/nix/var/nix/profiles/default/bin:</span><span class="nv">$PATH</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span> -e <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.nix-profile/etc/profile.d/nix.sh&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    . <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.nix-profile/etc/profile.d/nix.sh&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span> -e <span class="s2">&#34;/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    . <span class="s2">&#34;/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">####### Nix desktop files</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -d <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.nix-profile/share&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nb">export</span> <span class="nv">XDG_DATA_DIRS</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.nix-profile/share</span><span class="si">${</span><span class="nv">XDG_DATA_DIRS</span><span class="p">:+:</span><span class="nv">$XDG_DATA_DIRS</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">####### flatpak desktop files</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -d <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/share/flatpak/exports/share&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nb">export</span> <span class="nv">XDG_DATA_DIRS</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/share:</span><span class="nv">$HOME</span><span class="s2">/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:</span><span class="si">${</span><span class="nv">XDG_DATA_DIRS</span><span class="k">:-</span><span class="p">/usr/local/share:/usr/share</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">####### Reset ~/.local/bin on guix</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/bin:</span><span class="nv">$PATH</span><span class="s2">&#34;</span></span></span></code></pre></div>
</div>
<div id="outline-container-headline-7" class="outline-3">
<h3 id="headline-7">
Guix Home for Managing Dotfiles
</h3>
<div id="outline-text-headline-7" class="outline-text-3">
<p>I do not plan to declaratively define my dotfiles using guix-home using <a href="https://guix.gnu.org/manual/devel/en/html_node/Essential-Home-Services.html#:~:text=home%2Dfiles%2Dservice%2Dtype">home-files-service-type</a>, it just doesn&#39;t make sense since I use them on other linux distros.
I will continue to use <a href="https://www.gnu.org/software/stow/">GNU Stow</a> as I like the flexibility it provides. I keep all personal git repos in one root directory and symlink files out as needed.</p>
<p>
In fact I actually have 2 dotfiles repos: one minimal containing just the things needed for a headless environment, and another containing just the desktop related stuff.
That way I can keep the files lean on servers/containers and bloated on desktops.
Plus a scripts repo which I symlink to <code>~/.local/bin</code>.
This is an easy task to accomplish with GNU Stow since it is just a symlink manager, but would not be possible using the <a href="https://www.atlassian.com/git/tutorials/dotfiles">git bare repo method</a> for managing dotfiles.
Continuing to use GNU Stow also seems right in-line with the Emacs/Guix/GNUTard pipeline.</p>
<p>
<em>However</em>, guix also has <a href="https://guix.gnu.org/manual/devel/en/html_node/Essential-Home-Services.html#:~:text=home%2Ddotfiles%2Dservice%2Dtype">home-dotfiles-service-type</a> which effectively functions as a hybrid between GNU Stow and <code>home-files-service-type</code>.
You declare your dotfiles repos in your guix home config, and guix home will create symlinks to a created copy in <code>/gnu/store</code>.
This is a best of both world&#39;s approach. It gives you the stability and roll-back features of <code>home-files-service-type</code> and the flexibility of an independent dotfiles repo.
It also prevents you from having to run something like <code>stow -R -t $HOME --no-folding .</code> every time you add a file to your dotfiles repo, but it does require re-configuring guix home for every edit.
For now I&#39;m holding off on using this as your dotfiles become managed in the gnu store, which means on-the-fly edits are not possible without running <code>guix home reconfigure</code> afterward.</p>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-8" class="outline-2">
<h2 id="headline-8">
Desktop Mods
</h2>
<div id="outline-text-headline-8" class="outline-text-2">
<p>
Surprising to me, though I suppose not surprising when you consider GNU&#39;s history, Guix defaults to Gnome and retains Gnome tools by default.
The default login manager is gdm, though they support a somewhat limited number of other <a href="https://guix.gnu.org/manual/1.5.0/en/html_node/X-Window.html">login managers</a>.
Having tried a number of them, I would just stick to the default gdm.
It Just Werks<sup>TM</sup> on Guix.
LXDM and ly are my favorite, but those are not included in the Guix repos.</p>
<div id="outline-container-headline-9" class="outline-3">
<h3 id="headline-9">
Gnome Keyring, ssh/gpg-agent Woes:
</h3>
<div id="outline-text-headline-9" class="outline-text-3">
<p>Without getting in the configuration weeds, all alternatives to gdm (lightdm, tty+startx, etc) required typing your keyring password in after every login out of the box.
Since your gpg and ssh keys are also stored in your keyring, you&#39;ll need to enter those passwords as well depending on your setup.
In my case emacs sources <code>~/.authinfo.gpg</code> immediately on startup, and I launch emacs automatically on login
This results in a race condition where I needed to enter my keyring password before emacs could read <code>~/.authinfo.gpg</code>, else I had to additionally type in my gpg passphrase.</p>
<p>
Additionally your ssh-agent will be affected by how you log in. When using the default gcr (gnome) gpg agent, logging in from gdm works fine.
Launching X from the tty resulted in dbus-related errors so that ssh refused using gcr as an ssh-agent.
I managed to get gpg-agent to work as an ssh agent, but that involved adding a couple of lines to my <code>~/.xinitrc</code> to export the GPG_TTY environmental variable among other things.
The keyring unlock race condition still existed when launching X from tty.</p>
<p>
I had all kinds of bugs using slim which is otherwise a good but underused login manager.
When logging out of any DE or WM, slim would pop up from within the DE/WM and lock up.</p>
<p>
If you are really particular about your login manager, Guix is probably not the system for you unless you&#39;re ready to create your own packages and services.
This isn&#39;t so much a guix problem, more of a dubs/keyring/secret-service problem that nobody enjoys troubleshooting.
This is also why it is a not-uncommon practice to have password-less gpg and ssh keys, something I will not do because schizo.</p>
<p>
I could go down the path of troubleshooting all this stuff and trying to get a perfect solution, but it&#39;s something I really don&#39;t care much about.
If I can get a consistently working solution with all little mucking as possible, I&#39;m going to take that every time.</p>
<p>
I haven&#39;t even addressed the fact that some programs also have hard-coded requirements for gnome-keyring even though pass and keepassxc can both function as secret-service alternatives.</p>
</div>
</div>
<div id="outline-container-headline-10" class="outline-3">
<h3 id="headline-10">
Window Managers
</h3>
<div id="outline-text-headline-10" class="outline-text-3">
<p>Openbox worked fine out of the box, though I haven&#39;t touched my openbox config in
years. There is not a menu-generation package in Guix/Nix which is expected as most of those are no longer maintained, so you&#39;ll just have to edit the xml directly.</p>
<p>
Before using dwm I used <a href="https://github.com/conformal/spectrwm">spectrwm</a> (formerly called scrotwm).
It is a very nice dwm-clone which uses a plain-text config file.
Think of it as dwm with sane defaults, a few useful patches, and an extremely minimal bar that is just a shell script.</p>
<p>
This spectrwm package manager is included in guix, however I could not get it to launch from gdm.
It ends up logging out to a locked gdm screen that won&#39;t accept password input.
Side note: I had similar errors with gdm when using an <code>~/.xsession</code> file.
I have yet to troubleshoot this spectrwm launching issue, though I found a reddit dot com post from 4 years describing the same behavior.</p>
</div>
</div>
<div id="outline-container-headline-11" class="outline-3">
<h3 id="headline-11">
Suckless Software
</h3>
<div id="outline-text-headline-11" class="outline-text-3">
<p>In guix you cannot just <code>git clone &lt;repo&gt; &amp;&amp; cd &lt;repo&gt; &amp;&amp; sudo make clean install</code> like you&#39;ll see in tutorials.
For starters you generally never have a need to blanket <code>make install</code> as root, your makefile should be set to a local directory (<code>~/.local</code>) unless a root directory is explicitly needed.</p>
<p>
In guix the ideal solution is to package your own builds of suckless software (or anything else you&#39;d otherwise compile).
I&#39;ll go down this route eventually, but in the short-term I just needed something to build to the suckless starter-pack: dmenu, dwm, slstatus, and st.</p>
<p>
I ended up just cloning my repos as normal, changing to the repo directory, and then launching <code>guix shell</code> with the required compilation dependencies.
Then I ran <code>make</code> and <code>make install</code> with a environmental variables to get <code>gcc</code> and function properly and to install to <code>~/.local/bin</code>.
Ideally you create a <code>manifest.scm</code> file for repeatable <code>guix shell</code> environments, but in my case I just wrote a shell script which I could use on each of the 4 suckless packages I use:</p>
<div class="src src-shell">
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/bin/sh
</span></span></span><span class="line"><span class="cl"><span class="nb">set</span> -e
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="s2">&#34;</span><span class="k">$(</span>dirname <span class="s2">&#34;</span><span class="nv">$0</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">guix shell <span class="se">\
</span></span></span><span class="line"><span class="cl">     gcc-toolchain pkg-config make <span class="se">\
</span></span></span><span class="line"><span class="cl">     libx11 libxft libxinerama fontconfig freetype <span class="se">\
</span></span></span><span class="line"><span class="cl">     -- make <span class="se">\
</span></span></span><span class="line"><span class="cl">     <span class="nv">CC</span><span class="o">=</span>gcc <span class="se">\
</span></span></span><span class="line"><span class="cl">     <span class="nv">X11INC</span><span class="o">=</span><span class="k">$(</span>guix build libx11<span class="k">)</span>/include <span class="se">\
</span></span></span><span class="line"><span class="cl">     <span class="nv">X11LIB</span><span class="o">=</span><span class="k">$(</span>guix build libx11<span class="k">)</span>/lib <span class="se">\
</span></span></span><span class="line"><span class="cl">     <span class="nv">FREETYPEINC</span><span class="o">=</span><span class="k">$(</span>guix build freetype<span class="k">)</span>/include/freetype2
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">guix shell <span class="se">\
</span></span></span><span class="line"><span class="cl">     gcc-toolchain pkg-config make libx11 libxft libxinerama fontconfig freetype <span class="se">\
</span></span></span><span class="line"><span class="cl">     -- make <span class="se">\
</span></span></span><span class="line"><span class="cl">     <span class="nv">CC</span><span class="o">=</span>gcc <span class="se">\
</span></span></span><span class="line"><span class="cl">     <span class="nv">X11INC</span><span class="o">=</span><span class="k">$(</span>guix build libx11<span class="k">)</span>/include <span class="se">\
</span></span></span><span class="line"><span class="cl">     <span class="nv">X11LIB</span><span class="o">=</span><span class="k">$(</span>guix build libx11<span class="k">)</span>/lib <span class="se">\
</span></span></span><span class="line"><span class="cl">     <span class="nv">FREETYPEINC</span><span class="o">=</span><span class="k">$(</span>guix build freetype<span class="k">)</span>/include/freetype2 <span class="se">\
</span></span></span><span class="line"><span class="cl">     <span class="nv">PREFIX</span><span class="o">=</span>~/.local <span class="se">\
</span></span></span><span class="line"><span class="cl">     install clean</span></span></code></pre></div>
</div>
<p>
Not strictly necessary, but I just lumped in every dependency I needed for all 4 programs in one shell.
Those dependencies will depend on which features are patched in your builds.</p>
</div>
</div>
<div id="outline-container-headline-12" class="outline-3">
<h3 id="headline-12">
Launching X from tty
</h3>
<div id="outline-text-headline-12" class="outline-text-3">
<p>Getting startx/xinit to work took some tinkering. Startx my not directly run from the tty (from what I can tell, see <a href="https://lists.gnu.org/archive/html/help-guix/2018-07/msg00080.html">mailing list thread from 2018</a>).</p>
<p>
HOWEVER guix has a &#34;package&#34; which actually originates from the AUR called <code>xinitrc-xsession</code>.
This build script creates a symbolically-linked <code>xinitrc.desktop</code> file which launches whatever is in your <code>~/.xinitrc</code>.
This desktop file can then be read by your login manager so you have an easy way to effectively run <code>startx ~/.xinitrc</code> from gdm.
With this you retain all the benefits/conveniences of your login manager without the PATH/environment issues that can arise from something like lightdm which can prevent execution of stuff called out in <code>~/.xinitrc</code>.</p>
<p>
There are ways you can set this up from <code>config.scm</code>, but you might as well hit the easy-button and use a provided solution.</p>
<p>
In time I would like to have my own dwm package to set up both the executable and .desktop file, which I figure will be an easy intro to guix packaging.
For now this will suffice, I can launch dwm without issue from gdm and all <em>muh keys</em> <sup>TM</sup> unlock at login.</p>
<hr>
</div>
</div>
</div>
</div>
<div id="outline-container-headline-13" class="outline-2">
<h2 id="headline-13">
What Comes Next?
</h2>
<div id="outline-text-headline-13" class="outline-text-2">
<p>My laptop is functionally riced out exactly as I have it in Artix, Devuan, and in the future Alpine and perhaps even Gentoo via ansible.
I won&#39;t come to any conclusions regarding continuing to use Guix for the long-term until I&#39;ve given it at least 6 months.
So far it is very promising, and after configuration Guix to no surprise runs like any other GNU/Linux distro if you can accept the few defaults I&#39;ve mentioned here.</p>
<p>
At first I was a bit bothered by not being able to edit files in /etc, /usr/local/bin, or /usr/share, but I&#39;m not sure that will be a long-term concern.</p>
<p>
I will continue to read the Guix reference manual over the coming days and after that will probably shift gears to properly learning Guile scheme.
Fortunately knowing emacs-lisp goes a long way in Guix.</p>
<p>
I probably won&#39;t bother trying to get off gdm, I&#39;ve done enough dbus/secret-service troubleshooting to know that the juice is usually not worth the squeeze, and is only a distraction from learning more useful things like guix packaging.
In addition to that I&#39;d also like to explore guix containers, guix deployment, running or replacing podman, and other features that might make me want to migrate some of my servers to guix.</p>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Guix Part 1: Install and Muse about GNU Guix</title>
      <link>https://linuxmaxxing.com/posts/09_guix/</link>
      <pubDate>Wed, 24 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/09_guix/</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;N.B.&lt;/strong&gt;&lt;/em&gt;: &lt;em&gt;Throughout this post I refer to Guix System Distribution simply&#xA;as Guix and NixOS as Nix. Understand that Guix and Nix ackshully refer&#xA;to the respective package managers which can be run on any&#xA;distro. Additionally I adopt &lt;a href=&#34;https://www.tonybtw.com/&#34;&gt;tony_btw&lt;/a&gt;&amp;rsquo;s&#xA;phrase of &amp;ldquo;legacy&amp;rdquo; distros to refer to GNU/Linux distros which are not&#xA;Guix System or NixOS.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Guix has been on my radar for a number of years now. I always viewed&#xA;it as an elite distro for a number of key reasons:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><em><strong>N.B.</strong></em>: <em>Throughout this post I refer to Guix System Distribution simply
as Guix and NixOS as Nix. Understand that Guix and Nix ackshully refer
to the respective package managers which can be run on any
distro. Additionally I adopt <a href="https://www.tonybtw.com/">tony_btw</a>&rsquo;s
phrase of &ldquo;legacy&rdquo; distros to refer to GNU/Linux distros which are not
Guix System or NixOS.</em></p>
<hr>
<hr>
<p>Guix has been on my radar for a number of years now. I always viewed
it as an elite distro for a number of key reasons:</p>
<ol>
<li>It is a GNU project and an FSF-approved distro which means it meets
the Free System Distribution guidelines, meaning the distro will
work perfectly on a couple dual-core thinkpads and YMMV on all
other hardware. Harder and more limiting systems are always better.</li>
<li>It is source-based.</li>
<li>It is (was) rarely used in the wild. &ldquo;Neofetch flex potential&rdquo; as
it has been called. <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
</ol>
<p>The main selling point of both Guix System and NixOS for the average user would be
the source-controlled configuration file which results in an easily
&ldquo;reproducible&rdquo; system.
I put reproducible in quotes because I don&rsquo;t care so much as whether
the system is technically reproducible - meaning exact binary copy
down to a hash - but more a system that has all the same packages and
configuration.</p>
<p>But I&rsquo;ve been able to make any distro &ldquo;reproducible&rdquo; by using shell
scripts and more recently ansible.
My ansible roles work extremely well for me on any arch or debian
derivative distro, or at least any derivative distro I would consider running.
Install a couple packages, run a quick playbook, add the device to my
tailnet, and then run another playbook that completely recreates my
workstation setup after 15 minutes or so.
There are other simpler roles I use on servers to also include alpine.</p>
<p>I bring up ansible only to say that at this point I don&rsquo;t have a &ldquo;need&rdquo;
for Guix System or NixOS insofar as they are fantastic tools for
reproducibility, roll-backs, snapshots, etc.
I get get all those features from &ldquo;legacy&rdquo; distros with btrfs
snapshots and ansible.</p>
<p>But that doesn&rsquo;t mean I&rsquo;m going to not try Guix and NixOS (well, maybe
not NixOS).
And more importantly it would be a mistake to believe Guix and Nix are
primarily systems for easily recreating your rice. That is just a nice touch.
The shells, containers, and deployment capabilities are the real
selling points of Guix and Nix IMHO, which for me are not that important as I have
zero issues using lxc and docker/podman for containers.</p>
<p>I say all of this as background to explain my perspective as a user
who is perfectly happy using normie GNU/linux tools. Inertia is a
thing, so even if the process of learning FHS linux, ansible, docker,
etc took significantly longer than learning Guix/Nix equivalents
would, I&rsquo;m already at the end-point. Anything different is probably
going to seem &ldquo;harder&rdquo; or &ldquo;complicated,&rdquo; and I don&rsquo;t think any Guix or
Nix enjoyer would describe those systems as simple or easy. So if I
come across as a Guix denier at any point in this post or other related ones
in the future, it&rsquo;s coming from a perspective that the other solutions
<em><strong>Just Werk</strong></em>™ for me at this time.</p>
<hr>
<h2 id="definitions">Definitions</h2>
<p>Like Nix, Guix has its own terminology for various things. Here are a
few important ones:</p>
<ol>
<li><a href="https://guix.gnu.org/cookbook/en/html_node/Channels.html">Channel</a>:</li>
</ol>
<blockquote>
<p>Guix and its package collection can be extended through channels. A
channel is a Git repository, public or not, containing .scm files that
provide packages or services.</p>
</blockquote>
<ul>
<li>I.e. software repository</li>
</ul>
<ol start="2">
<li><a href="https://guix.gnu.org/manual/1.5.0/en/html_node/Official-Substitute-Servers.html">Substitutes</a></li>
</ol>
<blockquote>
<p>Build farms that build packages from Guix continuously for some
architectures, and make them available as substitutes (binaries).</p>
</blockquote>
<ul>
<li>Similar to binary repositories on Gentoo.</li>
<li>Compiling from source is still the fallback.</li>
</ul>
<ol start="3">
<li><a href="https://gitlab.com/nonguix/nonguix">Nonguix</a></li>
</ol>
<blockquote>
<p>Nonguix is a software repository for the
GNU Guix package manager, which packages some software which cannot
be included in the official distribution for ethical or policy-related reasons.</p>
</blockquote>
<ol start="4">
<li>Generation</li>
</ol>
<ul>
<li>An instance of your Guix system at a given time.</li>
<li>Generations are created every time you regeneration your system for
the system configuration file.</li>
<li>Each generation can be booted from grub, giving you the
functionality of grub-btrfs on legacy distros regardless of file system.</li>
<li>New generations are created each time you boot into an older generation.</li>
</ul>
<hr>
<h2 id="known-challenges">Known Challenges</h2>
<p>Like all <a href="https://www.gnu.org/distros/free-distros.html">FSF-approved
distros</a> the Achilles'
heel of Guix is the inability to run proprietary software. It comes with
the linux-libre kernel which removes the proprietary firmware blobs
found in the mainline linux kernel. There&rsquo;s a lot of misleading tribal
knowledge about this on the internet, I figured my computers likely
would not even boot as a result. Whilst this may be true for some
machines, particularly new ones, this simply is not true for the
overwhelming majority of computers.
Your computer will boot fine and your graphics will function for at least
rendering screens unless it is a new card. The one thing won&rsquo;t have is
wifi. Honestly, not the end of the world.</p>
<h3 id="dont-talk-about-nonguix">Don&rsquo;t Talk about Nonguix</h3>
<p>That being said I fully intended to get wifi working using the
freedom-hating <a href="https://gitlab.com/nonguix/nonguix">nonguix
channel</a> which provides
both the mainline linux kernel and proprietary firmware which are
likely needed for your wifi.
Adding a channel in Guix is akin to adding a 3rd party repo in legacy distros.</p>
<p>Nonguix is like Fight Club: everybody knows about it and partakes, but
nobody talks about it in official Guix fora. A bit silly, but I
understand why and have become more and more FSF-aligned over time
despite its flaws and characters.</p>
<hr>
<h2 id="installation-prep">Installation Prep</h2>
<p>Before ever installing Guix System, I used the Guix package manager on
a so-called foreign distro, in my case it was Devuan GNU/Linux. So I
came in with some guix knowledge already. And consoomed System
Crafter&rsquo;s Guix vidyos which are good and still relevant.</p>
<p>I first installed Guix in VM, but after 20 minutes or so I decided to
just live dangerously and install it on bare metal. I gained nothing
of importance from installing in a VM other than knowing that Guix
does not set up btrfs subvolumes by default. More on that later.</p>
<h3 id="bare-metal-weapon-of-choice">Bare Metal Weapon of Choice</h3>
<p>In my stack of old corporate thinkpads I had an unallocated W541 and an
X230 running Artix GNU/Linux. The W541 has a 4th-gen i7 processor (4-core,
8-thread) and a whopping 4 RAM slots which I filled with a number of
DDR3 sticks I had laying around to get a total of 20GB. Plenty enough
for compiling for this cpu.</p>
<p>Despite having an NVIDIA Quadro K1100M
discrete graphics card, I chose the W541 over the X230 because of the
beefier processor since Guix is a source-based distro by
default. This computer also has an integrated graphics card, and from
my research the discrete GPU is old enough that it works fine with
nouveau drivers which are included in the linux-libre kernel. So for
the sake of getting a bootable and functioning system that can also
compile a kernel, the W541 works.</p>
<p>The wifi chip is also easily accessible and there likely is a linux-libre
compatible chip which I can use in the future.</p>
<h3 id="installation-media--nonguix">Installation Media &amp; Nonguix</h3>
<p>If you&rsquo;re absolutely set on having wifi working during installation,
you will need to use a nonguix ISO. The latest
<a href="https://gitlab.com/nonguix/nonguix/-/releases">official nonguix ISO</a> is
from 2022, though from vidyos it seems like there were more recent
images from 2024 which are no-longer on the gitlab. Whilst this is a
very minimal base system and the installer itself probably has not
changed, <em><strong>0/10 WOULD NOT RECOMMEND</strong></em> using a 4-year-old ISO.</p>
<p>For starters it is using Guix 1.4, a version behind the current 1.5 which
brought major changes to the package manager. It probably also
still references the old, slow GNU Savannah channel which
Guix used to be hosted on.
Guix is now on
<a href="https://codeberg.org/guix">codeberg</a> which is a significantly faster
channel. Aside from that you probably don&rsquo;t need me to explain why
using an iso that old is probably a bad idea.</p>
<p>System Crafters used to publish their own nonguix iso on their
<a href="https://github.com/SystemCrafters/guix-installer/releases/tag/v202503220148">github</a>. The
community has since moved to
<a href="https://codeberg.org/SystemCrafters">codeberg</a> and no longer
publishes custom Guix ISOs. My guess is due to codeberg limitations.</p>
<p>You can also generate your own nonguix iso image on any system with
the Guix package manager installed. Instructions are provided on the
<a href="https://gitlab.com/nonguix/nonguix#installation-image">nonguix
gitlab</a>. Keep
in mind this compiles an iso, so expect around an hour. I have not
verified how well this works or if there are any gotchas with this
method, though an <a href="https://joshblais.com/">unnamed friend</a> may or may
not have had issues with this until finally conquering.</p>
<p>I chose to use the official GNU Guix ISO over ethernet and add the
nonguix channel later. If your system boots the
linux-libre kernel - which you should know based on the ISO booting or
not - it is a simple enough process to just add the nonguix channel
after installation. You can even add the nonguix channel during
installation via ssh, see <a href="https://youtu.be/p1w8dwHxFnk">Thanos Apollo&rsquo;s Guix Installation
video</a> for how that can be done.</p>
<h3 id="official-iso-issues">Official ISO Issues</h3>
<p>There are two flavors of the official Guix iso:
<a href="https://guix.gnu.org/en/download/">standard</a> and latest. At the time
of this post the standard ISO is from January, 2026, so I knew going
it that I would basically be pulling the entire system with this
ISO.</p>
<p>There is additionally a latest ISO which should be a weekly build.</p>
<p>Both are direct downloads, I could not find any torrent links.</p>
<p>There are issues with both of these though. The latest official ISO
only includes a gpg signature file. No sha256 or sha512 hash. The
latest ISO includes <em>neither</em> a gpg signature <em>nor</em> hash. <strong>This is
unacceptable.</strong></p>
<p>Since I still like to live a bit dangerously, I was fine with
signature verification of the standard ISO, but I would not recommend
using the latest ISO unless you like to <code>curl | sh</code> your entire
operating system.</p>
<hr>
<h2 id="guix-installation">Guix Installation</h2>
<p>There are several videos running through a Guix installation,
<a href="https://youtu.be/3mbCH7sBLeI">tony_btw</a>&rsquo;s shows a cli install per the
Guix Reference Manual.
This is honestly one of the more straightforward cli installs out there.</p>
<p>I opted to go the graphical/tui route as I value ease and
reproducibility of installation. I was more curious how sane the
defaults were, and when I choose a distro for long-term use I will not
choose one that requires a bunch of cli nursing and a handbook to
install. It&rsquo;s just not worth my time, I want something I can
mindlessly install to get back up and running as quickly as possible.</p>
<p>Let me tell you that the Guix tui installer is fantastic, up there
with the MX Linux gui installer as my favorites.</p>
<h3 id="encryption">Encryption</h3>
<p>Guix by default uses true Full-Disk encryption save the EFI system
partition with GPT partition tables. From what I understand this is only
possible (or more consistent) with the older LUKS1 encryption vs the
newer LUKS2 which generally requires an un-encrypted /boot. LUKS2 with
un-encrypted /boot is the
default on Debian where an encrypted /boot installation will fail from within the
tui installer. I generally prefer an encrypted boot if possible and
convenient. Of course it&rsquo;s always possible from the cli.</p>
<h4 id="btrfs-snapshots-boot--encryption">Btrfs Snapshots, /boot, &amp; Encryption</h4>
<p>This is not an issues on Guix, but on legacy distros using btrfs
snapshots for roll-backs, an un-encrypted boot partition can bite you
if your initramfs gets corrupted or significantly differs from the
installed kernel. For instance if you&rsquo;re on arch where there are only
two officially supported kernels and want to restore an old btrfs
snapshot, it probably will not boot as the kernels on the snapshot
could be completely different kernels. You would need to chroot from
an iso or boot into the snapshot from grub, regenerate initramfs, and modify the
btrfs root subvolume name to point to the actual root subvolume and not the
btrfs snapshot you&rsquo;re booted in. It potentially becomes messy and
could render your /boot unrecoverable without a usb chroot.</p>
<p>So on legacy systems with encryption and btrfs, encrypted /boot is
always preferred as the /boot partition is included in the btrfs root snapshot.</p>
<h3 id="file-systems">File Systems</h3>
<p>On the VM I installed btrfs which included zero btrfs subvolumes. My
initial strategy was to use btrfs snapshots to have restorable backups
even if I garbage-collect (prune) my Guix system. Not having
subvolumes makes this impossible without the cli installer.</p>
<p>As I thought about it though, btrfs snapshots offer you absolutely nothing on a
Guix system. Btrfs snapshots are incremental, so on a legacy distro
each snapshot references the previous snapshot and adds/removes packages
as appropriate. This works because the packages/files are installed in the
same place just with different timestamps and/or versions.</p>
<p>On Guix and Nix, upgraded packages are stored in a completely different locations
and are just symlinked to the appropriate place is the system or user
path. As far as I know btrfs would have no way of identifying these as
the same package since they are in completely different folders, which
means you probably gain zero incremental benefits. So whether you are using
btrfs snapshots or Guix generations, it probably makes no difference, the same
amount of space exists on disk. This is probably why grub-btrfs is not
included in otherwise massive nix package repo.</p>
<p>Further, to roll-back using btrfs you
must restore and reboot the system, while Guix can roll back in
real-time using <code>guix system roll-back</code> without reboot.</p>
<p>So as far as I can tell there
is zero upside to using btrfs snapshots and only downside considering
installation complexity. Additionally if you have both btrfs snapshots
and generations, you are effectively saving the same packages multiple
times, taking up more disk space which I imagine can be
precious on Guix and Nix.</p>
<p>So why choose btrfs? Compression, copy-on-write, and data integrity
features.</p>
<p>For this install I chose the default ext4 on a 500gb drive,
and depending on how much space I have after transferring my /home
folder over and keeping a couple months of generations, I may
re-evaluated switching to btrfs for the compression benefits which are
not insignificant.</p>
<h3 id="substitutes">Substitutes</h3>
<p>You&rsquo;ll want to enable substitutes during installation. It&rsquo;s a long
process regardless, but substitutes will insure installation takes
under an hour. Speed is a weakness of Guix unfortunately, but keep in
mind it was a source-based distro for a number of years. Substitutes
are secondary. If a substitute server does not have the binary, it
will be built from source. That being said I would say around 80% of
the packages installed were from binary substitutes, and most of the
compiled programs were small anyway. I haven&rsquo;t looked in detail but I
imagine Guix is selective as to which packages even make it to substitute.</p>
<h3 id="desktops">Desktops</h3>
<p>Guix includes a few desktop environments and window managers, I opted
for XFCE as that is my go-to on all systems as a fallback.</p>
<hr>
<h2 id="post-install">Post-Install</h2>
<h3 id="moving-etcconfigscm">Moving /etc/config.scm</h3>
<p>I kept the default <code>/etc/config.scm</code> file generated by the
installer. Whilst I could have modified it at this point, I really
wanted to experience default Guix before I go-a-ricing. Additionally
my plan was to copy the system config.scm for modifications, leaving the
original one intact at <code>/etc/config.scm</code>.</p>
<p>Tony directly edits <code>/etc/config.scm</code> to install packages and services in his guide.
This works, but you miss out out version controlling your config file.
Instead I would recommend leaving the default <code>config.scm</code> as is and copying it to a
folder of your choosing where you can add it to git.
It can be anywhere. I chose <code>~/.config/guix/config.scm</code>.</p>
<h3 id="adding-nonguix">Adding nonguix</h3>
<p>The instructions for adding the nonguix channel are
straightforward.</p>
<p>Make sure you add the nonguix channel in a <code>channels.scm</code> file:</p>





<pre tabindex="0"><code>(cons* (channel
        (name &#39;nonguix)
        (url &#34;https://gitlab.com/nonguix/nonguix&#34;)
        ;; Enable signature verification:
        (introduction
         (make-channel-introduction
          &#34;897c1a470da759236cc11798f4e0a5f7d4d59fbc&#34;
          (openpgp-fingerprint
           &#34;2A39 3FFF 68F4 EF7A 3D29  12AF 6F51 20A0 22FB B2D5&#34;))))
       %default-channels)</code></pre><p><code>~/.config/guix/channels.scm</code> is a good place for this.</p>
<p>One thing you&rsquo;ll want to do is add the nonguix
substitute server. The
<a href="https://gitlab.com/nonguix/nonguix#substitutes-for-nonguix">instructions</a>
are clear enough, but the code block included will conflict somewhat
with the default <code>(services ...)</code> block in <code>/etc/config.scm</code></p>
<p>Here is the default <code>(services ...)</code> block in <code>/etc/config.scm</code>
after an XFCE installation.</p>





<pre tabindex="0"><code>(services
 (append (list (service xfce-desktop-service-type)
               (service openssh-service-type)
               (set-xorg-configuration
                (xorg-configuration (keyboard-layout keyboard-layout))))
         ;; This is the default list of services we are appending to.
         %desktop-services))</code></pre><p>And here is what nonguix provides:</p>





<pre tabindex="0"><code>(services (modify-services %desktop-services
           (guix-service-type config =&gt; (guix-configuration
             (inherit config)
             (substitute-urls
              (append (list &#34;https://substitutes.nonguix.org&#34;)
                %default-substitute-urls))
             (authorized-keys
              (append (list (local-file &#34;./signing-key.pub&#34;))
                %default-authorized-guix-keys))))))</code></pre><p>Crystal clear, right? Not going to get into the weeds of scheme here,
but defining services beginning with <code>append</code> and then later
<code>modify-services</code> is not the cleanest approach.</p>
<p>Instead I borrowed what is done on a default base Guix installation
(no desktop) and use <code>cons*</code> to add elements to a list rather than use <code>append</code>
which concatenates lists.</p>
<p>This:</p>





<pre tabindex="0"><code>(services
 (append (list (service xfce-desktop-service-type)
               (service openssh-service-type)
         %desktop-services))</code></pre><p>Is functionally equivalent to</p>





<pre tabindex="0"><code>(services
 (cons* (service xfce-desktop-service-type)
        (service openssh-service-type)
        %desktop-services))</code></pre><p>In both cases we are adding two services to <code>desktop-services</code> which
exists by default on the system.</p>
<p>The difference is we can directly pass <code>(modify-services ...)</code> in the <code>(cons* ...)</code>
block, wheres it would have to be a list in order to include it in the
<code>(append ...)</code> block.</p>
<p>Here&rsquo;s what I ended up with:</p>





<pre tabindex="0"><code> (services (cons* (service xfce-desktop-service-type)
                  (service gnome-keyring-service-type)
                  (service nix-service-type)
                  tailscale-service

                  (modify-services
                   %desktop-services
                   (guix-service-type config =&gt;
                                      (guix-configuration
                                       (inherit config)
                                       (substitute-urls
                                        (append
                                         (list &#34;https://substitutes.nonguix.org&#34;)
                                         %default-substitute-urls))
                                       (authorized-keys
                                        (append
                                         (list
                                          (local-file
                                           &#34;./signing-key.pub&#34;))
                                         %default-authorized-guix-keys)))))))</code></pre><p>Then just change the location of <code>signing-key.pub</code> to a file location
of your choosing. I chose <code>~/.config/guix/nonguix.pub</code>. You can find
the contents for this key file <a href="https://substitutes.nonguix.org/signing-key.pub">here</a>.</p>
<p>Per nonguix instructions:</p>
<blockquote>
<p>Guix System will only use the substitution server after it has been
reconfigured.  The substitution server will therefore by default not
be used the first time you run guix system reconfigure after adding
the substitution server.  It is therefore recommended to explicitly
specify the use of the substitution server the first time
you reconfigure your system:</p>
</blockquote>





<pre tabindex="0"><code>sudo guix archive --authorize &lt; signing-key.pub
sudo guix system reconfigure ~/.config/home/config.scm \
--substitute-urls=&#39;https://ci.guix.gnu.org \
https://bordeaux.guix.gnu.org https://substitutes.nonguix.org&#39;</code></pre><p>Just be sure to put the proper location of <code>config.scm</code> for your
system.</p>
<h4 id="adding-the-non-libre-linux-kernel-and-firmware">Adding the non-libre Linux Kernel and Firmware</h4>
<p>Add the following to <code>config.scm</code>:</p>





<pre tabindex="0"><code>(use-modules
    ...
    (nongnu packages linux)
    (nongnu system linux-initrd)
    ...
    )
...

(operating-system
  ...
 (kernel linux-lts)
 (initrd microcode-initrd)
 (firmware (list linux-firmware))
 ...
 )</code></pre><p>Then run:</p>





<pre tabindex="0"><code>sudo guix system reconfigure ~/.config/guix/config.scm</code></pre><p>And you will begin pulling the binary linux-lts kernel (assuming the
latest build exists, if not it will be source) and linux
firmware. After rebooting you should have working wifi.</p>
<p>Notice I have also added a nix service and tailscale service. The nix
service is <a href="https://guix.gnu.org/manual/1.5.0/en/html_node/Miscellaneous-Services.html">provided by
Guix</a>.</p>
<p>The tailscale service is a module I created with tailscale installed from
nix. You can additionally find guix channels to install tailscale.</p>
<hr>
<h2 id="profiles">Profiles</h2>
<h3 id="system-profile">System Profile</h3>
<p>Now is a good time to mention profiles. By default you have a system
profile and a user profile. When you add software to <code>config.scm</code> and
run:</p>





<pre tabindex="0"><code>sudo guix system reconfigure ~/.config/guix/config.scm</code></pre><p>(substituting your file location), you are revising your system
profile. Any packages installed will be installed to the system profile. The
default path for executable files installed to your system profile is:</p>
<p><code>/run/current-system/profile/bin</code></p>
<p>Running <code>guix install &lt;package&gt;</code> as root (or with sudo) installs that <code>&lt;package&gt;</code> to your system
profile.</p>
<p>There are certain packages you might want in your system profile, for
instance desktop environments and system services, but most software
should not be installed in your system profile, whether that is
installed via <code>sudo guix install</code> or defined in <code>config.scm</code>.</p>
<h3 id="user-profile">User Profile</h3>
<p>Software installed via <code>guix install &lt;package&gt;</code> will be installed to
your user profile located at:</p>
<p><code>~/.guix-profile/bin</code></p>
<p>In general, do not
install software this way. Not that it is bad or wrong, it just
doesn&rsquo;t get you anything beneficial.</p>
<p>Instead, use Guix Home.</p>
<h3 id="home-profile--guix-home">Home Profile / Guix Home</h3>
<p>Guix Home is akin to NixOS home-manager except it is also a
profile and parallels your system configuration. A default home
configuration file is located at <code>~/guix-home-config.scm</code>. Once
again I recommend making a copy of this file and placing it in a
convenient place, and keeping the original saved.
I chose <code>~/.config/guix/home/home-config.scm</code>. Add
packages to this file in a similar manner to <code>config.scm</code>, and update
your home profile by running:</p>





<pre tabindex="0"><code>guix home reconfigure ~/.config/guix/home/home-config.scm</code></pre><p>What is the advantage of this? You have all the same generational
features of your Guix system, just with your home profile. Removing a
package is done by simple removing it from <code>home-config.scm</code>. Your
configuration can also become modular which is what I intend to do. In
this way your <code>home-config.scm</code> and related modules are your source of
truth as to what is installed on your system, not whatever happens to
be in <code>~/.guix-profile/bin</code> which you installed by <code>guix install &lt;package&gt;</code>.</p>
<p>There&rsquo;s a ton more to say about Guix Home but for now I&rsquo;m pretty much just using
it to install packages.</p>
<hr>
<h2 id="installing-software">Installing software</h2>
<p>There is very little included at first, frustratingly so at times.
For instance the <code>clear</code> command is not included and needs to be installed using
the <code>ncurses</code> package.</p>
<p>At a minimum you will need to add the appropriate package module and
package name to the respective sections of your home config
file. There are multiple ways of doing this depending on if you are
using a modularized configuration or not. The default
<code>guix-home-config.scm</code> file is modular which you may or may not want
to immediately use. I would recommend starting from scratch at first,
so keep a copy of the default file and then make your own in a more
simple format.</p>
<p>Here&rsquo;s an example for installing emacs, emacs-guix, and ncurses packages:</p>





<pre tabindex="0"><code>(use-modules
  (gnu home)
  (gnu home services)
  (gnu packages)
  (gnu packages emacs)
  (gnu packages emacs-xyz)
  (gnu packages ncurses))

(home-environment
  (packages
    (list
     emacs
     emacs-guix
     ncurses)))</code></pre><p><code>emacs-guix</code> is a fantastic way to manage your Guix system. It is
extremely useful for searching for packages, much quicker than <code>guix search &lt;package&gt;</code>.</p>
<p>You&rsquo;ll need the <code>location</code> item from <code>guix search &lt;package&gt;</code>, for
example:</p>





<pre tabindex="0"><code>guix search emacs-guix

name: emacs-guix
version: 0.6.1
...
location: gnu/packages/emacs-xyz.scm:8355:2
...</code></pre><p>This location is simply the folder structure of the Guix git repo, and
<code>emacs-xyz</code> is the guile scheme module which includes <code>emacs-guix</code>.
Add <code>emacs-xyz</code> to <code>(use-modules ...)</code> and you&rsquo;ll now have that module
loaded.
Add <code>emacs-guix</code> to your package list and guix home will install
<code>emacs-guix</code> from the <code>emacs-xyz</code> scheme module.</p>
<p>Pretty much everything in Guix follows this format. If we look the
<code>config.scm</code> section above which installed the kernel and firmware
from nonguix, notice how the modules were in the nongnu folder
(channel), not the default gnu channel.</p>
<p>For more information on a modularized configuration, see the Guix
Reference Manual and <a href="https://youtu.be/PGGvoabfvkM">System Crafters
video</a></p>
<hr>
<h2 id="to-be-continued">To be Continued</h2>
<p>At this point I have a Guix system running the mainline linux-lts
kernel, functioning wifi, the XFCE desktop, with some
packages installed in my home profile, and quite a bit more that I&rsquo;ll
save for another post. For me this is the minimum
viable product, I would be perfectly happy living in XFCE. In my next
post I&rsquo;ll go through what I&rsquo;ve done to modify my desktop setup,
install suckless software, copy over (and subsequently modify) my
dotfiles, and the successes and failures I&rsquo;ve had in the process.</p>
<p>Things don&rsquo;t always <strong>Just Werk</strong>™ on Guix, for instance after
writing this post and installing hugo from nonguix, hugo is not
rendering the page at all. So add that to the list of things to
troubleshoot. But, once the troubleshooting is done, everything is
documented in a file and I&rsquo;m sure Guix will at least be just as usable as any
other distro. I&rsquo;m just doing my best to enjoy the journey of getting there.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://youtu.be/3mbCH7sBLeI">video</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Deboonking &#39;Debian is Bad on the Desktop</title>
      <link>https://linuxmaxxing.com/posts/08_deboonking_debian_desktop/</link>
      <pubDate>Sat, 06 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/08_deboonking_debian_desktop/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been running Artix GNU/Linux exclusively on my laptop for over 5&#xA;years. I&amp;rsquo;ve had zero complaints or issues using Arch and Arch-based&#xA;distributions. Before that I ran Ubuntu (Gnome2 days) and later&#xA;Debian.&#xA;Likewise I had few complaints&#xA;and had no issues making Debian-based distros work.&lt;/p&gt;&#xA;&lt;p&gt;Historically I&amp;rsquo;ve put Linux Mint on my wife&amp;rsquo;s computer, but when&#xA;setting up a new (used) laptop several months ago I decided to go with&#xA;CachyOS. The laptop had plenty of resources, and I wanted something&#xA;that would be a quick and reproducible setup that had btrfs &amp;amp;&#xA;snapshots configured out of the box. Not to mention the eye-candy is&#xA;nice, maybe that&amp;rsquo;s just default KDE these days.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve been running Artix GNU/Linux exclusively on my laptop for over 5
years. I&rsquo;ve had zero complaints or issues using Arch and Arch-based
distributions. Before that I ran Ubuntu (Gnome2 days) and later
Debian.
Likewise I had few complaints
and had no issues making Debian-based distros work.</p>
<p>Historically I&rsquo;ve put Linux Mint on my wife&rsquo;s computer, but when
setting up a new (used) laptop several months ago I decided to go with
CachyOS. The laptop had plenty of resources, and I wanted something
that would be a quick and reproducible setup that had btrfs &amp;
snapshots configured out of the box. Not to mention the eye-candy is
nice, maybe that&rsquo;s just default KDE these days.</p>
<p>My only issue is this: my wife knows goose egg about arch linux. I do
not enable automatic upgrades on arch linux for obvious reasons, but I
found myself forgetting to run updates on her computer every couple of
weeks. In one case there was the classic archlinux keyring issue that
comes once or twice a year, not a big deal. But if I was not around
that is something she unfortunately would not be able to rectify.</p>
<p>So I decided to look at moving her computer back to something
Debian-based so I could unattended-upgrades and chill without thinking
much of it. If I were to suddenly die, it would be an easier machine
to manage and find solutions for, at least until Debian drops the
current version from lts support.</p>
<p>Whilst I could have just installed
Linux Mint or LMDE, that&rsquo;s now how we do things at
linuxmaxxing dot com. So I decided I would distro-hop on an unused laptop for the
first time in over 5
years to properly retest Debian. Not sure if it counts as a proper hop since I&rsquo;m very familiar
with Debian and still run it on a ton of servers and containers.</p>
<h2 id="my-last-experience-with-debian-on-the-desktop">My Last Experience with Debian on the Desktop</h2>
<p>The last Debian version I used on the desktop was Buster (Debian
10). I was never much of a desktop environment user outside of LXDE
and XFCE, and I remember at that time KDE and Gnome were having
rapid development so the versions on Buster were not only out of
date but missing features, and buggy on the KDE side IIRC.
Not surprising, but it didn&rsquo;t phase me.
The only real complaint I had was not having a number of the <a href="https://lukesmith.xyz/programs/">Luke Smith
starter pack</a> in the Debian repos.</p>
<h2 id="installing-devuan-in-2026">Installing Devuan in 2026</h2>
<p>Because I fell for the &ldquo;muh init freedom&rdquo; meme a long time ago, I
prefer to not run systemd. Tons to say about that of
course, but to keep it simple my overall perspective (which also
applies beyond just GNU/Linux)
is to hedge all sides and not put all your eggs in one
basket. Even if I were a systemdmaxxer I would want projects like
Artix, Devuan, Gentoo, Alpine, etc to continue to succeed even
as a proof of concept that GNU/Linux can still function without
systemd. This approach bypasses all the &ldquo;bloat&rdquo; or &ldquo;unix philosophy&rdquo;
arguments for something much more pragmatic and practical IMHO. More
on that in another post.</p>
<p>I&rsquo;ve loved using openrc, I used runit for a while, and hadn&rsquo;t really
messed much with sysvinit since back in the day I really didn&rsquo;t tinker
much. So Devuan with sysvinit made sense as a learning
experience. I&rsquo;ll cover Devuan in more detail in another post.</p>
<p>From here on out I may refer to installing Debian, just know it was
actually Devuan. It practically makes no difference, Devuan is
extremely similar to Debian, much closer than Artix is to Arch. In
fact all of the sysvinit scripts used by Devuan are included in
Debian. Don&rsquo;t believe me? Check out <code>/etc/init.d/</code> on your Debian server.</p>
<p>I like Debian&rsquo;s netinstaller. It&rsquo;s a good balance between minimal 1337
installers and something that&rsquo;s relatively easy if you know your way around. I installed
with encrypted btrfs subvolumes which requires a bit of tribal
knowledge which will yet again be covered in another post.</p>
<h2 id="what-does-stable-actually-mean">What does &ldquo;Stable&rdquo; actually mean?</h2>
<p>Stable means exactly one thing: no updates. The opposite of
updatemaxxing. That&rsquo;s it.</p>
<p>All you&rsquo;re getting are Security updates which are patched by
the Debian security team. After the roughly 6-month testing freeze
prior to a stable release, bugs are fixed/patched by the Debian
team to ensure the pinned package versions are as bug-free as
possible. It is not 100%, there will be bugs that are discovered that
do not get resolved until the next stable release. But I&rsquo;ve found this
to be few and far between, it is just features you will be missing out
on.</p>
<p>This gets at a major ideological point of
departure from Arch linux: Debian packages are often heavily modified
compared to upstream. I&rsquo;ll save Debian vs Arch for another post.</p>
<h3 id="what-are-some-advantages-of-stable-packages">What are some advantages of &ldquo;stable&rdquo; packages?</h3>
<ol>
<li>No updates.</li>
<li>Minimized possibility of updates breaking stuff (not 100%
eliminated, there was a Debian 12 point release which had issues).</li>
<li>Small file downloads. If you update Arch every week or two, you
will be pulling in at least 1 GB of updates, 2+ for most
users. This adds up if you are bandwidth or space limited.</li>
</ol>
<h3 id="are-there-packages-we-dont-care-about-upgrading">Are there packages we don&rsquo;t care about upgrading?</h3>
<p>Absolutely. I could not care less about running the latest kernel, in
fact I am still running the latest version 5 LTS kernel offered in
artix linux. I am not a programmer or developer, so I don&rsquo;t care about
any of the language libraries. Even so, for what programming I have to
do, it is all stuff that really hasn&rsquo;t changed much: C, shell,
elisp/scheme. Python is probably the most dynamic.</p>
<p>There is so much on my system that I don&rsquo;t care about upgrading, by
far outweighing what I do care about.</p>
<h2 id="but-everything-is-out-of-date">BUT EVERYTHING IS OUT OF DATE!</h2>
<p>So trve, king. But here&rsquo;s a question: 2 years ago, was your GNU/Linux
system completely unusable, or was it just as usable as it is today?</p>
<p>If you&rsquo;re a KDE or Gnome user, this argument held water a several
years ago. But ever since around 2023 / Debian 12 Bookworm I can&rsquo;t
recall any massive changes to either of those projects. Maybe wayland
support. Likewise there have not been major changes to the linux kernel
since version 6 which was included in Debian 12. Drew from <a href="https://www.youtube.com/@JustAGuyLinux">Just a Guy
Linux</a> maintains that Debian
12 was a major release from Debian standpoint because of how many
features it added from large projects, but ever since that not much
has changed. I tend to agree.</p>
<p>Unless you&rsquo;ve got a
new computer, your desktop experience probably hasn&rsquo;t changed much
since Debian 12.
And with the rise of vibed projects there have even been deliberate
regressions in forks. Calibre and vim comes to mind.</p>
<p>In some cases out of date packages are an advantage. Take Nvidia for
example, the lastest driver you can get in any Debian variant
is 550; stable and sid (unstable) are both at 550. Peculiar choice, I
would prefer 560-580.
But it ensures
that all nvidia cards (save brand new ones) are fully supported,
unlike Arch which went right along with Nvidia&rsquo;s decision to drop
legacy cards from their drivers after 580.</p>
<h2 id="how-can-i-get-the-newer-softwares-though">HOW CAN I GET THE NEWER SOFTWARES THOUGH?</h2>
<p>Astute question dear reader. Answer:</p>
<ol>
<li>Backports</li>
<li>Extrepo</li>
<li>Nix/Guix</li>
<li>Legacy universal packaging formats</li>
</ol>
<h3 id="backports">Backports</h3>
<p>This is the most well-known method of getting version updates to
software in the Debian repos. The key concept here is things that are
already in Debian repos for your release. If it&rsquo;s in testing but not
in stable, you probably aren&rsquo;t going to find it in backports.</p>
<h3 id="extrepo-the-mostly-official-unofficial-third-party-repo">Extrepo: the Mostly-Official Unofficial Third Party Repo</h3>
<p>The <a href="https://pkginfo.devuan.org/cgi-bin/policy-query.html?c=package&amp;q=extrepo&amp;x=submit&amp;eXtra=98.170.216.79">extrepo</a> package is new (for me) to the Debian repos and includes a
fairly large collection of third-party repositories. <a href="https://wiki.Debian.org/DontBreakDebian">Don&rsquo;t Break
Debian</a> is a longstanding
Debian mantra meaning don&rsquo;t add ppas or other third-party repositories
that could conflict or break existing software. Extrepo is like a
middle ground between ppas and official repos. They are vetted repos
which are built to work on Debian, but are not official. When you add
a repository with extrepo, it creates an additional sources file in
<code>/etc/apt/sources.list.d/</code> which apt uses to pull software and updates.</p>
<p>I am surprised extrepo is not that well-known. I am using
extrepo to get librewolf, Signal (soygnal), and tailscale, but there
are a ton of other free and proprietary packages available from
upstream providers on extrepo.</p>
<h3 id="nixguix-package-managers">Nix/Guix Package Managers</h3>
<p>My brother in Christ <a href="https://youtu.be/7M00ZDCjAA8">tony_btw</a> recently
put out a tier list which had Debian solidly in the meh category. His
reasons were reasonable: not user-forward, seriously out of date
development packages. I definitely agree Debian is not user friendly
as a desktop distro unless you know what you&rsquo;re doing, but if not now
that was certainly true with arch and gentoo from years ago. Nothing
you can&rsquo;t overcome. It provides a moderately
satisfying flex on newbs FWIW.</p>
<p>If you&rsquo;re a developer, I think nix and/or guix
package managers are a must have regardless of distro. The env shells
and local build environments simply are game changers that you don&rsquo;t
even get with up-to-date distros like arch or gentoo.
I don&rsquo;t even care much about
reproducibility since I&rsquo;m very comfortable with ansible, the env
shells are the killer feature for me beyond just larger package repos.</p>
<p>I use nix for a handful of
packages I can&rsquo;t find in the Debian repos. I never liked flatpak,
AppImage is ok but is not guaranteed to be auto-updating and requires
creating/adding your own desktop files, man pages, etc. Nix and Guix
just werk (once you know what you&rsquo;re doing) if you can spare the
couple GB of extra storage.</p>
<p>So at this point I don&rsquo;t even really care how big, small, new, or old
Debian&rsquo;s repos are - which now include many packages I
wanted but were not available in Buster. So if I were a dev, I would
rather rate a distro on how easy it is to install nix
packages. Nix-bin is in the Debian repos and works great. On Devuan I
simply had to write a sysvinit script for the daemon which was not
difficult. But even then the daemon is not necessary, you can manually
run it whenever you need to make changes to nix. Guix includes a
sysvinit daemon, but the package selection is much smaller, so rather
than running both, I keep everything additional I need on nix.</p>
<h2 id="debian-is-good-for-emacs">Debian is good for Emacs</h2>
<p>Not strictly necessary, but there are many GNU elpa packages in the
official Debian repos. Why is this nice? These packages will be pinned
with the version of emacs in Debian repos. You won&rsquo;t run into the
problem of Debian&rsquo;s emacs package being a version behind the newest
stable emacs - which will shortly happen when emacs 31 is
released. Once that happens the gnu elpa packages may be modified for
version 31 which could introduce breaking changes if still on
emacs 30 unless you&rsquo;ve addressed that possibility in your config
(e.g. pinning packages with use-package). Whilst I look forward to
emacs 31, I don&rsquo;t really care much about it, version 30 has been great
and I haven&rsquo;t seen any must-have features in version 31.</p>
<p><code>vterm</code> is available so zero compilation is required. The latest version
of <code>jabber.el</code> with OMEMO support is in Debian backports, again zero
compilation required.
There are also a number of emacs packages built around
Debian and apt. <code>debian-el</code> provides modes and interfaces to apt for
use in emacs, also available in Debian repos. Next to GNU Guix SD and
maybe NixOS, Debian is probably the best distro for emaxxers.</p>
<h2 id="other-features">Other features</h2>
<p>I&rsquo;ve always liked the <code>apt-cacher-ng</code>
package. Since most of my servers and containers at home are running
Debian, I run <code>apt-cacher-ng</code> on an lxc to keep a cache of all packages
installed and point all my machines to that cache. That way updates
are pulled from the upstream repo on the first run from whichever
machine updates first, and then all other machines pull the same
update from the apt-cacher-ng lxc over the LAN. Nice to have when you&rsquo;re
bandwidth limited and want to minimize hits to the repo servers, but also nice because you can clear the apt cache
on your machine and know you still have the ability to revert packages
from <code>apt-cacher-ng</code>. This also gives you the ability to update
machines which are blocked from the internet.</p>
<p>I also like the <code>aptitude</code> package for researching packages from the
terminal. It is an apt wrapper but also has a tui menu which contains all
the metadata for each packages. Think of it as synaptic package
manager for the terminal. This was a feature I missed when I first moved
off Debian for arch-based distros. A nice to have, not a necessity.</p>
<p>Because of how Debian packages, you end up with a greater number of
packages but the overall install size is smaller than Arch. This is
because Debian breaks up source and development packages from
binaries.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I have my ansible playbooks dialed so that it mirrors my setup from
Artix onto Debian/Devuan. From an operator standpoint there is no
difference. I can get updated software that I care about from various
sources, particularly Nix for those things not in Debian backports or
extrepo. The question I have to ask myself is this: what do I actually
get from running Artix/Arch? Do I have a compelling reason to run
Artix/Arch over Debian/devuan? Not really, it would boil down to init
preferences, e.g. Artix fully supports openrc (PID1, openrc-specific
scripts) whereas openrc on Devuan is just a substitute for sysvinit&rsquo;s
service manager, PID1 is still sysvinit and the vast majority of
init scripts are sysvinit from Debian upstream. And Artix has dinit
which is a really nice &ldquo;minimal systemd&rdquo; init system.</p>
<p>If I were choosing between Artix/Arch and Debian unstable, I would
choose Artix. Debian stable/oldstable is the reason to run Debian IMHO.</p>
<p>Will I be ditching Artix entirely? Absolutely not, the installation
I&rsquo;ve used will
stay on the same laptop and I will continue to be as active as I can in forums and
contributing to <a href="https://auris.artixlinux.org/auris">AURIS</a>. But I
will be giving Devuan a solid run to learn the ins and outs of
sysvinit and see if it could work for me in all edge cases.</p>
<p>Of course this will all change when I get around to installing GNU
Guix System Distribution.</p>
<p>Now all I have left is to install a Debian-based distro on my wife&rsquo;s
computer which is why I went down this rabbit hole to begin with.
I&rsquo;ll get around to that eventually too.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Emacs is perfect for the suckless LARP</title>
      <link>https://linuxmaxxing.com/posts/07_emacs/</link>
      <pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/07_emacs/</guid>
      <description>&lt;p&gt;Recently a reader brought to my attention that I had not made any&#xA;poasts in a little while.&#xA;I have no choice but to give the people what they want.&#xA;Fortunately I already had a couple poasts in the queue, this being&#xA;one of them.&lt;/p&gt;&#xA;&lt;p&gt;I have been a GNU/Linux power user for about 6 years now.&#xA;Most of work is done in the terminal and I have used all the LARPy&#xA;suckless or suckless-adjacent software.&#xA;It&amp;rsquo;s both a preference and a hobby.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Recently a reader brought to my attention that I had not made any
poasts in a little while.
I have no choice but to give the people what they want.
Fortunately I already had a couple poasts in the queue, this being
one of them.</p>
<p>I have been a GNU/Linux power user for about 6 years now.
Most of work is done in the terminal and I have used all the LARPy
suckless or suckless-adjacent software.
It&rsquo;s both a preference and a hobby.</p>
<p>Likewise I fell for the meme takes of this or that software being
bloat, most notably emacs and systemd.
I&rsquo;ll save the latter for another poast, stay tuned avid reader.</p>
<p>Whilst I won&rsquo;t directly address whether emacs is bloat or not, I will
address the fact that most terminal/suckless cosplayers - like
myself - are actually
be better suited using emacs over the <a href="https://lukesmith.xyz/programs/">Luke
Smith</a> starter pack.</p>
<p>In general, I think most terminal or power users care less about
whether the program is strictly suckless - meaning lacking features
but having some way to be extended via patching or separate programs -
and more about the functionality and quality of the software.
Whether something is suckless or bloat is ultimately a goal-posted
opinion where there goal-posts are free to move. Vim is bloat, use
vi. Vi is bloat, use ex. Ex is bloat, use ed. Ed is bloat, use cat,
sed, and awk. Sed and awk are bloat, just don&rsquo;t make mistakes.</p>
<p>There&rsquo;s no real standard, except that in the end software quality will
play the biggest role in our decision to use one thing or the other.
Vim is full of features that are inspired from heckin-bloated
emacs, but because it is so nice to use and the features can stay out
of the way, nobody cares. I&rsquo;ll save the bloat rant for another post.</p>
<h3 id="key-features-of-emacs">Key Features of Emacs</h3>
<ol>
<li>Unified &ldquo;Terminal&rdquo; System</li>
<li>Daemon/Client - tmux not required. Quitting the editor does not kill the buffer, unlike vim.</li>
<li>Documentation (info pages) and help system.</li>
</ol>
<p>I&rsquo;ll cover points 1 and 2 in this post, and save the documentation for
another post.</p>
<h3 id="inconsistent-system-experience">Inconsistent System Experience</h3>
<p>One of the problems I have after falling for the minimal software meme is remembering and deconflicting all the terminal bindings.
Bash and other shells use the readline library with emacs bindings by
default (I prefer this over vi even before using emacs), tmux has its own bindings mostly using ctrl-b (this
conflicts with readline/emacs),
weechat has its own bindings using alt/ctrl, vim has its bindings for
splits, profanity, newsboat, lynx, etc etc.
Plus your window manager keybindings.
You really have to think about all of those and be strategic about how
you lay out the bindings for your entire system.</p>
<p>Regardless of which window manager or DE (LXDE if anything) I am
running, I use sxhkd to launch all programs.
The sxhkdrc file is my master-list of all keybindings on my system.
Everything is arranged by prefix keys, and I keep everything used by
various programs commented out so I don&rsquo;t override them with sxhkd
bindings.
For most programs, I think there&rsquo;s a lot of wisdom in sticking with
default keybindings because chances are they won&rsquo;t conflict as much with other terminal/program bindings.</p>
<h3 id="consistent-and-unified-system-ui-and-experience">Consistent and Unified System UI and Experience</h3>
<p>Emacs simplifies a ton of this. Everything in Emacs is an Emacs
buffer, so Emacs keybindings - whether those keybindings are emacs,
vi/evil, meow, or some other editing system - work on every program.
There&rsquo;s no mucking around trying to get scroll support in a terminal
or program, or a way to copy text into your clipboard, or having different keybindings for vim splits and tmux splits.
It&rsquo;s all the same.</p>
<p>Likewise because everything is run in an Emacs buffer, all editor
commands perform the same operation whether it&rsquo;s a text file, web
browser, irc client, or email message.
You can create functions to manipulate text in one type of file/buffer that will work on other buffers.
Tsoding had a <a href="https://youtu.be/DMbrNhx2zWQ">recent video</a> on the usefulness of emacs, and
one of the key points was that emacs unifies programs that were never
designed to work together.
So trve, king.</p>
<p>Here&rsquo;s an extremely useful but simple example: scanning for urls.
Luke Smith&rsquo;s <a href="https://github.com/LukeSmithxyz/st">st</a> has series of
combined patches that allow you scan the terminal window for
urls, pipe them to dmenu, and then launch the selected url in the default system browser.
Super handy.
It doesn&rsquo;t necessarily work with all terminal programs, though.</p>
<p>But in emacs, this is a very easy function to write that can be easily
extended.
It&rsquo;s all done in about 20 lines.
I&rsquo;ve bound this function to <code>C-c g g</code> (for go).
A minibuffer pops up with a list of all url&rsquo;s in the
buffer, similar to st/dmenu.
I select the url I&rsquo;m looking for with <code>C-n/C-p</code>, hit enter, and the link opens in my default browser.
If I press <code>C-c g e</code>, it opens in <code>eww</code> (emacs built-in browser).
If I press <code>C-c g c</code>, the same base function is used to show all urls in a minibuffer, hitting return just copies the url to clipboard instead of opening in a browser.
No scrolling through the articles or text (which is also easier than most apps because of unified emacs keybindings).
No highlighting and pressing a copy keybinding.
I can use that url copy function in any buffer - irc, webpage, text file.
It&rsquo;s all the same in Emacs.</p>
<p>These functions scan for urls in the entire buffer.
But what if I just want to select the urls that are shown on screen?
Easy modification.
No patching sauce code or anything like that.</p>
<h3 id="programs-ive-replaced-with-emacs">Programs I&rsquo;ve replaced with Emacs</h3>
<table>
  <thead>
      <tr>
          <th>Function</th>
          <th>Terminal (old)</th>
          <th>Emacs (new)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>terminal</td>
          <td>st</td>
          <td>vterm</td>
      </tr>
      <tr>
          <td>editor</td>
          <td>vim</td>
          <td>emacs</td>
      </tr>
      <tr>
          <td>multiplexer</td>
          <td>tmux</td>
          <td>daemon/client + TRAMP</td>
      </tr>
      <tr>
          <td>file manager</td>
          <td>vifm</td>
          <td>dired</td>
      </tr>
      <tr>
          <td>web browser (text)</td>
          <td>lynx</td>
          <td>eww</td>
      </tr>
      <tr>
          <td>email</td>
          <td>mutt</td>
          <td>mu4e</td>
      </tr>
      <tr>
          <td>irc</td>
          <td>weechat</td>
          <td>erc</td>
      </tr>
      <tr>
          <td>xmpp</td>
          <td>profanity</td>
          <td>jabber.el</td>
      </tr>
      <tr>
          <td>matrix</td>
          <td>gomuks</td>
          <td>ement</td>
      </tr>
      <tr>
          <td>git</td>
          <td>cli</td>
          <td>magit / vc</td>
      </tr>
      <tr>
          <td>rss</td>
          <td>newsboat</td>
          <td>elfeed</td>
      </tr>
      <tr>
          <td>image viewer</td>
          <td>nsxiv</td>
          <td>image-mode</td>
      </tr>
      <tr>
          <td>pdf</td>
          <td>zathura</td>
          <td>doc-view</td>
      </tr>
  </tbody>
</table>
<p>Most of these are very easy to set up. <code>Elfeed</code> is definitely the
exception if you&rsquo;re coming from newsboat, and I haven&rsquo;t taken the time
to completely replicate my newsboat setup which used mpv for podcasts
and youtube vidyo, plus has a number of browser keybindings so I can
open articles in text browsers (assuming a full-feed isn&rsquo;t provided)
and graphical browsers.
So for now I am still mostly using newsboat.</p>
<p>Which of these are &ldquo;killer apps&rdquo;? For my uses, I&rsquo;m not sure I would
call any of them killer apps on their own, though I don&rsquo;t even view org-mode as a killer emacs app.
I can survive just fine without them.</p>
<p>They become killer apps by virtue of being a part of emacs.</p>
<p>I&rsquo;ve never been a huge terminal file-manager user, cli has always just
worked.
<code>Dired</code> on its own is a good file manager, no better or worse than
vifm, ranger, yazi, lf - they&rsquo;re all kind of the same in my book.
Where it is extremely powerful is that it integrates with everything
else in emacs, including <code>TRAMP</code> which is EXTREMELY useful when editing
on remote machines.
Not going to cover it here in detail, but <code>TRAMP</code> allows you to edit
remote files in your current emacs environment. Before using emacs I
had in my head that in order to use emacs on a server, you needed to
install emacs on the server (which could add a bit of gbs). Not the
case.</p>
<p>My favourite <code>dired</code> feature is the ability to open two <code>dired</code> buffers in
a split window, and any command done in one buffer is assumed to end
in the other buffer.
I.e. if I copy a file/files or make symbolic link/links in one <code>dired</code> buffer, emacs assumes the target is the other <code>dired</code> buffer.
Very handy.
You can also change the target directory if that isn&rsquo;t the case.</p>
<p><code>erc</code> is the best irc client I&rsquo;ve used.</p>
<p><code>ement</code> is a very good matrix client, not that the competition is great.</p>
<p><code>jabber.el</code> has seen recent improvements, it works fine.</p>
<p><code>magit</code> is the closest to a killer app on its own, not for me, but for anyone who
heavily uses git.
Your git workflow becomes extremely fast with magit and the ability to
quickly do partial commits is a huge qol feature.</p>
<h3 id="key-difference-gui-vs-terminal-features">Key Difference: GUI vs terminal features</h3>
<p>For most of these programs, a big difference is that when run as a
graphical emacs session (normally the case), they are gui programs.
They do not have the limitations of terminal text.
Fonts can vary in size and are not limited to monospace.
Images render just fine.</p>
<p>So whilst <code>mu4e</code> and <code>eww</code> are good programs, they are much better than
terminal variants because they render text and images as designed.
So with emacs you get the best of both worlds: minimal and hackable
terminal interface with gui quality of life features.</p>
<h3 id="edit-note-on-documentation">Edit: Note on Documentation</h3>
<p>Whilst I will cover emacs documentation in another post, it&rsquo;s a key
feature to mention here because all the features and keybindings for a
given program are easily accessible with emacs built-in documentation, usually with simple popup-menus.
Can&rsquo;t remember how to do a certain operation in <code>dired</code>? <code>C-h m</code> shows
you all of the <code>dired</code> keybindings and mode information. Curious what a keybinding does? <code>C-h k</code>
will show you what the next keybinding you press does. Want to see all your
bindings tied to <code>C-c</code>? <code>which-key</code> makes that a breeze in the
minibuffer.
Recently added <code>help-quick-toggle</code> with <code>C-h C-q</code> is probably
the easiest of the bunch I&rsquo;ve listed.</p>
<p>No man pages, no creating keybinding tables to track everything.</p>
<h3 id="conclusion">Conclusion</h3>
<p>The suckless/terminal mindset is aimed at simplicity. Use simple
programs that generally do one thing well, and stay in the terminal to
avoid bloat, distraction, slow, feature creep, etc.
Making all this happen can be a complex process though.</p>
<p>In my opinion, you&rsquo;re better of accepting the &ldquo;bloat&rdquo; and &ldquo;complexity&rdquo;
of emacs - I put complexity in quotes because emacs itself is actually
a simple program - in order for simplicity of configuration, less mental
taxation, and easier/better extensibility.
Admittedly this will come at the expense of an extra gb or
two of storage if you can spare it.</p>
<p>We all have fallen for the suckless LARP, why not fall for the
emacs meme as well?</p>
]]></content:encoded>
    </item>
    <item>
      <title>Ditching Evil Mode in Emacs</title>
      <link>https://linuxmaxxing.com/posts/06_emacs_bindings/</link>
      <pubDate>Mon, 09 Mar 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/06_emacs_bindings/</guid>
      <description>&lt;p&gt;Several weeks ago after a bit of unnecessary tinkering (as we are wont to do) I realized that my emacs config, whilst fine and functional, only worked with evil-mode, evil-collection, and general.el.&#xA;If any one of those packages didn&amp;rsquo;t load, or if I wanted to move away from them for whatever reason, I would be looking at a substantial rewrite.&#xA;So I decided I would embrace a bit of conventional wisdom and go down the path of using default navigation keybindings to the greatest extent possible, and create new custom keybindings for features I frequently use without default bindings.&#xA;Here I&amp;rsquo;m talking about just navigation things - moving between buffers, windows, tabs, etc.&#xA;(At the time) I was not talking about editing modes - I was still 100% committed to vim/evil.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Several weeks ago after a bit of unnecessary tinkering (as we are wont to do) I realized that my emacs config, whilst fine and functional, only worked with evil-mode, evil-collection, and general.el.
If any one of those packages didn&rsquo;t load, or if I wanted to move away from them for whatever reason, I would be looking at a substantial rewrite.
So I decided I would embrace a bit of conventional wisdom and go down the path of using default navigation keybindings to the greatest extent possible, and create new custom keybindings for features I frequently use without default bindings.
Here I&rsquo;m talking about just navigation things - moving between buffers, windows, tabs, etc.
(At the time) I was not talking about editing modes - I was still 100% committed to vim/evil.</p>
<p>Nevertheless I wanted my custom bindings to be &ldquo;mode&rdquo; agnostic, so that ruled out using general.el and also using SPC as a leader key (since the non-modal default bindings need to insert a space with SPC).
I tried Alt-SPC for a while instead, but that was surprisingly awkward even though it can be done with either hand, one hand, or even just the thumb if angled correctly.
I decided to just embrace conventional emacs wisdom and rewrite my custom keybindings with C-c.</p>
<p>On my first read-through of Mastering Emacs, I thought some of the emacs navigation bindings were just ridiculous; the shear volume of bindings with no seeming rhyme or reason seemed absurd.
After forcing myself to learn them, I realize the navigation bindings are actually surprisingly consistent - it&rsquo;s the same pattern across windows, frames, and tabs.
And C-c for custom bindings has been just fine.
It was with this in mind that I thought: maybe I&rsquo;m just misunderstanding the emacs editor keybindings as well?</p>
<p>My first step was to create my emacs keybindings that I could access in insert-mode (called insert state in evil).
Not really necessary as you can locally toggle evil-mode with C-z, but this at least got me to start using things like M-d or C-k when I was too lazy to hit the ESC key to get back into normal mode.
This was fine, but it wasn&rsquo;t enough to seriously learn and use them.</p>
<p>So after a couple minutes of consideration and somewhat on a whim, I decided to rip off the bandaid and switch away from using evil-mode in gnu emacs.
Here are some of my reasons for doing so:</p>
<h3 id="abbreviated-and-impromptu-list-of-reasons-to-not-use-evil-mode">Abbreviated and impromptu list of reasons to not use evil mode:</h3>
<ol>
<li>Curiosity.</li>
<li>Improve my bash default keybinding proficiency.</li>
<li>Not be completely retarded when my init.el fails to fully load or cannot load at all.</li>
<li>Cut down on emacs packages and abstractions.</li>
<li>Most importantly: experience emacs packages as the developers intended.</li>
</ol>
<p>I really want to focus mostly on the last one because that is my primary motivation.
It was during my research of elfeed that I realized evil-mode and specifically evil-collection do quite a bit of violence to the original keybindings from the author.
By now I knew that evil-collection made changes on most packages - e.g. dired - but for the most part they&rsquo;re pretty sane and retain most of the original bindings.
This was not the case with elfeed, and reading documentation proved to be somewhat a moot point as basically everything had been changed to line up with a vim-style workflow.
It made me wonder - what other aspects of emacs are being substantially changed because of evil and evil-collection?
Am I losing out on some gucci emacs feature because of evil-mode?</p>
<h3 id="concerns">Concerns</h3>
<p>I have three major concerns with switching away from vim/evil keybindings:</p>
<ol>
<li>Inability to rewire my brain away from vim habits.</li>
<li>Missing out on features I&rsquo;ve come to love (e.g. visual-mode commands and vim&rsquo;s search/replace mechanism).</li>
<li>Finger strain - the famed emacs-pinky.</li>
</ol>
<p>For the first one I figure there&rsquo;s only one way around - practice.
I took a look at god-mode and meow-mode to see if there was a way to &ldquo;have my cake and eat it too&rdquo; so to speak, but I realized these would be similar to vim and at the same time different enough that I am unlearning and replacing vimisms with vim-like alternatives rather than learning a whole new set of bindings.
Meow would still require me to have some proficiency with emacs bindings for the non-text buffers.
As I went through the emacs tutor again, I came to the conclusion that emacs and vim bindings are probably different enough that I should be able to compartmentalize them fairly well.
The same cannot be said of vim and meow as I would chose one to the detriment of the other.</p>
<p>For the second one, this is more of a concern with having to learn new ways of doing the same things.
Considering emacs is a lisp-interpreter ultimately and isn&rsquo;t bound by the same restrictions as vim or any other program, I&rsquo;m confident solutions for my favorite vim features exists in emacs - it just won&rsquo;t start with a colon or some other vim key.</p>
<p>Regarding the last one, I figure this is something I&rsquo;ll be able to learn soon enough to prevent any issues should they arise.
However, I heard from one of Prot&rsquo;s vids that the best hack for emacs bindings is learning proper typing habits using both the left and right hand.
This is something I had been working on anyway; C-c as your leader key gives you a ton of practice breaking away from just using the left hand, for example.
With caps lock remapped to control, I&rsquo;ve had very little issue using both hands for CTRL and ALT key operations, even when those keybindings involve switching hands in the middle of a key combination.
As an added bonus I am no longer engaging caps-lock and doing all kinds of unintended stuff.</p>
<p>So if anything I look at this as a really good opportunity to perfect my typing
The next step will be split-keyboards, but that&rsquo;s a level of autism I shouldn&rsquo;t approach lest I go down another potentially expensive rabbit hole.</p>
<h3 id="one-week-review">One Week Review</h3>
<p>So far I have no real complaints not using vim/evil mode.
It hasn&rsquo;t taken long to get familiarized with the default keybindings for various programs; really that aspect of emacs is no different, just using b/n/p/f instead of h/j/k/l.
One of my main fears was that I would always need two-hands to do any kind of scrolling or navigating, but this really isn&rsquo;t the case.
In almost all buffers, &ldquo;n&rdquo; and &ldquo;p&rdquo; will move up/down without the control key, so one-handed browsing has not really been an issue.</p>
<p>I&rsquo;ve stopped mashing ESC all the time like I am accustomed to.</p>
<p>Learning the emacs bindings is surprisingly easy because they&rsquo;re largely pneumonic-based.
Even in programs like dired: up-directory is ^, a symbol that looks like an up arrow, for example.
Not always the easiest to reach, but easy to remember.</p>
<p>Editable buffers took about a day to knock off some <del>rust</del> iron oxide, but it&rsquo;s been fine.
Slower for sure, but not drastically.
I suppose akin to the non-soystemd inits booting quicker - it is true dinit and runit boot quicker, but not to the extent that it drastically changes your computing experience.
A lifetime of booting with dinit over soystemd will save tens of minutes.
Likewise I think the extra time it takes with emacs bindings is a drop in the bucket, at least for my uses.
Completely different story for programmers.</p>
<p>I can already tell emacs bindings have some advantages over vim - probably fewer compared with vim&rsquo;s advantages but for the right person the advantages of emacs bindings could be substantial.
For writing prose I can see how emacs bindings are really nice - there are a ton of sentence/paragraph oriented operations that don&rsquo;t have parallels in vim (and don&rsquo;t need to tbh considering the target audience of sysadmins and programmers).
Plus transposition of characters and words.</p>
<p>I think the biggest strength of emacs bindings (so far) is the kill ring.
Yes, there&rsquo;s a way you can access it in evil, but you have to customize that yourself.
And this is a feature that carries over to bash which does not have an equivalent in readline vi-mode (to my knowledge).</p>
<h3 id="long-term-prediction">Long-Term Prediction</h3>
<p>I&rsquo;m going to give it some time, probably close to a year to get an honest review, but if I had to guess I am most likely done with evil/vim bindings in emacs.
I&rsquo;d also like to toy with the built-in viper-mode which emulates vi-bindings.
My vimrc is split up between vi-compatible options and vim-specific stuff, so I&rsquo;m already very familiar with the vi limitations and honestly, it doesn&rsquo;t bother me much.
But that probably will not be a long-term solution if viper-mode affects more than just the editor.
I can already tell that emacs &ldquo;feels&rdquo; better integrated and more consistent with the default bindings.</p>
<p>So if you&rsquo;re like me and seemingly tied to vim-bindings, try breaking out of your comfort zone a bit and cracking open the emacs tutor with C-h t.
Emacs tutor is extremely useful even beyond the editor bindings, but it&rsquo;s unfortunately kind of useless if done in evil mode.</p>
<p>You might be surprised.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Rewriting my Emacs Config</title>
      <link>https://linuxmaxxing.com/posts/05_emacs/</link>
      <pubDate>Sat, 21 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/05_emacs/</guid>
      <description>&lt;p&gt;In my last post entitled &lt;a href=&#34;https://linuxmaxxing.com/posts/04_Emacs/&#34;&gt;&amp;ldquo;RETVRN to Emacs&amp;rdquo;&lt;/a&gt; I described a bit of my recent foray back into GNU Emacs.&#xA;Despite using my own config (after using Doom Emacs for a short time), it had been so long since I used Emacs that I had zero strong opinions on any of the packages or configuration.&#xA;So I decided to change that.&#xA;When I dive into something, I tend to go on a head-first deep dive with a millstone tied around me.&#xA;Maybe not the best way to describe it considering certain passages, but it&amp;rsquo;s accurate.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In my last post entitled <a href="https://linuxmaxxing.com/posts/04_Emacs/">&ldquo;RETVRN to Emacs&rdquo;</a> I described a bit of my recent foray back into GNU Emacs.
Despite using my own config (after using Doom Emacs for a short time), it had been so long since I used Emacs that I had zero strong opinions on any of the packages or configuration.
So I decided to change that.
When I dive into something, I tend to go on a head-first deep dive with a millstone tied around me.
Maybe not the best way to describe it considering certain passages, but it&rsquo;s accurate.</p>
<p>As I dug through my init.el, I realized there was a ton of functionality that I no longer used, and my inconsistent package syntax was sloppy at best.
It was a mash-up of use-package and conventional commands, the result of copypasta from both old and [more] modern solutions found on the internet.
For some of the packages I did not remember why they were there or where I originally found inspiration for them.
And the videos I watched to help write my init.el are all now 4-5 years old.
Surely there have been changes to Emacs.</p>
<h2 id="there-have-been-changes-to-emacs">There have been changes to Emacs.</h2>
<p>It turns out Emacs has changed a lot since I last used it (which I believe was the tail end of version 27 when I wrote my config).
A ton of features are now built-in: use-package, which-key, fido-mode, project.el, and treesitter to name a few.
Many small packages were absorbed or made obsolete by Emacs built-ins (e.g. hide-dotfiles-mode package for dired).
Ivy and Counsel still exist, as does the older Helm, but has largely been replaced by Vertico/Orderless among the hoi poloi.
These, like many other modern Emacs packages, extend upon built-in functionality rather than reinventing the wheel with separate frameworks like Helm, Ivy, and Counsel.</p>
<p>One of the best Emacs references is the book and blog <a href="https://www.masteringemacs.org/">Mastering Emacs</a>.
The author goes through most of the core functionality of Emacs in
enough detail to be thorough but not to the point of overwhelming a new user.
You&rsquo;ll be surprised what is already built-in.</p>
<h3 id="example-completion">Example: Completion</h3>
<p>Here&rsquo;s an example of my thought/discovery process for completion:</p>
<p>Since I want some kind of completion framework, I started with bare-bones ido mode.
This is good to know anyway when you inevitably have run a clean config because of an error.
Ido mode adds completions for switch-to-buffer and find-file, which is not enough for me.
Then I added in the relatively new fido mode which adds completions to the minibuffer with flex matching, and has effectively superseded ido mode (thought ido is still included).
I disabled fido mode and enabled only icomplete-vertical-mode.
When configured this can be used to get similar functionality to fido-vertical-mode without flex matching.
Then I added Vertico, Orderless, Marginalia, and Corfu in order to see what those popular packages give me.</p>
<p>After testing and configuring all of these options, I&rsquo;ve settled on the built-in fido-vertical-mode because I like flex completions, and configuring in-buffer completions (but not point completions - completions show up in a separate minibuffer) so that completions are shown in a minibuffer but are not selectable (found it a bit clunky).
This combo has all the functionality I need from Ivy/Counsel, and the built-in Emacs completion framework is suprisingly customizable.
I have no need for the slimmer (relative to Ivy/Counsel) Vertico, Orderless, Marginalia, Corfu, etc, though I do concede they could add value.
Corfu does still seem to be the best route for point-completions if you want that, I just found the built-in minibuffer completions to be good enough.
The reality is that I&rsquo;m just so good and tpynig that a rboust cmopltetion framweork is not smoething I need.
The lesson in all that is you&rsquo;d never even know this if you only go through one of the older Emacs video series from SystemCrafters, distrotube, etc.</p>
<p>Another example: themes.
The built-in modus themes are on another level.
I generally gravitate toward dark-purple/blue themes like palenight, onedark, tokyonight, etc, and also have a huge love for gruvbox because of how easy it is on the eyes, even if it sometimes looks baby-green (parents know what I mean).
The Modus themes are in the same vein as palenight/onedark but are incredibly customizable and readable.
On first load, modus-vivendi-tinted was passable but not my favorite.
After spending some time with it, researching the excellent documentation, and making it work for proof-of-concept, I&rsquo;ve found I <em>really</em> like it.
Customizing takes some work, but that&rsquo;s part of the fun.
The point is that I would not have discovered this unless I deliberately gave built-in functionality a try.</p>
<p>I&rsquo;ll also add that a similar theme to Modus-Vivendi can be found on Vim: zaibatsu default colorscheme with only a few tweaks (background color, etc).</p>
<p>In short time I have basically rewritten my Emacs config and done so in a way that is much smarter imho.
And I&rsquo;ve learned a ton of the nuts-and-bolts of Emacs.
In another post I&rsquo;ll go through my config line-by-line-ish, but for this one I&rsquo;m just going to share some principles I&rsquo;ve adopted in writing my config.</p>
<h2 id="my-guidelines-for-writing-my-emacs-config">My Guidelines for Writing My Emacs config:</h2>
<ol>
<li>If you&rsquo;re short on time, Doom and other pre-built configs are far better than a quick, franken copypasta&rsquo;d &ldquo;custom&rdquo; config.</li>
<li>Avoid copying old (or any) guides.</li>
<li>Identify the functionality you need and base your package selection off that.</li>
<li>Know the built-in solutions first.</li>
<li>Look at alternatives before selecting one.</li>
<li>Be consistent with package calls, i.e. either utilize use-package or don&rsquo;t.</li>
<li>Use early-init.el.</li>
<li>Split your init.el into multiple files.</li>
<li>Literate configs should not be necessary.</li>
<li>Evil-mode: use evil-collection and general.</li>
<li>Custom keybindings should be completely unique for functions without existing keybindings.</li>
<li>Custom functions should be identifiable as custom.</li>
<li>Create an additional static &ldquo;rescue&rdquo; init.el facsimile with bare-bones built-in configuration.</li>
</ol>
<p>In another post I&rsquo;ll go into detail each of these points, and perhaps add to them.
For now, ponder them for yourself and develop your own interpretation or opinion about them.
These are my opinions, you may (and should reasonably) disagree with them.</p>
]]></content:encoded>
    </item>
    <item>
      <title>RETVRN to Emacs</title>
      <link>https://linuxmaxxing.com/posts/04_emacs/</link>
      <pubDate>Mon, 16 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/04_emacs/</guid>
      <description>&lt;p&gt;I recently made a return to GNU Emacs after several years away.&#xA;I quickly remembered why I used to like it so much - it is fun and satisfying.&#xA;A bit like riding a bike or skiing, it comes back quickly after shaking a little dust.&lt;/p&gt;&#xA;&lt;h2 id=&#34;prior-to-emacs&#34;&gt;Prior to Emacs&lt;/h2&gt;&#xA;&lt;p&gt;Prior to using Emacs I was (and still am) a vim user, preferring simple configs and using as much of the built-in features of vim as possible.&#xA;See my first blog post for more info.&#xA;I bought in to a lot of the meme takes that emacs is bloated and contrary to the Unix philosophy, suckless dot org type stuff.&#xA;In hindsight this is really just saying &amp;ldquo;Emacs is hard, I don&amp;rsquo;t understand it yet, so I&amp;rsquo;m going to have a reason to justify not using it.&amp;rdquo;&#xA;I&amp;rsquo;ll save the bloat rant for another post.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I recently made a return to GNU Emacs after several years away.
I quickly remembered why I used to like it so much - it is fun and satisfying.
A bit like riding a bike or skiing, it comes back quickly after shaking a little dust.</p>
<h2 id="prior-to-emacs">Prior to Emacs</h2>
<p>Prior to using Emacs I was (and still am) a vim user, preferring simple configs and using as much of the built-in features of vim as possible.
See my first blog post for more info.
I bought in to a lot of the meme takes that emacs is bloated and contrary to the Unix philosophy, suckless dot org type stuff.
In hindsight this is really just saying &ldquo;Emacs is hard, I don&rsquo;t understand it yet, so I&rsquo;m going to have a reason to justify not using it.&rdquo;
I&rsquo;ll save the bloat rant for another post.</p>
<p>Like most things unknown, I never felt a need to use Emacs - everything I needed was found in vim and a slew of cli tools.
So I thought.</p>
<h2 id="need-to-learn-lisp">Need to Learn Lisp</h2>
<p>In early 2022 I started a new job which involved a lot of design work on AutoCAD. AutoCAD is a piece of software I have a love/hate relationship with: I love it because I have to use it and have so for the majority of my engineering training and career.
It does what it needs to well enough, and is pretty extensible for such a monstrosity of proprietary software.
I can say a whole lot more negative about it, and I already said I&rsquo;d save the blot rant for another post.</p>
<p>AutoCAD is a relatively old piece of software, so old that it uses Lisp for scripting.
Like all Lisps, AutoCAD Lisp is its own flavor but shares a lot of similarities with Common Lisp.
Prior to this job I never had a need to use scripting within AutoCAD, so I took the opportunity to learn Lisp from one of the handful of popular Lisp projects: GNU Emacs.</p>
<h3 id="starting-with-doom-emacs">Starting with Doom Emacs</h3>
<p>I took the route most vim users probably do and start with a pre-configured Emacs rice, and for me that was Doom Emacs.
It was a way to get my feet wet and see what Emacs is capable of before going all-in on a vanilla config.
With projects like Doom and Spacemacs you get a possible end result for inspiration, perspective, and motivation, because you sure as hell are getting none of that when launching vanilla emacs for the first time.
Similar to using a pre-riced window manager like those which used to ship with Arco Linux, or any of the many Arch Linux-based &ldquo;distros&rdquo; which are simply just bloated wm rices (Omarchy).</p>
<p>Even though I no longer use Doom and don&rsquo;t recommend using pre-packaged configs for long term use, I still think it&rsquo;s a good place to start if you&rsquo;re just curious and don&rsquo;t want to dedicate too much time or effort to software you may not actually like.
Doom and Spacemacs give a very pleasant first impression, which was not the case when I first opened vanilla emacs out of curiosity when I was brand-new to Linux.
And those first impressions are often what will make or break your opinion of the software - the same reason we almost always recommend Linux Mint to new users.</p>
<h3 id="leaving-doom">Leaving Doom</h3>
<p>Doom is a very opinionated Emacs framework.
The mods are impressive and its speed is something to behold considering all the bloat (it is actually bloated).
As a newb I found it very difficult to figure out exactly what was going on under the hood; it felt very far removed from the core system and I felt like a lot of the Emacs guidance in older posts or references simply didn&rsquo;t apply.
Emacs is different enough as it is, and I think that&rsquo;s the main reason people write off Emacs as bloated or dumb or inferior: it is different and hard when coming from anything else.
To an extent Doom adds to that complexity, almost like added another OS-layer onto a program that is jokingly (but accurately) called an OS running on an OS.</p>
<p>Overall Doom Emacs gave me a very positive opinion on Emacs, but I knew I would be on a vanilla config in short time.</p>
<h3 id="building-a-vanilla-config">Building a Vanilla Config</h3>
<p>After some time I decided to move to a vanilla emacs configuration.
SystemCrafters and Prot were really helpful.
My fairly lean ~1500 line init.el had a lot of the features you found in the wild at that time - use-package, evil &amp; evil-collection, general, which-key, counsel, ivy, ivy-rich, corfu, etc.
It relied pretty heavily on outside packages but was not nearly the beast that is Doom.
And it was set and forget for about a year of primary use.</p>
<h2 id="i-stopped-using-emacs">I stopped using Emacs</h2>
<p>I stopped using Emacs because I found myself using my personal computer less and less. Other interests occupied my spare time; linux hacking became dull.
I&rsquo;ll go on several-month to year+ &ldquo;binges&rdquo; on whatever interests me the most.
At that time I was making a lot of improvements to my house, so a lot of time was spent playing with power tools and clearing out woods.
And a ton of biking.</p>
<p>If I was using an editor, it was vim, simply because I wasn&rsquo;t doing anything more than tweaking a few shell scripts.</p>
<h2 id="return-to-emacs">Return to Emacs</h2>
<p>Alas last fall my interest in linux was renewed through self-hosting and making a homelab, as cringe as that word is.
It is a level of fun I hadn&rsquo;t tapped into and a side of linux (larping as a sysadmin) I hadn&rsquo;t done much of before.
And so it was only a matter of time before I jumped back on the Emacs train, even if just out of curiousity.</p>
<p>Fast foward to January, 2026. I haven&rsquo;t touched Emacs in over 2 years, probably closer to 3.
I see and consoom a bit of new emacs content on yt, some new guys in the game, joined some chats with a number of emacs users, and decide to fire her up again.
After a couple minutes of package updating, to my surprise it launches just as I remembered it.
Had to refresh myself on some of the major keybindings, but that came back pretty quick.
Emacs is great because M-x (alt-x) can be used to launch anything, and C-h can be used to quickly access help files.
So when you can&rsquo;t remember what to do it doesn&rsquo;t take much effort to figure out, especially if using some kind of matching/completion system in the minibuffer (recommended).
I take the same approach with vim - learn the commands over the keybindings.</p>
<p>As I re-write my config and experiment more with Emacs, I&rsquo;ll post here to document what I&rsquo;ve done without the need for a bloated literate config, as well as some spicy Emacs takes.
That way any of you Emacs-curious readers or Doom Emacs enjoyers can learn from my <del>mistakes</del> experiences and be on your way to a diy config.</p>
<p>If you&rsquo;re curious about Emacs, give it a try. It&rsquo;s fun - that&rsquo;s the only reason you need.</p>
]]></content:encoded>
    </item>
    <item>
      <title>The Crux of the SSPX: Crisis and Necessity</title>
      <link>https://linuxmaxxing.com/posts/03_sspx/</link>
      <pubDate>Sun, 08 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/03_sspx/</guid>
      <description>&lt;p&gt;On February 2, 2026, the Feast of the Presentation/Purification, the Superior General of the Society of St. Pius X (SSPX) announced his intentions to consecrate new bishops for the SSPX on July 1, 2026.&lt;/p&gt;&#xA;&lt;h2 id=&#34;my-background-regarding-the-sspx&#34;&gt;My Background regarding the SSPX&lt;/h2&gt;&#xA;&lt;p&gt;During my second year of college I began attending the Traditional Latin Mass (TLM), which was at an Fraternity of St. Peter (FSSP) apostolate a reasonable drive away.&#xA;For those who don&amp;rsquo;t know, the FSSP was founded by former SSPX members who disagreed with SSPX founder Archbishop Marcel Lefebvre&amp;rsquo;s decision to consecrate 4 bishops without Rome&amp;rsquo;s approval in 1988.&#xA;As a result the priests of the FSSP are anti-SSPX to varying degrees, mostly hardline.&#xA;There was no SSPX presence near me at college nor where I grew up.&#xA;I didn&amp;rsquo;t have strong feelings towards the SSPX other than I did not believe I could attend their chapels and I found their website harsh (in hindsight they had a pretty decent site layout, I don&amp;rsquo;t even think they were using https).&#xA;I began to follow the SSPX closer when their negotiations with Vatican began and ultimately failed around 2009.&#xA;I attended the FSSP almost exclusively until 2013 when I moved to an area with no FSSP presence.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>On February 2, 2026, the Feast of the Presentation/Purification, the Superior General of the Society of St. Pius X (SSPX) announced his intentions to consecrate new bishops for the SSPX on July 1, 2026.</p>
<h2 id="my-background-regarding-the-sspx">My Background regarding the SSPX</h2>
<p>During my second year of college I began attending the Traditional Latin Mass (TLM), which was at an Fraternity of St. Peter (FSSP) apostolate a reasonable drive away.
For those who don&rsquo;t know, the FSSP was founded by former SSPX members who disagreed with SSPX founder Archbishop Marcel Lefebvre&rsquo;s decision to consecrate 4 bishops without Rome&rsquo;s approval in 1988.
As a result the priests of the FSSP are anti-SSPX to varying degrees, mostly hardline.
There was no SSPX presence near me at college nor where I grew up.
I didn&rsquo;t have strong feelings towards the SSPX other than I did not believe I could attend their chapels and I found their website harsh (in hindsight they had a pretty decent site layout, I don&rsquo;t even think they were using https).
I began to follow the SSPX closer when their negotiations with Vatican began and ultimately failed around 2009.
I attended the FSSP almost exclusively until 2013 when I moved to an area with no FSSP presence.</p>
<p>Around 2019 I transitioned away from a stressful job with long-hours [finally] had [some] free time on my hands.
I became a big consoomer of traditional Catholic media, listening to good number of the usual podcasts (Marshall, Remnant, Flanders, Fatima Center, etc) whilst commuting and touching/mowing grass.
I read a ton of the standard trad books (Gruner, Ferrara, Davies, +Lefebvre, etc), and overall kept up with the goings on in the Church - at least within my segment of the hoi poloi.</p>
<p>I became sympathetic to the position of +Lefebvre and believed that he acted in good faith. I gained <strong>perspective</strong> towards him and understood that he was a practical man, a missionary for most of his clerical career, and was used to having to diy in the bush of Africa.
Whether he was right or wrong to consecrate said bishops in 1988 and incur what normally would be automatic excommunication, I realized in his own eyes he was making a prudential judgment in a situation that went beyond the letter of the law - I daresay even an inconceivable situation (queue guy from Princess Bride) - believing his obligation before God was to follow not the letter, but the spirit of the law.
I concluded that even if he were wrong his decision, he was in good faith and as a result was protected from excommunication in both the 1917 and 1983 codes of canon law, which have a few exceptions for state of emergency, necessity, etc. Far more learned men have come to the same conclusion. <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Furthermore in reviewing Rome&rsquo;s correspondence with and regarding the SSPX since the early 1990s, I came to believe Catholics could attend SSPX chapels provided they do not take on a schismatic attitudes (I&rsquo;m omitting details here).
If you drew a line in the sand and asked me if +Lefebvre was right or wrong side of the line, I would say right.
I began attending the local SSPX chapel as I viewed it as the best option in my area for my family.
If there was an FSSP, ICRSS, or consistent diocesan church around, I would have probably attended those instead.</p>
<h3 id="tldr">TL;DR:</h3>
<p>I say all this as background to explain that:</p>
<ol>
<li>I have been on both sides of the SSPX issue.</li>
<li>I have considered all sides to some [hopefully adequate] degree</li>
<li>I still am not hardline one way or the other.</li>
<li>I acknowledge that I could be wrong.</li>
</ol>
<h2 id="the-crux-of-the-sspx-crisis-and-necessity">The Crux of the SSPX: Crisis and Necessity</h2>
<p>The correctness or even liceity of the SSPX hinges on the fact that:</p>
<ol>
<li>The Church is in a crisis.</li>
<li>There is a necessity for bishops to continue the mission of the Church (could also mean a state of emergency).</li>
</ol>
<p>That&rsquo;s it, really. To be squeaky clean, you really need both of these to be true.</p>
<h3 id="talking-heads">Talking Heads</h3>
<p>Since around 2022 I have mostly removed myself from consoom Catholic media. For the most part it is a distraction for me.
I make exception for Avoiding Babylon for the lulz and meme content.
There is nothing about the faith I can&rsquo;t learn from books I have, and there is nothing truly important that I won&rsquo;t find out from the pulpit or some meme chats.
I am not saying you need to tune these out or falling into the classic &ldquo;you&rsquo;re not a real Catholic unless you ___&rdquo; fallacy, but especially with Lent upon us I would advise at least considering it.
Plenty of the talking heads laudably take time off during Lent.</p>
<p>Regarding the recent SSPX announcement, I have deliberately not cared much what others have said about it.
I will now contradict what I just said and say that from a cursory glance of the headlines it seems the response has been fairly measured and optimistic, especially considering there is time.
This is a good thing.</p>
<h2 id="thought-experiment---the-actually-schismatic-sspx2">Thought Experiment - the actually schismatic SSPX2</h2>
<p>It is the year 2026. The fictional SSPX2 has existed as a formal parallel church since 1970 and has set up its own dioceses since then.
The conditions in the Church are otherwise exactly as they are now.
We treat the SSPX2 as heretics and formal schismatics.</p>
<p>With that being said:</p>
<ol>
<li>Is there a crisis in the Church?</li>
<li>Is there a need for Catholic Bishops?</li>
</ol>
<p>If you ask any faithful Catholic who attends anything to the right of a garden variety American Novus Ordo, he will almost certainly say yes to both of those questions.</p>
<p>So if Bp. Someguy decided out of necessity he was going to consecrate bishops so that the sacraments of confirmation and holy orders could be continued (and more things), would he be justified under the current circumstances?</p>
<p>If not, I don&rsquo;t necessarily fault you for saying so; you are saying so out of filial obedience to the Church. But my question to you would be:</p>
<ol>
<li>What would be the point at which we are in a crisis and/or it becomes necessary?</li>
<li>If there is never a point where consecrating bishops (or breaking some other disciplinary law) is justified, then why does canon law even give qualifications for doing such things? Why would it leave those kinds of outs?</li>
</ol>
<h2 id="my-take">My Take</h2>
<p>Looking at everything from a 30k ft view (that&rsquo;s about 10km, long live the Empire) over the last 100 years or so, I don&rsquo;t know how much worse it has to get before we can officially call it a crisis.
It seems to quack quack a lot louder than a single duck.
I would say even if 25% of the churches were in as bad shape as the average one is today (or was in 1970), we&rsquo;d be in a crisis (though we might have enough bishops in that case).</p>
<p>Likewise when there are literally single-digit number of bishops who we call /ourguy/ - and the lead of them is an Auxiliary Bishop from Kazakhstan - I&rsquo;m not sure how we can say there isn&rsquo;t a need for bishops.
Fortunately a number of them still have some time left - provided they don&rsquo;t get Cdl. Pell&rsquo;d (RIP) - in 10 or 20 years how many will be left?
Are any of the good diocesan or religious priests getting elevated?
Who is determining who becomes bishop (aka &ldquo;king makers&rdquo;), and who has been elevated over the last 15-ish years?
Is there any end in sight to this?
Can we even see an inflection point where things begin to slow down?</p>
<h2 id="perspective-is-key">Perspective is Key</h2>
<p>I ask that anyone who feels strongly one way or the other, please have charity and <strong>steel-man</strong> your opponent.</p>
<p>If you are strongly against the SSPX, please read the works of +Lefebrve, especially Open Letter to Confused Catholics.
Read his biography, watch his documentary.
Do everything you can to gain his <strong>perspective</strong>.</p>
<p>Afterwards if you don&rsquo;t think he was justified, that&rsquo;s alright, just have an answer as to when he (or someone) would be justified in doing so.</p>
<p>Put yourself in the shoes of a faithful bishop in 2026.
What would you do?
What would you feel comfortable bringing to your judgment?
I don&rsquo;t envy any of these men, it is a hard decision all around, and I think it would take a tremendous amount of fortitude to <em>faithfully</em> choose one side or the other.
It is not a clean win-lose situation, but a bad-worse situation.</p>
<p>Above all I pray God&rsquo;s will be done. AMDG.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://fsspx.org/en/concerning-fr-murrays-thesis-31127">Fr. Gerald Murray&rsquo;s thesis on Canon Law</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Septuagesima, or I forgot that Lent draweth nigh (and that&#39;s ok)</title>
      <link>https://linuxmaxxing.com/posts/02_septuagesima/</link>
      <pubDate>Tue, 03 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/02_septuagesima/</guid>
      <description>&lt;p&gt;It happens every year. I get caught up in the routine of Christmas and New Year. I&amp;rsquo;m usually busier at work early in the year. This year in particular I went on vacation with extended family for a week in late January.&#xA;I show up for Sunday Mass thinking about everything going on during the rest of the day, as I am wont to do.&#xA;And then I see something I wasn&amp;rsquo;t expecting:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>It happens every year. I get caught up in the routine of Christmas and New Year. I&rsquo;m usually busier at work early in the year. This year in particular I went on vacation with extended family for a week in late January.
I show up for Sunday Mass thinking about everything going on during the rest of the day, as I am wont to do.
And then I see something I wasn&rsquo;t expecting:</p>
<p>Purple.</p>
<h3 id="septuagesima">Septuagesima</h3>
<p>From the Catholic encyclopedia:</p>
<blockquote>
<p>(Latin septuagesima, the seventieth).</p>
<p>Septuagesima is the ninth Sunday before Easter, the third before Lent known among the Greeks as &ldquo;Sunday of the Prodigal&rdquo; from the Gospel, Luke 15, which they read on this day, called also Dominica Circumdederunt by the Latins, from the first word of the Introit of the Mass. In liturgical literature the name &ldquo;Septuagesima&rdquo; occurs for the first time in the Gelasian Sacramentary. Why the day (or the week, or the period) has the name Septuagesima, and the next Sunday Sexagesima, etc., is a matter of dispute among writers. It is certainly not the seventieth day before Easter, still less is the next Sunday the sixtieth, fiftieth, etc. Amularius, &ldquo;De eccl. Off.&rdquo;, I, I, would make the Septuagesima mystically represent the Babylonian Captivity of seventy years, would have it begin with this Sunday on which the Sacramentaries and Antiphonaries give the Introit &ldquo;Circumdederunt me undique&rdquo; and end with the Saturday after Easter, when the Church sings &ldquo;Eduxit Dominus populum suum.&rdquo; Perhaps the word is only one of a numerical series: Quadragesima, Quinquagesima, etc. Again, it may simply denote the earliest day on which some Christians began the forty days of Lent, excluding Thursday, Saturday, and Sunday from the observance of the fast.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
</blockquote>
<p>The Church in her wisdom gives us a time prior to Lent so that we may better be prepared for the Lenten season.
It is supposed to be a time of fasting and penance, and nobody who seriously takes up that practice - or any bodily practice - jumps in cold turkey with a day&rsquo;s notice.
The idea is that the entire 40-day period (it&rsquo;s not quite 40 days, just like Septuagesima/Sexagesima/Quinquagesima aren&rsquo;t actually 70/60/50 days respectively. Math is hard.) is a time of fasting and prayer in preparation for Easter.
Lent isn&rsquo;t meant to be a 40-day journey where we gradually ween ourselves and hopefully by Easter we&rsquo;re able to fast for a day or two.</p>
<p>Ideally we are so aware of the liturgical year that at all times we know how many days remain until Lent and so we can be prepared on our own without any reminders.
Rephrased: ideally we should not have concupiscence and other effects of Original Sin.
Unfortunately that is not how people are, so the Church gives us these visible reminders that Lent is coming.
The colour is purple which we only see in Lent and Avent; the Gloria is ommitted; the Epistle and Gospel readings have a more penitential or reflective tone so that we are encouraged to reflect on the Mercy of God and where we need to be in 3 weeks (and at the end of our lives).</p>
<p>This is not a practice isolated to the Latin Church, and it has its parallels in the Greek Church with Meatfare and Cheesefare, plus with similar trends in the readings.
This is a universal (i.e. catholic) practice meant to strengthen our weak human nature, which is yet again something the authors of the Novus Ordo - who at least claimed to appeal to &lsquo;modern man&rsquo; and the East - got completely wrong.</p>
<p>When I attended the NO - which has been over a decade ago at this point - it very frequently happened that I would show up to Mass on Sunday with Fr. in green vestments, not knowing what Sunday it is in Ordinary Time, and there might be a comment in the homily that Ash Wednesday is this week.
I haven&rsquo;t changed, so I was just as forgetful or side-tracked as I am now. That gave me 3 days to quickly move away from feasting and consooming product to the opposite.
Then whatever things I wanted to do for Lent generally didn&rsquo;t happen at the start, which means they pretty much didn&rsquo;t happen.
Failing in our resolutions is inevitable to at least some degree, but it sure helps if we at least have a fighting chance and make headway from the get go. That initial momentum helps us make it to the end.</p>
<p>So, if you&rsquo;re like me and completely forgot about Septuagesima Sunday, that is completely ok.
The Calendar is designed with us in mind. It&rsquo;s part of the plan - trvst the plan.</p>
<p>I wish you all a blessed [preparation for] Lent.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://www.newadvent.org/cathen/13721b.htm">Catholic Encylopedia article on Septuagesima</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Why I use [paleo] vim</title>
      <link>https://linuxmaxxing.com/posts/01_vim/</link>
      <pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/posts/01_vim/</guid>
      <description>&lt;p&gt;It is 2026, over 10 years after the neovim project forked from vim.&#xA;For the first couple years of the project I didn&amp;rsquo;t think much of it; cool ideas, but ultimately something I thought would fizzle out and/or merge into the original project.&#xA;Most people were using identical vimrc in vimscript, just named init.vim, and using the same plugins which have proved valuable for a long time - fedora tip to Tim Pope.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>It is 2026, over 10 years after the neovim project forked from vim.
For the first couple years of the project I didn&rsquo;t think much of it; cool ideas, but ultimately something I thought would fizzle out and/or merge into the original project.
Most people were using identical vimrc in vimscript, just named init.vim, and using the same plugins which have proved valuable for a long time - fedora tip to Tim Pope.</p>
<p>Around 2020 (from what I can remember) things started to change.
Lua was becoming a first-class citizen in neovim, various projects which resembled Emacs distributions were popping up, lua plugins were becoming more popular and optimized, and other things to distinguish neovim from vim.
Whilst I always tried to minimize the number of plugins I used in vim - mainly for speed since was/am using old laptops as GNU/Linux enjoyers are wont to do - in neovim it became increasingly irrelevent because of how snappy &ldquo;bloated&rdquo; lua configs could be.
Neovim could easily become a full-blown terminal IDE without any effect on performance, making it loads faster than its IDE competition, and better by virtue of being vim vs a compatibility layer like in vscodium.</p>
<p>With that in mind, why do I continue to use paleo-vim?</p>
<h3 id="use-case">Use case</h3>
<p>Let&rsquo;s first look at my vim use case. Professionally I am not a programmer (as much as I might cosplay with LISP at work).
I use vim primarily for what it is: a text editor.
I use vim to edit files on my desktop and the many servers that I use.
I need to have a simple config that I can quickly copy over to a server when needed, or if not possible one that I can generate on my own.
Vimrc in its default location (~/.vimrc) is quickest to transfer over to another server.
Whilst this could easily be located in ~/.vim/vimrc, using scp this introduces a bit more complexity: removing the ~/.vimrc file if it exists, checking for the ~/.vim directory and creating it if not present, and then copying vimrc to the ~/.vim directory.
Easy to script, but often I don&rsquo;t want to hassle with that.
The same logic applies for neovim just with ~/.config/init.vim.
Many people make a big deal about crowding the home directory with dotfiles, and that&rsquo;s something my tism really doesn&rsquo;t care about. Perhaps more on that in another post.</p>
<h3 id="plugins">Plugins</h3>
<p>I have only a handful of plugins and each are lazy-loaded, meaning they don&rsquo;t load until I call the plugin&rsquo;s function.
Currently [at this time] I am using vimwiki, vifm, and fzf. Generally I never use any of these plugins, and fzf comes in handly on occation when I&rsquo;m looking for specific files for libraries in /var.
I only use default colorschemes, and thanks to neovim-inspired updates there are some really good default colorschemes - retrobox gang rise up.
My vimrc checks if the plugins directory exists before loading pluins - which is basically never on servers - so my vimrc is itempotent.
Additionally I unfortunately use Windowns at work, so my vimrc has conditionals for Windowns and GNU/Linux/BSD.
All this is possible on neovim of course, but it gets back to a more fundamental issue: why change an editor if nothing will change what I do with said editor?</p>
<h3 id="vimscript">Vimscript</h3>
<p>I like vimscript. I use it all the time for ad-hoc commands within vim. Does it suck as a plugin language? Probably.
Do I like lua? Yeah, I do like lua. It&rsquo;s a smart choice for what neovim aims to achieve. I have used it in other programs.
I don&rsquo;t have strong feelings towards vim9script because I&rsquo;ve never felt a need to deviate from paleo-vimscript.
Hats off to neovim for keeping backwards compatibility with vimscript and also making compatability for vim9.
Regardless of what you think of vimscript, you need to now some of it - at least ad-hoc set commands.</p>
<h3 id="gill-bates">Gill Bates</h3>
<p>Speaking of Windowns, I&rsquo;ve found gvim works better than neovim. Despite vimrc having a different name in Windows (_vimrc), I have a macro that copies .vimrc and renames to _vimrc.
Neovim on Windowns only works in the terminal, and short of alacritty the windows terminals aren&rsquo;t great.
I haven&rsquo;t tried neovim on Windowns in about several years, so this has probably changed as a lot has happened with Neovim since.</p>
<h3 id="contrarian">Contrarian</h3>
<p>As much as I try to fight it, I can be a contrarian. In 2026 using paleo-vim is the contrarian thing to do.
When Bram passed (RIP) I wasn&rsquo;t sure where the project would go, but fortunately it has been more or less business as usual since.
This contrarian spirit extends to much of my computing setup: openrc, xorg / xlibre, Emacs, vanilla GNU/Emacs, C vs rust, etc, and in plenty of other areas in my life.</p>
<h3 id="emacs-is-neovim-but-better">Emacs is Neovim but better</h3>
<p>Rather than ricing out neovim to be an IDE, I prefer just using Emacs. I don&rsquo;t buy in to the Emacs vs Vim wars because I use each to their strengths.
I love the complete operating system Emacs provides and how it eliminates using terminal programs which have similarities, but ultimately are different beasts; they often have keybindings that conflict with one-another, or have some vim-like keybindings but not all of them.
This is not a problem in Emacs, and even if it were it would be easy to rectify because of the major/minor mode structure.
Weechat, newsboat, tmux, etc, are all wrapped into Emacs.</p>
<p>As a text editor and in the tty, vim absolutely reigns supreme, but Emacs with evil bindings is a passable vim.
I have no issues opening a terminal and using vim if needed. But on all my servers vim is a must-have; I would never even think of installing Emacs on my alpine Linux servers just to edit text.
More on Emacs in follow-up posts.</p>
<h3 id="separating-vim-from-neovim">Separating Vim from Neovim</h3>
<p>All that being said I have a lua-config of neovim on my system. I am a tinkerer and still enjoy the process of learning new things. I have nvchad installed and can open it with a specific config.
I don&rsquo;t regularly use these neovim flavours, but having them completely separate from my vim configs means I can fuxx around with neovim IDEs without touching my .vimrc or creating multiple inits which are called depending on what I want.</p>
<h3 id="love-for-the-neovim-project">Love for the Neovim project</h3>
<p>[Again] All that being said I think neovim has been a net-positive. It has completely changed that possibilities with vim, and the project itself has brought improvements to paleo-vim, e.g. improved default colorschemes as mentioned about. Regardless of how you feel about vim9script, it would not have happened had neovim never forked.
Neovim has caused a splintering in the projects which I think is a good thing. <del>Old</del> crusty guys like myself stick to vim because of its simplicity and minimalism (relatively speaking; vim is actually bloat compared to nvi which is bloat compared to vi which is bloat compared to ed), and guys who want the plugin-heavy or IDE environment have moved away to a separate project.</p>
<p>This can keep the goals of each project consistent, and vim no longer has to cater to pull requests for features it deems appropriate for neovim.
TJ and Prime and other neovim faces also seem like legitimately good guys, and in the past neovim devs have pushed their own improvements to vim when appropriate.
Tons of users have discovered the goodness that is vim (in the broad sense) and have moved away from vscode, sublime, gedit (jk nobody uses that trash), or other IDEs/editors because of the hype neovim has on the internet.
All good things and I think it shows the long-term (albeit seemingly rare) mutual success that can be possible from project forks.</p>
<p>Emacs is still better though.</p>
<p>Who else uses paleo-vim? Let me know.</p>
]]></content:encoded>
    </item>
    <item>
      <title></title>
      <link>https://linuxmaxxing.com/links/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/links/</guid>
      <description>&lt;h3 id=&#34;webrings&#34;&gt;Webrings&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://heaventree.xyz&#34;&gt;Heaventree&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://webring.cashmere.rs/&#34;&gt;Technical Renaissance&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;bros&#34;&gt;Bros&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://kelvinsandigo.com/&#34;&gt;Kelvin&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://denshi.org/&#34;&gt;Denshi&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://joshblais.com&#34;&gt;Joshua Blais&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://matthew-kennedy.com/&#34;&gt;Matthew Kennedy&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.tonybtw.com/&#34;&gt;tony, btw&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://cashmere.rs/&#34;&gt;cashmere&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;gnulinux-references&#34;&gt;GNU/Linux References&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://readline.kablamo.org/emacs.html&#34;&gt;Readline Cheat Sheet&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;gnulinux-distros-i-use-and-recommend&#34;&gt;GNU/Linux Distros I use and recommend&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://guix.gnu.org/&#34;&gt;GNU Guix&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.devuan.org/&#34;&gt;Devuan GNU+Linux&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://artixlinux.org&#34;&gt;Artix GNU/Linux&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.alpinelinux.org/&#34;&gt;Alpine Linux&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://proxmox.com/en/&#34;&gt;Proxmox&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;various-sites&#34;&gt;Various Sites&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://unixdigest.com&#34;&gt;Unix Digest&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://bogleheads.org/&#34;&gt;Bogleheads&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://realulimatepower.net&#34;&gt;Official Ninja Webpage&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://example.com&#34;&gt;Hecking based website&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;channels-i-regularly-consoom&#34;&gt;Channels I regularly consoom&lt;/h2&gt;&#xA;&lt;h3 id=&#34;tech&#34;&gt;tech:&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;learnlinuxtv&lt;/li&gt;&#xA;&lt;li&gt;system crafters&lt;/li&gt;&#xA;&lt;li&gt;prot&lt;/li&gt;&#xA;&lt;li&gt;dt&lt;/li&gt;&#xA;&lt;li&gt;tony_btw&lt;/li&gt;&#xA;&lt;li&gt;joshua blais&lt;/li&gt;&#xA;&lt;li&gt;denshi&lt;/li&gt;&#xA;&lt;li&gt;dj ware&lt;/li&gt;&#xA;&lt;li&gt;kris occhipinti&lt;/li&gt;&#xA;&lt;li&gt;unaboomer&lt;/li&gt;&#xA;&lt;li&gt;Old Tech Bloke (RIP)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;catholicism&#34;&gt;Catholicism:&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;SSPX Podcast&lt;/li&gt;&#xA;&lt;li&gt;Sensus Fidelium&lt;/li&gt;&#xA;&lt;li&gt;Avoiding Babylon&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;finance&#34;&gt;finance:&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Ben Felix&lt;/li&gt;&#xA;&lt;li&gt;Patrick Boyle&lt;/li&gt;&#xA;&lt;li&gt;The Money Guy&lt;/li&gt;&#xA;&lt;li&gt;DiamondNestEgg&lt;/li&gt;&#xA;&lt;li&gt;Strong Man Personal Finance&lt;/li&gt;&#xA;&lt;li&gt;RJ Financial / Run on the Bank&lt;/li&gt;&#xA;&lt;li&gt;Bank Account Bonus Central&lt;/li&gt;&#xA;&lt;li&gt;Clark Howard&lt;/li&gt;&#xA;&lt;/ul&gt;</description>
      <content:encoded><![CDATA[<h3 id="webrings">Webrings</h3>
<ul>
<li><a href="https://heaventree.xyz">Heaventree</a></li>
<li><a href="https://webring.cashmere.rs/">Technical Renaissance</a></li>
</ul>
<h3 id="bros">Bros</h3>
<ul>
<li><a href="https://kelvinsandigo.com/">Kelvin</a></li>
<li><a href="https://denshi.org/">Denshi</a></li>
<li><a href="https://joshblais.com">Joshua Blais</a></li>
<li><a href="https://matthew-kennedy.com/">Matthew Kennedy</a></li>
<li><a href="https://www.tonybtw.com/">tony, btw</a></li>
<li><a href="https://cashmere.rs/">cashmere</a></li>
</ul>
<h3 id="gnulinux-references">GNU/Linux References</h3>
<ul>
<li><a href="https://readline.kablamo.org/emacs.html">Readline Cheat Sheet</a></li>
</ul>
<h3 id="gnulinux-distros-i-use-and-recommend">GNU/Linux Distros I use and recommend</h3>
<ul>
<li><a href="https://guix.gnu.org/">GNU Guix</a></li>
<li><a href="https://www.devuan.org/">Devuan GNU+Linux</a></li>
<li><a href="https://artixlinux.org">Artix GNU/Linux</a></li>
<li><a href="https://www.alpinelinux.org/">Alpine Linux</a></li>
<li><a href="https://proxmox.com/en/">Proxmox</a></li>
</ul>
<h3 id="various-sites">Various Sites</h3>
<ul>
<li><a href="https://unixdigest.com">Unix Digest</a></li>
<li><a href="https://bogleheads.org/">Bogleheads</a></li>
<li><a href="http://realulimatepower.net">Official Ninja Webpage</a></li>
<li><a href="https://example.com">Hecking based website</a></li>
</ul>
<h2 id="channels-i-regularly-consoom">Channels I regularly consoom</h2>
<h3 id="tech">tech:</h3>
<ul>
<li>learnlinuxtv</li>
<li>system crafters</li>
<li>prot</li>
<li>dt</li>
<li>tony_btw</li>
<li>joshua blais</li>
<li>denshi</li>
<li>dj ware</li>
<li>kris occhipinti</li>
<li>unaboomer</li>
<li>Old Tech Bloke (RIP)</li>
</ul>
<h3 id="catholicism">Catholicism:</h3>
<ul>
<li>SSPX Podcast</li>
<li>Sensus Fidelium</li>
<li>Avoiding Babylon</li>
</ul>
<h3 id="finance">finance:</h3>
<ul>
<li>Ben Felix</li>
<li>Patrick Boyle</li>
<li>The Money Guy</li>
<li>DiamondNestEgg</li>
<li>Strong Man Personal Finance</li>
<li>RJ Financial / Run on the Bank</li>
<li>Bank Account Bonus Central</li>
<li>Clark Howard</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>About</title>
      <link>https://linuxmaxxing.com/about/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/about/</guid>
      <description>&lt;p&gt;My name is Scotty and I am a GNU/Linux enthusiast. I am not a developer,&#xA;programmer, sysadmin, or IT professional, though I will cosplay to&#xA;varying degrees.&lt;/p&gt;&#xA;&lt;p&gt;I have many interests and my site is or will be a way of documenting them - GNU/Linux and tech of course, but also Catholicism, personal finance, credit cards/bank account churning, cycling, ball and disc golf, home improvement, and many more.&#xA;Computers in general and GNU/Linux specifically are one of many&#xA;interests, but one I consistently keep busy with and will hopefully be able to as long as I live.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>My name is Scotty and I am a GNU/Linux enthusiast. I am not a developer,
programmer, sysadmin, or IT professional, though I will cosplay to
varying degrees.</p>
<p>I have many interests and my site is or will be a way of documenting them - GNU/Linux and tech of course, but also Catholicism, personal finance, credit cards/bank account churning, cycling, ball and disc golf, home improvement, and many more.
Computers in general and GNU/Linux specifically are one of many
interests, but one I consistently keep busy with and will hopefully be able to as long as I live.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Contact</title>
      <link>https://linuxmaxxing.com/contact/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://linuxmaxxing.com/contact/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://web.libera.chat/&#34;&gt;irc.libera.chat&lt;/a&gt;: Sneed1911&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://codeberg.org/guy&#34;&gt;codeberg&lt;/a&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://web.libera.chat/">irc.libera.chat</a>: Sneed1911</p>
<p><a href="https://codeberg.org/guy">codeberg</a></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
