<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Blog — George Mishurovsky: posts tagged mac</title>
<link>https://mishurovsky.com/blog/?go=tags/mac/</link>
<description>A blog by George Mishurovsky — a senior software engineer with a medical degree. Drawing from both engineering and scientific thinking, he explores software, architecture, design, psychology, and product thinking.</description>
<author></author>
<language>en</language>
<generator>Aegea 11.3 (v4134e)</generator>

<itunes:owner>
<itunes:name></itunes:name>
<itunes:email>george@mishurovsky.com</itunes:email>
</itunes:owner>
<itunes:subtitle>A blog by George Mishurovsky — a senior software engineer with a medical degree. Drawing from both engineering and scientific thinking, he explores software, architecture, design, psychology, and product thinking.</itunes:subtitle>
<itunes:image href="https://mishurovsky.com/blog/pictures/userpic/userpic-square@2x.jpg?1753619610" />
<itunes:explicit>no</itunes:explicit>

<item>
<title>How to Add Version Control Info to zsh Prompt</title>
<guid isPermaLink="false">53</guid>
<link>https://mishurovsky.com/blog/?go=all/how-to-add-version-control-info-to-zsh-prompt/</link>
<pubDate>Mon, 31 Aug 2026 11:33:01 +0200</pubDate>
<author></author>
<comments>https://mishurovsky.com/blog/?go=all/how-to-add-version-control-info-to-zsh-prompt/</comments>
<description>
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://mishurovsky.com/blog/pictures/vcs-in-zsh@2x.png" width="1287" height="668" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;MacOS default shell, &lt;span class="inline-code"&gt;zsh&lt;/span&gt;, comes with a neat version control system integration. It works not only with &lt;span class="inline-code"&gt;git&lt;/span&gt;, but also with &lt;span class="inline-code"&gt;svn&lt;/span&gt;, &lt;span class="inline-code"&gt;mercurial&lt;/span&gt;, and many others! The capabilities are quite extensive — you can see yourself in &lt;a href="https://zsh.sourceforge.io/Doc/Release/User-Contributions.html#SEC273"&gt;zsh manual&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One thing that I found always missing from default terminal views into folders was if the folder is part of some git repo and which branch is currently checked out. Turns out, about 10 lines of &lt;span class="inline-code"&gt;.zshrc&lt;/span&gt; is enough to provide not only that, but also whether the current branch contains changes, staged and unstaged! I am sharing a script for &lt;span class="inline-code"&gt;zsh&lt;/span&gt; and its alternative for &lt;span class="inline-code"&gt;bash&lt;/span&gt;, which does not have a VCS integration and thus relies on native &lt;span class="inline-code"&gt;git&lt;/span&gt; commands. The result is branch name with &lt;span class="inline-code"&gt;+&lt;/span&gt; and &lt;span class="inline-code"&gt;*&lt;/span&gt; markers appended to the end of folder path, whenever the folder belongs to some git repository.&lt;/p&gt;
&lt;h2&gt;Zsh&lt;/h2&gt;
&lt;div class="e2-code-block" data-language="sh"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-sh"&gt;# Always display version control info in the prompt
autoload -Uz vcs_info
autoload -Uz add-zsh-hook
add-zsh-hook precmd vcs_info

zstyle &amp;#039;:vcs_info:*&amp;#039; check-for-changes true
zstyle &amp;#039;:vcs_info:git:*&amp;#039; unstagedstr &amp;#039;%F{red}*%f&amp;#039;
zstyle &amp;#039;:vcs_info:git:*&amp;#039; stagedstr &amp;#039;%F{green}+%f&amp;#039;
zstyle &amp;#039;:vcs_info:git:*&amp;#039; formats &amp;#039; %F{cyan}[%b%u%c%F{cyan}]%f&amp;#039;

setopt PROMPT_SUBST
PROMPT=&amp;#039;%~${vcs_info_msg_0_} %# &amp;#039;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2&gt;Bash&lt;/h2&gt;
&lt;div class="e2-code-block" data-language="sh"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-sh"&gt;COLOR_CYAN=&amp;#039;\e[36m&amp;#039;
COLOR_RED=&amp;#039;\e[31m&amp;#039;
COLOR_GREEN=&amp;#039;\e[32m&amp;#039;
COLOR_RESET=&amp;#039;\e[0m&amp;#039;

__git_prompt() {
    local branch

    # If not a Git repository
    branch=$(git symbolic-ref --short HEAD 2&amp;gt;/dev/null) ||
        branch=$(git rev-parse --short HEAD 2&amp;gt;/dev/null) ||
        return

    GIT_PROMPT=&amp;quot;${COLOR_CYAN}[${branch}&amp;quot;

    # Staged changes
    if ! git diff --cached --quiet 2&amp;gt;/dev/null; then
        GIT_PROMPT+=&amp;quot;${COLOR_GREEN}+${COLOR_CYAN}&amp;quot;
    fi

    # Unstaged changes
    if ! git diff --quiet 2&amp;gt;/dev/null; then
        GIT_PROMPT+=&amp;quot;${COLOR_RED}*${COLOR_CYAN}&amp;quot;
    fi

    GIT_PROMPT+=&amp;quot;]${COLOR_RESET}&amp;quot;
}

__update_prompt() {
    GIT_PROMPT=&amp;quot;&amp;quot;
    __git_prompt
    PS1=&amp;#039;\w &amp;#039;&amp;quot;${GIT_PROMPT}&amp;quot;&amp;#039; \$ &amp;#039;
}

PROMPT_COMMAND=&amp;quot;__update_prompt${PROMPT_COMMAND:+;$PROMPT_COMMAND}&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
</item>

