<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><script src="https://www.rss.style/js/atom-style.js" xmlns="http://www.w3.org/1999/xhtml"/><title>Tower of Kubes</title><link rel="self" type="application/atom+xml" hreflang="en" href="https://www.towerofkubes.com/tags/git/feed.xml"/><link rel="alternate" type="text/html" hreflang="en" href="https://www.towerofkubes.com/tags/git/"/><link rel="alternate" type="application/rss+xml" hreflang="en" href="https://www.towerofkubes.com/tags/git/index.xml"/><id>/</id><updated>2025-11-13T00:00:00Z</updated><author><name>Ro'i Bandel</name></author><generator>Hugo 0.157.0</generator><entry><title>Grebedoc</title><link rel="alternate" type="text/html" hreflang="en" href="https://www.towerofkubes.com/articles/grebedoc/"/><id>https://www.towerofkubes.com/articles/grebedoc/</id><updated>2025-11-13T00:00:00Z</updated><summary type="html">Today I learned about Grebedoc — static site hosting for git forges.</summary><content type="html"><![CDATA[<p>Today I learned about <a href="https://grebedoc.dev/"  target="_blank" rel="noreferrer">Grebedoc — static site hosting for git forges</a>.</p>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Grebedoc is Codeberg spelled backwards. I find this name very clever, especially since it has “doc” in it (static site hosting can be used for Markdown Documentation|documentation).</p>
      </div>
    </div><p>This is a new option for Static Website Hosting, which can serve as an alternative to GitHub Pages. Codeberg already a similar solution called <a href="https://docs.codeberg.org/codeberg-pages/"  target="_blank" rel="noreferrer">Codeberg Pages</a>, though Codeberg Pages had a big scary warning that says it is in <a href="https://codeberg.org/Codeberg/pages-server/issues/399"  target="_blank" rel="noreferrer">maintenance mode</a>. Grebedoc is using new software, <a href="https://codeberg.org/git-pages/git-pages"  target="_blank" rel="noreferrer">git-pages</a>, so does not rely on <a href="https://codeberg.org/Codeberg"  target="_blank" rel="noreferrer">Codeberg</a>/<a href="https://codeberg.org/Codeberg/pages-server"  target="_blank" rel="noreferrer">pages-server</a> (which is the part of the Codeberg Pages stack that is in maintenance mode).</p>

<h2 class="relative group">Origin Story
    <div id="origin-story" class="anchor"></div>
    
</h2>

    <div class="admonition quote">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M448 296c0 66.3-53.7 120-120 120l-8 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l8 0c30.9 0 56-25.1 56-56l0-8-64 0c-35.3 0-64-28.7-64-64l0-64c0-35.3 28.7-64 64-64l64 0c35.3 0 64 28.7 64 64l0 32 0 32 0 72zm-256 0c0 66.3-53.7 120-120 120l-8 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l8 0c30.9 0 56-25.1 56-56l0-8-64 0c-35.3 0-64-28.7-64-64l0-64c0-35.3 28.7-64 64-64l64 0c35.3 0 64 28.7 64 64l0 32 0 32 0 72z"/></svg>
        <span>Quote</span>
      </div>
      <div class="admonition-content">
        <p>One of Grebedoc maintainers here! I came up with the idea for Grebedoc (and its underlying software, git-pages) when I realized that I have an extreme degree of dependency on GitHub Pages from many years of using GitHub, but it also seemed pretty likely that sooner or later, GitHub will stop subsidizing my efforts one way or another, and I need a backup plan.</p>
<p>I originally wanted to just use Codeberg Pages, but it had some significant scaling and uptime issues (that I don’t want to rehash here). I ended up concluding that the reasonable way forward is a redesign, which is what I’ve built and deployed with a small team of other volunteers. It took about a month of work and the whole thing, anycast and all, costs about 35€/mo to run. Also, Codeberg Pages is currently trialing the use of git-pages as the new pages backend, and you should be able to use it on the *.codeberg.page domain already (it responds to the same POST/PUT requests as Grebedoc)/</p>
      </div>
    </div><ul>
<li><a href="https://lobste.rs/~whitequark"  target="_blank" rel="noreferrer">whitequark</a>’s comment on <a href="https://lobste.rs/c/wmoqsn"  target="_blank" rel="noreferrer">Grebedoc — static site hosting for git forges | Lobsters</a></li>
</ul>
<p>The fact that the entire global stack “costs about 35€/mo to run” is impressive. Though, I wonder what the increase in cost will be when more people start using Grebedoc.</p>

<h2 class="relative group">Can I use Grebedoc for my personal projects?
    <div id="can-i-use-grebedoc-for-my-personal-projects" class="anchor"></div>
    
