<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>emre şahin's digital garden 🍃 - git</title>
    <link>https://emresahin.net/tags/git/</link>
    <description>Posts in the git tag</description>
    <language>en</language>
    <managingEditor>contact@emresahin.net (Emre Şahin)</managingEditor>
    <lastBuildDate>Tue, 15 Sep 2026 19:46:32 +0000</lastBuildDate>
    <atom:link href="https://emresahin.net/tags/git/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>bits 21</title>
      <published>2025-09-28T18:00:14+00:00</published>
      <updated>2025-09-28T18:00:14+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 18:00:14 +0000</pubDate>
      <link>https://emresahin.net/bits-21/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-21/</guid>
      <description>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini. Gemini proposed this: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length What I ended up ...</description>
      <category>bits</category>
      <category>programming</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>LLM</category>
      <content:encoded><![CDATA[<p>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini.</p>
<p>Gemini proposed this:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>What I ended up doing:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I notice LLMs tend to err on the side of complexity. I don’t know if this is due to their training to spit out as many tokens as possible, but using regular expressions when a simple string replacement may suffice is a good sign that they may be adding more complexity than necessary.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 20</title>
      <published>2025-09-28T17:52:31+00:00</published>
      <updated>2025-09-28T17:52:31+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 17:52:31 +0000</pubDate>
      <link>https://emresahin.net/bits-20/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-20/</guid>
      <description>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini. Gemini proposed this: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length What I ended up ...</description>
      <category>bits</category>
      <category>programming</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>LLM</category>
      <content:encoded><![CDATA[<p>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini.</p>
<p>Gemini proposed this:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>What I ended up doing:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I notice LLMs tend to err on the side of complexity. I don’t know if this is due to their training to spit out as many tokens as possible, but using regular expressions when a simple string replacement may suffice is a good sign that they may be adding more complexity than necessary.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 19</title>
      <published>2025-09-28T17:42:11+00:00</published>
      <updated>2025-09-28T17:42:11+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 17:42:11 +0000</pubDate>
      <link>https://emresahin.net/bits-19/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-19/</guid>
      <description>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini. Gemini proposed this: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length What I ended up ...</description>
      <category>bits</category>
      <category>programming</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>LLM</category>
      <content:encoded><![CDATA[<p>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini.</p>
<p>Gemini proposed this:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>What I ended up doing:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I notice LLMs tend to err on the side of complexity. I don’t know if this is due to their training to spit out as many tokens as possible, but using regular expressions when a simple string replacement may suffice is a good sign that they may be adding more complexity than necessary.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 18</title>
      <published>2025-09-28T17:22:48+00:00</published>
      <updated>2025-09-28T17:22:48+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 17:22:48 +0000</pubDate>
      <link>https://emresahin.net/bits-18/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-18/</guid>
      <description>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini. Gemini proposed this: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length What I ended up ...</description>
      <category>bits</category>
      <category>programming</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>LLM</category>
      <content:encoded><![CDATA[<p>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini.</p>
<p>Gemini proposed this:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>What I ended up doing:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I notice LLMs tend to err on the side of complexity. I don’t know if this is due to their training to spit out as many tokens as possible, but using regular expressions when a simple string replacement may suffice is a good sign that they may be adding more complexity than necessary.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 17</title>
      <published>2025-09-28T17:20:37+00:00</published>
      <updated>2025-09-28T17:20:37+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 17:20:37 +0000</pubDate>
      <link>https://emresahin.net/bits-17/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-17/</guid>
      <description>I wanted to calculate the number of words added between commits in my blog posts. I asked Gemini for a Nushell pipeline based on git diff : It proposed the following: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length H...</description>
      <category>shell</category>
      <category>productivity</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>ai</category>
      <content:encoded><![CDATA[<p>I wanted to calculate the number of words added between commits in my blog posts. I asked Gemini for a Nushell pipeline based on <code>git diff</code>:</p>
<p>It proposed the following:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>However, I ended up using this simplified version:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I’ve noticed that LLMs often lean toward overly complex solutions. I’m not sure if this is due to being trained to generate as many tokens as possible, but using regular expressions where a simple string replacement would suffice is often a sign of unnecessary complexity.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 16</title>
      <published>2025-09-28T17:15:52+00:00</published>
      <updated>2025-09-28T17:15:52+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 17:15:52 +0000</pubDate>
      <link>https://emresahin.net/bits-16/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-16/</guid>
      <description>I wanted to get the number of word changes between commits in my blogs. I asked for a git diff based nushell pipeline from Gemini: Gemini proposed this: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length What I ended up...</description>
      <category>bits</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>LLM</category>
      <category>workflow</category>
      <content:encoded><![CDATA[<p>I wanted to get the number of word changes between commits in my blogs. I asked for a <code>git diff</code> based nushell pipeline from Gemini:</p>
<p>Gemini proposed this:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>What I ended up doing:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I notice LLMs tend to err on the side of complexity. I don’t know if this is due to their training to spit as many tokens as possible, but using regular expressions when a simple single replace may suffice is a good sign that they may be adding more complexity than necessary.</p>]]></content:encoded>
    </item>
    <item>
      <title>devlog 15</title>
      <published>2025-01-20T12:51:56+00:00</published>
      <updated>2025-01-20T12:51:56+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 20 Jan 2025 12:51:56 +0000</pubDate>
      <link>https://emresahin.net/devlog-15/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-15/</guid>
      <description>🐢 Should we go into completions directly, or do we have anything to update in the working scripts? 🐇 Starting with completions is better. One question I have is whether the completions work for subcommands as usual, like in the static completions. 🐢 Yes, they work. We need to add: source &lt;(COMPLE...</description>
      <category>xvc</category>
      <category>clap</category>
      <category>clap-complete</category>
      <category>zsh</category>
      <category>git</category>
      <category>xvc-file</category>
      <category>xvc-pipeline</category>
      <category>Rust</category>
      <category>refactoring</category>
      <category>CLI design</category>
      <category>feature flags</category>
      <content:encoded><![CDATA[<p>🐢 Should we go into completions directly, or do we have anything to update in the working scripts?</p>
<p>🐇 Starting with completions is better. One question I have is whether the completions work for subcommands as usual, like in the static completions.</p>
<p>🐢 Yes, they work. We need to add:</p>
<pre><code class="language-sh">source &lt;(COMPLETE=zsh xvc)
</code></pre>
<p>to <code>.zshrc</code>, though.</p>
<p>🐇 It looks like we can drop the <code>xvc completions</code> command. We’re just checking the <code>COMPLETE</code> environment variable, and there’s no need for a separate command in this case.</p>
<p>🐢 Yes, let’s remove that.</p>
<p>🐇 Maybe we can employ a <code>completions</code> module for all completion-related functionality. It’s a separate thing, you know.</p>
<p>🐢 Which completions do we need?</p>
<p>🐇 We can just mark them with TODOs now.</p>
<p>🐢 Yes, let’s check where we need completions and what.</p>
<p>🐇 I noticed we’re repeating options in <code>XvcFileCLI</code>, and some of the options are missing, e.g., <code>--from-ref</code> and <code>--to-branch</code>.</p>
<p>🐢 Because it can compile into a different binary, and <code>global</code> options don’t work that way. Maybe we can move all these binaries into different files under <code>lib</code>. We can have feature flags to turn off certain features and compile different binaries.</p>
<p>🦊 Let’s search for it; I haven’t seen it before: building different binaries with feature flags using <code>cargo</code>.</p>
<p>🐢 There is no clear-cut solution, but it looks like we can move all binaries to the root with the <a href="https://rustwiki.org/en/cargo/reference/cargo-targets.html#the-required-features-field"><code>required-features</code></a> field.</p>
<p>🐲 We can postpone this to another release.</p>
<p>🐇 Yes, let’s not spend time on this now.</p>
<p>🐢 Should we try making <code>pipeline_name</code> a global option? It’s repeating everywhere.</p>
<p>🐇 That will make it easier to maintain.</p>
<p>🐢 We’ll have to pass <code>pipeline_name</code> to subcommands, though.</p>
<p>🐇 I think we can have a set of global options that we can pass. For the time being, that’s only the <code>pipeline_name</code>.</p>
<p>🐢 Okay. Let’s do this.</p>
<p>🐇 A similar option is the step name for pipeline steps.</p>
<p>🐢 Dependencies will need a revamp in the next version anyway. So let’s keep it for now.</p>
<p>🐲 Also, the semantics of <code>step-name</code> are different for these commands. <code>step new</code> interprets it as a new name, while <code>step dependency</code> interprets it as an existing name. The first can be renamed to <code>--name</code>, and the second can be <code>--to</code>, as one of its aliases suggests.</p>
<p>🐢 Yes, let’s keep it for now, and we’ll continue to work on others.</p>
<p>🦊 I want to post a comment in the <code>clap</code> discussions.</p>
<p>🐢 Wrote it. Now let’s build it after changing the <code>pipeline_name</code>.</p>
<p>🐇 It compiles. Should we test it?</p>
<p>🐢 I think we’ll test after all this completion work is done.</p>
<p>🐇 We did most of the <code>strum</code>-related completion.</p>
<p>🐢 Didn’t test them yet, though.</p>
<p>🐇 This is Rust. It will work if it compiles, and it compiles.</p>]]></content:encoded>
    </item>
    <item>
      <title>devlog 14</title>
      <published>2025-01-20T10:55:15+00:00</published>
      <updated>2025-01-20T10:55:15+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 20 Jan 2025 10:55:15 +0000</pubDate>
      <link>https://emresahin.net/devlog-14/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-14/</guid>
      <description>🐢 Now, let’s return to clap and its dynamic completions. Last time you said: 🐇 The examples are only found in the tests: https://github.com/clap-rs/clap/blob/master/clap_complete/tests/testsuite/engine.rs#L604 and we can try to create those random strings this way. 🦊 The issue is that we may not ...</description>
      <category>xvc</category>
      <category>jj</category>
      <category>clap</category>
      <category>clap-complete</category>
      <category>zsh</category>
      <category>gitoxide</category>
      <category>git</category>
      <category>Rust</category>
      <category>dynamic completion</category>
      <category>shell</category>
      <category>development</category>
      <content:encoded><![CDATA[<p>🐢 Now, let’s return to <code>clap</code> and its dynamic completions. Last time you said:</p>
<blockquote>
<p>🐇 The examples are only found in the tests: https://github.com/clap-rs/clap/blob/master/clap_complete/tests/testsuite/engine.rs#L604</p>
</blockquote>
<p>and we can try to create those random strings this way.</p>
<p>🦊 The issue is that we may not be able to use the <code>derive</code> method to add these custom completers. All examples are using the builder API.</p>
<p>🐢 We should be able to obtain the end result from derives and modify them to use these custom completers, but let’s try to use <code>add = ArgValueCompleter</code> first.</p>
<p>🐇 It looks like we have another problem. We need to add a <code>rust-toolchain.toml</code> file to the project, you know, to keep it on the stable channel.</p>
<p>🐢 Ah, yeah, we now use multiple channels. Let’s do that.</p>
<p>🐇 I found <a href="https://docs.rs/clap_complete/latest/clap_complete/engine/struct.ArgValueCompleter.html">an example</a>, actually:</p>
<pre><code class="language-rust">#[derive(Debug, Parser)]
struct Cli {
    #[arg(long, add = ArgValueCompleter::new(custom_completer))]
    custom: Option&lt;String&gt;,
}</code></pre>
<p>🐢 I’m trying to add this to the <code>--from-ref</code> option of Xvc. It’s not running anything. The custom completer should return a random string, but it doesn’t.</p>
<p>🦊 Maybe test with a constant first.</p>
<p>🐢 It didn’t work that way either.</p>
<p>🐇 Let’s do a <code>cargo clean</code>.</p>
<p>🐢 Nothing has changed.</p>
<p>🐇 Let’s take a look at the output of <code>xvc completions</code>:</p>
<pre><code class="language-sh">xvc completions
...
'(--skip-git)--from-ref=[Checkout the given Git reference (branch, tag, commit etc.) before performing the Xvc operation. This runs \`git checkout &lt;given-value&gt;\` before running the command]:FROM_REF:_default' \
...
</code></pre>
<p>🐢 This is the only place <code>--from-ref</code> is mentioned and, as far as I can see, there is nothing that calls a dynamic command here.</p>
<p>🐇 Umm, right. There’s something weird here. Maybe we lack the <code>ArgExt</code> trait or something.</p>
<p>🐢 It should be turned on by <code>clap-complete</code> with its <code>unstable-dynamic</code> feature, but who knows.</p>
<p>🐇 It didn’t work.</p>
<p>🐢 <code>ArgExt</code> is available, but it isn’t being used.</p>
<p>🐇 I think I found the answer in https://jj-vcs.github.io/jj/latest/install-and-setup/#command-line-completion:</p>
<pre><code class="language-sh">source &lt;(COMPLETE=zsh xvc)
</code></pre>
<p>is the command we should use.</p>
<p>🐢 It doesn’t produce a completion script, though.</p>
<p>🐇 We have this in <code>jj/cli/src/cli_util.rs</code>:</p>
<pre><code class="language-rust">        if env::var_os("COMPLETE").is_some() {
            return handle_shell_completion(ui, &amp;self.app, &amp;config, &amp;cwd);
        }</code></pre>
<p>🐢 I think the whole <code>handle_shell_completion</code> set is written by them. I’d like to see what <code>jj</code> with <code>COMPLETE=zsh</code> outputs.</p>
<p>🐇 Building it to see now.</p>
<p>🐢 As expected, it calls a function:</p>
<pre><code class="language-sh">❯ COMPLETE=zsh target/debug/jj
#compdef jj
function _clap_dynamic_completer_jj() {
    local _CLAP_COMPLETE_INDEX=$(expr $CURRENT - 1)
    local _CLAP_IFS=$'\n'

    local completions=("${(@f)$( \
        _CLAP_IFS="$_CLAP_IFS" \
        _CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
        COMPLETE="zsh" \
        /Users/iex/github.com/etc/jj/target/debug/jj -- ${words} 2&gt;/dev/null \
    )}")

    if [[ -n $completions ]]; then
        _describe 'values' completions
    fi
}

compdef _clap_dynamic_completer_jj jj
</code></pre>
<p>🐇 Now we should make it the same; the shell must call <code>xvc</code> to get the completions. That’s how all these will work.</p>
<p>🐢 Our goal now is to make a random string output from the <code>--from-ref</code> completion.</p>
<p>🐇 The plan is to make completions work as quickly as possible.</p>
<p>🐢 We have done it. 🎉🥳</p>
<p>🐇 Cool. Now we need to fill up all those completion methods.</p>
<p>🦊 Yeah. We can start with basic ones and move from there.</p>
<p>🐢 There will probably be architectural changes as well. We cannot just run <code>xvc</code> functions directly. We need to run internal functions, but not through commands. At that point, when the user hits tab, we don’t know which command to run.</p>
<p>🐇 Maybe it’s time to move to a Git library. “What is the best Git library for Rust?”</p>
<p>🐲 Should we use a Git library?</p>
<p>🐢 It looks like <a href="https://github.com/GitoxideLabs/gitoxide">Gitoxide</a> is the default Rust way to interact with Git repositories. We can keep our way of using the Git binary and use this as an experimental way to learn.</p>
<p>🦊 Yep. That’s a good idea. We can include it in the lib for the time being and start to use it in completions.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 6</title>
      <published>2024-05-23T16:33:49+00:00</published>
      <updated>2024-05-23T16:33:49+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Thu, 23 May 2024 16:33:49 +0000</pubDate>
      <link>https://emresahin.net/bits-6/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-6/</guid>
      <description>We’re using Graphite at work. Today, I configured a tmux shortcut to open a new pane and run gt create , which creates a new branch and commits all staged Git files. bind-key -n S-F12 split-window -h -c "#{pane_current_path}" "gt c" I just hit Shift-F12, and it opens a new pane in the current dir...</description>
      <category>bits</category>
      <category>tmux</category>
      <category>graphite</category>
      <category>cli</category>
      <category>git</category>
      <category>workflow</category>
      <category>productivity</category>
      <content:encoded><![CDATA[<p>We’re using <a href="https://graphite.dev">Graphite</a> at work. Today, I configured a tmux
shortcut to open a new pane and run <code>gt create</code>, which creates a new branch and
commits all staged Git files.</p>
<pre><code>bind-key -n S-F12 split-window -h -c "#{pane_current_path}" "gt c"
</code></pre>
<p>I just hit Shift-F12, and it opens a new pane in the current directory and runs the
command.</p>]]></content:encoded>
    </item>
    <item>
      <title>TIL: Get the Latest Git Commit SHA-1</title>
      <published>2024-02-13T09:58:12+00:00</published>
      <updated>2024-02-13T09:58:12+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Tue, 13 Feb 2024 09:58:12 +0000</pubDate>
      <link>https://emresahin.net/til-6/</link>
      <guid isPermaLink="true">https://emresahin.net/til-6/</guid>
      <description>This command retrieves the SHA-1 hash of the latest commit: git rev-parse HEAD</description>
      <category>Shell</category>
      <category>Version Control</category>
      <category>Git</category>
      <category>CLI</category>
      <category>SHA-1</category>
      <content:encoded><![CDATA[<p>This command retrieves the SHA-1 hash of the latest commit:</p>
<pre><code class="language-bash">git rev-parse HEAD
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Global .gitignore</title>
      <published>2023-10-17T13:11:34+00:00</published>
      <updated>2023-10-17T13:11:34+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Tue, 17 Oct 2023 13:11:34 +0000</pubDate>
      <link>https://emresahin.net/gitignore_global/</link>
      <guid isPermaLink="true">https://emresahin.net/gitignore_global/</guid>
      <description>You can configure a global .gitignore file to ignore files or directories (e.g., .vscode/ ) in all your repositories. Write it as a standard .gitignore file and save it somewhere, like ~/.gitignore_global . Then, you can use the following configuration to set this as an excludes file : git config...</description>
      <category>til</category>
      <category>git</category>
      <category>git</category>
      <category>configuration</category>
      <category>tips</category>
      <content:encoded><![CDATA[<p>You can configure a global <code>.gitignore</code> file to ignore files or directories (e.g., <code>.vscode/</code>) in all your repositories. Write it as a standard <code>.gitignore</code> file and save it somewhere, like <code>~/.gitignore_global</code>. Then, you can use the following configuration to set this as an <em>excludes file</em>:</p>
<pre><code class="language-bash">git config --global core.excludesfile ~/.gitignore_global
</code></pre>
<p>Note that the path given to <code>git config</code> should be an absolute path.</p>]]></content:encoded>
    </item>
    <item>
      <title>Xvc Devlog - 221108</title>
      <published>2022-11-09T10:42:00+00:00</published>
      <updated>2022-11-09T10:42:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Wed, 09 Nov 2022 10:42:00 +0000</pubDate>
      <link>https://emresahin.net/xvc-devlog---221108/</link>
      <guid isPermaLink="true">https://emresahin.net/xvc-devlog---221108/</guid>
      <description>🐇 I think we can begin by checking GitHub PRs . What do you have today? 🐢 The tests for yesterday’s Git integration PR failed. I’ll begin by checking the logs. 🐇 Maybe run the tests locally to see. They might fail on your machine as well. It might be a simple thing. 🐢 Probably, yes. I’ve started ...</description>
      <category>devlog</category>
      <category>Software Development</category>
      <category>xvc</category>
      <category>git</category>
      <category>testing</category>
      <category>trace</category>
      <category>rsync</category>
      <category>storage</category>
      <category>github actions</category>
      <category>automation</category>
      <content:encoded><![CDATA[<p>🐇 I think we can begin by checking <a href="https://github.com/pulls">GitHub PRs</a>. What do you have today?</p>
<p>🐢 The tests for yesterday’s <a href="https://github.com/iesahin/xvc/pull/105">Git integration PR</a> failed. I’ll begin by checking the logs.</p>
<p>🐇 Maybe run the tests locally to see. They might fail on your machine as well. It might be a simple thing.</p>
<p>🐢 Probably, yes. I’ve started the tests now. I shouldn’t forget to run them before testing the PR; it may save some time.</p>
<p>🐇 Ah, yeah, maybe. Yesterday you were already at the end of the workday, so it didn’t matter much. But you can shorten the testing time by reducing the number of files, etc. I think a separate benchmark suite might be good to have. Tests should be shorter; benchmarks should only run for tags.</p>
<p>🐢 Yup. I should shorten the tests. I should make them use a shorter list of files, maybe.</p>
<p>🐇 Local tests have passed. You’ll have to check the logs.</p>
<p>🐢 It seems <code>git diff --name-only --cached</code> returns files not yet added. That’s causing an error in new repositories with <code>stash</code>. Stashing shouldn’t run at all when there are no staged files.</p>
<p>🐇 Git behavior between the local version and the remote version seems different.</p>
<p>🐢 I think the trace should also show the Git version string.</p>
<p>🐇 You should also require a minimum Git version for the commands. You’re using some newer options, and not all users may have them.</p>
<p>🐢 Yep. Let’s see the version on the CI now.</p>
<hr>
<p>🐢 Ah, I see—the problem is not about versions. It’s that the CI doesn’t configure <code>git config --global user.email</code> and <code>user.name</code>. Commits don’t work without these.</p>
<p>🐇 You should remove the Git version report, then. It’s one more process call for no reason.</p>
<p>🐢 I think it should be in <code>trace!</code>; I can check the verbosity level and call it only if it’s trace.</p>
<hr>
<p>🐢 I completed the xvc-config docs as well. I think I can merge it before the tests finish, as it’s just a documentation update.</p>
<p>🐇 You seem to be rushing for the release.</p>
<p>🐢 Yeah, I want to really start testing this on the servers.</p>
<p>🐇 Then go ahead; let’s release a new version.</p>
<hr>
<p>🐇 It looks like you’re back for an evening session. I think it’s time to start using Xvc on your torrent server.</p>
<p>🐢 Ah, yeah. Let’s see how it goes.</p>
<p>🐇 Create a repository for torrents. Then you can add files to it with cache-type = symlink or cache-type = hardlink.</p>
<p>🐢 I think creating a local storage may also help. I can use it to store files to retrieve them later.</p>
<p>🐇 Umm. We don’t have a “garbage collection” facility yet, you know. There are no file deletions at the moment. It may be better to have a repository-to-repository transfer feature, using SSH.</p>
<p>🐢 Yeah, we don’t have SSH storage either. I think this highlights a lack of features.</p>
<p>🐇 It’s possible to mimic Rsync storage with <code>xvc storage new generic</code>, but it doesn’t feel quite the same.</p>
<p>🐢 Then I’m adding these three tickets.</p>
<p>🐇 I think for 0.3.4, the command we add might be <code>xvc file delete</code>. You can work on this and add <code>rsync</code> support as well.</p>
<p>🐢 There is a <a href="https://docs.rs/librsync/latest/librsync/">librsync</a> bindings library for Rust. But it looks like it doesn’t allow transferring file contents between hosts. There is also <a href="https://github.com/your-tools/rusync">rusync</a>, which is similar to rsync and implemented in Rust. There is also <a href="https://lib.rs/crates/fast_rsync">fast_rsync</a> in pure Rust. It uses MD4 to calculate deltas, though, I think.</p>
<p>🐇 None of these seem to have network capability, though.</p>
<p>🐢 I think it’s better just to use the process for now. We are trying to come up with the simplest solution <em>for now.</em> We are just trying to be more general.</p>
<p>🐇 Yes, I think we can just use the process for the time being.</p>
<p>🐢 Then let’s start by adding it.</p>
<p>🐇 We should also begin to create release notes. GitHub can generate them from PRs. <a href="https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuring-automatically-generated-release-notes">Automatically generated release notes</a></p>
<p>🐢 Created an issue for that. Now we’re starting <a href="https://github.com/iesahin/xvc/issues/111"><code>xvc storage new rsync</code> #111</a>.</p>
<p>🐇 Ok. Let’s do this.</p>]]></content:encoded>
    </item>
    <item>
      <title>Xvc Devlog - 221107</title>
      <published>2022-11-08T09:57:00+00:00</published>
      <updated>2022-11-08T09:57:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Tue, 08 Nov 2022 09:57:00 +0000</pubDate>
      <link>https://emresahin.net/xvc-devlog---221107/</link>
      <guid isPermaLink="true">https://emresahin.net/xvc-devlog---221107/</guid>
      <description>🐇 Welcome to the November 7th issue of Xvc Devlog. In the previous devlog , we began to implement Git integration. How is it going, Mr. Tortoise? 🐢 It looks like we don’t have many architectural problems. 🐇 You’re forgetting how Exec::cmd works, though. You have those kinds of problems. 🐢 Yeah, I...</description>
      <category>devlog</category>
      <category>Software Development</category>
      <category>xvc</category>
      <category>architecture</category>
      <category>shell</category>
      <category>git</category>
      <category>gitignore</category>
      <category>rust-analyzer</category>
      <category>LSP</category>
      <content:encoded><![CDATA[<p>🐇 Welcome to the November 7th issue of Xvc Devlog. In the <a href="https://emresahin.net/xvc-devlog-221105">previous devlog</a>, we began to implement Git integration. How is it going, Mr. Tortoise?</p>
<p>🐢 It looks like we don’t have many architectural problems.</p>
<p>🐇 You’re forgetting how <code>Exec::cmd</code> works, though. You have those kinds of problems.</p>
<p>🐢 Yeah, I’m figuring that out. <code>Exec::shell</code> requires a string to run on the shell, while <code>Exec::cmd</code> just needs a command name. You have to supply <code>args</code> with another function. I’m writing a closure now to handle this.</p>
<hr>
<p>🐢 It started working, but it revealed a much bigger problem: <code>.xvc/</code> is not added to <code>.gitignore</code> in <code>xvc init</code>.</p>
<p>🐇 Wow, that’s a showstopper.</p>
<p>🐢 Yup. I think I should fix it as well.</p>
<p>🐇 On closer glance, I think the problem is not <code>xvc init</code> <em>not modifying</em> <code>.gitignore</code>; it modifies it incorrectly. Maybe putting certain files on a whitelist is a better idea than trying to blacklist everything.</p>
<p>🐢 Yeah, we should define a set of <em>Git-tracked</em> files and directories, whitelist them, and let all other files be ignored.</p>
<p>🐇 Go ahead, then.</p>
<p>🐢 I think I’ve fixed it. There were two problems: the initial gitignore content was wrong, and it was placed in the root of the repository instead of <code>.xvc</code>.</p>
<p>🐇 Maybe putting it directly in the root is better than hiding it in <code>.xvc</code>. What do you think?</p>
<p>🐢 There seems to be an assumption in <code>file track</code> to have a <code>.gitignore</code> file somewhere. I think we should also handle changes in <code>.gitignore</code> files throughout the repository.</p>
<p>🐇 Umm, yes. We should handle <code>.gitignore</code> files as well. But not all <code>.gitignore</code> files are changed by Xvc. How can we make sure they were modified by Xvc?</p>
<p>🐢 I think there are ways to do that, like tracking them somewhere. But I believe we shouldn’t try. We should just get a list of <code>.gitignore</code> files, <code>git add</code> them, and include them in the commit.</p>
<p>🐇 Ok. Let’s write a test for this as well. No <code>.gitignore</code> should appear in <code>git status -s</code>.</p>
<p>🐢 I wrote the test. It fails now. Do you think we should check the output of Git status to determine which files to add?</p>
<p>🐇 That may be a good idea. Git status should already know which files need to be added. We can use that information.</p>
<p>🐢 <a href="https://css-tricks.com/git-pathspecs-and-how-to-use-them/">It looks like <code>pathspec</code></a> is enough to modify <code>git add</code> behavior. We should be able to write <code>*.gitignore</code> and let all gitignore files be added.</p>
<p>🐇 Let’s try this manually.</p>
<p>🐢 Yep, it works. <code>git add '*.gitignore'</code> adds all <code>.gitignore</code> files in the subdirectories, too.</p>
<p>🐇 Congrats. 👏🥳</p>
<hr>
<p>🐢 I’m writing the missing documentation for the crates. There are <code>proc_macro not expanded</code> errors all over the code.</p>
<p>🐇 I think you should update rust-analyzer and everything. There must be a command for this.</p>
<p>🐢 I reinstalled RA with <code>:LspInstall</code> and restarted the LSP. It seems to work correctly now.</p>]]></content:encoded>
    </item>
    <item>
      <title>Xvc Devlog - 221105</title>
      <published>2022-11-07T10:04:00+00:00</published>
      <updated>2022-11-07T10:04:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 07 Nov 2022 10:04:00 +0000</pubDate>
      <link>https://emresahin.net/xvc-devlog---221105/</link>
      <guid isPermaLink="true">https://emresahin.net/xvc-devlog---221105/</guid>
      <description>It’s Saturday, November 5th. The best part of free software development seems to be being able to work whenever you want, including Saturdays. Ah yeah, when you work for free, you can do so at any time you want, perhaps. You also don’t have team members, and that means when you sit in front of th...</description>
      <category>devlog</category>
      <category>Software Development</category>
      <category>xvc</category>
      <category>git</category>
      <category>dvc</category>
      <category>git-lfs</category>
      <category>git-annex</category>
      <category>shell</category>
      <category>process</category>
      <category>which</category>
      <category>VCS</category>
      <content:encoded><![CDATA[<p>It’s Saturday, November 5th. The best part of free software development seems to be being able to work whenever you want, including Saturdays.</p>
<p>Ah yeah, when you work for free, you can do so at any time you want, perhaps. You also don’t have team members, and that means when you sit in front of this, you can move it.</p>
<p>Umm, right. Let’s take a look at <a href="https://github.com/iesahin/xvc/pulls">the outstanding PRs</a>.</p>
<p>You have a <a href="https://github.com/iesahin/xvc/pull/93">documentation PR#93</a>. You also have work that you’ve <a href="https://github.com/iesahin/xvc/issues/74">begun to integrate Git into.</a> I think it’s better to focus on the latter today.</p>
<p>Right. Let’s think about the relationship between Git and Xvc. I believe we should identify a general relation to avoid ending up in a mess like DVC and Git.</p>
<p>Why do you think the DVC and Git relationship is a mess?</p>
<p>They don’t automate common Git operations like a commit after <code>dvc add</code>. There is only <em>auto-stage</em>, and that’s turned off by default. This makes it seem that DVC wants to intervene as little as possible with the user’s Git workflow. That’s understandable. I support this. But on the other hand, they use the <code>.git/</code> directory itself to store and manage experiments in a custom way that creates custom stash objects for experiments. This is against the principle of minimum intervention.</p>
<p>So this makes it a mess?</p>
<p>The mess, in my opinion, is caused by the second factor. If DVC doesn’t perform any Git operations, that’s alright. It was intended to be VCS-agnostic. Then experiments came and used Git internals in a way that no other similar tool uses.</p>
<p>Git-LFS and Git-Annex seem to use some non-standard mechanisms as well.</p>
<p>Ok. Not <em>no other tool</em> uses, but in a way that no other tool has used.</p>
<p>You know, GitHub PRs are also stored in a similar way. They also use non-standard machinery.</p>
<p>Yeah, but these tools are all Git-specific tools. They accept the <em>dominion of Git</em>, and don’t try to bring any VCS-agnosticism.</p>
<p>And Xvc tries to have this agnosticism?</p>
<p>I believe the initial design of DVC, which aims to be VCS-agnostic or being able to run without a VCS, is valuable. I like the idea behind Git, but the interface and implementation show that it’s a <em>gradual development.</em> There is no library behind it.</p>
<p>Libgit?</p>
<p><a href="https://libgit2.org">Libgit2</a> is something different. Although it’s said to have some common code, it doesn’t support all features. Git is command-line software with a mix of scripts and compiled executables, and not all code seems to be written in a way that could be used by external tools.</p>
<p>Hmm. <a href="https://github.com/iesahin/xvc/issues/74#issuecomment-1302531362">The comment</a> you added to the issue says <code>git stash push --staged</code> is not available in libgit2. Can’t you mimic it like DVC does for branches?</p>
<p>I don’t want to depend on Git at that level.</p>
<p>So, you’ll be using the CLI and shell for Git?</p>
<p>Yes, I believe, at the moment, before any performance tests, that this doesn’t matter much. Running Git commands once in a while using the shell shouldn’t make much difference in overall performance.</p>
<p>Then you’ll use it like a command-line tool, like the user?</p>
<p>Yes, and I’ll make it run outside of the usual threads. All Git will be like a sandwich, wrapping around Xvc operations. If there are <code>--git-ref</code> instructions in an <code>xvc</code> command, it will be run before Xvc performs the command, and if there are any changes in Xvc metafiles, they will be committed to the current branch.</p>
<p>Like</p>
<pre class="mermaid">graph LR

co["git checkout"] --&gt; xvc
xvc --&gt; cm["git commit"]

</pre>

<p>The first could be a branch as well. So we have:</p>
<pre class="mermaid">graph LR

br["git branch"] --&gt; xvc
co["git checkout"] --&gt; xvc
xvc --&gt; cm["git commit"]

</pre>

<p>Looks sensible. How will you reflect these in the command line?</p>
<p>With something like <code>xvc --git-checkout my-branch file list</code></p>
<p>Hmm, and for a branch?</p>
<p>I think instead of different options for <code>branch</code>, <code>checkout</code> or <code>tag</code>, we can have a <code>git-ref</code> option that marks the option as a git reference. It will be checked out, or created as a branch from the current one if it doesn’t exist.</p>
<p>I think creating a branch is not a good idea. It should be explicit. You can just send the <code>--git-ref</code> value to <code>git checkout</code> and perform the Xvc operation. If the user wants to create a branch, I think they can do it themselves.</p>
<p>What about storing the results in a branch? After adding a bunch of files, they may want to store them in another branch, maybe?</p>
<p>That’s sensible. We can have another option, like <code>--to-branch</code> in certain operations.</p>
<p>Or in the <code>xvc</code> command as a general option. In that case, we can change the option names to <code>--from-ref</code> and <code>--to-branch</code>. It will be like:</p>
<pre class="mermaid">graph LR

fr["git checkout $(--from-ref)"] --&gt; xvc
xvc --&gt; tb["git checkout --branch $(--to-branch)"]
tb --&gt; co["git add .xvc &amp;&amp; git commit -m 'xvc cmd'"]

</pre>

<p>If no such options are given, xvc will run without branching, right?</p>
<p>Yep. <code>--from-ref</code> and <code>--to-branch</code> options are just shortcuts for user behavior. Any other VCS tool could be used this way. We don’t need to integrate Git at the library level.</p>
<p>This brings up the question of portability, though. When you aim for the software to be portable, you can’t rely on the existence of Git on the host, right?</p>
<p>I think a <code>git.command</code> option in the configuration is a good idea. Xvc will issue a warning if it can’t run the commands.</p>
<p>Will you use the shell to run this command? Otherwise no <code>$PATH</code> configuration is possible, you know.</p>
<p>I believe that could be another option: <code>git.use_shell</code>. If <code>git.command</code> is set to an absolute path, Xvc may use it without the shell. Otherwise, it can use the shell. Running the process directly will make it faster and more secure.</p>
<p>There is also this option to run Xvc in another process. Because we may access Git in the shell that runs Xvc, and if we can access it, maybe we don’t need shell execution in the process.</p>
<p>That’s a cool idea. But I wouldn’t add that extra complexity. Instead, we can try to find the <code>git</code> executable if <code>git.command</code> is not an absolute path. If <code>git.command = /usr/bin/git</code> in the configuration, we use it as is. Otherwise, we can get <code>$PATH</code> or <code>%PATH%</code> from the environment and search for <code>git.command</code> in that to find the exact executable.</p>
<p>It looks like there is a crate called <a href="https://crates.io/crates/which">which</a> that does exactly what we are looking for.</p>
<p>Ah, cool. Then we can just use that to find the executable and run it. We don’t need to drop to a shell.</p>
<p>Yep. Let’s go back to implementation now.</p>]]></content:encoded>
    </item>
    <item>
      <title>TIL May 1</title>
      <published>2020-05-01T22:19:07+00:00</published>
      <updated>2020-05-01T22:19:07+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 01 May 2020 22:19:07 +0000</pubDate>
      <link>https://emresahin.net/til-may-1--17222/</link>
      <guid isPermaLink="true">https://emresahin.net/til-may-1--17222/</guid>
      <description>Nota seems like a nice command-line calculator. It converts what you type into ASCII art formulas. In[1]: 10 + 10 Out[1]: 20.0 _____ In[2]: ╲╱ 100 Out[2]: 10.0 ┌ ┐ In[3]: Max │ 10 , 1 , 21 , -3 │ └ ┘ Out[3]: 21.0 In[4]: ⟨Emre's Number⟩ ≡ 79 Out[4]: 79.0 _______________ In[5]: ╲╱ Emre's Number Out...</description>
      <category>TIL</category>
      <category>Nota</category>
      <category>CLI</category>
      <category>Calculator</category>
      <category>Haskell</category>
      <category>Intention</category>
      <category>Productivity</category>
      <category>Git</category>
      <category>Bash</category>
      <category>Zsh</category>
      <content:encoded><![CDATA[<ul>
<li><a href="https://kary.us/nota/">Nota</a> seems like a nice command-line calculator. It converts what you type into ASCII art formulas.</li>
</ul>
<pre><code>  In[1]: 10 + 10

 Out[1]: 20.0


           _____
  In[2]: ╲╱ 100

 Out[2]: 10.0


             ┌                  ┐
  In[3]: Max │ 10 , 1 , 21 , -3 │
             └                  ┘

 Out[3]: 21.0


  In[4]: ⟨Emre's Number⟩ ≡  79

 Out[4]: 79.0


           _______________
  In[5]: ╲╱ Emre's Number

 Out[5]: 8.888194417315589


                      2
  In[6]: Emre's Number

 Out[6]: 6241.0


                      Emre's Number
  In[7]: Emre's Number

 Out[7]: 8.1759873707105095e149

</code></pre>
<pre><code>It looks a bit heavy for a CLI calculator, as it is written in Haskell and downloads 100+ MB of libraries, but when you need ASCII art to display your calculations or want to use spaces in variable names, it may prove useful.
</code></pre>
<ul>
<li>
<p>I began to use <a href="https://www.getintention.com/">Intention</a> to limit my Twitter time. It allows you to set a limited time (1, 5, 10, or 15 minutes) for yourself and tracks the total time you spend on <em>addictive sites.</em> When this total time is lower than your goal for a period, you get a streak. It looks visually and psychologically nicer than LeechBlock.</p>
</li>
<li>
<p>I read the <a href="http://www.git-scm.com/book/en/v2/Git-Tools-Reset-Demystified"><code>git reset</code></a> section in the Git book. It details how <code>git reset</code> behaves with its <code>--soft</code>, <code>--mixed</code>, and <code>--hard</code> parameters. The first resets only the <code>HEAD</code>; the second resets both the index and <code>HEAD</code>; and the third resets the working tree and copies files back from the current <code>HEAD</code> to the working tree.</p>
<p>One important point: Contrasting <code>git checkout master</code> and <code>git reset master</code>: the first moves <code>HEAD</code> to the <code>master</code> branch, while the second moves the current branch to <code>master</code>.</p>
<p><code>git reset</code> can also be used to squash commits. Basically, you <code>git reset --mixed</code> to an earlier commit like <code>HEAD~3</code> and recommit. This creates a new commit, taking <code>HEAD~3</code> as the parent and skipping <code>HEAD~2</code> and <code>HEAD~1</code>, resulting in a new <code>HEAD</code>.</p>
</li>
<li>
<p><a href="https://github.com/dylanaraps/pure-bash-bible">Here</a> are many useful pure Bash functions to be used in scripts. I’m a Zsh person, but writing Bash scripts is more portable, of course.</p>
</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>SSH Keys for Multiple Accounts on GitHub</title>
      <published>2018-11-18T16:18:58+00:00</published>
      <updated>2018-11-18T16:18:58+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 18 Nov 2018 16:18:58 +0000</pubDate>
      <link>https://emresahin.net/ssh-keys-multiple-accounts-14371-8399/</link>
      <guid isPermaLink="true">https://emresahin.net/ssh-keys-multiple-accounts-14371-8399/</guid>
      <description>I have multiple GitHub accounts, and some of these are collaborators on others. I don’t like to enter my password every time I push, so I set up SSH keys for my accounts. However, GitHub (understandably) doesn’t accept the same key for more than one account. (Otherwise, how would it know which ac...</description>
      <category>DevOps</category>
      <category>Tutorial</category>
      <category>SSH</category>
      <category>GitHub</category>
      <category>Git</category>
      <category>Config</category>
      <content:encoded><![CDATA[<p>I have multiple GitHub accounts, and some of these are collaborators on others. I
don’t like to enter my password every time I push, so I set up SSH keys for my
accounts. However, GitHub (understandably) doesn’t accept the same key for more than one
account. (Otherwise, how would it know which account is being used?)</p>
<p>Fortunately, there is a way to use the <code>~/.ssh/config</code> file to specify different keys for different
target URLs.</p>
<pre><code class="language-config"># Personal account - the default config
Host github.com
   HostName github.com
   User git
   IdentityFile ~/.ssh/id_rsa

# Work Account
Host work.github.com
   HostName github.com
   User git
   IdentityFile ~/.ssh/id_rsa_work
</code></pre>
<p>Now you can create another SSH key in <code>~/.ssh/id_rsa_work</code> and add it to your
work account. When you configure a repository, you need to specify
<code>git@work.github.com:user/repo.git</code> as the repository address rather than
<code>git@github.com:user/repo.git</code>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Adding version information to executables in CMake projects</title>
      <published>2018-02-16T11:04:16+00:00</published>
      <updated>2018-02-16T11:04:16+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 16 Feb 2018 11:04:16 +0000</pubDate>
      <link>https://emresahin.net/versioning-through-cmake-14095-76258/</link>
      <guid isPermaLink="true">https://emresahin.net/versioning-through-cmake-14095-76258/</guid>
      <description>In programming, versioning your code files is of immense importance. Most files need to be constantly updated, renamed, and merged. You also need backups, as everyone learns after losing work due to various computer problems. Another problem we face is establishing a connection between an executa...</description>
      <category>Development</category>
      <category>C/C++</category>
      <category>CMake</category>
      <category>CMake</category>
      <category>C</category>
      <category>Versioning</category>
      <category>Git</category>
      <category>Build Systems</category>
      <category>Automation</category>
      <content:encoded><![CDATA[<p>In programming, versioning your code files is of immense importance. Most
files need to be constantly updated, renamed, and merged. You also need
backups, as everyone learns after losing work due to various computer problems.</p>
<p>Another problem we face is establishing a connection between an
executable file or library and its source code. We normally don’t add executable
files to version control, as they are produced from code files. A common
solution to this is writing version information to an “About” page or
something similar.</p>
<p>When I was using Subversion some 15 years ago, I would create hooks to change the
code files for the necessary versioning info, but this is not a recommended
approach in Git because of its distributed nature. I have never tried it, but it
would likely create more problems than it solves. The recommended way is to use the
build system’s facilities to retrieve the versioning information and add it to
the necessary places.</p>
<p>While developing the C library for dervaze, I wanted to add descriptive
versioning information, as the library will also contain wordlists, and more words
will be added over time.</p>
<p>In CMake, it’s possible to set versioning information and supply it through
compiler options. It’s also possible to replace strings formatted as
<code>@CHANGE_THIS@</code> in source files. To supply version information to the executable,
you can use these facilities:</p>
<pre><code class="language-cmake">
set (DERVAZE_VERSION_MAJOR 1)
set (DERVAZE_VERSION_MINOR 0)
string(TIMESTAMP DERVAZE_TIMESTAMP "%y%m%d%H%M%S")
# current branch
execute_process(
  COMMAND git rev-parse --abbrev-ref HEAD
  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
  OUTPUT_VARIABLE DERVAZE_GIT_BRANCH
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

# abbreviated commit hash
execute_process(
  COMMAND git log -1 --format=%h
  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
  OUTPUT_VARIABLE DERVAZE_GIT_COMMIT_HASH
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

</code></pre>
<p>This information can be supplied to the C files by creating a header file that
will be used as a template.</p>
<pre><code class="language-c">
#define DERVAZE_VERSION_MAJOR       "@DERVAZE_VERSION_MAJOR@"
#define DERVAZE_VERSION_MINOR       "@DERVAZE_VERSION_MINOR@"
#define DERVAZE_TIMESTAMP           "@DERVAZE_TIMESTAMP@"
#define DERVAZE_LIB_GIT_BRANCH      "@DERVAZE_GIT_BRANCH@"
#define DERVAZE_LIB_GIT_COMMIT_HASH "@DERVAZE_GIT_COMMIT_HASH@"
</code></pre>
<p>Suppose this file is named <code>version.h.in</code>; the following command creates the
actual <code>version.h</code> for each build.</p>
<pre><code class="language-cmake"># configure a header file to pass some of the CMake settings
# to the source code
configure_file (
  "${PROJECT_SOURCE_DIR}/version.h.in"
  "${PROJECT_SOURCE_DIR}/version.h"
  )

</code></pre>
<p>It’s also possible to write this file only during the build by using
<code>${PROJECT_BINARY_DIR}/version.h</code> as the second argument, but in my experience,
keeping such a file in the source directory is sometimes needed by build tools.
When you keep it in the source directory, it’s better to ignore the generated
<code>version.h</code> by adding it to <code>.gitignore</code>.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