<item>
<title>How to View Past Terminal Commands — from Simple to Robust</title>
<guid isPermaLink="false">5</guid>
<link>https://mishurovsky.com/blog/?go=all/how-to-quickly-retrieve-past-terminal-commands/</link>
<pubDate>Sun, 03 Aug 2025 14:18:24 +0200</pubDate>
<author></author>
<comments>https://mishurovsky.com/blog/?go=all/how-to-quickly-retrieve-past-terminal-commands/</comments>
<description>
&lt;p&gt;Suppose you want to re-run a shell command you used ten days ago. It was complex one; you do not remember exact flags and arguments, so it is going take a long time to reconstruct an exact text. What can you do?&lt;/p&gt;
&lt;h2&gt;1. The upwards arrow&lt;/h2&gt;
&lt;p&gt;Majority of devs know it. Press “up” to see the previous command, press “down” for the next command, press “Ctrl+C” to drop whatever is in the prompt and start fresh.&lt;/p&gt;
&lt;p&gt;This approach works, but gets tedious when you need to find a line you used last month. Once more than ten or twenty commands have passed, scrolling becomes no better than writing a command from scratch.&lt;/p&gt;
&lt;h2&gt;2. Terminal history file&lt;/h2&gt;
&lt;p&gt;All the commands you enter into a terminal get stored in &lt;span class="inline-code"&gt;.bash_history&lt;/span&gt; file (or &lt;span class="inline-code"&gt;.zsh_history&lt;/span&gt; if you are on Mac) up to a certain limit. Thus, you can run:&lt;/p&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;cat ~/.bash_history # output all into the terminal
less ~/.bash_history # or use any text viewer
tail -n 20 ~/.bash_history # or observe only the most recent n lines
cat ~/.bash_history | grep whatever # to search for specific patterns&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This method gives you full access to your history file and lets you see context around commands of interest, plus now you can search for them.&lt;/p&gt;
&lt;h2&gt;3. &lt;span class="inline-code"&gt;history&lt;/span&gt; command&lt;/h2&gt;
&lt;p&gt;Almost the same as using the history file directly: you get a list commands, but now it is &lt;i&gt;numbered&lt;/i&gt;.&lt;/p&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;history -20 # show the last 20 commands
history -500 | grep ssh # search for a specific patter in a command
!780 # execute command with order number 780&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But there is one important difference from direct usage of &lt;span class="inline-code"&gt;.bash_history&lt;/span&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="inline-code"&gt;history&lt;/span&gt; command uses the last &lt;span class="inline-code"&gt;HISTSIZE&lt;/span&gt; history entries (default 1000)&lt;/li&gt;
&lt;li&gt;&lt;span class="inline-code"&gt;.bash_history&lt;/span&gt; file uses the last &lt;span class="inline-code"&gt;HISTFILESIZE&lt;/span&gt; entries (default 2000)&lt;br /&gt;
It means that if your command was run a really long time ago, &lt;span class="inline-code"&gt;history&lt;/span&gt; may not find it. The line though, is still persisted, so direct inspection of &lt;span class="inline-code"&gt;.bash_history&lt;/span&gt; will help.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. &lt;span class="inline-code"&gt;fc -l&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;This command behaves very similar to &lt;span class="inline-code"&gt;history&lt;/span&gt;, with an additional ability to display ranges of command numbers::&lt;/p&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;fc -l -20 # show the last 20 commands
fc -l 100 150 # show commands 100 to 150&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2&gt;5. Reverse-i-search&lt;/h2&gt;
&lt;p&gt;This is the most powerful approach. Press “Ctrl+R” to enter &lt;i&gt;reverse incremental search&lt;/i&gt; mode. Initially you get no output; start writing any part of a command you remember, e. g. &lt;span class="inline-code"&gt;ssh&lt;/span&gt; or &lt;span class="inline-code"&gt;input.json&lt;/span&gt; or &lt;span class="inline-code"&gt;-n 10&lt;/span&gt; — and you will see the first full command entry with that match!&lt;/p&gt;
&lt;p&gt;From there, you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Press “Enter” to execute the command immediately&lt;/li&gt;
&lt;li&gt;Use left/right arrow keys to move within a command to edit it, then press “Enter” to execute&lt;/li&gt;
&lt;li&gt;Press “Ctrl+R” again to go to the next, older match&lt;/li&gt;
&lt;li&gt;Press “Ctrl+S” to go to the previous, newer match (note a comment below)&lt;/li&gt;
&lt;li&gt;Press up-down arrows to view to nearby entries in history around the match&lt;/li&gt;
&lt;li&gt;Press “Ctrl+C” or “Ctrl+G” to exit the search&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On many systems “Ctrl+S” shortcut will not work, as it is prioritized to pause terminal output (if that happens to you, press “Ctrl+Q” to resume). To make it work for reverse-i-search, add &lt;span class="inline-code"&gt;stty -ixon&lt;/span&gt; to your shell config. This incantation disables terminal flow control with “Ctrl+S” / “Ctrl+Q” shortcuts:&lt;/p&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;echo &amp;quot;stty -ixon&amp;quot; &amp;gt;&amp;gt; ~/.bashrc
source ~/.bashrc&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Happy command line manipulation!&lt;/p&gt;
&lt;p&gt;💡 This post has a second part: &lt;a href="https://mishurovsky.com/blog/all/shell-configs-for-better-command-history-search/"&gt;Shell Configs for Better Command History Search&lt;/a&gt;&lt;/p&gt;
</description>
</item>


</channel>
</rss>