</h2>
<p>One of my concerns was that Grebedoc sites would <em>have</em> to use a Codeberg repository. Codeberg looks good, though it is more limiting than GitHub or GitLab since <a href="https://docs.codeberg.org/getting-started/faq/#can-i-host-content-without-a-free-and-open-source-license%3F"  target="_blank" rel="noreferrer">they require every repo to use an open source license</a>. This also raises concerns when creating a website/blog of my own, will any content hosted on a Codeberg repo also have to be licensed for the public domain?</p>
<p>However, based on <a href="https://grebedoc.dev/"  target="_blank" rel="noreferrer">grebedoc.dev</a>, using Codeberg is <strong>not</strong> mandatory. There is a small learning curve to understanding how to host a site on Grebedoc, but the main page explains different scenarios clearly. There is a size limitation:</p>

    <div class="admonition quote">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M448 296c0 66.3-53.7 120-120 120l-8 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l8 0c30.9 0 56-25.1 56-56l0-8-64 0c-35.3 0-64-28.7-64-64l0-64c0-35.3 28.7-64 64-64l64 0c35.3 0 64 28.7 64 64l0 32 0 32 0 72zm-256 0c0 66.3-53.7 120-120 120l-8 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l8 0c30.9 0 56-25.1 56-56l0-8-64 0c-35.3 0-64-28.7-64-64l0-64c0-35.3 28.7-64 64-64l64 0c35.3 0 64 28.7 64 64l0 32 0 32 0 72z"/></svg>
        <span>Quote</span>
      </div>
      <div class="admonition-content">
        <p>The size of a website is currently limited to <strong>1 GiB</strong>. We are aiming to eventually raise this to 10 GiB.</p>
      </div>
    </div><hr>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p><strong>UPDATE:</strong> Originally, the size limit was 768 MiB, but this has recently been raised to 1 GiB.</p>
      </div>
    </div><p>Based on all of that, it looks like I should be able to host small websites (up to 1 GiB) on Grebedoc, no matter which Git forge I choose to use and the repo can also remain private. I can use my own domains if I want, though Grebedoc also allows using <code>*.grebedoc.dev</code> or <code>*.codeberg.page</code> subdomains. All of this is <strong>free</strong>, as far as I can tell there is no paid-tier (Codeberg is a non-profit, they can be <a href="https://join.codeberg.org/"  target="_blank" rel="noreferrer">supported</a>, but this is not required for using anything they offer).</p>

<h2 class="relative group">Resources
    <div id="resources" class="anchor"></div>
    
</h2>
<ul>
<li><a href="https://grebedoc.dev/"  target="_blank" rel="noreferrer">Grebedoc — static site hosting for git forges</a></li>
<li><a href="https://news.ycombinator.com/item?id=45888143"  target="_blank" rel="noreferrer">Grebedoc – static site hosting for Git forges | Hacker News</a></li>
<li><a href="https://lobste.rs/s/btdj9j/grebedoc_static_site_hosting_for_git"  target="_blank" rel="noreferrer">Grebedoc — static site hosting for git forges | Lobsters</a></li>
<li><a href="https://unterwaditzer.net/2025/codeberg.html"  target="_blank" rel="noreferrer">Moving from GitHub to Codeberg, for lazy people - Markus Unterwaditzer</a></li>
</ul>
<hr>
<p><em>Featured image by <a href="https://unsplash.com/@trolf?utm_source=hugo&utm_medium=referral"  target="_blank" rel="noreferrer">Tina Rolf</a> on <a href="https://unsplash.com/?utm_source=hugo&utm_medium=referral"  target="_blank" rel="noreferrer">Unsplash</a>.</em></p>
]]></content><author><name>Ro'i Bandel</name></author><category term="til" label="Til" scheme="https://www.towerofkubes.com/tags/til/"/><category term="web" label="Web" scheme="https://www.towerofkubes.com/tags/web/"/><category term="website" label="Website" scheme="https://www.towerofkubes.com/tags/website/"/><category term="git" label="Git" scheme="https://www.towerofkubes.com/tags/git/"/><published>2025-11-13T00:00:00Z</published></entry><entry><title>Pre-commit hooks for Node.js projects</title><link rel="alternate" type="text/html" hreflang="en" href="https://www.towerofkubes.com/articles/pre-commit-hooks/"/><id>https://www.towerofkubes.com/articles/pre-commit-hooks/</id><updated>2025-11-10T00:00:00Z</updated><summary type="html">Step-by-step Node.js pre-commit setup: Husky + lint-staged + Oxc to enforce linting, formatting, and TypeScript checks before every git commit.</summary><content type="html"><![CDATA[<p>This is my workflow for integrating pre-commit hooks for Node.js (NPM) projects. I combine this with my <a href="/articles/oxc-workflow/" >Oxc Workflow</a>.</p>

