<?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 🍃 - shell</title>
    <link>https://emresahin.net/tags/shell/</link>
    <description>Posts in the shell tag</description>
    <language>en</language>
    <managingEditor>contact@emresahin.net (Emre Şahin)</managingEditor>
    <lastBuildDate>Tue, 29 Sep 2026 14:57:43 +0000</lastBuildDate>
    <atom:link href="https://emresahin.net/tags/shell/rss.xml" rel="self" type="application/rss+xml"/>
    <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 9</title>
      <published>2024-05-24T21:02:38+00:00</published>
      <updated>2024-05-24T21:02:38+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 24 May 2024 21:02:38 +0000</pubDate>
      <link>https://emresahin.net/bits-9/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-9/</guid>
      <description>I have quite a few LazyVim plugins, but this simple keymapping makes my life much easier: vim.keymap.set({ "n", "i" }, "&lt;F2&gt;", '&lt;esc&gt;&lt;esc&gt;yy:r!&lt;c-r&gt;"') I type a command, like ls , on a line and hit &lt;F2&gt; . That line is copied, executed, and the output is read into the buffer just below it. I use i...</description>
      <category>Bits</category>
      <category>Tools</category>
      <category>Neovim</category>
      <category>Shell</category>
      <category>LazyVim</category>
      <category>Productivity</category>
      <category>Workflow</category>
      <content:encoded><![CDATA[<p>I have quite a few LazyVim plugins, but this simple keymapping makes my life much easier:</p>
<pre><code class="language-lua">vim.keymap.set({ "n", "i" }, "&lt;F2&gt;", '&lt;esc&gt;&lt;esc&gt;yy:r!&lt;c-r&gt;"')
</code></pre>
<p>I type a command, like <code>ls</code>, on a line and hit <code>&lt;F2&gt;</code>. That line is copied, executed, and the output is read into the buffer just below it.</p>
<p>I use it for all sorts of tasks, from listing open PRs with <code>gh</code> to fetching website text with <code>w3m</code>. It has significantly improved my workflow, allowing me to use the shell effectively within textual notebooks.</p>]]></content:encoded>
    </item>
    <item>
      <title>Devlog 1: XVC Root and Python Bindings Debugging</title>
      <published>2024-05-24T10:12:50+00:00</published>
      <updated>2024-05-24T10:12:50+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 24 May 2024 10:12:50 +0000</pubDate>
      <link>https://emresahin.net/devlog-1/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-1/</guid>
      <description>I should have a dispatch method that receives an XvcRootOpt and runs a command with it. The dispatcher can also update the XvcRoot from None to Some(XvcRoot) in some cases, so it should receive a mutable XvcRootOpt or return one after receiving ownership. The issue with having a mutable element i...</description>
      <category>Devlog</category>
      <category>XVC</category>
      <category>xvc</category>
      <category>debugging</category>
      <category>shell</category>
      <category>jupyter-lab</category>
      <category>rust</category>
      <category>python-bindings</category>
      <content:encoded><![CDATA[<p>I should have a dispatch method that receives an <code>XvcRootOpt</code> and runs a command
with it. The dispatcher can also update the <code>XvcRoot</code> from <code>None</code> to <code>Some(XvcRoot)</code>
in some cases, so it should receive a mutable <code>XvcRootOpt</code> or return one after
receiving ownership.</p>
<p>The issue with having a mutable element is that we need to update <code>XvcRoot</code> from
<code>Arc&lt;XvcRootInner&gt;</code> to <code>Arc&lt;RwLock&lt;XvcRootInner&gt;&gt;</code>, which will cause almost all
machinery around <code>XvcRoot</code> to require locking the object first. This is too
large a refactoring.</p>
<p>However, I can write an <code>xvc_root!</code> macro to replace <code>xvc_root.read()</code> or
whatever is required to minimize the code changes. <code>XvcRoot</code> can also be a wrapper
object, but that’s too much fuss, and the responsibility may be misplaced.</p>
<p>Let’s update the type and let the dispatcher update <code>XvcRootInner</code> only
when necessary. Let’s see what will require updating.</p>
<hr>
<p>We have a bug in the Python bindings where <code>xvc.file.track()</code> enters an infinite loop.</p>
<p>There can be multiple reasons, but are you sure that the <code>xvc</code> CLI works with the
same command?</p>
<p>Let’s start testing by creating another notebook server.</p>
<p>The notebook creates an Xvc repo successfully and returns the root with
<code>xvc.root()</code>, but <code>xvc.file().track("test-data/dir-0001/")</code> never finishes.</p>
<p>It’s likely that this is caused by something in the background threads.</p>
<p>The CLI command completes successfully, but we may be passing the command
incorrectly. Let’s try <code>dir-0001</code> only.</p>
<p>Even if an incorrect path is provided, it shouldn’t enter an infinite loop.</p>
<p>When I provide the correct path, <code>dir-0001</code>, it returns. There may be something going on with finding the root of the Xvc repo.</p>
<p>It returns, but it’s also giving an error that it cannot find a repository. I should take a closer look.</p>
<p>The <code>Xvc</code> struct wasn’t implementing <code>Debug</code>, so I added it.</p>
<p>Let’s add it to others, <code>XvcFile</code>, etc.</p>
<p>Added it. Recompiling to get more info when we run <code>track</code> with an incorrect path.</p>
<p>You can also make it run after a commit to avoid starting a new server if one is already running.</p>
<p>Added conditionals, and now it runs the server if none is running in the background.</p>
<pre><code class="language-zsh">PORT=7979
if [[ ! -s "$(ps ax | rg -v ps | rg jupyter-lab | rg $PORT)" ]] ; then
  jupyter lab --port=$PORT --notebook-dir=Readme/ &amp;
  open http://localhost:${PORT}/lab/workspaces/auto-H/tree/Readme.ipynb
fi
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Shell function to open URLs in Kagi Summarizer</title>
      <published>2024-04-30T20:07:00+00:00</published>
      <updated>2024-04-30T20:07:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Tue, 30 Apr 2024 20:07:00 +0000</pubDate>
      <link>https://emresahin.net/shell-function-to-open-urls-in-kagi-summarizer/</link>
      <guid isPermaLink="true">https://emresahin.net/shell-function-to-open-urls-in-kagi-summarizer/</guid>
      <description>I’m a fan and an avid user of Kagi Search. I’m an Ultimate plan subscriber and use their Summarizer frequently. I needed a way to quickly open URLs in the Kagi Summarizer from my terminal. I wrote a small shell function to do that: ksum() { local urlenc=$(jq -rn --arg x "$1" '$x|@uri') open "http...</description>
      <category>Tools</category>
      <category>shell</category>
      <category>kagi</category>
      <category>search</category>
      <category>summarizer</category>
      <category>zsh</category>
      <category>automation</category>
      <content:encoded><![CDATA[<p>I’m a fan and an avid user of Kagi Search. I’m an Ultimate plan subscriber and use their Summarizer frequently.</p>
<p>I needed a way to quickly open URLs in the Kagi Summarizer from my terminal. I wrote a small shell function to do that:</p>
<pre><code class="language-zsh">ksum() {
  local urlenc=$(jq -rn --arg x "$1" '$x|@uri')
  open "https://kagi.com/summarizer/index.html?target_language=&amp;summary=takeaway&amp;url=${urlenc}"
}
</code></pre>]]></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>Xvc Devlog 221031</title>
      <published>2022-11-05T09:18:00+00:00</published>
      <updated>2022-11-05T09:18:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sat, 05 Nov 2022 09:18:00 +0000</pubDate>
      <link>https://emresahin.net/xvc-devlog-221031/</link>
      <guid isPermaLink="true">https://emresahin.net/xvc-devlog-221031/</guid>
      <description>🐇 It’s the last day of October. How are you doing, mister? 🐢 Yep. It was a nice October. I would like to finish by increasing the coverage. 🐇 How about setting up a runner on your machine to test them locally? 🐢 Not now. I should fix the tests as always. Later I can take a look at it. I don’t wan...</description>
      <category>xvc</category>
      <category>devlog</category>
      <category>ci/cd</category>
      <category>cross-compilation</category>
      <category>apple silicon</category>
      <category>testing</category>
      <category>xvc</category>
      <category>redirection</category>
      <category>shell</category>
      <content:encoded><![CDATA[<p>🐇 It’s the last day of October. How are you doing, mister?</p>
<p>🐢 Yep. It was a nice October. I would like to finish by increasing the coverage.</p>
<p>🐇 How about setting up a runner on your machine to test them locally?</p>
<p>🐢 Not now. I should fix the tests as always. Later I can take a look at it. I don’t want to distract myself with it.</p>
<p>🐇 You’ll need to create Apple Silicon binaries, though.</p>
<p>🐢 I think I can cross-compile for aarch.</p>
<p>🐇 You were doing this in the past, trying to build all binaries on a single Ubuntu VM. It didn’t work, as far as I remember.</p>
<p>🐢 I hadn’t even read <a href="https://rust-lang.github.io/rustup/cross-compilation.html">this section</a> then, and there is a <a href="https://github.com/cross-rs/cross">dedicated project</a> for it. The proper way is to use that. I’ll return to this after fixing the tests. I think we can release Xvc on all platforms supported by Rust.</p>
<p>🐇 Using <code>cross-rs</code> on GitHub Actions may be challenging. It seems <a href="https://stackoverflow.com/questions/66849112/how-do-i-cross-compile-a-rust-application-from-macos-x86-to-macos-silicon">Apple Silicon doesn’t need that much work</a> either. You can set up cross-compilation with <code>rustup</code> targets.</p>
<p>🐢 Yeah. It looks so. Let’s take a look at the <a href="https://emresahin.net/xvc-devlog-221030">previous devlog.</a></p>
<p>🐇 The page seems missing. Would you like to fix your site first?</p>
<p>🐢 Ah, yeah. I should, maybe. Now it’s time to fix those PRs. I’ll take care of the site later. I should change the theme anyway.</p>
<p>🐇 Ok. Let’s take a look at <a href="https://github.com/iesahin/xvc/pulls">outstanding PRs</a>.</p>
<p>🐢 I want to fix the failed tests locally first. Yesterday I wasn’t able to redirect the output. It looks like the correct way of doing it is <code>cargo test --all-features --no-fail-fast &gt; $TMPDIR/xvc-test.log 2&gt;&amp;1</code>. It’s required to put <code>2&gt;&amp;1</code> at the end, not between the redirection and output.</p>
<p>🐇 There is also the <code>&amp;&gt;</code> operator that you may want to use. <code>cargo test --all-features --no-fail-fast &amp;&gt; $TMPDIR/xvc-test.log</code> should be equivalent to this.</p>
<p>🐢 I’ll try it when the tests finish.</p>
<p>🐇 In the meantime, you can work to fix the documentation. In another repository, perhaps.</p>
<p>🐢 Good idea. Let me clone that.</p>
<p>🐇 Looks like the tests have finished and the only error is Minio. It couldn’t find the env variables you defined.</p>
<p>🐢 Restarted the shell and the tests. Returned to docs now.</p>
<hr>
<p>🐢 Added a few function docs. Storage tests seem to pass. Probably they will fail in GA because of <code>rsync</code> tests using <code>one.emresult.com</code> login in my name.</p>
<p>🐇 If that’s expected, you can push and begin to fix it.</p>
<p>🐢 Yeah, let’s push the tests.</p>
<hr>
<p>🐢 It looks like connecting to localhost also poses a challenge. Instead, I can create a user for <code>xvc</code> on the server and limit its usage.</p>
<p>🐇 Hmm. Good idea for now.</p>
<p>🐢 I created a new user <code>xvc-test@one.emresult.com</code> and its SSH keys. I’ll update the action to write the secret to the keyfile.</p>
<hr>
<p>🐇 Looks like you forgot to install <code>mc</code> for Minio connection.</p>
<p>🐢 Yeah, I must convert Minio tests to use <code>s3cmd</code>.</p>
<p>🐇 And your region in <code>s3</code> configuration seems to be wrong.</p>
<p>🐢 I’ll need to check this.</p>
<p>🐇 There is this line in the tests <code>let region = env::var("AWS_DEFAULT_REGION").unwrap_or("us-east-1".to_string());</code> that’s probably causing that error. You should define the region properly.</p>
<p>🐢 I set this to <code>eu-central-1</code> directly and will check the Minio error in the next session.</p>
<p>🐇 👏</p>
<hr>
<p>🐢 It’s working except for the rsync tests now.</p>
<p>🐇 I think you can just use localhost to run the tests. You may need to install openssh-server, but it should work with localhost without configuration.</p>
<p>🐢 There may be a step missing in the configuration. I’ll try to make it run.</p>
<hr>
<p>🐢 I’m trying to use <code>.ssh/config</code> in GA to allow connections to the server. It doesn’t work for some reason.</p>
<p>🐇 You may try to log in to the server outside of the tests and check if it’s running.</p>
<hr>
<p>🐢 Yesterday’s last attempt was successful, and now we have working remote storage tests.</p>
<p>🐇 👏👏👏🥳</p>
<p>🐢 I’m merging the PR.</p>]]></content:encoded>
    </item>
    <item>
      <title>Sending i3 messages from shell scripts</title>
      <published>2021-01-28T12:35:21+00:00</published>
      <updated>2021-01-28T12:35:21+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Thu, 28 Jan 2021 12:35:21 +0000</pubDate>
      <link>https://emresahin.net/sending-i3-messages-from-shell/</link>
      <guid isPermaLink="true">https://emresahin.net/sending-i3-messages-from-shell/</guid>
      <description>It’s possible to send messages to the i3 window manager from shell scripts using i3-msg . For example: i3-msg workspace "n"</description>
      <category>Scripting</category>
      <category>i3</category>
      <category>X</category>
      <category>desktop</category>
      <category>automation</category>
      <category>shell</category>
      <category>til</category>
      <content:encoded><![CDATA[<p>It’s possible to send messages to the i3 window manager from shell scripts using <code>i3-msg</code>. For example:</p>
<pre><code class="language-bash">i3-msg workspace "n"
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Zenity</title>
      <published>2018-12-03T18:54:26+00:00</published>
      <updated>2018-12-03T18:54:26+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 03 Dec 2018 18:54:26 +0000</pubDate>
      <link>https://emresahin.net/zenity-14386-18029/</link>
      <guid isPermaLink="true">https://emresahin.net/zenity-14386-18029/</guid>
      <description>When you need a simple dialog to get input from a user or display information in a GUI dialog box, zenity is a great tool. It allows shell scripts to interact with users through various types of dialogs. zenity --info --text="Merge complete. Updated 3 of 10 files."</description>
      <category>shell</category>
      <category>tool</category>
      <category>zenity</category>
      <category>bash</category>
      <category>shell</category>
      <category>linux</category>
      <category>gui</category>
      <content:encoded><![CDATA[<p>When you need a simple dialog to get input from a user or display information in a GUI dialog box, <a href="https://help.gnome.org/users/zenity/3.24/"><code>zenity</code></a> is a great tool. It allows shell scripts to interact with users through various types of dialogs.</p>
<pre><code class="language-bash">zenity --info --text="Merge complete. Updated 3 of 10 files."
</code></pre>
<p><img src="https://emresahin.net/images/zenity-ss.png" alt="zenity-ss"></p>]]></content:encoded>
    </item>
    <item>
      <title>zsh'de dosya seçim operatörleri</title>
      <published>2014-03-02T22:00:00+00:00</published>
      <updated>2014-03-02T22:00:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 02 Mar 2014 22:00:00 +0000</pubDate>
      <link>https://emresahin.net/zsh-dosya-operasyonlari/</link>
      <guid isPermaLink="true">https://emresahin.net/zsh-dosya-operasyonlari/</guid>
      <description>zsh, dosya seçmek için bash’ten biraz daha gelişmiş operatörlere sahip. Bunlar sayesinde bir defada bir dizindeki dosyaların tamamına erişip onlar üzerinde işlemler yapmak mümkün. Burada kısa bazı örnekler vereceğim. Bir dizindeki tüm dosyalar: ls * Bulunduğumuz dizin ve tüm alt dizinlerdeki dosy...</description>
      <category>Software Development</category>
      <category>zsh</category>
      <category>shell</category>
      <category>linux</category>
      <category>tutorial</category>
      <content:encoded><![CDATA[<p>zsh, dosya seçmek için bash’ten biraz daha gelişmiş operatörlere sahip. Bunlar sayesinde bir defada bir dizindeki dosyaların tamamına erişip onlar üzerinde işlemler yapmak mümkün. Burada kısa bazı örnekler vereceğim.</p>
<p>Bir dizindeki tüm dosyalar: <code>ls *</code></p>
<p>Bulunduğumuz dizin ve tüm alt dizinlerdeki dosyalar: <code>ls **/*</code></p>
<p>Son iki haftada değiştirilmiş dosyalar: <code>ls **/*(.mw-2)</code></p>
<p>Boyutu 100 MB’tan büyük olan dosyalar: <code>ls **/*(.Lm+100)</code></p>
<p>Sadece okunabilir olan dosyalar: <code>ls **/*(.R)</code></p>
<p><code>/etc</code> dizininde dünya tarafından yazılabilir olan dosyalar: <code>ls /etc/**/*(.W)</code></p>
<p><code>/etc</code> dizininde son bir haftada değiştirilmiş ve dünya tarafından yazılabilir olan dosyalar: <code>ls /etc/**/*(.Wmw-1)</code></p>
<p>zsh’in sağladığı bu gibi seçeneklerin yanında dosya adını parçalamak da kolay. Örneğin dosyanın uzantıdan önceki kısmını almak için <code>*(:r)</code> yazıyoruz. Dizindeki <em>jpg</em> dosyalarını ImageMagick’in <code>convert</code> programıyla aynı isimli png dosyalarına çevirmek şöyle mümkün:</p>
<pre><code class="language-bash">for f in **/*.jpg(.) ; do
   convert ${f:r}.jpg ${f:r}.png
done
</code></pre>
<p>Burada anlatılanları <code>find</code> yoluyla yapmak da mümkün ancak çok daha uzun sürebiliyor. Bilhassa dosya adlarını parçalamak, dönüştürmek gibi işlemleri yaparken zsh’in nimetleri çok. Tek satırlık bir operasyonla çok güçlü bazı dönüşümler yapabilmek mümkün.</p>]]></content:encoded>
    </item>
    <item>
      <title>Shell (Bash and Zsh) Notes</title>
      <published>2013-08-16T21:00:00+00:00</published>
      <updated>2013-08-16T21:00:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 16 Aug 2013 21:00:00 +0000</pubDate>
      <link>https://emresahin.net/shell-notes/</link>
      <guid isPermaLink="true">https://emresahin.net/shell-notes/</guid>
      <description>Don’t use ~ in scripts; use $HOME I have used ~ several times in scripts, but it may not always expand as expected depending on the environment. Use $HOME to refer to the home directory; it is much more reliable and always works.</description>
      <category>Scripting</category>
      <category>CLI</category>
      <category>shell</category>
      <category>bash</category>
      <category>zsh</category>
      <category>scripting</category>
      <category>best practices</category>
      <category>til</category>
      <content:encoded><![CDATA[<h1 id="dont-use--in-scripts-use-home">Don’t use <code>~</code> in scripts; use <code>$HOME</code></h1>
<p>I have used <code>~</code> several times in scripts, but it may not always expand as expected depending on the environment. Use <code>$HOME</code> to refer to the home directory; it is much more reliable and always works.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
