<?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 🍃 - zsh</title>
    <link>https://emresahin.net/tags/zsh/</link>
    <description>Posts in the zsh 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/zsh/rss.xml" rel="self" type="application/rss+xml"/>
    <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>Poor man's secrets manager with pass</title>
      <published>2025-01-02T15:21:45+00:00</published>
      <updated>2025-01-02T15:21:45+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Thu, 02 Jan 2025 15:21:45 +0000</pubDate>
      <link>https://emresahin.net/poor-man-s-secrets-manager-with-pass/</link>
      <guid isPermaLink="true">https://emresahin.net/poor-man-s-secrets-manager-with-pass/</guid>
      <description>I recently restarted using pass , the standard Unix password manager, to store my passwords. At work, we use Doppler to manage our secrets. It injects environment variables before running commands. I thought, why can’t I do the same with a simple script? The following script evaluates all passwor...</description>
      <category>Security</category>
      <category>Tools</category>
      <category>pass</category>
      <category>secrets-management</category>
      <category>cli</category>
      <category>shell-script</category>
      <category>zsh</category>
      <content:encoded><![CDATA[<p>I recently restarted using <code>pass</code>, the standard Unix password manager, to store my passwords.</p>
<p>At work, we use Doppler to manage our secrets. It injects environment variables before running commands. I thought, why can’t I do the same with a simple script?</p>
<p>The following script evaluates all password files with <code>env</code> in their names. I keep my environment variables in files named <code>env-aws</code>, etc., in the following format:</p>
<pre><code class="language-bash">export AWS_ACCESS_KEY_ID="123456...."
</code></pre>
<p>The following script, which I called <code>rws</code> (short for <code>run-with-secrets</code>), allows me to run a command like <code>rws s3cmd</code> and use <code>pass</code> to inject the variables. So far, I’m happy with it.</p>
<pre><code class="language-zsh">#!/bin/zsh

cmd="$@"

fd -F env $HOME/.password-store | while read file ; do
  bb="${file:t:r}"
  eval $(pass show ${bb})
done

exec $cmd
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Merging all Tmux windows in a single session</title>
      <published>2024-06-12T21:24:15+00:00</published>
      <updated>2024-06-12T21:24:15+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Wed, 12 Jun 2024 21:24:15 +0000</pubDate>
      <link>https://emresahin.net/merging-all-tmux-windows-in-a-single-session/</link>
      <guid isPermaLink="true">https://emresahin.net/merging-all-tmux-windows-in-a-single-session/</guid>
      <description>I use a new Tmux session for each day. Over time, I noticed that sessions proliferate, and I often need windows from earlier sessions in a central location. This script merges all windows from all Tmux sessions into a single session. You must specify the target session as a parameter. If you save...</description>
      <category>Shell</category>
      <category>Terminal</category>
      <category>Tmux</category>
      <category>Script</category>
      <category>Zsh</category>
      <category>Automation</category>
      <category>Productivity</category>
      <content:encoded><![CDATA[<p>I use a new Tmux session for each day. Over time, I noticed that sessions proliferate, and I often need windows from earlier sessions in a central location.</p>
<p>This script merges all windows from all Tmux sessions into a single session. You must specify the target session as a parameter. If you save the script as <code>merge-tmux-sessions.zsh</code>, you can run it like:</p>
<pre><code class="language-bash">merge-tmux-sessions.zsh my-current-session
</code></pre>
<p>It will then move all windows into the session you specify.</p>
<pre><code class="language-zsh">#!/bin/zsh

# set -vuex
#
#  Specify the target session

target_session=$1

# Get the list of all sessions

sessions=$(tmux list-sessions -F '#S')

# Iterate through each session

for session in ${(@f)sessions}; do
    if [ "$session" != "$target_session" ]; then
        # Get the list of windows in the current session
        windows=$(tmux list-windows -t $session -F '#I')

        # Move each window to the target session
        for window in ${(@f)windows}; do
            tmux move-window -s ${session}:${window} -t ${target_session}
        done
    fi

done
</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>Deleting duplicate files in Google Drive using rclone</title>
      <published>2021-01-06T21:31:08+00:00</published>
      <updated>2022-08-06T00:00:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Wed, 06 Jan 2021 21:31:08 +0000</pubDate>
      <link>https://emresahin.net/rclone-google-drive-deletion/</link>
      <guid isPermaLink="true">https://emresahin.net/rclone-google-drive-deletion/</guid>
      <description>I’m a Google One user, and my Google Drive has about 1TB of content from various sources. A few years ago, I used a third-party utility to sync my Linux boxes to Drive, which created many duplicate files. I had around 3-4 different versions of some directories with different sets of files. As a t...</description>
      <category>Scripts</category>
      <category>Cloud Storage</category>
      <category>drive</category>
      <category>duplicates</category>
      <category>rclone</category>
      <category>google-drive</category>
      <category>automation</category>
      <category>zsh</category>
      <content:encoded><![CDATA[<p>I’m a Google One user, and my Google Drive has about 1TB of content from various sources.
A few years ago, I used a third-party utility to sync my Linux boxes to Drive, which created many duplicate files.
I had around 3-4 different versions of some directories with different sets of files.
As a true procrastinator, I postponed addressing the problem until Google One alerted me about my quota.</p>
<p>Nowadays, I’m using <a href="https://rclone.org/"><code>rclone</code></a>.
It has become my primary way of using Drive on Linux after that half-baked sync application.
I discovered that <code>rclone</code> supports server-side MD5 hashes for files.
I decided to write a script to delete the duplicate files in Drive.</p>
<p>First, I obtain the MD5 hashes of all files in Drive using:</p>
<pre><code>$ rclone md5hash drive:/ &gt; $HOME/Google-Drive-md5-$(date +%F).txt
</code></pre>
<p>This may take some time depending on the number of files, but it finished more quickly than I expected.</p>
<p>The file contents look like this:</p>
<pre><code>39044094333de4a47d7478227cfa22a9  Facebin/vgg-clean/clea_duvall/00000501.jpg
398d44f0b32d1b4855e838754c2c49fc  Facebin/vgg-clean/Bob_Barker/00000445.jpg
c2090f9412b93d71fed884bb45b26518  Facebin/vgg-clean/Adam_Goldberg/00000432.jpg
cd6971f809a534ab02ee1b03eb6c1183  CHECK Uploads/Google Photos/2017/06/IMG_1461.JPG
1fb80cc9ef622410557cb42c4abf26a8  Facebin/vgg-clean/Angell_Conwell/00000510.jpg
160793fd8f24fcf27efb9a2b3698a9c8  Facebin/makimface-artifacts/dataset-images/user-test-v4/00056--/img-5c1fdc5d4fd82c1e12a7d49937d7f47b.png
bcddf6ebbc1eed83950d908c2ccbac4a  Facebin/vgg-clean/danny_pino/00000102.jpg
eb2746a9559e7a93fd0002f4bfd90517  Facebin/makimface-artifacts/dataset-images/user-test/00009--/ds-767cd16bfdf4e31860d597708a586979.png
9605d69aaf5fb83d309f10f9e2544630  Facebin/vgg-clean/Adam_Beach/00000953.jpg
68e6d3ec3cca8a728a78fb60c9a1ddba  Facebin/vgg-clean/corey_stoll/00000137.jpg
</code></pre>
<p>The first 32 characters are the MD5 hash of the file, and the rest of the line is the file path.</p>
<p>You’ll probably see some blanks for the MD5 hashes of certain files.
<strong>It’s important</strong> to remove these from the list, as these are your Google Docs, spreadsheets, etc.</p>
<pre><code>$ grep '[^0-9a-f]' $HOME/Google-Drive-md5-$(date +%F).txt &gt; $HOME/Google-Drive-md5-cleaned.txt
</code></pre>
<p>Next, we sort the file using:</p>
<pre><code>$ sort $HOME/Google-Drive-md5-cleaned.txt &gt; $HOME/Google-Drive-md5-sorted.txt
</code></pre>
<p>We keep all intermediate files because you might want to review the differences after the cleanup.</p>
<p>Then, we write the following script and run it:</p>
<pre><code class="language-zsh">#!/bin/zsh

PREV_MD5=""
PREV_PATH=""
CURRENT_MD5=""
CURRENT_PATH=""
MD5_FILE=$HOME/Google-Drive-md5-sorted.txt
cat $MD5_FILE | while read current_line ; do
    # echo $current_line
    CURRENT_MD5=$(echo "$current_line" | cut -c -32)
    CURRENT_PATH=$(echo "$current_line" | cut -c 35-)
    # echo $CURRENT_MD5
    # echo "$CURRENT_PATH"
    if [[ "$CURRENT_MD5" == "$PREV_MD5" ]] ; then
        echo "EQUAL: $CURRENT_MD5 $PREV_MD5"
        echo "DELETE: drive:/$CURRENT_PATH"
        rclone -v delete "drive:/$CURRENT_PATH"
    else
        PREV_MD5=$CURRENT_MD5
        PREV_PATH=$CURRENT_PATH
    fi
done
</code></pre>
<p>You can save this script as <code>google-drive-delete-duplicates.sh</code> and run:</p>
<pre><code>$ chmod +x google-drive-delete-duplicates.sh
$ ./google-drive-delete-duplicates.sh
</code></pre>
<p>The script checks each line one by one; if two duplicate MD5 hashes are found consecutively, the second file is deleted.
It keeps only one of the duplicates, even if there are more than two copies.</p>
<p>I have gained about 200GB by running this script.</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>Regular and Recurring Tasks in todo.txt</title>
      <published>2018-02-06T10:23:22+00:00</published>
      <updated>2018-02-06T10:23:22+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Tue, 06 Feb 2018 10:23:22 +0000</pubDate>
      <link>https://emresahin.net/recurrent-todo-txt-14085-74577/</link>
      <guid isPermaLink="true">https://emresahin.net/recurrent-todo-txt-14085-74577/</guid>
      <description>I use the todo.txt format to manage some of my daily tasks. It’s a plain-text format, and both iOS and Android have apps, such as SimpleTasks . Emacs and Vim also support the format. Actually, you don’t need a special editor for it; the format is so simple that even Notepad is sufficient. I have ...</description>
      <category>Productivity</category>
      <category>Automation</category>
      <category>todo.txt</category>
      <category>zsh</category>
      <category>productivity</category>
      <category>cron</category>
      <category>linux</category>
      <content:encoded><![CDATA[<p>I use the <a href="http://todotxt.org/">todo.txt format</a> to manage some of my daily tasks. It’s a
plain-text format, and both iOS and Android have apps, such as
<a href="https://play.google.com/store/apps/details?id=nl.mpcjanssen.todotxtholo">SimpleTasks</a>. <a href="https://github.com/avillafiorita/todotxt-mode">Emacs</a> and <a href="https://github.com/freitass/todo.txt-vim">Vim</a> also support
the format. Actually, you don’t need a special editor for it; the format
is so simple that even Notepad is sufficient.</p>
<p>I have a shell script to add daily recurring tasks, such as “Drink Water” or
“Pray Maghreb,” to this file. SimpleTasks expects the file to be at
<code>$HOME/Dropbox/todo/todo.txt</code>, and I use a template file, such as <code>template.txt</code>,
to populate it. However, as anyone who has used TODO files knows, there are
always unfinished tasks; for instance, I don’t always drink my daily allotted
amount of water.</p>
<p>So, my evening script moves completed tasks (marked as DONE) to their own file,
removes recurring tasks from <code>todo.txt</code>, and repopulates them for a new day.
(Note: The code below is not usable as-is; you must define the <code>TODO</code>, <code>DONE</code>,
and <code>TEMPLATE</code> file paths correctly.)</p>
<pre><code class="language-zsh">
TODO=.../todo.txt
DONE=.../$(date +%F)-done.txt
TEMPLATE=.../template.txt

#Move Done to $DONE
grep -wE '^x ' $TODO &gt;&gt; $DONE
sed -i '/^x /d' $TODO

#Delete lines with @Evening, @Morning or @Regular tags from todo
sed -i "/\b\(@Morning|@Evening|@Regular\)\b/d" $TODO
cat $TODO_TEMPLATE &gt;&gt; $TODO
</code></pre>
<p>I have configured <code>cron</code> to run this every day at 6 PM. (My “evening” starts
earlier than my morning.)</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>