<h2 class="relative group">What is a Git pre-commit hook?
    <div id="what-is-a-git-pre-commit-hook" class="anchor"></div>
    
</h2>
<p>A <code>pre-commit</code> is a type of <a href="https://git-scm.com/book/ms/v2/Customizing-Git-Git-Hooks"  target="_blank" rel="noreferrer">Git Hook</a> that runs before each commit. It can help with verifying code standards (linting, formatting, testing etc.).</p>

<h2 class="relative group">Pre-commit tools
    <div id="pre-commit-tools" class="anchor"></div>
    
</h2>

<h3 class="relative group"><a href="https://pre-commit.com/"  target="_blank" rel="noreferrer">pre-commit</a>
    <div id="pre-commit" class="anchor"></div>
    
</h3>
<p>A tool written in Python, though can be used with projects in any language. Can be configured to run many hooks including <a href="https://github.com/pre-commit/pre-commit-hooks"  target="_blank" rel="noreferrer">pre-commit/pre-commit-hooks</a> and <a href="https://github.com/gitleaks/gitleaks?tab=readme-ov-file#pre-commit"  target="_blank" rel="noreferrer">Gitleaks</a>. I have used this tool and like it for Python and other projects, though for Node.js projects, I prefer the options below.</p>

<h3 class="relative group"><a href="https://typicode.github.io/husky/"  target="_blank" rel="noreferrer">Husky</a>
    <div id="husky" class="anchor"></div>
    
</h3>
<p>A pre-commit hooks tool written in JavaScript. I prefer this tool for Node.js projects since it can be easily integrated in <code>package.json</code> scripts.</p>

<h3 class="relative group"><a href="https://www.npmjs.com/package/lint-staged"  target="_blank" rel="noreferrer">lint-staged</a>
    <div id="lint-staged" class="anchor"></div>
    
</h3>
<p>Another tool that’s written in JavaScript, to help run checks against staged files (see <a href="/articles/pre-commit-hooks/#guide-husky--lint-staged--oxc-workflow" >Guide</a> below). Lint-staged does not configure git pre-commit hooks on its own, but can be combined with Husky.</p>

<h3 class="relative group"><a href="https://github.com/toplenboren/simple-git-hooks"  target="_blank" rel="noreferrer">simple-git-hooks</a>
    <div id="simple-git-hooks" class="anchor"></div>
    
</h3>
<p>Another git hooks manager written in JavaScript. Use to be more lightweight than Husky, but newer versions of Husky closed the gap.</p>

<h2 class="relative group">Guide: Husky + Lint-staged + Oxc workflow
    <div id="guide-husky--lint-staged--oxc-workflow" class="anchor"></div>
    
</h2>
<ol>
<li>
<p>Configure <code>oxlint</code> and <code>oxfmt</code> based on my <a href="/articles/oxc-workflow/" >Oxc Workflow</a>.</p>
</li>
<li>
<p>Install <code>devDependencies</code>:</p>
</li>
</ol>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">npm install --save-dev husky lint-staged</span></span></code></pre></div></div>
<!-- markdownlint-disable MD029 -->
<ol start="3">
<li>Initialize <code>husky</code>:</li>
</ol>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># Installed</span>
</span></span><span class="line"><span class="cl">./node_modules/.bin/husky --init</span></span></code></pre></div></div>
<hr>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># Not installed</span>
</span></span><span class="line"><span class="cl">npx run husky --init</span></span></code></pre></div></div>
<ol start="4">
<li>Configure <code>lint-staged</code> to run Oxc and <code>tsc</code> checks:</li>
</ol>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="p">[</span><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @filename: lint-staged.config.js
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @type {import('lint-staged').Configuration}
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="s1">'**/*.[jt]s?(x)'</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">'oxfmt'</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">'oxlint --type-aware --type-check --fix'</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="s1">'**/*.ts?(x)'</span><span class="o">:</span> <span class="p">()</span> <span class="o">=></span> <span class="s1">'tsc -p tsconfig.json --noEmit'</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Can be further configured, but this is a good start for a project using TypeScript and Oxc.</p>
      </div>
    </div><ol start="5">
<li>Configure <code>husky</code> to run <code>lint-staged</code> as a pre-commit hook:</li>
</ol>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">npm <span class="nb">exec</span> -- lint-staged --config lint-staged.config.js</span></span></code></pre></div></div>
<ol start="6">
<li>Add a <code>prepare</code>script in <code>package.json</code> :</li>
</ol>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl">  <span class="s2">"scripts"</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">"prepare"</span><span class="p">:</span> <span class="s2">"husky"</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span><span class="err">,</span></span></span></code></pre></div></div>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>This script may have already been added by <code>husky --init</code>.</span>
      </div>
    </div><hr>
<p><em>Featured image by <a href="https://unsplash.com/@yancymin?utm_source=hugo&utm_medium=referral"  target="_blank" rel="noreferrer">Yancy Min</a> on <a href="https://unsplash.com/photos/a-close-up-of-a-text-description-on-a-computer-screen-842ofHC6MaI?utm_source=hugo&utm_medium=referral"  target="_blank" rel="noreferrer">Unsplash</a>.</em></p>
]]></content><author><name>Ro'i Bandel</name></author><category term="node" label="Node" scheme="https://www.towerofkubes.com/tags/node/"/><category term="javascript" label="Javascript" scheme="https://www.towerofkubes.com/tags/javascript/"/><category term="typescript" label="Typescript" scheme="https://www.towerofkubes.com/tags/typescript/"/><category term="pre-commit" label="Pre-Commit" scheme="https://www.towerofkubes.com/tags/pre-commit/"/><category term="git" label="Git" scheme="https://www.towerofkubes.com/tags/git/"/><published>2025-11-10T00:00:00Z</published></entry><entry><title>Git Setup for Windows and WSL</title><link rel="alternate" type="text/html" hreflang="en" href="https://www.towerofkubes.com/articles/git-setup-for-windows-and-wsl/"/><id>https://www.towerofkubes.com/articles/git-setup-for-windows-and-wsl/</id><updated>2025-10-16T00:00:00Z</updated><summary type="html">Configure Git once for both Windows and WSL: install Git and GCM, reuse your .gitconfig, and enable seamless credential handling across environments.</summary><content type="html"><![CDATA[
<h2 class="relative group">Introduction
    <div id="introduction" class="anchor"></div>
    
</h2>
<p>Setting up Git correctly on both <strong>Windows</strong> and <strong>WSL</strong> is essential for a smooth development workflow, especially in environments where you switch between the two.</p>

<h3 class="relative group">This guide ensures
    <div id="this-guide-ensures" class="anchor"></div>
    
</h3>
<ul>
<li><strong>Consistency</strong> – Your Git configuration and credentials work the same way in Windows and WSL, avoiding conflicts or repeated prompts.</li>
<li><strong>Security</strong> – By using <a href="https://github.com/git-ecosystem/git-credential-manager"  target="_blank" rel="noreferrer"><strong>Git Credential Manager (GCM)</strong></a> from Windows inside WSL, you get secure token storage without duplicating credentials.</li>
<li><strong>Efficiency</strong> – No need to manage separate credential helpers or manually sync <code>.gitconfig</code> files between systems.</li>
</ul>
<p>Follow these steps to install Git, configure it properly, and enable seamless authentication across both environments.</p>

<h2 class="relative group">Git for Windows Setup
    <div id="git-for-windows-setup" class="anchor"></div>
    
</h2>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>These commands all need to run in Windows PowerShell.</p>
      </div>
    </div>
<h3 class="relative group">1. Install Git for Windows. Git for Windows can be installed with the following command
    <div id="1-install-git-for-windows-git-for-windows-can-be-installed-with-the-following-command" class="anchor"></div>
    
</h3>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="n">winget</span> <span class="n">install</span> <span class="n">-e</span> <span class="p">-</span><span class="n">-id</span> <span class="n">Git</span><span class="p">.</span><span class="py">Git</span> <span class="p">-</span><span class="n">-source</span><span class="p">=</span><span class="n">winget</span></span></span></code></pre></div></div>

    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Tip</span>
      </div>
      <div class="admonition-content">
        <p>If you need Git LFS, also run the following command:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="n">winget</span> <span class="n">install</span> <span class="n">-e</span> <span class="p">-</span><span class="n">-id</span> <span class="n">GitHub</span><span class="p">.</span><span class="py">GitLFS</span> <span class="p">-</span><span class="n">-source</span><span class="p">=</span><span class="n">winget</span></span></span></code></pre></div></div>
      </div>
    </div>
<h3 class="relative group">2. Verify installation of Git for Windows (including Git Credentials Manager) with the following command
    <div id="2-verify-installation-of-git-for-windows-including-git-credentials-manager-with-the-following-command" class="anchor"></div>
    
</h3>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="n">git</span> <span class="p">-</span><span class="n">-version</span><span class="p">;</span> <span class="n">git</span> <span class="nb">credential-manager</span> <span class="p">-</span><span class="n">-version</span></span></span></code></pre></div></div>

<h3 class="relative group">3. Set your Git email and username
    <div id="3-set-your-git-email-and-username" class="anchor"></div>
    
</h3>

    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Tip</span>
      </div>
      <div class="admonition-content">
        <p>If you are using GitHub, you can use your GitHub username and the “no-reply” email address from <a href="https://github.com/settings/emails"  target="_blank" rel="noreferrer">GitHub Email settings</a>.</p>
      </div>
    </div><div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$env:GIT_EMAIL</span> <span class="p">=</span> <span class="s2">"your-git-email"</span></span></span></code></pre></div></div>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$env:GIT_USER</span>  <span class="p">=</span> <span class="s2">"your-git-username"</span></span></span></code></pre></div></div>

<h3 class="relative group">4. Run the following commands to set initial settings for <code>.gitconfig</code>
    <div id="4-run-the-following-commands-to-set-initial-settings-for-gitconfig" class="anchor"></div>
    
</h3>

    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Tip</span>
      </div>
      <div class="admonition-content">
        <p>These settings help avoid common git warnings.</p>
      </div>
    </div><div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="n">git</span> <span class="n">config</span> <span class="p">-</span><span class="n">-global</span> <span class="n">color</span><span class="p">.</span><span class="py">ui</span> <span class="s2">"auto"</span>
</span></span><span class="line"><span class="cl"><span class="n">git</span> <span class="n">config</span> <span class="p">-</span><span class="n">-global</span> <span class="n">init</span><span class="p">.</span><span class="py">defaultBranch</span> <span class="s2">"main"</span>
</span></span><span class="line"><span class="cl"><span class="n">git</span> <span class="n">config</span> <span class="p">-</span><span class="n">-global</span> <span class="n">user</span><span class="p">.</span><span class="py">email</span> <span class="s2">"</span><span class="nv">$env:GIT_EMAIL</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"><span class="n">git</span> <span class="n">config</span> <span class="p">-</span><span class="n">-global</span> <span class="n">user</span><span class="p">.</span><span class="py">name</span>  <span class="s2">"</span><span class="nv">$env:GIT_USER</span><span class="s2">"</span></span></span></code></pre></div></div>

<h2 class="relative group">Git on WSL Setup
    <div id="git-on-wsl-setup" class="anchor"></div>
    
</h2>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>These commands all need to run in WSL.</p>
      </div>
    </div>
<h3 class="relative group">1. Install git and git-lfs packages. On Debian/Ubuntu
    <div id="1-install-git-and-git-lfs-packages-on-debianubuntu" class="anchor"></div>
    
</h3>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get update <span class="o">&&</span> sudo apt-get install git git-lfs</span></span></code></pre></div></div>

<h3 class="relative group">2. Verify installation of Git and Git LFS
    <div id="2-verify-installation-of-git-and-git-lfs" class="anchor"></div>
    
</h3>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git --version <span class="o">&&</span> git lfs --version</span></span></code></pre></div></div>

<h3 class="relative group">3. Copy your existing <code>.gitconfig</code> file from Windows
    <div id="3-copy-your-existing-gitconfig-file-from-windows" class="anchor"></div>
    
</h3>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cp <span class="s2">"</span><span class="k">$(</span>wslpath -a <span class="s2">"</span><span class="k">$(</span>cmd.exe /c <span class="s2">"<nul set /p x=%USERPROFILE%\.gitconfig"</span><span class="k">)</span><span class="s2">"</span><span class="k">)</span><span class="s2">"</span> <span class="s2">"</span><span class="si">${</span><span class="nv">HOME</span><span class="si">}</span><span class="s2">/.gitconfig"</span></span></span></code></pre></div></div>

<h3 class="relative group">4. Configure Git Credentials Manager
    <div id="4-configure-git-credentials-manager" class="anchor"></div>
    
</h3>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global credential.helper <span class="s2">"</span><span class="k">$(</span>wslpath -a <span class="s2">"</span><span class="k">$(</span>powershell.exe -NoProfile -Command <span class="s2">"Write-Host -NoNewline (Join-Path ((Get-Item (git --exec-path)).Parent.Parent.FullName) 'bin\git-credential-manager.exe')"</span><span class="k">)</span><span class="s2">"</span><span class="k">)</span><span class="s2">"</span></span></span></code></pre></div></div>
<hr>
<p><em>Featured image by <a href="https://unsplash.com/@6heinz3r?utm_source=hugo&utm_medium=referral"  target="_blank" rel="noreferrer">Gabriel Heinzer</a> on <a href="https://unsplash.com/photos/a-close-up-of-a-computer-screen-with-a-bunch-of-words-on-it-EUzk9BIEq6M?utm_source=hugo&utm_medium=referral"  target="_blank" rel="noreferrer">Unsplash</a>.</em></p>
]]></content><author><name>Ro'i Bandel</name></author><category term="git" label="Git" scheme="https://www.towerofkubes.com/tags/git/"/><category term="guide" label="Guide" scheme="https://www.towerofkubes.com/tags/guide/"/><category term="snippets" label="Snippets" scheme="https://www.towerofkubes.com/tags/snippets/"/><published>2025-10-16T00:00:00Z</published></entry><entry><title>Bitbucket versus the Competition</title><link rel="alternate" type="text/html" hreflang="en" href="https://www.towerofkubes.com/articles/bitbucket-vs-the-competition/"/><id>https://www.towerofkubes.com/articles/bitbucket-vs-the-competition/</id><updated>2025-09-30T00:00:00Z</updated><summary type="html">Comparison of Bitbucket vs. GitHub vs. GitLab</summary><content type="html"><![CDATA[<p>TIL that Bitbucket is the slowest of the major software forges according to <a href="https://forgeperf.org/"  target="_blank" rel="noreferrer">Software Forge Performance Index</a>.</p>
<p><a href="https://forgeperf.org/"  target="_blank" rel="noreferrer">forgeperf.org</a> is maintained by SourceHut, who are not impartial. SourceHut just so happen to lead most of the performance results. I can believe that the benchmarks are accurate, though the benchmarks may have been designed in a way that favors SourceHut.</p>
<p>Even so, performance is not everything. SourceHut’s UI is noticeably basic, which might be good for performance but not necessarily the most pleasent to use. SH patchsets are sent over email, an antiquated workflow (even if it’s still how Linux kernel development works).</p>
<p>The reasons I dislike Bitbucket have little to do with its subpar performance. Bitbucket, like most of Atlassian’s suite, just feels <em>aggressively average</em>. I wouldn’t go as far as to say Bitbucket is <em>bad</em>, it functions fine as a git forge. It does the basics decently well and many enterprises successfully use it. However, when compared to its main competitors, Bitbucket feels objectively worse. It kind of sucks.</p>

<h2 class="relative group">The Competition
    <div id="the-competition" class="anchor"></div>
    
</h2>
<p>I would consider the main competitors to be GitHub and GitLab. Both of these have many more features than Bitbucket. Bitbucket is falling behind and playing catch-up, slowly implementing features that the competitors had <em>years</em> ago. Here are several examples.</p>

<h3 class="relative group">Artifact Registry
    <div id="artifact-registry" class="anchor"></div>
    
</h3>
<p>There was a recent announcement of <a href="https://www.atlassian.com/blog/bitbucket/coming-soon-bitbucket-packages"  target="_blank" rel="noreferrer">Bitbucket Packages</a>. This will be Bitbucket’s own solution for an artifact registry, starting with a container registry. GitHub and GitLab already had artifact registry solutions for <em>years</em>, for example GitHub launched <a href="https://github.blog/news-insights/product-news/introducing-github-package-registry/"  target="_blank" rel="noreferrer">GitHub Package Registry in 2019</a> and <a href="https://github.blog/news-insights/product-news/github-packages-container-registry-generally-available/"  target="_blank" rel="noreferrer">GitHub Container Registry in 2021</a>. Even when Bitbucket Packages does launch it will be limited to containers only at first. I also noticed that Bitbucket is missing a Releases feature, which both <a href="https://github.blog/news-insights/product-news/github-packages-container-registry-generally-available/"  target="_blank" rel="noreferrer">GitHub</a> and <a href="https://docs.gitlab.com/user/project/releases/"  target="_blank" rel="noreferrer">GitLab</a> have.</p>

<h3 class="relative group">CI/CD
    <div id="cicd" class="anchor"></div>
    
</h3>
<p>Atlassian has had several different CI/CD solutions over the years. Bamboo used to be the main one, and many companies used (or still use) a Bitbucket+Jenkins combo. Nowadays, Bitbucket Pipelines is the main solution that Atlassian tries to push onto its customers. One of these customers is my current client. I came into the client having to deal with the hacky Bitbucket pipelines that the previous team left me. We are using self-hosted Bitbucket Runners running on EC2 instances, because the hosted Bitbucket Runners are underpowered. Overall, I got the pipeline working, although the entire time I wished I was using GitHub Actions instead. To be fair, it’s clear Bitbucket Pipelines is being actively developed and is getting new features. However, it’s also clear that it’s playing catch-up against others CI/CD solutions (including GHA and GitLab CI/CD). There are all sorts of weird limitations, some of which have been fixed and others which still haven’t. For example, until recently, <a href="https://blog.devops.dev/breaking-the-2-hour-pipeline-barrier-a-bitbucket-cloud-saga-9780b1bfa7e9"  target="_blank" rel="noreferrer">Bitbucket Pipeline steps were limited to only 2 hours</a>. This has thankfully been fixed, and now the <a href="https://support.atlassian.com/bitbucket-cloud/docs/global-options/#Max-time"  target="_blank" rel="noreferrer"><code>max-time</code></a> can be set up to <code>720</code> (12 hours), enough to most (but not all) jobs. Other limitations continue to exist. GitHub has the <a href="https://github.com/marketplace?type=actions"  target="_blank" rel="noreferrer">Actions Marketplace</a> full of community actions that can be easily integrated into GHA workflows, often completely for free (many actions are FOSS). Bitbucket works with the <a href="https://marketplace.atlassian.com/product/bitbucket"  target="_blank" rel="noreferrer">Atlassian Marketplace</a> and <a href="https://bitbucket.org/product/features/pipelines/integrations"  target="_blank" rel="noreferrer">Bitbucket Pipes integrations | Bitbucket</a>, but this is much more limited and harder to use with Pipelines, and the majority of Apps are paid.</p>

<h3 class="relative group">SSH commit signature verification
    <div id="ssh-commit-signature-verification" class="anchor"></div>
    
</h3>
<p>I was excited about SSH commit signature verification this feature when I learned about it during my bootcamp in late 2022. <a href="https://github.blog/changelog/2022-08-23-ssh-commit-verification-now-supported/"  target="_blank" rel="noreferrer">GitHub was quick to implement it</a> and <a href="https://about.gitlab.com/releases/2022/12/22/gitlab-15-7-released/"  target="_blank" rel="noreferrer">GitLab not long after</a>. I then started working with clients that used Bitbucket and was surprised to find this feature missing! Only in <a href="https://www.atlassian.com/blog/bitbucket/you-can-now-sign-commits-with-ssh-keys"  target="_blank" rel="noreferrer">2025 did Bitbucket Cloud release SSH commit signature verification</a>, more than two years after both GitHub and GitLab.</p>

<h3 class="relative group">Bitbucket CLI
    <div id="bitbucket-cli" class="anchor"></div>
    
</h3>
<p>Bitbucket has <strong>no</strong> official CLI tool in the style of <a href="https://cli.github.com/"  target="_blank" rel="noreferrer">GitHub CLI</a> or <a href="https://gitlab.com/gitlab-org/cli"  target="_blank" rel="noreferrer">GitLab CLI</a>. Of course, the standarad Git CLI does work well with Bitbucket, but the <code>gh</code> and <code>glab</code> CLI tools go beyond that and allow to do many actions directly using commands. Bitbucket does have REST APIs (<a href="https://developer.atlassian.com/server/bitbucket/rest/"  target="_blank" rel="noreferrer">The Bitbucket Data Center REST API</a> and <a href="https://developer.atlassian.com/cloud/bitbucket/rest/"  target="_blank" rel="noreferrer">The Bitbucket Cloud REST API</a>) which do work, but are harder to use than an equivalent CLI tool would be.</p>

<h3 class="relative group">Cloud Development Environments
    <div id="cloud-development-environments" class="anchor"></div>
    
</h3>
<p>GitHub has <a href="https://docs.github.com/en/codespaces"  target="_blank" rel="noreferrer">Codespaces</a> and GitLab has <a href="https://docs.gitlab.com/user/workspace/"  target="_blank" rel="noreferrer">Workspaces</a>. Both are Cloud Development Environments based on VS Code. Microsoft maintains both GitHub and VS Code. VS Code itself remains open source under the <a href="https://github.com/microsoft/vscode?tab=MIT-1-ov-file#readme"  target="_blank" rel="noreferrer">MIT license</a>. GitLab maintains their own <a href="https://gitlab.com/gitlab-org/gitlab-web-ide-vscode-fork"  target="_blank" rel="noreferrer">VS Code fork</a>.</p>
<p>Bitbucket offers to use <a href="https://support.atlassian.com/bitbucket-cloud/docs/install-cloud-ide-add-ons/"  target="_blank" rel="noreferrer">Cloud IDE add-ons</a> <a href="https://bitbucket.org/integrations/cloud"  target="_blank" rel="noreferrer">Bitbucket Integrations</a>, but doesn’t have anything built-in.</p>

<h3 class="relative group">Automatic Dependency Updates
    <div id="automatic-dependency-updates" class="anchor"></div>
    
</h3>
<p><a href="https://github.com/dependabot"  target="_blank" rel="noreferrer">Dependabot</a> is built into GitHub. GitLab uses <a href="https://gitlab.com/gitlab-org/frontend/renovate-gitlab-bot"  target="_blank" rel="noreferrer">Renovate GitLab Bot</a>. Renovatebot can work with Bitbucket, either self-hosted or supported by Mend, however there is nothing built-in to Bitbucket.</p>

<h3 class="relative group">Fragmented Hosting Solutions
    <div id="fragmented-hosting-solutions" class="anchor"></div>
    
</h3>
<p>A major point of confusion for me when using Bitbucket has been the the differences between Bitbucket Data Center and Bitbucket Cloud. While both look like Bitbucket, they are essentially two different products with different features and development cycles. Every time I look up Bitbucket documentation, I have to make sure that I am following docs for the right product. There are even two different Bitbucket REST APIs.</p>
<p>In comparison, GitLab operates <a href="https://about.gitlab.com/blog/2019/08/23/a-single-codebase-for-gitlab-community-and-enterprise-edition/"  target="_blank" rel="noreferrer">under a single codebase for Community and Enterprise editions</a> and the same codebase is used to run GitLab.com:</p>

    <div class="admonition quote">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M448 296c0 66.3-53.7 120-120 120l-8 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l8 0c30.9 0 56-25.1 56-56l0-8-64 0c-35.3 0-64-28.7-64-64l0-64c0-35.3 28.7-64 64-64l64 0c35.3 0 64 28.7 64 64l0 32 0 32 0 72zm-256 0c0 66.3-53.7 120-120 120l-8 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l8 0c30.9 0 56-25.1 56-56l0-8-64 0c-35.3 0-64-28.7-64-64l0-64c0-35.3 28.7-64 64-64l64 0c35.3 0 64 28.7 64 64l0 32 0 32 0 72z"/></svg>
        <span>Quote</span>
      </div>
      <div class="admonition-content">
        <p>The largest known GitLab instance is on GitLab.com, which is deployed using our <a href="https://docs.gitlab.com/charts/"  target="_blank" rel="noreferrer">official GitLab Helm chart</a> and the <a href="https://about.gitlab.com/install/"  target="_blank" rel="noreferrer">official Linux package</a>.</p>
      </div>
    </div><ul>
<li><a href="https://docs.gitlab.com/development/architecture/"  target="_blank" rel="noreferrer">GitLab architecture overview | GitLab Docs</a></li>
</ul>

<h3 class="relative group">Bitbucket Kills Self-Hosting
    <div id="bitbucket-kills-self-hosting" class="anchor"></div>
    
</h3>
<p><a href="https://www.atlassian.com/licensing/server-end-of-support#what-does-end-of-support-mean"  target="_blank" rel="noreferrer">Bitbucket Server reached end of support in 2024</a>. My clients at the time were already using Bitbucket Data Center so they weren’t affected. However, Bitbucket recently announced that Data Center will reach end of life in 2029. The solution going forward will be <strong>only</strong> Bitbucket Cloud. While this solves the fragmentation problem, it does so at the expanse of any self-hosted solutions. I believe there are still organizations for whom self-hosting is non-negotiable. Atlassian is essentially giving up on these customers. Perhaps they’ve done their cold calculations and decided that continuing to develop and support Bitbucket Data Center is no longer worth it even if they do lose some customers.</p>
<p>Meanwhile, GitLab continues to to offer a self-hosted solution, that can even run for free with <a href="https://gitlab.com/rluna-gitlab/gitlab-ce"  target="_blank" rel="noreferrer">GitLab Community Edition</a>. GitHub offers <a href="https://docs.github.com/en/enterprise-server@3.14/admin/overview/about-github-enterprise-server"  target="_blank" rel="noreferrer">GitHub Enterprise Server</a>.</p>
<hr>
<p><em>Featured image by <a href="https://unsplash.com/@courtneyam98?utm_source=hugo&utm_medium=referral"  target="_blank" rel="noreferrer">Courtney Moore</a> on <a href="https://unsplash.com/?utm_source=hugo&utm_medium=referral"  target="_blank" rel="noreferrer">Unsplash</a>.</em></p>
]]></content><author><name>Ro'i Bandel</name></author><category term="til" label="Til" scheme="https://www.towerofkubes.com/tags/til/"/><category term="git" label="Git" scheme="https://www.towerofkubes.com/tags/git/"/><published>2025-09-30T00:00:00Z</published></entry></feed>