<?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/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>sglavoie.com – Tag: Terminal</title><link>https://www.sglavoie.com/tags/terminal/</link><description>Posts and writings by Sébastien Lavoie. Customization setups and articles on productivity for developers who like minimalism.</description><language>en</language><atom:link href="https://www.sglavoie.com/tags/terminal/feed.xml" rel="self" type="application/rss+xml"/><lastBuildDate>Mon, 02 Jan 2023 09:52:00 -0600</lastBuildDate><item><title>Git worktrees for a better parallel workflow</title><link>https://www.sglavoie.com/posts/git-worktrees-for-a-better-parallel-workflow/</link><description>Git leads to a wealth of discoveries. Once SSH and GPG are set up, once dotfiles are under control and a reasonable Git workflow has been adopted, there’s still room to be amazed by a feature like Git worktrees!</description><content:encoded>&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Why are Git worktrees needed in the first place? Well, there are certainly a couple of different use cases where they come in handy, but the reason they exist is to allow a developer to check out multiple branches at once without having to do any kind of cleanup when switching branches. Introduced back in &lt;a href="https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/"&gt;2015 in Git 2.5&lt;/a&gt;, it&amp;rsquo;s certainly not a new feature anymore, yet it seems like its adoption really took off more recently. I was an avid user of &lt;a href="https://git-scm.com/docs/git-stash"&gt;&lt;code&gt;git stash&lt;/code&gt;&lt;/a&gt; but I often got myself cornered in some specific situations where it was not as convenient as I thought it was&amp;hellip; and &lt;a href="https://git-scm.com/docs/git-worktree"&gt;&lt;code&gt;git worktree&lt;/code&gt;&lt;/a&gt; was for me a pretty good solution without much overhead.&lt;/p&gt;
&lt;h2 id="why-is-git-stash-not-enough"&gt;Why is &lt;code&gt;git stash&lt;/code&gt; not enough?&lt;/h2&gt;
&lt;p&gt;It &lt;em&gt;can&lt;/em&gt; be enough, but there are edge cases where it falls short of its promise of keeping stuff neatly around without too much fuss.&lt;/p&gt;
&lt;h2 id="switching-context"&gt;Switching context&lt;/h2&gt;
&lt;p&gt;One particular use case in favor of &lt;code&gt;git worktree&lt;/code&gt; is when you have to quickly switch context and you have a dirty branch checked out &amp;ndash; which is basically the same use case advertised in the &lt;a href="https://git-scm.com/docs/git-worktree#_examples"&gt;Git documentation&lt;/a&gt;. Committing changes away might be a bit risky if those aren&amp;rsquo;t ready to be pushed to the remote server and stashing files might be annoying because there could be newer files to include with &lt;code&gt;git stash --include-untracked&lt;/code&gt; or some files already staged in the middle of the work where a &lt;code&gt;git stash --keep-index&lt;/code&gt; is also appropriate, or maybe even a case where you have created temporary files that match ignored files where &lt;code&gt;git stash --all&lt;/code&gt; might do. It gets even messier if you want to leave things in a clean state with only the changes you need with &lt;code&gt;git stash --patch&lt;/code&gt; where it would be needed to select interactively all your hunks. And then, you might already have multiple stashes or you might have forgotten to give one or more stash(es) a name, which makes it harder to &lt;code&gt;git stash pop&lt;/code&gt; or &lt;code&gt;git stash apply&lt;/code&gt; your changes later without having to inspect the content of your stashes with something like &lt;code&gt;git stash show -p&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;git worktree&lt;/code&gt;, this is no longer an issue! If you don&amp;rsquo;t already have a new worktree you can switch to, creating one with &lt;code&gt;git worktree add&lt;/code&gt; is quick and easy, as shown in the docs:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add -b emergency-fix ../temp master&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This will create a branch named &lt;code&gt;emergency-fix&lt;/code&gt;, creating a new worktree at &lt;code&gt;../temp&lt;/code&gt; checking the branch &lt;code&gt;emergency-fix&lt;/code&gt; that will be based off the &lt;code&gt;master&lt;/code&gt; branch. Then, you would switch to that branch which is managed with a new worktree by changing directory into it (or &lt;code&gt;pushd ../temp&lt;/code&gt; to put the directory into a stack so you can come back later to your current branch with &lt;code&gt;popd&lt;/code&gt;). You would add changes and commit them, likely pushing them to a remote, then you could resume your work by going back to the original directory for your &lt;code&gt;master&lt;/code&gt; branch. Now, you could leave behind the &lt;code&gt;emergency-fix&lt;/code&gt; branch but if that was meant as a temporary one, you could just delete the worktree with &lt;code&gt;git worktree remove ../temp&lt;/code&gt;. Besides being removed by path, worktrees can also be removed by the name they are associated with, which is the branch name shown in square brackets when issuing the command &lt;code&gt;git worktree list&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Arguably, it&amp;rsquo;s not too hard to see that dealing with temporary changes in this way is a lot more straightforward since the current changes can be left intact without having to commit or stash them.&lt;/p&gt;
&lt;h2 id="running-tasks-in-the-background"&gt;Running tasks in the background&lt;/h2&gt;
&lt;p&gt;Whether that be running a test suite that takes a while to execute or leaving the build of a system untouched while it is happening (like building a Docker image), it can be really useful to go work on something else while leaving the original task in the background. More than this, it allows one to go on another branch and make changes to the repository while files from the other branch are being accessed. In the case of building a Docker image for instance, it could cause hard-to-find bugs if the build is stopped or failed and meanwhile files were modified in the repo on the same branch to then be copied back in the next build of the Docker image only to realize that the files being copied have changed prior to being copied.&lt;/p&gt;
&lt;p&gt;More generally though, I find I just like to leave my terminal windows open into a specific path (branch) while having the freedom to go work elsewhere. This way, a virtual environment (for instance, in Python) can be left &amp;ldquo;activated&amp;rdquo; and ready to use when context is switched again. Often, different branches in a project might have slightly different requirements and so a distinct virtual environment must be activated. Having them entirely separated by path in separate worktrees makes this kind of workflow a lot simpler to manage.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="how-i-operate-worktrees"&gt;How I operate worktrees&lt;/h2&gt;
&lt;h2 id="first-other-approaches-in-the-wild"&gt;First, other approaches in the wild&lt;/h2&gt;
&lt;p&gt;I have seen some great programmers use them within a &amp;ldquo;&lt;a href="https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---bare"&gt;bare repository&lt;/a&gt;&amp;rdquo;, notably &lt;strong&gt;ThePrimeagen&lt;/strong&gt; in his YouTube video titled &lt;a href="https://www.youtube.com/watch?v=2uEqYw-N8uE"&gt;Git&amp;rsquo;s Best And Most Unknown Feature&lt;/a&gt;. This works by creating different directories inside the bare repository, effectively having all worktrees at the same level. There is a slight chance of colliding with a reserved directory name by Git, though, since all the metadata is stored there too.&lt;/p&gt;
&lt;p&gt;Another interesting approach I&amp;rsquo;ve witnessed is the use of a &lt;code&gt;.worktrees&lt;/code&gt; directory inside the directory containing the main branch of the project, which works by adding a match for &lt;code&gt;.worktrees/&lt;/code&gt; in a global &lt;code&gt;.gitignore&lt;/code&gt; file (kudos to &lt;strong&gt;Redhwan Nacef&lt;/strong&gt; in his &lt;a href="https://www.youtube.com/watch?v=4_p1OdLeDLE"&gt;Git Worktree Tutorial&lt;/a&gt;!). I like this approach because it can list all working trees anyways with &lt;code&gt;git worktree list&lt;/code&gt; and there&amp;rsquo;s nothing really cluttering the directory structure. However, there might be some huge build files or environments and nesting everything that way might require creating a script to find all worktrees as it is not immediately obvious where they would be stored, if at all.&lt;/p&gt;
&lt;h2 id="what-i-actually-do-these-days"&gt;What I actually do these days&lt;/h2&gt;
&lt;p&gt;The approach I have adopted is very simple and requires almost no setup at all.&lt;/p&gt;
&lt;h3 id="starting-with-a-project-with-existing-branches"&gt;Starting with a project with existing branches&lt;/h3&gt;
&lt;p&gt;Assuming a project is already cloned and worked on with no additional worktrees, then it leads to a minor hiccup because the project will need to be renamed. So what I currently do is create a top-level directory for the project, say &lt;code&gt;sglavoie.github.io&lt;/code&gt; for this website. Then, I will literally reuse the branch name to create some hierarchy inside that project. Let&amp;rsquo;s say that right now, I&amp;rsquo;m writing multiple articles in different branches and I keep my &lt;code&gt;main&lt;/code&gt; branch as always, then I would first &lt;code&gt;mkdir sglavoie.github.io/main&lt;/code&gt; and put the project&amp;rsquo;s content in there with its &lt;code&gt;.git&lt;/code&gt; folder. Mind you, this is a one-time thing when not already using worktrees!&lt;/p&gt;
&lt;p&gt;At this stage, the project structure is laid out nicely and ready to accommodate new worktrees. So for instance, if I want to work on a new branch to create a new article without touching the &lt;code&gt;main&lt;/code&gt; branch, I would &lt;code&gt;git worktree add ../articles/article-name-here article-name-here&lt;/code&gt; from the &lt;code&gt;main&lt;/code&gt; branch and voilà, a new worktree will be ready to use. Once a couple of worktrees are created in this way, the directory structure might look as follows:&lt;/p&gt;
&lt;div class="code-block code-block--wrap"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;~/dev/sglavoie/sglavoie.github.io
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── articles
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── adv-web-dev-social-network-app
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── git-worktree
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── react-native-app
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── text-based-diagrams
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this way, it&amp;rsquo;s just a matter of changing directory and opening the worktree in your editor of choice (e.g., &lt;code&gt;nvim&lt;/code&gt; for neovim or &lt;code&gt;code .&lt;/code&gt; with VS Code). From now on, it&amp;rsquo;s easy to switch between branches of a project right from the editor by searching for the &amp;ldquo;topic&amp;rdquo; (e.g., &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;articles&lt;/code&gt; in this case, but that could be &lt;code&gt;hotfix&lt;/code&gt; or &lt;code&gt;feat&lt;/code&gt; depending on how your name your branches) or for a project name directory, which would show all the branches that have been opened previously to allow further filtering. This is what &lt;code&gt;git worktree list&lt;/code&gt; would display in this example:&lt;/p&gt;
&lt;div class="code-block code-block--wrap"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/some/path/sglavoie.github.io/main d67dd8d2 [main]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/some/path/sglavoie.github.io/articles/adv-web-dev-social-network-app 666a0b43 [articles/adv-web-dev-social-network-app]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/some/path/sglavoie.github.io/articles/git-worktree d67dd8d2 [git-worktree]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/some/path/sglavoie.github.io/articles/react-native-app bcfac34e [articles/react-native-app]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/some/path/sglavoie.github.io/articles/text-based-diagrams 4b25371d [articles/text-based-diagrams]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="new-worktrees-from-a-project-already-using-worktrees"&gt;New worktrees from a project already using worktrees&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;ve already embraced this approach, then the next time you want to create a worktree, it will just be a matter of switching to the &amp;ldquo;base&amp;rdquo; branch you want to create a worktree from and creating one at whatever location you fancy outside the current directory. For instance, for this article:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add ../articles/git-worktree articles/git-worktree&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This would take care of using the branch name &amp;ldquo;nesting&amp;rdquo; convention (e.g., &lt;code&gt;feat/feature-name&lt;/code&gt;, &lt;code&gt;hotfix/bug-name&lt;/code&gt;, etc.) and create sub-directories as needed too. Assuming a worktree has been merged in another branch and/or is no longer necessary, it can be removed with &lt;code&gt;git worktree remove git-worktree&lt;/code&gt; in this example (where &lt;code&gt;git-worktree&lt;/code&gt; is the name given to the worktree&amp;hellip; a bit confusing here, you&amp;rsquo;re right).&lt;/p&gt;
&lt;h3 id="keeping-worktrees-around"&gt;Keeping worktrees around&lt;/h3&gt;
&lt;p&gt;Although the official Git documentation presents a useful case for a temporary fix where the worktree is almost immediately deleted upon the completion of a task, I have found myself in a situation where I&amp;rsquo;d rather have multiple longstanding worktrees. For example, I regularly need to boot a web application from a different branch, either because the currently checked out branch does not have the necessary requirements installed or because I want to leave a web application running in the background without affecting my current work. In my case, I use it to process some data when it arrives and come back to the work I was doing earlier, doing practically the same thing shown in the Git docs but keeping the worktree around for future use.&lt;/p&gt;
&lt;p&gt;On a large project, I might have a couple of worktrees, but still I would keep branches around without them being part of worktrees when these are meant to be short-lived feature branches that share the same dependencies as other branches as long as they won&amp;rsquo;t need to be checked out at the same time. Another time I might have separate branches like this could be to avoid having to duplicate gigantic &lt;code&gt;node_modules&lt;/code&gt; or &lt;code&gt;.venv&lt;/code&gt; directories when not needed, because working with different worktrees is pretty much like cloning an entire repository in a separate folder and having to reinstall requirements. There is also a nice Git feature where worktrees are prefixed with a plus sign when issuing &lt;code&gt;git branch&lt;/code&gt;, so that makes them distinctly different from regular branches:&lt;/p&gt;
&lt;div class="code-block" data-prompted&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;git branch
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+ articles/react-native-app &lt;span class="c1"&gt;# worktree&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; gh-pages &lt;span class="c1"&gt;# other regular branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+ git-worktree &lt;span class="c1"&gt;# worktree&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;* main &lt;span class="c1"&gt;# currently checked out&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I have briefly touched upon a downside of worktrees, which is that it&amp;rsquo;s almost like cloning over and over a repository, which can take up a lot of disk space with large projects. Nevertheless, this is a tradeoff I am entirely willing to make given the advantages Git worktrees bring with them! If you&amp;rsquo;re not already using them, maybe this little post might have inspired you to give them a go!&lt;/p&gt;
&lt;h2 id="resources-and-references"&gt;Resources and references&lt;/h2&gt;
&lt;h3 id="external-links"&gt;External links&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/"&gt;Git 2.5, including multiple worktrees and triangular workflows - github.blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-stash"&gt;&lt;code&gt;git-stash&lt;/code&gt; - git-scm.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-worktree"&gt;&lt;code&gt;git-worktree&lt;/code&gt; - git-scm.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-worktree#_examples"&gt;&lt;code&gt;git-worktree&lt;/code&gt; / Examples - git-scm.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---bare"&gt;&lt;code&gt;git-clone&lt;/code&gt; on &lt;code&gt;--bare&lt;/code&gt; - git-scm.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=2uEqYw-N8uE"&gt;Git&amp;rsquo;s Best And Most Unknown Feature - ThePrimeagen, YouTube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=4_p1OdLeDLE"&gt;Git Worktree Tutorial - Redhwan Nacef, YouTube&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="from-this-website"&gt;From this website&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.sglavoie.com/posts/git-the-gist-of-it-common-commands-for-a-working-workflow/"&gt;Git the gist of it: common commands for a working workflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sglavoie.com/posts/managing-dotfiles-with-git-bare-repository/"&gt;Managing dotfiles with a Git bare repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sglavoie.com/posts/setting-up-ssh-and-git-for-multiple-accounts/"&gt;Setting up SSH and Git for Multiple Accounts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Mon, 02 Jan 2023 09:52:00 -0600</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/git-worktrees-for-a-better-parallel-workflow/</guid><category>tips-and-tricks</category><category>git</category><category>terminal</category></item><item><title>A retrospective on creating an impractical little tool just for fun</title><link>https://www.sglavoie.com/posts/a-retrospective-on-creating-an-impractical-little-tool-just-for-fun/</link><description>Sometimes, spending hundreds of hours on something that could easily have been achieved in a fraction of the time makes for a rewarding journey! While it’s important to be selective about where our attention goes in the first place, it matters equally to realize when the end of the rope is in sight so we can jump off the boat with good enough timing…</description><content:encoded>&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Building little projects to scratch one&amp;rsquo;s own needs is a great way to practice the craft of software development, or at least this is some consistent advice I have picked up from legendary figures in the field. Whether it be formulated by the famous &lt;a href="https://twitter.com/ID_AA_Carmack"&gt;John Carmack&lt;/a&gt; (&lt;a href="https://www.youtube.com/watch?v=I845O57ZSy4"&gt;an excellent interview with Lex Fridman was recently released&lt;/a&gt;) or a rising star like &lt;a href="https://twitter.com/ThePrimeagen"&gt;The Primeagen&lt;/a&gt; (he has a fantastic &lt;a href="https://www.youtube.com/c/ThePrimeagen"&gt;YouTube channel&lt;/a&gt; too), the core of the message remains the same: &lt;em&gt;deliberate practice is required to make progress&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;While I was in the middle of completing a BSc in computer science, I decided that a simple and reliable spreadsheet was not enough to keep track of my grades and decided to tackle the creation of a TUI &amp;ndash; terminal user interface &amp;ndash; to come to my rescue by providing, hopefully, correct answers. And so &lt;a href="https://github.com/sglavoie/uol-grades-calculator"&gt;uol-grades-calculator&lt;/a&gt; (&lt;code&gt;ugc&lt;/code&gt; for short) was born out of the desire to learn more about &lt;a href="https://en.wikipedia.org/wiki/Test-driven_development"&gt;TDD&lt;/a&gt; in a practical way, the &lt;a href="https://hypothesis.readthedocs.io/en/latest/"&gt;Hypothesis&lt;/a&gt; testing library written in Python, &lt;a href="https://www.sphinx-doc.org/"&gt;Sphinx&lt;/a&gt; and &lt;a href="https://readthedocs.org/"&gt;Read The Docs&lt;/a&gt; for publishing nice-looking documentation in reStructuredText format (the Markdown format being another option). Even though the goal of this tool has never been to gather a growing user base, it was a good learning experience because alternatives to it spawn up over time and, arguably, some of them were much friendlier and simpler to use (who would have thought that&amp;hellip;).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="setting-up-the-stage"&gt;Setting up the stage&lt;/h2&gt;
&lt;p&gt;To put things in perspective, this tool was never really under active development, but it did receive updates from August 2020 until September 2022, so the project slowly took shape in a time frame of a little over 2 years as can be shown in the following repurposed Gantt chart, where the length associated with each Git commit corresponds to the amount of time elapsed from one commit to the next.&lt;/p&gt;
&lt;div style="max-width:100%; height: 60vh; overflow:auto;"&gt;
&lt;img src="https://www.sglavoie.com/images/posts/0030_a_retrospective_on_creating_an_impractical_little_tool_just_for_fun/diagram.svg" alt="ugc's development timeline" class="max-size-img-post" style="min-width: 1800px" /&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://www.sglavoie.com/files/posts/0030_a_retrospective_on_creating_an_impractical_little_tool_just_for_fun/diagram.txt"&gt;Mermaid source of this diagram&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="vim-magic-behind-the-scenes"&gt;Vim magic behind the scenes&lt;/h2&gt;
&lt;p&gt;While the chart itself is neither the prettiest nor the most interesting, the &lt;em&gt;how&lt;/em&gt; of it has a possibly more entertaining story attached to it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The list of commits was extracted with the command &lt;code&gt;git log --pretty=format:'%s : %cs' &amp;gt; commits.txt&lt;/code&gt; to get a one-liner for each commit with the description followed by the date, piped/stored in the text file &lt;code&gt;commits.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Some Vim regex magic was done to substitute the first &amp;lsquo;&lt;code&gt;:&lt;/code&gt;&amp;rsquo; character on each line so that &lt;a href="https://mermaid-js.github.io/"&gt;Mermaid&lt;/a&gt; wouldn&amp;rsquo;t struggle to render the graph: &amp;lsquo;&lt;code&gt;%s/^\(\w\+\): /\1 - /&lt;/code&gt;&amp;rsquo;. This pattern was actually predictable because the &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/"&gt;Conventional Commits specification&lt;/a&gt; was followed religiously right from the beginning.&lt;/li&gt;
&lt;li&gt;The previous command left some &amp;lsquo;&lt;code&gt;:&lt;/code&gt;&amp;rsquo; characters in there that weren&amp;rsquo;t needed, so they were removed except where a digit was immediately following such that the last part of the line including the date would be left in place (here, &lt;code&gt;\v&lt;/code&gt; is the &amp;ldquo;very magic&amp;rdquo; flag used so that we don&amp;rsquo;t need backslashes around the captured group, i.e. the digit, which looks a bit more readable): &amp;lsquo;&lt;code&gt;%s/\v: (\d)@!/ -&lt;/code&gt;&amp;rsquo;.&lt;/li&gt;
&lt;li&gt;To show the elapsed time between two commits, a Vim macro was concocted so that the date on the current line would be copied and pasted on the following line in a specific way, which works because the Git history appears in chronological order by default: &amp;lsquo;&lt;code&gt;kf:wv9ly0jA,^[pj0&lt;/code&gt;&amp;rsquo;. It&amp;rsquo;s Ugly but it gets the job done ;).
&lt;ul&gt;
&lt;li&gt;In essence, it goes up by one line (&lt;code&gt;k&lt;/code&gt;), searches for the &lt;code&gt;:&lt;/code&gt; character with &lt;code&gt;f&lt;/code&gt; (which precedes the date), moves ahead by one &lt;code&gt;w&lt;/code&gt;ord, highlights the date which is in the format &lt;code&gt;YYYY-MM-DD&lt;/code&gt; with &lt;code&gt;v9l&lt;/code&gt;, yanks/copies the date with &lt;code&gt;y&lt;/code&gt;, goes back to the beginning of the line (&lt;code&gt;0&lt;/code&gt;), goes down one line with &lt;code&gt;j&lt;/code&gt;, &lt;code&gt;A&lt;/code&gt;ppends a &lt;code&gt;,&lt;/code&gt; at the end of the line, goes out of insert mode with a &lt;code&gt;Ctrl - c&lt;/code&gt; shortcut (which visually translates to the escape sequence &lt;code&gt;^[&lt;/code&gt;), &lt;code&gt;p&lt;/code&gt;astes the date, goes down one line again with &lt;code&gt;j&lt;/code&gt; to be ready to process the next date and goes to the beginning of the line again with &lt;code&gt;0&lt;/code&gt; so we don&amp;rsquo;t miss a match. Vim macros are definitely quite expressive in a terse way!&lt;/li&gt;
&lt;li&gt;Then, it&amp;rsquo;s just a matter of running the macro for the number of lines below the cursor in the file. We can quickly get a sense of the number of lines in the file with &lt;code&gt;Ctrl - g&lt;/code&gt; and run the macro on the required number of lines with &lt;code&gt;100@a&lt;/code&gt;, replacing &lt;code&gt;100&lt;/code&gt; with the number of lines and &lt;code&gt;a&lt;/code&gt; with the register where the macro was saved (in this case, in the register &lt;code&gt;a&lt;/code&gt; with &lt;code&gt;qa&lt;/code&gt; to start recording the macro there, which is to be stopped by pressing &lt;code&gt;q&lt;/code&gt; once more in normal mode).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Once the macro is run, the output needs to be reversed so that the initial commit can be displayed where the graph starts in the top-left corner, which can be done with the &lt;a href="https://vim.fandom.com/wiki/Reverse_order_of_lines"&gt;clever command&lt;/a&gt; &amp;lsquo;&lt;code&gt;g/^/m0&lt;/code&gt;&amp;rsquo;: it runs on every line of the file, successively putting each line at the very top of the file (line &lt;code&gt;0&lt;/code&gt;) to have the effect of reversing the whole document!&lt;/li&gt;
&lt;li&gt;Finally, the graph is generated literally by copying and pasting that list of commits into a Mermaid textual chart, which shall be the topic of another post!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole process is done in a few minutes at most, making it a nice solution to deal with manageable outputs under a few thousand lines. Otherwise, it might be more convenient &amp;ndash; albeit a bit more time consuming too &amp;ndash; to write a script to do the work since Vim macros, when run at a relatively large scale, can be slow and error-prone.&lt;/p&gt;
&lt;h2 id="what-went-well"&gt;What went well&lt;/h2&gt;
&lt;p&gt;With all that fluffy preamble, we might hope that something went well in the end. Well, there were a couple of noteworthy things indeed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Even though I worked on this project alone, I created a bunch of &lt;a href="https://github.com/sglavoie/uol-grades-calculator/issues?q=is%3Aissue&amp;#43;is%3Aclosed"&gt;GitHub issues&lt;/a&gt; that I assigned to myself, each with its own set of tasks, acceptance criteria, a short analysis section as required, linked pull requests and referenced commits. It is more work and arguably could be done more simply in a notes application, but &lt;em&gt;it feels good to be publicly accountable for one&amp;rsquo;s work&lt;/em&gt; and that also encouraged (very few) people to submit their own issues. Additionally, managing tasks on a platform like GitHub makes it easy to review the state of a project and to manage a backlog of tasks, which was a nice plus when the project was linked to what was meant to be its successor, &lt;a href="https://github.com/sglavoie/uol-grades-calculator-server"&gt;uol-grades-calculator-server&lt;/a&gt;, a backend using &lt;code&gt;ugc&lt;/code&gt; as its API which was to be hooked to a React frontend to display the data (more on that in the list of things that went poorly).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The TDD approach was embraced&lt;/em&gt; right from the start and I consider that being one of the highlights of the project in hindsight. With over 250 tests (some of which are randomly generated to do property-based testing with Hypothesis), I felt confident it was giving somewhat sensible answers.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Best practices were adopted&lt;/em&gt;: Pylint, the king of complainers amongst the Python linters, was very verbose but set up to be slightly more quiet after a while. A &lt;code&gt;pyproject.toml&lt;/code&gt; file was included, a clean &lt;code&gt;pytest&lt;/code&gt; config file was used to automate the input of some parameters when running the test suite, the project was developed with the intention of being &lt;a href="https://pypi.org/project/uol-grades-calculator/"&gt;deployed as a package&lt;/a&gt;, only popular and stable libraries were used and just as importantly, only portable formats like JSON made it to the final version of the project.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Sub-modules were created&lt;/em&gt; to make everything a bit more&amp;hellip; modular.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;A GitHub workflow was added to automatically deploy new releases&lt;/em&gt; to &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Documentation was generated and hosted on Read The Docs&lt;/em&gt;, with clean outputs and images and an entire section dedicated to developers (i.e., myself :)) so that reproducibility wouldn&amp;rsquo;t become an issue for any step taken along the way.&lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://pre-commit.com/"&gt;pre-commit&lt;/a&gt; helped with catching some annoyingly formatted code before reaching production by taking advantage of &lt;em&gt;Git pre-commit hooks&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With what seems like a glowing review so far, needless to say, a whole lot went wrong too.&lt;/p&gt;
&lt;figure&gt;
 &lt;a href="https://www.sglavoie.com/images/posts/0030_a_retrospective_on_creating_an_impractical_little_tool_just_for_fun/ugc_summarize.png"&gt;&lt;img src="https://www.sglavoie.com/images/posts/0030_a_retrospective_on_creating_an_impractical_little_tool_just_for_fun/ugc_summarize.png" alt="ugc_summarize" class="max-size-img-post"&gt;&lt;/a&gt;
 &lt;figcaption&gt;Illustrating the &lt;code&gt;summarize&lt;/code&gt; command.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr&gt;
&lt;h2 id="what-could-have-been-done-better"&gt;What could have been done better&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Independently of the expectations with regards to the user base, it would have been beneficial to &lt;em&gt;do more research and planning&lt;/em&gt; initially before writing a single line of code. After working on other projects, I&amp;rsquo;ve found that what works well for me is to follow a specific set of steps, broadly speaking:
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Gather a list of requirements&lt;/em&gt; as precisely as possible to validate assumptions and define the correct scope for the work to be done (&lt;a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it"&gt;YAGNI&lt;/a&gt;!);&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Produce some early flowcharts&lt;/em&gt; to make sense of how the application might be structured and iterate on this until a clear winner emerges;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Produce low-fidelity wireframes&lt;/em&gt; (even for a TUI, that would be informative before committing to doing the wrong thing!) &amp;ndash; even if this is just quickly sketched on paper;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Create high-fidely mockups&lt;/em&gt; (or at least reasonable-looking ones for a small project like this one) once things have been thought through carefully;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Design a general plan of work&lt;/em&gt; before writing any code to have a better idea of how the different parts of the system should communicate together;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Research existing tools to be leveraged&lt;/em&gt; so as to not re-invent the wheel all the time and see how they fit in together;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Go with TDD&lt;/em&gt; and stick to it piously for anything of relative importance that should be tested;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Go at it with atomic commits&lt;/em&gt; so that rewinding errors on the path will be a breeze &amp;ndash; at least that ought to help significantly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Related to the first point, issues could have been more substantial instead of having to often deal with useless refactoring due to not thinking things through fully.&lt;/li&gt;
&lt;li&gt;With more mental processing done upfront, it would have been clear which features should have been part of the tool earlier and which ones could have waited longer. Glancing at the Gantt chart above, one can see that many commits happened at the beginning just to get the structure right, the project was not documented until much later and basic functionality such as being able to plot results (which was really a core feature to be expected) was not integrated until past the mid-life of the project.&lt;/li&gt;
&lt;li&gt;On top of the above, it was clear that using YAML in the presence of the other technologies used for a backend and frontend to the CLI made things more complicated and it wasn&amp;rsquo;t until one of the latest versions that JSON was used instead.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The user experience could have been improved drastically at the beginning by relying on well-known libraries&lt;/em&gt; such as &lt;a href="https://github.com/Textualize/rich"&gt;Rich&lt;/a&gt; to make the UI much more appealing and even a bit more interactive.&lt;/li&gt;
&lt;li&gt;Some features were presented in a way that did not make much sense. For instance, the &lt;code&gt;Dockerfile&lt;/code&gt; does allow one to use the tool from a Docker container, but not all functionality works out of the box that way (e.g., saving a plot to the filesystem or loading a configuration file).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;It wasn&amp;rsquo;t productive at all to start scattering the few resources put into this project&lt;/em&gt; by creating a sister repository &lt;a href="https://github.com/sglavoie/uol-grades-calculator-server"&gt;uol-grades-calculator-server&lt;/a&gt;. The CLI should have been more feature-rich before considering such an endeavor and it should have been made in a way that&amp;rsquo;s easier for a backend to consume.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
 &lt;a href="https://www.sglavoie.com/images/posts/0030_a_retrospective_on_creating_an_impractical_little_tool_just_for_fun/plot_output.png"&gt;&lt;img src="https://www.sglavoie.com/images/posts/0030_a_retrospective_on_creating_an_impractical_little_tool_just_for_fun/plot_output.png" alt="plot_output" class="max-size-img-post"&gt;&lt;/a&gt;
 &lt;figcaption&gt;Showing the image generated by running the &lt;code&gt;plot modules&lt;/code&gt; (sub-)command.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr&gt;
&lt;h2 id="key-lessons-learnt-and-where-to-take-it-from-here"&gt;Key lessons learnt and where to take it from here&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Studying better the available libraries&lt;/em&gt; would have avoided some headaches. For instance, the display of what should have been tabular data started out as plain JSON (printing a &lt;code&gt;dict&lt;/code&gt; object), then &lt;code&gt;beautifultable&lt;/code&gt; was used to create some colored output, then &lt;code&gt;pandas&lt;/code&gt; replaced the previous library because some limitations were found with it &amp;ndash; reverting to a plain table output in the process &amp;ndash; and finally &lt;code&gt;rich&lt;/code&gt; was used to create a more decent-looking output.&lt;/li&gt;
&lt;li&gt;Likewise, &lt;em&gt;spending more time in the planning department&lt;/em&gt; before getting started would probably have resulted in a tool that&amp;rsquo;s more straightforward to use, focused on the most needed features and possessing a broader feature set by not having invested extra resources on building a backend and a frontend separately. It should have been either a more powerful TUI limited to be used within a terminal or a web application, but not both in the way it was designed.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Work on small, isolated parts of the system&lt;/em&gt;. Some issues were split into large chunks of work (&lt;a href="https://github.com/sglavoie/uol-grades-calculator/issues/29"&gt;this one for instance&lt;/a&gt;, implementing at once the &lt;code&gt;plot&lt;/code&gt; command and its sub-commands). This wasn&amp;rsquo;t a problem per se given that I worked alone, but it would be harder for someone else to follow and to create a reasonably-sized pull request from such an issue. This feature could have been broken down into different sets of related flags.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
 &lt;a href="https://www.sglavoie.com/images/posts/0030_a_retrospective_on_creating_an_impractical_little_tool_just_for_fun/ugc-server.png"&gt;&lt;img src="https://www.sglavoie.com/images/posts/0030_a_retrospective_on_creating_an_impractical_little_tool_just_for_fun/ugc-server.png" alt="ugc-server" class="max-size-img-post"&gt;&lt;/a&gt;
 &lt;figcaption&gt;A simple component diagram demonstrating how the CLI was to be used in a greater context.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;blockquote&gt;
 &lt;p&gt;&amp;ldquo;Regardless of what we discover, we understand and truly believe that everyone did the best job they could, given what they knew at the time, their skills and abilities, the resources available, and the situation at hand.&amp;rdquo;&lt;/p&gt;

&lt;/blockquote&gt;&lt;div class="quote-author"&gt;&lt;a href="https://retrospectivewiki.org/index.php?title=The_Prime_Directive"&gt;--Norm Kerth, Project Retrospectives: A Handbook for Team Review&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;All in all, this has been a fun experiment to bring to fruition. It would have been much quicker to use a spreadsheet given what the final result ended up being, but the experience obtained was worth every drop of developer sweat! Using Python felt appropriate as iterations of the tool could be produced fast and the CLI was responsive enough to be useful and pleasant to interact with thanks to its intuitive commands. In the near future, using a library like &lt;a href="https://github.com/Textualize/textual"&gt;Textual&lt;/a&gt; could be more rewarding as one could expect a much nicer visual experience in the terminal at a fraction of the current implementation efforts.&lt;/p&gt;
&lt;h2 id="resources-and-references"&gt;Resources and references&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.conventionalcommits.org/en/v1.0.0/"&gt;Conventional Commits specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leaddev.com/communication-relationships/how-run-great-retrospective"&gt;How to run a great retrospective - LeadDev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/ID_AA_Carmack"&gt;John Carmack - Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=I845O57ZSy4"&gt;John Carmack: Doom, Quake, VR, AGI, Programming, Video Games, and Rockets | Lex Fridman Podcast - YouTube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mermaid-js.github.io/"&gt;Mermaid - Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/"&gt;PyPI - The Python Package Index&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://readthedocs.org/"&gt;Read The Docs - Create, host, and browse documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.atlassian.com/team-playbook/plays/retrospective"&gt;Retrospective - Atlassian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://retrospectivewiki.org/index.php?title=Retrospective_Plans"&gt;Retrospective Plans - Agile Retrospective Resource Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vim.fandom.com/wiki/Reverse_order_of_lines"&gt;Reverse order of lines - Vim Tips Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sphinx-doc.org/"&gt;Sphinx - Python Documentation Generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Test-driven_development"&gt;Test-driven development - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Textualize/textual"&gt;Textual - GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://retrospectivewiki.org/index.php?title=The_Prime_Directive"&gt;The Prime Directive - Agile Retrospective Resource Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/ThePrimeagen"&gt;The Primeagen - Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/c/ThePrimeagen"&gt;The Primeagen - YouTube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it"&gt;You aren&amp;rsquo;t gonna need it - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Sun, 18 Sep 2022 20:06:00 -0500</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/a-retrospective-on-creating-an-impractical-little-tool-just-for-fun/</guid><category>learnings</category><category>lessons</category><category>open-source</category><category>retrospective</category><category>terminal</category></item><item><title>Git the gist of it: common commands for a working workflow</title><link>https://www.sglavoie.com/posts/git-the-gist-of-it-common-commands-for-a-working-workflow/</link><description>Git is such a fascinating piece of software. It is not the only distributed version control system out there, but it is certainly ubiquitous in that category and has become a tool that must be mastered by any software developer in the modern era.</description><content:encoded>&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Git, the friend you wish you had met earlier, when organizing files like &lt;code&gt;report 1.pdf&lt;/code&gt;, &lt;code&gt;report 2.pdf&lt;/code&gt; and &lt;code&gt;report final 1.pdf&lt;/code&gt; still made sense somehow. It&amp;rsquo;s undoubtedly complicated to wrap one&amp;rsquo;s head around it at first, although there exist some GUIs to make the journey smoother – the best examples that come to mind would include &lt;a href="https://www.gitkraken.com/"&gt;GitKraken&lt;/a&gt; and &lt;a href="https://www.git-tower.com/"&gt;Tower&lt;/a&gt; at the top of the list. I have been advised to learn to use Git from the terminal and I would, without flinching, recommend anyone to do the same to really grok this tool and get an intuitive feel for what it does.&lt;/p&gt;
&lt;p&gt;Beyond the extraordinary technical prowess that it is, Git can be useful to a broad audience. It&amp;rsquo;s not good at dealing with large binary files (and &lt;a href="https://git-lfs.github.com/"&gt;Git LFS&lt;/a&gt; is not exactly easy to use just yet to cover that case), but for anything involving text that a human can read, it shines – may it be a programming language, some reports written with &lt;a href="https://www.latex-project.org/"&gt;LaTeX&lt;/a&gt;, some diagrams produced with &lt;a href="http://mermaid-js.github.io/mermaid/#/"&gt;Mermaid&lt;/a&gt; or &lt;a href="https://plantuml.com/"&gt;PlantUML&lt;/a&gt;, personal notes taken in plain text or in Markdown or the fact that synchronizing changes made to a project is a breeze and allows ones to work from multiple locations and machines.&lt;/p&gt;
&lt;p&gt;With platforms like &lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt; and &lt;a href="https://about.gitlab.com/"&gt;GitLab&lt;/a&gt;, it is easy to keep a &amp;ldquo;backup&amp;rdquo; of projects, which can be either public or private. And despite Git not being awesome with very large files, any smaller binary files like images and PDF files can easily be added to a Git repository, allowing users to keep different versions of these files very easily without using a cloud service such as Dropbox or Google Drive. So even if the original input is not written in a text-friendly manner, the output can still be saved under multiple versions with great details, backed up to different websites and fetched just as conveniently from anywhere else (assuming access has been set up to continue working on a project, or at least one can log in to the website and download files without issue).&lt;/p&gt;
&lt;p&gt;Sure, it is not for everyone, but the use cases just described can go a long way. I have used it to practice solving programming challenges, to document all my project at the university, to keep track of preferences and configuration files for my different systems (Linux and macOS) which I&amp;rsquo;ve described in &lt;a href="https://www.sglavoie.com/posts/managing-dotfiles-with-git-bare-repository/"&gt;Managing dotfiles with a Git bare repository&lt;/a&gt;, to work on pet programming projects and coding experiments that I keep private, to store code snippets into a growing library for reference purposes, to take notes in the form of &amp;ldquo;tutorials&amp;rdquo; or &amp;ldquo;wiki&amp;rdquo; (platforms like GitHub and GitLab have a nice wiki feature that renders prose nicely!) for different tasks I need to do infrequently and of course to build this very website in the open (the source code is &lt;a href="https://github.com/sglavoie/sglavoie.github.io-source"&gt;available on GitHub&lt;/a&gt;)! And that is just describing some of my personal use cases without touching on the fact that collaboration within a team is a huge reason to use it!&lt;/p&gt;
&lt;p&gt;So with that said, let&amp;rsquo;s explore some of its functionality, starting with a few tips and tricks, then delving deeper into a real-world workflow and topping it off with more useful commands and configuration settings.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="global-and-local-gitignore"&gt;Global and local &lt;code&gt;.gitignore&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;There are files we never care about adding to Git repositories, such as a thumbnail cache file &lt;code&gt;Thumbs.db&lt;/code&gt; on Windows or a &lt;code&gt;.DS_Store&lt;/code&gt; file storing custom attributes for folders on macOS. Instead of ignoring these kinds of entries in a per-repository &lt;code&gt;.gitignore&lt;/code&gt; file which might not contain everything we would like to exclude, it can be simpler and more efficient to use a global &lt;code&gt;.gitignore&lt;/code&gt;, which serves the same purpose but for &lt;em&gt;any&lt;/em&gt; Git repository regardless of the presence of a &lt;code&gt;.gitignore&lt;/code&gt; in that specific repository! In other words, this serves as a permanent list of things to ignore so that there is no need to remember about them later.&lt;/p&gt;
&lt;p&gt;The setup is straightforward: create a file &lt;code&gt;~/.gitignore&lt;/code&gt;, fill it as usual with patterns you are sure to want to exclude globally and set a configuration option to use that file – for instance by adding the following to &lt;code&gt;.gitconfig&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;git&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;pre tabindex="0"&gt;&lt;code class="language-git" data-lang="git"&gt;[core]
 excludesfile = ~/.gitignore&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://www.toptal.com/"&gt;Toptal&lt;/a&gt; provides &lt;a href="https://docs.gitignore.io/install/command-line"&gt;a nice tool on the command-line&lt;/a&gt; that can be used to easily exclude patterns. With it, typing &lt;code&gt;gi python &amp;gt;&amp;gt; .gitignore&lt;/code&gt; would append a bunch of common patterns to a &lt;code&gt;.gitignore&lt;/code&gt; file – in this case, for Python.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="meaningful-commit-messages-with-gitmessage"&gt;Meaningful commit messages with &lt;code&gt;.gitmessage&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Besides using the excellent &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/"&gt;Conventional Commits&lt;/a&gt; specification to write messages in a format that makes sense to other fellow human beings, using a template for Git commits can help with remembering &lt;em&gt;why&lt;/em&gt; commits are made and how they should be structured. There is a configuration setting, &lt;code&gt;commit.template&lt;/code&gt;, that can be used to set the default text being displayed when a text editor opens after typing &lt;code&gt;git commit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The following is the current template I use, which can be saved in a file like &lt;code&gt;~/.gitmessage&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block code-block--wrap"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# [Add/Fix/Remove/Update/Refactor/Document] [summary]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Why is it necessary? (Bug fix, feature, improvements?)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#-
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# How does the change address the issue?
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#-
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# What side effects does this change have?
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#-
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Include a link to the ticket, if any.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then, this can be added to the config file under the &lt;code&gt;[commit]&lt;/code&gt; section like so:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;git&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;pre tabindex="0"&gt;&lt;code class="language-git" data-lang="git"&gt;[commit]
 template = ~/.gitmessage&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;All lines starting with a hash symbol (&lt;code&gt;#&lt;/code&gt;) will be ignored, so there is no need to manually delete all of this verbose content when saving the commit message. Sweet!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="my-usual-git-workflow"&gt;My usual Git workflow&lt;/h2&gt;
&lt;p&gt;This section depends very much on team standards, if applicable. But in general terms and considering mostly a solo interaction with Git, I like to adhere to the following processes, without taking into account how one would plan, produce diagrams, brainstorm, etc. That shall be the topic of an upcoming post!&lt;/p&gt;
&lt;h2 id="starting-a-new-project"&gt;Starting a new project&lt;/h2&gt;
&lt;p&gt;There are different ways to proceed, although I like the simplicity of just creating a new empty repository on GitHub and cloning it on my machine using the SSH protocol, e.g. &lt;code&gt;git clone git@github.com:some/project.git&lt;/code&gt;. This has the benefit of automatically setting the &lt;code&gt;main&lt;/code&gt; branch and the remote URL, using &lt;code&gt;origin&lt;/code&gt; as the default remote. Cloning an empty repository has the same effect as starting with &lt;code&gt;git init&lt;/code&gt;, so this also becomes unnecessary.&lt;/p&gt;
&lt;p&gt;If I have been experimenting locally first, I would do a &lt;code&gt;git init&lt;/code&gt;, commit any changes and then push the new repository on GitHub, either using the great Git integration provided by VS Code or again creating a new repository on GitHub to set the remote URL in order to be able to push local changes, i.e. by doing &lt;code&gt;git remote add origin git@github.com:some/project.git&lt;/code&gt; and then pushing to that new URL. To make sure that my default branch is always &lt;code&gt;main&lt;/code&gt; (to match the new default on GitHub) and not something like &lt;code&gt;dev&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt;, I have a Git template lying around that ensures that the first branch created when doing &lt;code&gt;git init&lt;/code&gt; will be &lt;code&gt;main&lt;/code&gt;. It is as simple as creating the file &lt;code&gt;~/.git-templates/HEAD&lt;/code&gt; with this content:&lt;/p&gt;
&lt;div class="code-block code-block--wrap"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ref: refs/heads/main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then, in &lt;code&gt;~/.gitconfig&lt;/code&gt;, the following section is used to read the templates:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;git&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;pre tabindex="0"&gt;&lt;code class="language-git" data-lang="git"&gt;[init]
 defaultBranch = main
 templateDir = ~/.git-templates&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I have this template since before &lt;code&gt;init.defaultBranch&lt;/code&gt; was introduced in Git 2.28.0 back in July 2020: now just having the option &lt;code&gt;defaultBranch&lt;/code&gt; will do the trick. Still, it&amp;rsquo;s good to have a &lt;code&gt;templateDir&lt;/code&gt; configured for other purposes! Although admittedly, I don&amp;rsquo;t use them at all and would rather create a custom command to more easily script what I need for each repository: more on this in the section &lt;a href="#custom-git-sub-commands"&gt;Custom Git sub-commands&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="committing-to-an-existing-project"&gt;Committing to an existing project&lt;/h2&gt;
&lt;p&gt;Now that there is some folder set up for Git to track files, it&amp;rsquo;s time to make changes! I&amp;rsquo;m a big fan of committing &amp;ldquo;small and often&amp;rdquo; because Git is super flexible and updating the history is usually a simple process – unless you have pushed to a remote repository used by other people, in which case being a &amp;ldquo;force push&amp;rdquo; type of person won&amp;rsquo;t make you many friends. May the &lt;code&gt;--force&lt;/code&gt; be with you when you push to a private repository where you&amp;rsquo;re the only contributor, otherwise, it&amp;rsquo;s good practice to think twice before sending your final changes away as you should not mess up with public history (and depending on how your access is set up within a team, you might not even be able to use the &lt;code&gt;--force&lt;/code&gt; flag when pushing on certain branches!).&lt;/p&gt;
&lt;p&gt;So what&amp;rsquo;s the alternative to constantly rewriting history in a harmful way? Well, assuming you have a choice when working alone for instance, there are two main contenders: &lt;em&gt;merge&lt;/em&gt; vs &lt;em&gt;rebase&lt;/em&gt;. I think neither option is better than the other when used right, but it&amp;rsquo;s good to know that &lt;em&gt;rebase&lt;/em&gt; might create possible headaches because it modifies the Git history while &lt;em&gt;merge&lt;/em&gt; might create undesirable noise when used profusely by adding &amp;ldquo;merge commits&amp;rdquo;. I use both techniques, but for different purposes.&lt;/p&gt;
&lt;h3 id="being-committed"&gt;Being committed&lt;/h3&gt;
&lt;p&gt;The basic workflow might look like this, with a couple of different options thrown in to cover more scenarios:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Check the current state of the repo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Inspect the changes to commit (before staging)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git diff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Stage a file to be committed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add SOME_FILE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Stage all new/modified files, (i.e., --all)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add -A
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Stage all modified files (not any new ones, i.e., --update)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add -u
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Get fancy with adding patches only (part of files)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add -p
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Undo changes done to a tracked file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git restore -- FILE_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Inspect staged changes to commit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git diff --staged
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Unstage files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git reset HEAD --
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# or just &amp;#39;git commit&amp;#39; to open a text editor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git commit -m &lt;span class="s2"&gt;&amp;#34;SOME_MESSAGE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# See the changes done in the latest commit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git show
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# or a specific commit hash from earlier&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git show COMMIT_HASH &lt;span class="c1"&gt;# or ref like HEAD^&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Forgot to add something?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add SOME_OTHER_FILE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git commit --amend --no-edit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Want to rephrase the last commit?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git commit --amend
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Tag the latest commit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git tag TAG_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Tag earlier commit with annotation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git tag -a TAG_NAME COMMIT_HASH -m &lt;span class="s2"&gt;&amp;#34;MESSAGE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Undo a commit, keeping changes in the work tree&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git reset HEAD^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Undo a commit and DO NOT keep changes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git reset --hard HEAD^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Oops, get back a commit that was &amp;#34;lost&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git reflog &lt;span class="c1"&gt;# find the relevant commit hash&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git cherry-pick COMMIT_HASH &lt;span class="c1"&gt;# or git checkout COMMIT_HASH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Explore the repo at an earlier commit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git checkout COMMIT_HASH
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Committed some changes while checking out an old commit?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Save the changes to a new branch, then potentially merge them back&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git switch -c NEW_BRANCH_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git switch OTHER_BRANCH
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git merge NEW_BRANCH_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Undo a merge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git reflog &lt;span class="c1"&gt;# find the commit hash before the merge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git reset --hard COMMIT_HASH
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Or undo to a known point from a remote branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git reset --hard origin/main
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Stop tracking a file/directory (remove) but keep it on disk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git rm --cached FILE_OR_DIRECTORY
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Remove stuff that hasn&amp;#39;t been committed yet (interactive mode is nice)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Don&amp;#39;t forget about -n for a dry run first!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clean -i
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Want to undo a change that&amp;#39;s already pushed?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Add a new commit without rewriting history&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git revert COMMIT_HASH
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# See who did what to which file and when&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git blame FILE_NAME&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Arguably, basic editing doesn&amp;rsquo;t require stashing, but that can be helpful for a quick modification, like temporarily saving changes to quickly switch to another branch or pull some remote changes without having to commit first.&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# git stash --help - &amp;#34;Stash the changes in a dirty working directory away&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Maybe you want to keep changes around without committing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# --include-untracked and --keep-index might be needed!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git stash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Stash changes with a clearer name/purpose&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git stash save &lt;span class="s2"&gt;&amp;#34;SOME MESSAGE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Selectively stash changes (keep specific lines)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git stash save -p
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# List existing stashes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git stash list
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# See the summary of a stash&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git stash show
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# See all changes made in a stash (patch)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git stash show -p
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Apply changes from a stash (TAB completion after `apply` is useful)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git stash apply stash@&lt;span class="se"&gt;\{&lt;/span&gt;SOME_ID&lt;span class="se"&gt;\}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Apply and remove the last stash at the same time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git stash pop
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Remove a single stash&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git stash drop stash@&lt;span class="se"&gt;\{&lt;/span&gt;SOME_ID&lt;span class="se"&gt;\}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Remove all stashes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git stash clear&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="rebase"&gt;&lt;code&gt;rebase&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;When working locally with changes that are not yet part of the &amp;ldquo;permanent&amp;rdquo; Git history, I like to &lt;code&gt;git rebase&lt;/code&gt; very much to clean things up before making a &lt;code&gt;git push&lt;/code&gt;, which I like to view as an irrevocable decision. Because I &lt;em&gt;commit small and often&lt;/em&gt;, it&amp;rsquo;s often the case that I end up with a couple of very simple commits which really belong together and should form a bigger commit. While committing a big chunk of code at once avoids having to do any &lt;code&gt;rebase&lt;/code&gt; in the first place, it&amp;rsquo;s also inconvenient to undo a substantial amount of work when you&amp;rsquo;re experimenting and/or working on an unfamiliar codebase. So in the end, my preference goes towards smaller commits because rebasing is actually fun to do and lets you believe that you came up with brilliant solutions on your first attempt :). That makes for a cleaner Git history for sure, although you will lose some context if you never &lt;code&gt;merge&lt;/code&gt;. Meaning, rebasing creates a &amp;ldquo;linear&amp;rdquo; history while merging from different branches shows a more complete picture of how a repository really took shape over time. My simple rule of thumb on the matter is this: use &lt;code&gt;rebase&lt;/code&gt; to consistently ship meaningful commits (good descriptions, fewer typos, changes chunked logically and precisely, etc.) and use &lt;code&gt;merge&lt;/code&gt; to incorporate somewhat larger pieces of history, like a new feature coming from a &amp;ldquo;feature branch&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Some common commands I use:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# git rebase --help - &amp;#34;Reapply commits on top of another base tip&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Include all commits up to COMMIT_HASH, included&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git rebase -i COMMIT_HASH^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Include most recent commit only&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git rebase -i HEAD^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Include 2 most recent commits&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git rebase -i HEAD^^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Include 3 most recent commits&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git rebase -i HEAD~3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# make BRANCH have the history up to COMMIT_OR_BRANCH, included&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git rebase COMMIT_OR_BRANCH BRANCH
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Used after resolving a conflict&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git rebase --continue
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Reset to where you were before rebasing (move HEAD)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git rebase --abort
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# To update the next actions to be taken on the remaining commits&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git rebase --edit-todo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;There are a bunch of obscure options just like almost any other Git command in existence, but for that there is help available for each command (such as &lt;code&gt;git rebase --help&lt;/code&gt;) from the terminal and the trusty Stack Overflow ;).&lt;/p&gt;
&lt;h4 id="sample-workflow"&gt;Sample workflow&lt;/h4&gt;
&lt;p&gt;In the case of &lt;code&gt;rebase&lt;/code&gt;, I enjoy working in this way:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make small commits, maybe 5 to 10 depending on the complexity of the project – usually as long as a series of commits relates to a single topic whenever possible.&lt;/li&gt;
&lt;li&gt;Have a peak at the recent history with &lt;code&gt;git log&lt;/code&gt; to have a good idea of what just happened.&lt;/li&gt;
&lt;li&gt;Realize that the history can be improved. Find the oldest commit hash (not pushed yet!), take note of it and run &lt;code&gt;git rebase -i COMMIT_HASH^&lt;/code&gt; (with the &amp;ldquo;hat&amp;rdquo; &lt;code&gt;^&lt;/code&gt; at the end to reference the parent of that commit).&lt;/li&gt;
&lt;li&gt;This opens up a text editor with some explanations on how to proceed (for more on this, have a look at this great article on &lt;a href="https://www.atlassian.com/git/tutorials/rewriting-history"&gt;Rewriting history&lt;/a&gt;). From there, I usually use &lt;code&gt;r&lt;/code&gt; to reword, &lt;code&gt;s&lt;/code&gt; to squash, &lt;code&gt;e&lt;/code&gt; to edit and sometimes &lt;code&gt;f&lt;/code&gt; to fixup, besides also re-ordering commits and from time to time deleting one by removing the line entirely.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;r&lt;/code&gt; is pretty harmless as it will just show up a new window where the commit message and description can be modified.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; is more destructive because commits will be &amp;ldquo;merged&amp;rdquo; together! No actual change is lost, but if you squash a ton of commits by accident, it will create huge commits and you might want to undo that with &lt;code&gt;reflog&lt;/code&gt; before it&amp;rsquo;s too late (Atlassian has more to say about this in &lt;a href="https://www.atlassian.com/git/tutorials/rewriting-history/git-reflog"&gt;git reflog&lt;/a&gt;). The &lt;code&gt;s&lt;/code&gt; option is also nice because it keeps all commit messages, so you can edit the final log message as needed without discarding messages.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;e&lt;/code&gt; is used to modify the actual source code being committed, maybe to fix a typo or a minor bug after realizing that the test suite no longer passes (assuming you are not programming blindly without tests :)). This can cause some &amp;ldquo;conflicts&amp;rdquo; if you change some lines which are also part of other commits in the selection you made originally, so one has to be careful with that. This can also be used in conjunction with &lt;code&gt;git reset HEAD^&lt;/code&gt; to &amp;ldquo;cancel&amp;rdquo; the current commit when doing the rebase in order to split it into multiple smaller commits, which can then be &amp;ldquo;&lt;code&gt;git add&lt;/code&gt;ed&amp;rdquo; back, followed by a &lt;code&gt;git rebase --continue&lt;/code&gt; to keep going.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;f&lt;/code&gt; is a quick way of squashing commits together while discarding messages you don&amp;rsquo;t need. So maybe the last commit has a proper description (&amp;ldquo;Implemented something&amp;hellip;&amp;rdquo;) and a few commits before that one were just temporary &amp;ldquo;savepoints&amp;rdquo; with non-sensical commit messages (things like &amp;ldquo;testing&amp;hellip;&amp;rdquo; or &amp;ldquo;new commit&amp;rdquo;) that were done until you reached a final solution that&amp;rsquo;s worth committing. So of course in that case the history would be pretty ugly and hard to understand if all commits were kept intact, so these can be added to the latest change which encompasses a working solution as a whole. Just as well, maybe that last commit didn&amp;rsquo;t have a complete message just yet, so it could also be rewritten with the &lt;code&gt;r&lt;/code&gt; prefix.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When individual commits make the &lt;code&gt;rebase&lt;/code&gt; stop temporarily (such as when a commit is to be edited with &lt;code&gt;e&lt;/code&gt; and waits for changes to be made), then it&amp;rsquo;s time to do &lt;code&gt;git rebase --continue&lt;/code&gt; once the necessary changes are made.&lt;/li&gt;
&lt;li&gt;If something goes wrong along the way, there&amp;rsquo;s always a way back to the place you were right before rebasing with &lt;code&gt;git rebase --abort&lt;/code&gt;. It&amp;rsquo;s worth noting that Git itself will output some useful messages all along so it&amp;rsquo;s not necessary to remember all of this: it quickly becomes intuitive.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, &lt;code&gt;rebase&lt;/code&gt; covers the cases where local changes are done in isolation, but for something more imposing or simply to keep track of what happened in which branch, &lt;code&gt;merge&lt;/code&gt; remains a powerful ally.&lt;/p&gt;
&lt;h3 id="merge"&gt;&lt;code&gt;merge&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;So I don&amp;rsquo;t use &lt;code&gt;merge&lt;/code&gt; as much, especially when working solo, but it is undeniably useful and could replace the &lt;code&gt;rebase&lt;/code&gt; workflow completely (it might also be demanded by your colleagues anyways!). There&amp;rsquo;s nothing like a good &lt;code&gt;rebase&lt;/code&gt; session to keep things tidy, but a flat history line won&amp;rsquo;t look attractive on a huge project with many contributors, so &lt;code&gt;merge&lt;/code&gt; is there to deal with cleanly integrating changes from different places into some &lt;code&gt;main&lt;/code&gt; branch (it could be named differently based on the team workflow of course and there might be more than one &amp;ldquo;main&amp;rdquo; branch too). What makes &lt;code&gt;merge&lt;/code&gt; incredibly cool is the fact that branches in Git are &amp;ldquo;cheap&amp;rdquo;, meaning they can be created very quickly and efficiently, they don&amp;rsquo;t take space at all, you could have dozens of branches or more and you can incorporate changes from one branch to another with a simple &lt;code&gt;git merge&lt;/code&gt;, which is nicer than using &lt;code&gt;git cherry-pick&lt;/code&gt; to get multiple commits from one branch into another (for one or two commits though, &lt;code&gt;cherry-pick&lt;/code&gt; is handy).&lt;/p&gt;
&lt;p&gt;I see &lt;code&gt;rebase&lt;/code&gt; more like a cleaning step while &lt;code&gt;merge&lt;/code&gt; really shines when dealing with multiple commits between different branches. Even on a simple project when working alone, it has its uses! For example, let&amp;rsquo;s say we have a repository full of small programming challenges (maybe from &lt;a href="https://www.hackerrank.com/"&gt;HackerRank&lt;/a&gt; or &lt;a href="https://leetcode.com/"&gt;LeetCode&lt;/a&gt;). We might get stuck on a tricky problem and wouldn&amp;rsquo;t want to commit an unfinished solution. Also, maybe we would get fed up eventually working on a given challenge and would rather try our luck with a different one. Well, we can always leave changes uncommitted (not very safe as these might be lost!), but that can cause confusion and you might inadvertently end up committing files that have nothing to do with the latest challenge – effectively committing at the same time your unfinished challenge with the one you just completed. There&amp;rsquo;s always the possibility to &lt;code&gt;git stash&lt;/code&gt; individual files to keep them somewhere, but I find working with stashes to be a bit more cumbersome than branches and you might literally forget that you have changes stashed. If you do that a few times, you might not even know which stash is what if you didn&amp;rsquo;t give them a good name&amp;hellip;&lt;/p&gt;
&lt;p&gt;Some common commands I use:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# git merge --help - &amp;#34;Join two or more development histories together&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Get commits from BRANCH_NAME into the currently checked one&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git merge BRANCH_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Merge one branch into another&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git merge SOURCE_BRANCH_NAME DESTINATION_BRANCH_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Get out of conflicts...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git merge --abort
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Continue after resolving conflicts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git merge --continue
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Keep the &amp;#34;merge commit&amp;#34; in the history&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git merge --no-ff BRANCH_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Set a custom commit message for a &amp;#34;merge commit&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git merge -m &lt;span class="s2"&gt;&amp;#34;MESSAGE&amp;#34;&lt;/span&gt; BRANCH&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;sub&gt;Tip: The command &lt;code&gt;git merge --help&lt;/code&gt; describes some nice examples about the whole process of conflict resolution.&lt;/sub&gt;&lt;/p&gt;
&lt;h4 id="sample-workflow-kinda"&gt;Sample workflow (kinda)&lt;/h4&gt;
&lt;p&gt;So, instead of risking losing changes like this, creating a new branch with &lt;code&gt;git switch -c BRANCH_NAME&lt;/code&gt; and working from there is much easier to confine changes to a specific matter, which we can see like a &amp;ldquo;feature&amp;rdquo; that can later be added to the main &amp;ldquo;trunk&amp;rdquo;. We can commit unfinished business, switch to a different branch, come back to it later, &lt;code&gt;git reset&lt;/code&gt; any old commit to &amp;ldquo;revert&amp;rdquo; the history and keep working from a clean state. In any case, there&amp;rsquo;s always &lt;code&gt;rebase&lt;/code&gt; to avoid committing useless stuff, so there&amp;rsquo;s really no reason to avoid branches.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;merge&lt;/code&gt; command will be involved when changes from a &amp;ldquo;feature branch&amp;rdquo; (or whatever other purpose you give a branch) are merged into the main branch with &lt;code&gt;git merge BRANCH_NAME&lt;/code&gt;. When there are no &amp;ldquo;merge conflicts&amp;rdquo; (such as when you work on a totally new file that won&amp;rsquo;t clash with prior work), this is a seamless workflow where you can just &lt;code&gt;merge&lt;/code&gt; as if changes were made in the branch you are on in the first place. There are other instances where bigger changes can be merged successfully in the same way, but it&amp;rsquo;s interesting to keep track of how you worked on a project, so in this scenario you can always use &lt;code&gt;git merge --no-ff BRANCH_NAME&lt;/code&gt; (&lt;code&gt;--no-ff&lt;/code&gt; meaning &amp;ldquo;&lt;em&gt;no fast-forward&lt;/em&gt;&amp;rdquo;) to keep a &amp;ldquo;&lt;em&gt;merge commit&lt;/em&gt;&amp;rdquo;, which is strictly speaking not needed (because there&amp;rsquo;s no conflict resolution in the case of a &amp;ldquo;successful&amp;rdquo; merge) but can be a helpful reference point when you look back at your Git history two years from now to understand how you worked on something, piecing things together bit by bit.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also an indisputable advantage conferred by the use of branches, which is: &lt;em&gt;you can have plenty of them&lt;/em&gt;! So while &lt;code&gt;rebase&lt;/code&gt; is nice to use on a given branch, in no way can working on multiple things at once in a single branch and then rebasing all that effort be as straightforward as creating new branches for every new topic that&amp;rsquo;s being started and then merging it back into a single place of truth. Juggling different concerns in a single branch where you have a few things going on (maybe 2-3 partially implemented features) is far from being a cozy environment and rebasing that kind of work where commits are not even related to each other can become a nightmare in a short amount of time.&lt;/p&gt;
&lt;h2 id="managing-branches"&gt;Managing branches&lt;/h2&gt;
&lt;p&gt;So with all that was written earlier, I believe a point was made in favour of using branches. Because branches are such a wonderful feature of Git, here are some more commands I rely on (surprisingly, there aren&amp;rsquo;t that many for &lt;code&gt;git branch&lt;/code&gt;!):&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# git branch --help - &amp;#34;List, create, or delete branches&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# List local branches&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# List only remote branches&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch -r
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# List all branches&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch -a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Delete a branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch -d BRANCH_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Delete a branch forcefully&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch -D BRANCH_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Delete a remote branch (doesn&amp;#39;t matter if the local branch was deleted)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git push origin --delete REMOTE_NAME/BRANCH_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Rename a branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch -m BRANCH_NAME NEW_BRANCH_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Track a specific remote branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch BRANCH_NAME -u UPSTREAM_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# See which branches have been merged or not&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch --merged
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch --no-merged
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Switch to the branch last checked out&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git checkout -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Compare changes between branches&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git diff BRANCH_ONE..BRANCH_TWO
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Compare a file across branches&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git diff BRANCH_ONE:FILE_NAME BRANCH_TWO:FILE_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# See a file from another branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git show BRANCH_NAME:FILE_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Get a file from a different branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git restore --source OTHER_BRANCH_NAME path/to/file.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="aliases"&gt;Aliases&lt;/h2&gt;
&lt;p&gt;While I believe it&amp;rsquo;s good practice to type complete commands to remember them better, I also tend to use Git aliases for two reasons: because some deeply ingrained concepts don&amp;rsquo;t need as much reinforcement and because some obscure operations can be made so much more accessible with a pertinent mnemonic device.&lt;/p&gt;
&lt;p&gt;Some commands like &lt;code&gt;add&lt;/code&gt; just become unforgettable over time, while others – e.g., &lt;code&gt;unstage&lt;/code&gt; – just don&amp;rsquo;t exist yet. Without further ado, here is a list of the aliases I currently rely on when using Git, which comes straight from &lt;code&gt;~/.gitconfig&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For instance, instead of typing &lt;code&gt;git add&lt;/code&gt;, by setting the alias &lt;code&gt;a = add&lt;/code&gt; (see below), it is now possible to type &lt;code&gt;git a&lt;/code&gt; to get the same result.&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;git&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;pre tabindex="0"&gt;&lt;code class="language-git" data-lang="git"&gt;[alias]
 a = add
 br = branch
 c = commit
 ca = commit --amend
 can = commit --amend --no-edit
 ch = checkout
 d = diff
 ds = diff --staged
 po = push origin
 pos = push --set-upstream origin main
 rc = rebase --continue
 ri = rebase -i
 rl = reflog
 sh = show
 s = status --short
 st = status
 sw = switch
 t = tag
 unstage = reset HEAD --

 ; show changes since last commit
 difflast = diff --cached HEAD^

 ; show last commit
 last = l -1 HEAD

 ; undo last commit
 undo = &amp;#34;!f() { git reset --hard $(git rev-parse \
--abbrev-ref HEAD)@{${1-1}}; }; f&amp;#34;

 l = log

 ; short log
 sl = shortlog

 ; log graph
 lg = l --color --graph --pretty=format:&amp;#39;%Cred%h%Creset \
-%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&amp;lt;%an&amp;gt;%Creset&amp;#39; --abbrev-commit

 ; log name
 ln = l --name-only

 ; log decorate all
 logda = l --oneline --decorate --graph --all

 ; log stat
 ls = l --stat

 ; log pretty
 lp = log --pretty=&amp;#39;%C(yellow)%h %C(cyan)%ad %Cblue%aN%C(auto)%d \
%Creset%s&amp;#39; --date=relative --date-order --graph&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;At some point, using Git aliases will become second nature, but until then, it can be useful to have a separate terminal alias to list them all (or maybe you forgot the command an alias is using under the hood!). For this, you can set the following alias in a file like &lt;code&gt;~/.bash_aliases&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;gitaliases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;git config -l | grep alias | sed &amp;#34;s/^alias\.//g&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# For a fancier version displaying aliases in two columns neatly aligned:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;gitaliases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;git config -l | grep alias | sed &amp;#34;s/^alias\.//g&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; | sed &amp;#34;s/=/Ω/&amp;#34; | column -t -s &amp;#34;Ω&amp;#34;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That command will look in the Git configuration, extract all the lines containing &amp;ldquo;alias&amp;rdquo; and remove the &amp;ldquo;&lt;code&gt;alias.&lt;/code&gt;&amp;rdquo; prefix so that you can see all aliases in the form &lt;code&gt;a=add&lt;/code&gt; in the case of &lt;code&gt;git add&lt;/code&gt; being aliased to &lt;code&gt;git a&lt;/code&gt;. If you have a ton of aliases (which is probably a red flag&amp;hellip;), finding a specific one is just a matter of doing &lt;code&gt;gitaliases | grep keyword&lt;/code&gt;, of course replacing &lt;code&gt;keyword&lt;/code&gt; with something else ;).&lt;/p&gt;
&lt;p&gt;&lt;sub&gt;Tip: One can go further to shorten Git commands by shortening &lt;code&gt;git&lt;/code&gt; itself! You can put a line such as &lt;code&gt;alias g='git'&lt;/code&gt; in &lt;code&gt;~/.bash_aliases&lt;/code&gt; for instance. Now, &lt;code&gt;git add&lt;/code&gt; can become &lt;code&gt;g a&lt;/code&gt;. Pretty efficient! For more on Bash aliases, you might like to read &lt;a href="https://www.sglavoie.com/posts/aliases-also-known-as-terminal-users-best-friends/"&gt;Aliases: Also Known as Terminal User’s Best Friends&lt;/a&gt;.&lt;/sub&gt;&lt;/p&gt;
&lt;p&gt;This section is really just meant to give a taste of the possibilities that aliases offer. To learn about actual commands and their usage, there is nothing like the &lt;a href="https://git-scm.com/docs"&gt;official Git docs&lt;/a&gt; (there is a &lt;a href="https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases"&gt;short section on Git aliases&lt;/a&gt; too).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="custom-git-sub-commands"&gt;Custom Git sub-commands&lt;/h2&gt;
&lt;p&gt;There might be less of a need for custom-made commands to be created when so much can be done with Git alone, but there are cases where these custom additions can shine! As a concrete example, I have been updating this website with what I call my &amp;ldquo;&lt;a href="https://www.sglavoie.com/posts/what-it-took-to-propel-a-career-in-tech-in-five-years/"&gt;learning progress&lt;/a&gt;&amp;rdquo; but committing always the same kind of stuff gets boring, so I have created a simple Bash script to automate the process of publishing and committing (the content of the script is of no importance, it is only there to demonstrate that commands other than Git can be called too):&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Act as custom command `git addlearning` and perform all that follows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Assumes that `pelican` command is available&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/dev/sglavoie/sglavoie.github.io-source
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git commit -m &lt;span class="s2"&gt;&amp;#34;Add learning progress&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git push origin main
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pelican
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cp -r &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ~/dev/sglavoie/sglavoie.github.io-source/output/* &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ~/dev/sglavoie/sglavoie.github.io/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/dev/sglavoie/sglavoie.github.io &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git add .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git commit -m &lt;span class="s2"&gt;&amp;#34;Add learning progress&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git push origin main
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/dev/sglavoie/sglavoie.github.io-source&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It is noteworthy to know that any kind of executable programs can be used, such as Ruby, Python and so on, as long as they are made available in the &lt;code&gt;$PATH&lt;/code&gt;. This will be somewhat similar on most UNIX-based system where the &lt;code&gt;PATH&lt;/code&gt; variable needs to be exported (usually in a file like &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt;), for instance:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# $HOME refers to a place like /home/user on Linux or /Users/user on macOS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/dev/git-scripts:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;On Windows, there are &lt;a href="https://superuser.com/a/143121/1032549"&gt;separate instructions to be followed&lt;/a&gt;. With that done, there&amp;rsquo;s also a need to make sure the file is executable, which can be done on the command-line with &lt;code&gt;chmod +x FILENAME_HERE&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="some-lesser-known-yet-incredibly-useful-commands"&gt;Some lesser-known yet incredibly useful commands&lt;/h2&gt;
&lt;h2 id="git-bisect"&gt;&lt;code&gt;git bisect&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;I try to commit working code as often as possible, but sometimes there are just bugs that have flown by and took residence in the codebase a long time ago. In this scenario, there is one tool in the Git tool belt for that very specific use case: &lt;code&gt;git bisect&lt;/code&gt;. Working with the &lt;a href="https://en.wikipedia.org/wiki/Binary_search_algorithm"&gt;binary search algorithm&lt;/a&gt;, &lt;code&gt;git bisect&lt;/code&gt; searches in a range of commits and by identifying &amp;ldquo;good&amp;rdquo; and &amp;ldquo;bad&amp;rdquo; commits, will pinpoint exactly where the issue came from in the minimum number of iterations possible. You&amp;rsquo;ll need sub-commands like &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;bad&lt;/code&gt;, &lt;code&gt;good&lt;/code&gt; and &lt;code&gt;reset&lt;/code&gt; at the very least and for more advanced use cases where automatically running a script for each commit is needed, &lt;code&gt;run&lt;/code&gt; will do the job. See &lt;a href="https://git-scm.com/docs/git-bisect"&gt;the docs on &lt;code&gt;git bisect&lt;/code&gt;&lt;/a&gt; for more.&lt;/p&gt;
&lt;h2 id="git-rerere"&gt;&lt;code&gt;git rerere&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;I almost never use this in practice, but it &lt;em&gt;can&lt;/em&gt; be very useful when working in a large project where similar merge conflicts are likely to occur over time. Git can be &amp;ldquo;taught&amp;rdquo; to automatically solve merge conflicts! There are some &lt;a href="https://git-scm.com/docs/git-rerere"&gt;official docs on this&lt;/a&gt;, but I&amp;rsquo;ve found this &lt;a href="https://stackoverflow.com/a/49501436/8787680"&gt;Stack Overflow answer&lt;/a&gt; to be a more approachable read.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="a-word-on-the-configuration-file"&gt;A word on the configuration file&lt;/h2&gt;
&lt;p&gt;At the very minimum, setting a name and email will be necessary to use Git:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global user.name &lt;span class="s2"&gt;&amp;#34;Your Name&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global user.email &lt;span class="s2"&gt;&amp;#34;email@example.com&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Seeing the current configuration can come in handy, too:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global --list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And in the case of this particular command, &lt;code&gt;git config&lt;/code&gt;, it can&amp;rsquo;t be emphasized enough how useful searching through the help page in the terminal will be: &lt;code&gt;git config --help&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d be remiss if I didn&amp;rsquo;t include an example of my own configuration at this point, so here it goes! To make it so that it is convenient to commit this configuration file publicly, some private settings can be &amp;ldquo;included&amp;rdquo; from other paths inside the &lt;code&gt;[include]&lt;/code&gt; block. For clarity, I&amp;rsquo;ll show what that file looks like below the main configuration file.&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# This is the content of ~/.gitconfig&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;include&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;;&lt;/span&gt; This file can add more information to extend the main configuration.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;;&lt;/span&gt; For example, when inside a directory that match a certain pattern,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;;&lt;/span&gt; we can tell Git to change our name or email &lt;span class="o"&gt;(&lt;/span&gt;e.g., when committing
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;;&lt;/span&gt; with different credentials from a work email&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; ~/Dropbox/.custom/.gitconfig
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;color&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; auto
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; auto
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;grep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; auto
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;interactive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; auto
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; auto
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;ui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;alias&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# cut out for brevity, see the section on Aliases&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;core&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; nvim
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;excludesfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; ~/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;pager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; diff-so-fancy &lt;span class="p"&gt;|&lt;/span&gt; less --tabs&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt; -RFX
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;commit&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;gpgsign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; ~/.gitmessage
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;mergetool &lt;span class="s2"&gt;&amp;#34;nvim&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; nvim &lt;span class="nv"&gt;$MERGED&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;difftool &lt;span class="s2"&gt;&amp;#34;vscode&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; code --wait --diff &lt;span class="nv"&gt;$LOCAL&lt;/span&gt; &lt;span class="nv"&gt;$REMOTE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;mergetool&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;color &lt;span class="s2"&gt;&amp;#34;diff-highlight&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;oldNormal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; red bold
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;oldHighlight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; red bold &lt;span class="m"&gt;52&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;newNormal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; green bold
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;newHighlight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; green bold &lt;span class="m"&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;color &lt;span class="s2"&gt;&amp;#34;diff&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;commit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; yellow bold
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;frag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; magenta bold
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;11&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;new&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; green bold
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;old&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; red bold
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;whitespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; red reverse
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;push&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;recurseSubmodules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; on-demand
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;gpg&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; gpg2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;filter &lt;span class="s2"&gt;&amp;#34;lfs&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;clean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; git-lfs clean -- %f
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;smudge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; git-lfs smudge --skip -- %f
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; git-lfs filter-process --skip
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;required&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;pull&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;rebase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;credential&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;helper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; store
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;init&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;defaultBranch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;templateDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; ~/.git-templates
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;diff&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; vscode&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The file being referenced/included here, &lt;code&gt;~/Dropbox/.custom/.gitconfig&lt;/code&gt;, looks like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;user&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; email@example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; Your Name
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;signingkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 798034F11B2ADED2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;commit&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;gpgsign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# The following block could be used to update credentials when committing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# depending on what directory is opened. So, if you work for COMPANY_NAME&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# and the pattern &amp;#39;COMPANY_NAME&amp;#39; appears in the current working directory,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# then Git can be smart and update your configuration by including&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# something like the previous block so that the original email, name,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# signingkey and possibly other config options are overwritten.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; when working at COMPANY_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;includeIf &lt;span class="s2"&gt;&amp;#34;gitdir:**/COMPANY_NAME/**/.git&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; ~/Dropbox/.custom/.gitconfig-COMPANY_NAME&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;So this section briefly points out how custom tools like &lt;code&gt;diff-so-fancy&lt;/code&gt; can be used, how diff output colors can be customized, how specific code editors like Neovim and VS Code can be opened when committing or dealing with merge conflicts and generally just how the file is meant to be structured.&lt;/p&gt;
&lt;p&gt;A lot more on configuring Git can be found in &lt;a href="https://git-scm.com/docs/git-config"&gt;the official documentation on &lt;code&gt;git config&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Being exhaustive when it comes to using Git is definitely not something this article strived to achieve: Git is such a complex piece of software that has been evolving since 2005, when it became a necessity to deal with incoming patches sent to Linus Torvalds to make Linux the fantastic project it has become! There is much, much more to learn, but hopefully this post gives you a slightly different perspective and points the way to a well-rounded journey with the list of references below. Git good!&lt;/p&gt;
&lt;h2 id="more-resources-and-references"&gt;More resources and references&lt;/h2&gt;
&lt;h3 id="websites"&gt;Websites&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.conventionalcommits.org/"&gt;Conventional Commits&lt;/a&gt; - &lt;em&gt;&amp;ldquo;A specification for adding human and machine readable meaning to commit messages.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ndpsoftware.com/git-cheatsheet.html"&gt;Git Cheatsheet&lt;/a&gt; - &lt;em&gt;&amp;ldquo;Interactive Git Cheatsheet, categorizing commands based on what they affect.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://gitimmersion.com"&gt;Git Immersion&lt;/a&gt; - &lt;em&gt;&amp;ldquo;A guided tour that walks through the fundamentals of Git, inspired by the premise that to know a thing is to do it.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://githowto.com"&gt;GitHowTo&lt;/a&gt; - &lt;em&gt;&amp;ldquo;Git How To is a guided tour that walks through the fundamentals of Git, inspired by the premise that to know a thing is to do it.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lab.github.com/"&gt;GitHub Learning Lab&lt;/a&gt; - &lt;em&gt;&amp;ldquo;With GitHub Learning Lab, grow your skills by completing fun, realistic projects. Get advice and helpful feedback from our friendly Learning Lab bot.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learngitbranching.js.org"&gt;Learn Git Branching&lt;/a&gt; - &lt;em&gt;&amp;ldquo;An interactive Git visualization tool to educate and challenge!&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/learn/browse/?terms=Github"&gt;Microsoft Learn GitHub modules&lt;/a&gt; - &lt;em&gt;&amp;ldquo;Learn new skills and discover the power of Microsoft products with step-by-step guidance.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/book/en/v2"&gt;Pro Git Book&lt;/a&gt; - &lt;em&gt;&amp;ldquo;The entire Pro Git book, written by Scott Chacon and Ben Straub and published by Apress, is available here. All content is licensed under the Creative Commons Attribution Non Commercial Share Alike 3.0 license. Print versions of the book are available on Amazon.com.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://egghead.io/courses/productive-git-for-developers"&gt;Productive Git for Developers&lt;/a&gt; - &lt;em&gt;&amp;ldquo;You will walk through a series of scenarios which you’ll most commonly encounter in your daily work life as a developer.&amp;rdquo;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dataschool.io/how-to-contribute-on-github/"&gt;Step-by-step guide to contributing on GitHub&lt;/a&gt; - &lt;em&gt;&amp;ldquo;Have you thought about contributing to an open source project, but you&amp;rsquo;re too confused (or intimidated) by the process to even try? I&amp;rsquo;ve been there too!&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://try.github.io"&gt;Try GitHub&lt;/a&gt; - &lt;em&gt;Resources to learn Git: Handbook, cheat sheets, git commands in the browser, etc.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="youtube"&gt;YouTube&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=eulnSXkhE7I"&gt;CS50 Beyond 2019&lt;/a&gt; - CS50&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=SWYqp7iY_Tc"&gt;Git &amp;amp; GitHub Crash Course For Beginners&lt;/a&gt; - Traversy Media&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=RGOj5yH7evk"&gt;Git and GitHub for Beginners - Crash Course&lt;/a&gt; - freeCodeCamp.org&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=HVsySz-h9r4&amp;amp;list=PL-osiE80TeTuRUfjRe54Eea17-YfnOOAx"&gt;Git Tutorial for Beginners: Command-Line Fundamentals&lt;/a&gt; (playlist) - Corey Schafer&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=x0EYpi38Yp4"&gt;GitHub Basics Tutorial - How to Use GitHub&lt;/a&gt; - freeCodeCamp.org&lt;/li&gt;
&lt;li&gt;&lt;a href="https://missing.csail.mit.edu/2020/version-control"&gt;MIT Missing semester (Version Control) - Git data model&lt;/a&gt; - MIT&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=i76ts_0UryI"&gt;The Ultimate Git &amp;amp; GitHub Crash Course - Learn to Version Control for Beginners&lt;/a&gt; - Laith Harb&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="online-courses"&gt;Online courses&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/learning/git-essential-training"&gt;Git Essential Training&lt;/a&gt; (&lt;em&gt;free with free trial&lt;/em&gt;) - LinkedIn Learning. &lt;em&gt;&amp;ldquo;Using a step-by-step approach, author Kevin Skoglund presents the commands that enable efficient code management and reveals the fundamental concepts behind version control systems and the Git architecture. Discover how to track changes to files in a repository, review previous edits, and compare versions of a file; create branches to test new ideas without altering the main project; and merge those changes into the project if they work out.&lt;/em&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com/git-started-with-github/"&gt;Git Started with GitHub&lt;/a&gt; - Udacity. &lt;em&gt;&amp;ldquo;This course is designed to jump right into showing how Git and GitHub work together, focusing on the Git basic workflow. Students can expect to learn the minimum needed to start using Git in about 30 minutes.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://eu.udacity.com/course/how-to-use-git-and-github--ud775"&gt;How to Use Git and GitHub&lt;/a&gt; - Udacity. &lt;em&gt;&amp;ldquo;This course, built with input from GitHub, will introduce the basics of using version control by focusing on a particular version control system called Git and a collaboration platform called GitHub.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com/short-and-sweet-get-started-with-git-and-github-right-now/"&gt;Short and Sweet: Get Started with Git and GitHub Right Now&lt;/a&gt; - Udemy. &lt;em&gt;&amp;ldquo;In just 30 minutes, this &amp;ldquo;Short and Sweet&amp;rdquo; course covers the essential ideas you need to move forward, without a lot of filler. At the end of the course, you&amp;rsquo;ll be able to set up a GitHub account, install Git, create and configure new Git and GitHub repositories, create a change history for your software projects, and publish your software projects to GitHub.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com/the-ultimate-git-5-day-challenge/"&gt;The Ultimate GIT 5-day Challenge&lt;/a&gt; - Udacity. &lt;em&gt;&amp;ldquo;A quick way to determine if working with GIT is something you want to learn more about. This course takes us step-by-step through some basic GIT operations. The course will not dive too deep, and takes small steps on each of five days. As we continue through the course, we learn a basic, single-person workflow that could allow anyone to store files at GitHub or BitBucket.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.coursera.org/learn/version-control-with-git"&gt;Version Control with Git&lt;/a&gt; - Coursera. &lt;em&gt;&amp;ldquo;In this course, you will not learn everything there is to know about Git, but you will build a strong conceptual understanding of the technology, and afterward will be able to confidently dig deeper on any topic that interests you.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.coursera.org/learn/introduction-git-github"&gt;Introduction to Git and Github&lt;/a&gt; - Coursera. Offered by Google. &lt;em&gt;&amp;ldquo;In this course, you’ll learn how to keep track of the different versions of your code and configuration files using a popular version control system (VCS) called Git. We&amp;rsquo;ll also go through how to setup an account with a service called GitHub so that you can create your very own remote repositories to store your code and configuration.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="references-for-this-article"&gt;References for this article&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-rerere"&gt;&lt;code&gt;git rerere&lt;/code&gt;&lt;/a&gt; - git-scm.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.loginradius.com/blog/engineering/git-commands/"&gt;35+ Git Commands List Every Programmer Should Know&lt;/a&gt; - loginradius.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Binary_search_algorithm"&gt;Binary search algorithm&lt;/a&gt; - Wikipedia.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.gitignore.io/install/command-line"&gt;Gitignore&lt;/a&gt; - Toptal.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.hackerrank.com/"&gt;HackerRank&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leetcode.com/"&gt;LeetCode&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.atlassian.com/git/tutorials/rewriting-history"&gt;Rewriting history&lt;/a&gt; - Atlassian.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://about.gitlab.com/blog/2021/03/10/new-git-default-branch-name"&gt;The new Git default branch name&lt;/a&gt; - GitLab.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/49501436/8787680"&gt;What is git-rerere and how does it work?&lt;/a&gt; - Stack Overflow.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Sun, 03 Apr 2022 11:21:00 -0500</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/git-the-gist-of-it-common-commands-for-a-working-workflow/</guid><category>workflow</category><category>git</category><category>productivity</category><category>terminal</category></item><item><title>Using Google Sheets as a database to extract data converted to Markdown</title><link>https://www.sglavoie.com/posts/using-google-sheets-as-a-database-to-extract-data-converted-to-markdown/</link><description>Using Google Sheets API, this little tool reads a spreadsheet, extract relevant data and converts it into Markdown so it can be repurposed, in the example given in this article to generate an HTML output for this website with Pelican, a powerful static site generator.</description><content:encoded>&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;The purpose of this tool is to parse and convert the content of a Google Sheets spreadsheet to Markdown. In this specific case, I just wanted a simple script to reduce the friction in getting a valid output I could use to maintain a &lt;a href="https://www.sglavoie.com/posts/what-it-took-to-propel-a-career-in-tech-in-five-years/"&gt;&amp;ldquo;learning log&amp;rdquo; on my website&lt;/a&gt;. So that gives me a quick way to input data in a spreadsheet and then I can take the time whenever needed to update the learning progress as desired by reformatting any of the entries and optionally adding notes to them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="general-software-requirements"&gt;General software requirements&lt;/h2&gt;
&lt;p&gt;This tool should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be able to read a private spreadsheet with a service account configured with the Google Sheets API.&lt;/li&gt;
&lt;li&gt;Convert rows from a Google Sheets spreadsheet into Markdown (see &lt;strong&gt;Input&lt;/strong&gt; and &lt;strong&gt;Output&lt;/strong&gt; sections below for details).&lt;/li&gt;
&lt;li&gt;Skip rendering rows that are missing a value for the &lt;code&gt;Title&lt;/code&gt; or &lt;code&gt;Date&lt;/code&gt; columns (it couldn&amp;rsquo;t be rendered in the correct place properly without this information).
&lt;ul&gt;
&lt;li&gt;Other columns are optional and the corresponding level of nesting will be skipped if no value was provided (e.g. a category &lt;code&gt;Articles&lt;/code&gt; may have an item with no sub-category and could be missing any of the other values except for &lt;code&gt;Title&lt;/code&gt; and &lt;code&gt;Date&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Separate entries by month by parsing the &lt;code&gt;Date&lt;/code&gt; column.
&lt;ul&gt;
&lt;li&gt;A command-line parameter should exist to specify a year from which the date should be extracted.&lt;/li&gt;
&lt;li&gt;The output should be in reverse chronological order (i.e. for a full year with entries for each month, December will appear at the top and January at the bottom).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Output a hierarchical format like the following: &lt;code&gt;Category &amp;gt; Sub-category &amp;gt; Title &amp;gt; Activity &amp;amp; notes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Group entries by category &lt;strong&gt;and&lt;/strong&gt; sub-category (e.g. if a category is named &lt;code&gt;Articles&lt;/code&gt; and there is a sub-category named &lt;code&gt;Python&lt;/code&gt;, then &lt;code&gt;Python&lt;/code&gt; must be nested under &lt;code&gt;Articles&lt;/code&gt; for the corresponding month).&lt;/li&gt;
&lt;li&gt;Sort on the &lt;code&gt;Date&lt;/code&gt; column before doing any parsing on other columns (rows in the spreadsheet &lt;em&gt;can&lt;/em&gt; be in non-chronological order).
&lt;ul&gt;
&lt;li&gt;Dates from a single year will be kept (either the year received as a parameter or the most recent year found).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sort &lt;code&gt;Category&lt;/code&gt;, &lt;code&gt;Sub-category&lt;/code&gt; and &lt;code&gt;Title&lt;/code&gt; alphabetically (ascending order from top to bottom).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Not&lt;/strong&gt; sort the &lt;code&gt;Activity&lt;/code&gt; column to preserve the order in which rows were added to the spreadsheet.&lt;/li&gt;
&lt;li&gt;Render links only if the &lt;code&gt;Link&lt;/code&gt; column is a valid URL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Not&lt;/strong&gt; validate the content of columns except for &lt;code&gt;Date&lt;/code&gt; and &lt;code&gt;Link&lt;/code&gt; (i.e. render the other columns as is).&lt;/li&gt;
&lt;li&gt;Display items with no category above those having a category. The same logic would be true for sub-categories.&lt;/li&gt;
&lt;li&gt;Format the output:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Date&lt;/code&gt; should become headers (&lt;code&gt;##&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Category&lt;/code&gt; should be &lt;strong&gt;bolded&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Sub-category&lt;/code&gt; should be &lt;em&gt;emphasized&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Title&lt;/code&gt; should become a &lt;a href="https://example.com"&gt;link&lt;/a&gt; if the &lt;code&gt;Link&lt;/code&gt; column contains a valid URL.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Activity&lt;/code&gt; should be nested under &lt;code&gt;Title&lt;/code&gt;, occupying a new line for each activity.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Notes&lt;/code&gt; should appear (&lt;em&gt;italicized and inside parentheses&lt;/em&gt;) next to the activity.
&lt;ul&gt;
&lt;li&gt;If there is a value for &lt;code&gt;Notes&lt;/code&gt; but no value for &lt;code&gt;Activity&lt;/code&gt;, the output would be next to the &lt;code&gt;Title&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="input"&gt;Input&lt;/h3&gt;
&lt;p&gt;A spreadsheet with the following columns (starting with &lt;strong&gt;Date&lt;/strong&gt;):&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;[additional info]&lt;/th&gt;
 &lt;th&gt;Date&lt;/th&gt;
 &lt;th&gt;Category&lt;/th&gt;
 &lt;th&gt;Sub-category&lt;/th&gt;
 &lt;th&gt;Title&lt;/th&gt;
 &lt;th&gt;Activity&lt;/th&gt;
 &lt;th&gt;Link&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;November&lt;/td&gt;
 &lt;td&gt;13/11/2021&lt;/td&gt;
 &lt;td&gt;Articles&lt;/td&gt;
 &lt;td&gt;Python&lt;/td&gt;
 &lt;td&gt;Python slots, slots, and object layout&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://www.youtube.com/watch?v=Iwf17zsDAnY"&gt;https://www.youtube.com/watch?v=Iwf17zsDAnY&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;understood that X does Y&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;November&lt;/td&gt;
 &lt;td&gt;14/11/2021&lt;/td&gt;
 &lt;td&gt;Books&lt;/td&gt;
 &lt;td&gt;Software engineering&lt;/td&gt;
 &lt;td&gt;The Pragmatic Programmer&lt;/td&gt;
 &lt;td&gt;ch. 1&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/"&gt;https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;note 1 here&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;November&lt;/td&gt;
 &lt;td&gt;15/11/2021&lt;/td&gt;
 &lt;td&gt;Books&lt;/td&gt;
 &lt;td&gt;Software engineering&lt;/td&gt;
 &lt;td&gt;The Pragmatic Programmer&lt;/td&gt;
 &lt;td&gt;ch. 2&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;note 2 here too&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;November&lt;/td&gt;
 &lt;td&gt;16/11/2021&lt;/td&gt;
 &lt;td&gt;Books&lt;/td&gt;
 &lt;td&gt;Software engineering&lt;/td&gt;
 &lt;td&gt;Clean Code&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;December&lt;/td&gt;
 &lt;td&gt;1/12/2021&lt;/td&gt;
 &lt;td&gt;Documentation&lt;/td&gt;
 &lt;td&gt;Python&lt;/td&gt;
 &lt;td&gt;The Python Tutorial&lt;/td&gt;
 &lt;td&gt;Sections 1-4&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://docs.python.org/3/tutorial/index.html"&gt;https://docs.python.org/3/tutorial/index.html&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;December&lt;/td&gt;
 &lt;td&gt;2/12/2021&lt;/td&gt;
 &lt;td&gt;Books&lt;/td&gt;
 &lt;td&gt;Software engineering&lt;/td&gt;
 &lt;td&gt;Clean Code&lt;/td&gt;
 &lt;td&gt;ch. 2-10&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Render available values&lt;/td&gt;
 &lt;td&gt;2/12/2021&lt;/td&gt;
 &lt;td&gt;Books&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;Clean Coder&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;book note&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Render available values&lt;/td&gt;
 &lt;td&gt;2/12/2021&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;Python/C API Reference Manual&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://docs.python.org/3/c-api/index.html"&gt;https://docs.python.org/3/c-api/index.html&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Title missing: skip rendering&lt;/td&gt;
 &lt;td&gt;3/12/2021&lt;/td&gt;
 &lt;td&gt;Articles&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Title missing: skip rendering&lt;/td&gt;
 &lt;td&gt;3/12/2021&lt;/td&gt;
 &lt;td&gt;Articles&lt;/td&gt;
 &lt;td&gt;Software engineering&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;ch. 3&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://docs.python.org/3/c-api/index.html"&gt;https://docs.python.org/3/c-api/index.html&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;note&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Date missing: skip rendering&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;Articles&lt;/td&gt;
 &lt;td&gt;Python&lt;/td&gt;
 &lt;td&gt;Python slots, slots, and object layout&lt;/td&gt;
 &lt;td&gt;Section 2&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://www.youtube.com/watch?v=Iwf17zsDAnY"&gt;https://www.youtube.com/watch?v=Iwf17zsDAnY&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;note not rendered&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="output"&gt;Output&lt;/h3&gt;
&lt;p&gt;A Markdown output to the terminal in the following format:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;markdown&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## December
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; [&lt;span class="nt"&gt;Python/C API Reference Manual&lt;/span&gt;](&lt;span class="na"&gt;https://docs.python.org/3/c-api/index.html&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Books**
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; Clean Coder (_book note_)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; _Software engineering_
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; Clean Code
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; ch. 2-10
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Documentation**
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; _Python_
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; [&lt;span class="nt"&gt;The Python Tutorial&lt;/span&gt;](&lt;span class="na"&gt;https://docs.python.org/3/tutorial/index.html&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; Sections 1-4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## November
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Articles**
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; _Python_
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; [&lt;span class="nt"&gt;Python slots, slots, and object layout&lt;/span&gt;](&lt;span class="na"&gt;https://www.youtube.com/watch?v=Iwf17zsDAnY&lt;/span&gt;) (_understood that X does Y_)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **Books**
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; _Software engineering_
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; Clean Code
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; [&lt;span class="nt"&gt;The Pragmatic Programmer&lt;/span&gt;](&lt;span class="na"&gt;https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; ch. 1 (_note 1 here_)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - ch. 2 (_note 2 here too_)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;figure&gt;
 &lt;a href="https://www.sglavoie.com/images/posts/0028_google_sheets_as_database_output_markdown/input_output.png"&gt;
 &lt;img src="https://www.sglavoie.com/images/posts/0028_google_sheets_as_database_output_markdown/input_output.png" alt="Sample learning log input converted into markdown output" class="max-size-img-post" width="1915" height="941" loading="lazy" decoding="async"&gt;
 &lt;/a&gt;
 &lt;figcaption&gt;Input/Output example when using this tool.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="installing-this-tool"&gt;Installing this tool&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Tested only under Python 3.9.7&lt;/em&gt;, requires at least Python 3.7+.&lt;/p&gt;
&lt;h3 id="using-pip"&gt;Using &lt;code&gt;pip&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;[recommended] Activate a virtual environment.&lt;/li&gt;
&lt;li&gt;To install dependencies, run from this directory: &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Then execute the script to run the tool: &lt;code&gt;python3 get_learning_logs&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="using-pipenv"&gt;Using &lt;code&gt;pipenv&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Run from this directory: &lt;code&gt;pipenv install&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Activate the newly created virtual environment with &lt;code&gt;pipenv shell&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Then execute the script to run the tool: &lt;code&gt;python3 get_learning_logs&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-to-use-this-tool"&gt;How to use this tool&lt;/h2&gt;
&lt;p&gt;You will need to create an environment file (default path: &lt;code&gt;~/.learning-logs&lt;/code&gt; or edit the path for &lt;code&gt;LEARNING_LOGS_ENV_PATH&lt;/code&gt; in &lt;code&gt;get_learning_logs.py&lt;/code&gt;). It should look as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;env&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-env" data-lang="env"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SERVICE_ACCOUNT_FILEPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/path/to/service_account_learning_logs.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SPREADSHEET_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SPREADSHEET_ID &lt;span class="c1"&gt;# Found in the URL of the document&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;WORKSHEET_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# First sheet is 0 by default, it comes after the URL parameter `gid`&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To set up this project, you will have to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enable the Google Sheets API for your project (&lt;a href="https://support.google.com/googleapi/answer/6158841?hl=en%29"&gt;https://support.google.com/googleapi/answer/6158841?hl=en)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In the project, search for &amp;ldquo;service account&amp;rdquo; in the search bar and create new credentials.&lt;/li&gt;
&lt;li&gt;Download the credentials and put them at the path &lt;code&gt;SERVICE_ACCOUNT_FILEPATH&lt;/code&gt; referenced in the environment file.&lt;/li&gt;
&lt;li&gt;Create a spreadsheet with the following columns (not necessarily in that order): &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;Category&lt;/code&gt;, &lt;code&gt;Sub-category&lt;/code&gt;, &lt;code&gt;Title&lt;/code&gt;, &lt;code&gt;Activity&lt;/code&gt;, &lt;code&gt;Link&lt;/code&gt;, &lt;code&gt;Notes&lt;/code&gt;.
&lt;ul&gt;
&lt;li&gt;The column &lt;code&gt;Date&lt;/code&gt; Should match a date format: select all cells from that column, go to &lt;code&gt;Format &amp;gt; Number &amp;gt; Date&lt;/code&gt; to apply the expected format (&lt;code&gt;mm/dd/yyyy&lt;/code&gt;). You can then input a date (e.g. &lt;code&gt;11/1/2021&lt;/code&gt;) and you will see a calendar pop up when double-clicking on it. Dragging it down to a new cell will create a new date for the next day (in this example, &lt;code&gt;11/2/2021&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Get its &lt;code&gt;SPREADSHEET_ID&lt;/code&gt; (&lt;code&gt;https://docs.google.com/spreadsheets/d/SPREADSHEET_ID_HERE/edit#gid=0&lt;/code&gt;) to put it in the environment file.&lt;/li&gt;
&lt;li&gt;Get the &lt;code&gt;WORKSHEET_ID&lt;/code&gt; (the integer after &lt;code&gt;gid=&lt;/code&gt; in the URL of the document).&lt;/li&gt;
&lt;li&gt;From the spreadsheet document (or from the folder view in Google Drive), share with the email associated with the service account (e.g. &lt;code&gt;learning-logs-something@project-name-12345.iam.gserviceaccount.com&lt;/code&gt;). Read access (&amp;ldquo;Viewer&amp;rdquo; permission) is enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By now, the script is ready to be called. I just created an alias to execute it more easily by putting the following in &lt;code&gt;~/.bash_aliases&lt;/code&gt; (you will have to adapt the paths of course):&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; learning-logs&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;~/.local/share/virtualenvs/learning-logs-to-markdown-XJLvhmzn/bin/python3.9 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ~/dev/sglavoie/dev-helpers/learning-logs-to-markdown/get_learning_logs.py&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This will output the converted data from the spreadsheet to Markdown &lt;a href="#output"&gt;as shown above&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This little tool scratched an itch and will be helpful in making the process of updating the &lt;a href="https://www.sglavoie.com/posts/what-it-took-to-propel-a-career-in-tech-in-five-years/"&gt;learning progress&lt;/a&gt; of this website more straightforward, more convenient and less hands-on! You can find its source code &lt;a href="https://github.com/sglavoie/dev-helpers/tree/main/learning-logs-to-markdown"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Sun, 19 Dec 2021 21:00:00 -0600</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/using-google-sheets-as-a-database-to-extract-data-converted-to-markdown/</guid><category>automation</category><category>google sheets</category><category>markdown</category><category>python</category><category>terminal</category></item><item><title>Convenient and lightning fast backups with rsync</title><link>https://www.sglavoie.com/posts/convenient-and-lightning-fast-backups-with-rsync/</link><description>Cloud storage became affordable a long time ago while internet connection speeds have increased dramatically over the years. Yet, there is still a strong case to be made for daily backups of a whole system and for this purpose, there are few options to contend with rsync.</description><content:encoded>&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Coming into its fourth year of existence and usage in production environments, this simple script has proven to be a fantastic ally. Despite the plethora of great options out there that have more bells and whistles, I have yet to find a single complaint about this solution as it hinges on the minimalist side, requiring only a working Python 3 installation along with the powerful &lt;code&gt;rsync&lt;/code&gt; command-line tool.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s one of those pieces of code that perform some really basic tasks that can be scheduled with &lt;a href="https://opensource.com/article/17/11/how-use-cron-linux"&gt;&lt;code&gt;cron&lt;/code&gt;&lt;/a&gt; in the background, that one forgets about until a real need to access a backup comes up. This script is barely a Python wrapper calling &lt;code&gt;rsync&lt;/code&gt; but still, I felt like sharing it for those who like uncomplicated setups. I&amp;rsquo;ve literally left this script alone for years some time after the release of Python 3.6 which was released back in December 2016 and it has been going strong since then mostly due to the fact it does not do much more than backing files up and leveraging &lt;code&gt;rsync&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;As briefly mentioned above, this tool uses &lt;a href="https://www.python.org/"&gt;Python 3.6+&lt;/a&gt; and &lt;a href="https://rsync.samba.org/"&gt;&lt;code&gt;rsync&lt;/code&gt;&lt;/a&gt;. That&amp;rsquo;s it, really.&lt;/p&gt;
&lt;h2 id="setting-up-the-script"&gt;Setting up the script&lt;/h2&gt;
&lt;p&gt;This script is being made &lt;a href="https://github.com/sglavoie/dev-helpers/tree/main/rsync_backup"&gt;available on GitHub&lt;/a&gt; within a set of other small tools. To get the source code for this particular tool, you can either &lt;a href="https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository"&gt;clone the whole repository&lt;/a&gt;, get the content of the files separately or simply copy and paste &lt;a href="#the-script"&gt;the current version from below&lt;/a&gt;. You will need a &lt;code&gt;settings.json&lt;/code&gt; file as well as the Python script that will run the necessary commands.&lt;/p&gt;
&lt;h2 id="the-configuration-file"&gt;The configuration file&lt;/h2&gt;
&lt;p&gt;Right off the bat, you will most probably want to update the array of strings for the &lt;code&gt;data_sources&lt;/code&gt; key as well as the string for the key &lt;code&gt;data_destination&lt;/code&gt; in the following JSON file.&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;json&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;data_sources&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/home/sglavoie&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;data_destination&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/media/sglavoie/Elements&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;terminal_width&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sep&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;=-&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;log_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.backup_log_&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;log_format&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%y%m%d_%H_%M_%S&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;rsync_options&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;-vaAHh&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;--delete&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;--ignore-errors&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;--force&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;--prune-empty-dirs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;--delete-excluded&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;backup_exclude&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.backup_exclude&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;By default, the following options are passed to &lt;code&gt;rsync&lt;/code&gt;:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Option&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;-vaAH&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;verbose, archive, ACLs, hard-links (preserve)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--delete&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;&amp;ldquo;delete extraneous files from destination dirs&amp;rdquo;&lt;/em&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--ignore-errors&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;&amp;ldquo;delete even if there are I/O errors&amp;rdquo;&lt;/em&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--force&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;&amp;ldquo;force deletion of directories even if not empty&amp;rdquo;&lt;/em&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--prune-empty-dirs&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;&amp;ldquo;prune empty directory chains from the file-list&amp;rdquo;&lt;/em&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--delete-excluded&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;&amp;ldquo;also delete excluded files from destination dirs&amp;rdquo;&lt;/em&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="description-of-available-settings"&gt;Description of available settings&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Name of setting&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;data_sources&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Directories to backup, supplied as a list of strings (no slash at the end).&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;data_destination&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Single destination of the files to backup, supplied as a string. This can be overridden when passing option &lt;code&gt;-d&lt;/code&gt; or &lt;code&gt;--dest&lt;/code&gt; to the script&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;terminal_width&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Line length in the terminal, used for printing separators.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;sep&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Separator to use along with &lt;code&gt;terminal_width&lt;/code&gt;.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;log_name&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Sets the prefix of the log filename.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;log_format&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;This goes right after &lt;code&gt;log_name&lt;/code&gt; as a suffix.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;rsync_options&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Options to use with rsync as a list of strings.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;backup_exclude&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Default file in each source in &lt;code&gt;data_sources&lt;/code&gt; where files/directories will be ignored.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="the-script"&gt;The script&lt;/h2&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;python&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;Script that uses `rsync` to make a simple and convenient backup.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;Note: requires Python 3.6+. No other Python third-party libraries required.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;glob&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_backup&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;This is where all the action happens!&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_settings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# initiate the parser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;backup&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;-c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;--clear&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Delete all log files for current source in DATA_SOURCES.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;store_true&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;-s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;--src&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Specify an alternative source to backup as a string.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;store&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;-d&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;--dest&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;destination&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Specify an alternative destination for backup as a string.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;store&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# read arguments from the command line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# check for --source or -s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Replace potential list of sources to backup with this one&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data_sources&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Please enter a valid source to backup.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# check for --clear or -c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;clear_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;data_sources&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data_sources&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;log_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# check for --dest or -d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Replace destination to backup with this one&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destination&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data_destination&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destination&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Please enter a valid destination.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# don&amp;#39;t run the script if the destination doesn&amp;#39;t exist&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data_destination&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;The destination doesn&amp;#39;t exist.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_destination&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;backup_all_sources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backup_all_sources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Iterate over all sources to backup.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data_sources&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;date_now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;date_now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;log_format&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;log_name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;log_format&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_option&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;--log-file=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;log_filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;backup_source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;backup_cmd&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;backup_source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;log_option&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# files to ignore in backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exclude_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;backup_exclude&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exclude_file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exclude_option&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;--exclude-from=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exclude_file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;backup_source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;exclude_option&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data_destination&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# skips &amp;#39;--exclude-from&amp;#39; option if no file is found&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;backup_source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data_destination&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;backup_source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;backup_source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;log_filename&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_filename&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;backing_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;KeyboardInterrupt: Exiting operations.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backing_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Print information to STDOUT and to `log_filename` and executes the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; rsync command.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;sep&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;terminal_width&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cmd_executed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;backup_source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;msg_executed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Command executed:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cmd_executed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg_executed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;log_filename&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;w&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;msg_executed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;backup_source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# call communicate to get the return code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;FileNotFoundError: Is the `rsync` tool installed?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Backup completed for: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;source&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (return code: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;sep&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;terminal_width&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clear_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;log_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Clears log files for each source specified in SETTINGS.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data_sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Retrieve a list of all matching log files in `source`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;log_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;*&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;There is no log file to delete in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Log files in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_says_yes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Log files deleted.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Exiting script...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_says_yes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Do you want to delete log files for this source? (y/n) &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Asks the user to enter either &amp;#34;y&amp;#34; or &amp;#34;n&amp;#34; to confirm. Returns boolean.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;user_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;y&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Please enter either &amp;#34;y&amp;#34; or &amp;#34;n&amp;#34;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_settings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Get the settings from `settings.json`.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Returns:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; dict: Containing all settings used by the tool.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;settings.json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;backup_cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rsync&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;backup_cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rsync_options&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;backup_cmd&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;backup_cmd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;run_backup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="how-to-use"&gt;How to use&lt;/h2&gt;
&lt;h2 id="one-time-setup"&gt;One-time setup&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Set all values in &lt;code&gt;settings.json&lt;/code&gt; to suit your needs.&lt;/li&gt;
&lt;li&gt;Make sure that the backup destination is available/mounted. A simple warning will be echoed if the destination can&amp;rsquo;t be found.&lt;/li&gt;
&lt;li&gt;Call the Python script. As an example, if it&amp;rsquo;s located at &lt;code&gt;/home/user/backup/rsync_backup.py&lt;/code&gt;, then you could put the following alias in &lt;code&gt;~/.bash_aliases&lt;/code&gt; (or equivalent)*:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;backup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;python3 /home/user/backup/rsync_backup.py&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;* Python may be called differently on your system, e.g. simply &lt;code&gt;python&lt;/code&gt; instead of &lt;code&gt;python3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this instance, the script can now be executed in a terminal with the keyword &lt;code&gt;backup&lt;/code&gt; along with optional arguments.&lt;/p&gt;
&lt;h2 id="using-as-a-daily-driver"&gt;Using as a daily driver&lt;/h2&gt;
&lt;p&gt;Make sure the destination to back files up is available/mounted and simply call the script (e.g. &lt;code&gt;backup&lt;/code&gt; in this example).&lt;/p&gt;
&lt;p&gt;The output will look something like this:&lt;/p&gt;
&lt;div class="code-block" data-prompted&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;&lt;span class="nv"&gt;backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="o"&gt;=&lt;/span&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Command executed:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rsync -vaAHh --delete --ignore-errors --force --prune-empty-dirs &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --delete-excluded --log-file&lt;span class="o"&gt;=&lt;/span&gt;/home/sglavoie/.backup_log_210731_09_28_09 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --exclude-from&lt;span class="o"&gt;=&lt;/span&gt;/home/sglavoie/.backup_exclude /home/sglavoie /tmp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;building file list ... &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;deleting sglavoie/.backup_log_210731_08_47_24
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sglavoie/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sglavoie/.bash_aliases
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="avoid-deleting-files-at-the-destination"&gt;Avoid deleting files at the destination&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;rsync&lt;/code&gt; command first builds a list of the files that are part of the backup and will go on adding new files, updating existing files as well as removing files that are found in the destination but not in the source anymore according to the default list of flags passed to it in this implementation. It is therefore important to note that &lt;strong&gt;if you would like to back up files in the destination that no longer exist in the source&lt;/strong&gt;, a convenient solution is simply to create a new folder at the root level inside the destination directory and manage those files separately.&lt;/p&gt;
&lt;p&gt;For instance, I sometimes want to back up large video files but there is rarely a need to update them. In this case, it would be possible to add the directories to exclude in the file &lt;code&gt;.backup_exclude&lt;/code&gt; (default configuration option) like so:&lt;/p&gt;
&lt;div class="code-block code-block--wrap"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Videos/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Dropbox/other/folder/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then, these directories will be ignored when backing up the source (if they are nested inside the source directory to start with!). If the destination is set to &lt;code&gt;/media/user/backup&lt;/code&gt; and the source set to &lt;code&gt;/home/user/my_folder&lt;/code&gt;, then the script would recreate the &amp;ldquo;root&amp;rdquo; directory of the source if it doesn&amp;rsquo;t exist on the destination:&lt;/p&gt;
&lt;div class="code-block" data-prompted&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;ls /media/user/backup
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;my_folder&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You could keep track of those heavy files that do not need to be backed up frequently by putting them manually next to &lt;code&gt;my_folder&lt;/code&gt; in &lt;code&gt;/media/user/backup&lt;/code&gt;. This will lead to huge speed ups when running the script if you track many files in this way!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This was a quick overview of what &lt;code&gt;rsync&lt;/code&gt; can offer: I neither touched upon its remote syncing capabilities nor the vast majority of its options, for which you can find a lot more information about by typing &lt;code&gt;man rsync&lt;/code&gt; to display the manual page in the terminal.&lt;/p&gt;
&lt;p&gt;Without making use of any fancy features of recent versions of Python or &lt;code&gt;rsync&lt;/code&gt;, I have found this simple backup procedure to have worked flawlessly for a long time. Hardware inevitably fails at some point and after having heard about countless examples of people losing some or all of their most precious files (including my own story&amp;hellip;), I have come to enjoy backing my system with this little script.&lt;/p&gt;
&lt;p&gt;I wouldn&amp;rsquo;t trust my external drive to last forever either, hence I also rely on Dropbox and Google Drive for documents I find are a good fit. But overall, I just do not tend to back up every single system file in the cloud, so there is still a convincing use case where daily and complete backups become a possibility when using a fast approach like &lt;code&gt;rsync&lt;/code&gt; to incrementally save snapshots.&lt;/p&gt;
&lt;h2 id="more-resources-and-references"&gt;More resources and references&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rsync.samba.org/"&gt;rsync official website&lt;/a&gt; – samba.org.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/sysadmin/sync-rsync"&gt;Keeping Linux files and directories in sync with rsync&lt;/a&gt; – RedHat, going through the main options offered by the tool.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-rsync-to-sync-local-and-remote-directories"&gt;How To Use Rsync to Sync Local and Remote Directories&lt;/a&gt; – DigitalOcean, demoing how to use the tool and covering the case of remote file access.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Sat, 31 Jul 2021 09:01:00 -0500</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/convenient-and-lightning-fast-backups-with-rsync/</guid><category>automation</category><category>backup</category><category>python</category><category>rsync</category><category>script</category><category>terminal</category></item><item><title>Managing dotfiles with a Git bare repository</title><link>https://www.sglavoie.com/posts/managing-dotfiles-with-git-bare-repository/</link><description>Keeping configuration files under version control with a Git bare repository is a fast, elegant and convenient solution!</description><content:encoded>&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I have been managing my configuration files for the past year using a technique I originally came across in &lt;a href="https://www.youtube.com/watch?v=tBoLDpTWVOM"&gt;this YouTube video from DistroTube&lt;/a&gt;. This uses a Git bare repository instead of the &amp;ldquo;usual&amp;rdquo; way, which consists of using a normal Git repository where one would store the actual configuration files and create symbolic links to all those files to the expected path on the system.&lt;/p&gt;
&lt;p&gt;Needless to say, symlinks are a pain to manage when potentially hundreds of files need to be tracked in this way. Fortunately, working with a bare repository is much simpler: there&amp;rsquo;s neither a need to move files around nor store any of them in the dotfiles bare repository either. Let&amp;rsquo;s dive in to see how this works! I&amp;rsquo;ll keep the instructions to the point: for more details, please refer to the article &lt;a href="https://www.atlassian.com/git/tutorials/dotfiles"&gt;The best way to store your dotfiles: A bare Git repository&lt;/a&gt; from Atlassian, the source of knowledge behind this post.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-set-up-the-system"&gt;1. Set up the system&lt;/h2&gt;
&lt;p&gt;Open a terminal and brace yourself to type a few commands. This will need to be done only once on any machine you use.&lt;/p&gt;
&lt;p&gt;Set the following variable to the name of the directory where you want to store your dotfiles (e.g. &lt;code&gt;.dotfiles&lt;/code&gt;, &lt;code&gt;dotfiles&lt;/code&gt;, &lt;code&gt;cfg&lt;/code&gt;, etc.):&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;DOTFILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dotfiles&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then type the following commands:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git init --bare &lt;span class="nv"&gt;$HOME&lt;/span&gt;/&lt;span class="nv"&gt;$DOTFILES&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/usr/bin/git --git-dir=$HOME/$DOTFILES/ --work-tree=$HOME&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config config --local status.showUntrackedFiles no&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Depending on where you store your terminal aliases (e.g. &lt;code&gt;.bashrc&lt;/code&gt;, &lt;code&gt;.zshrc&lt;/code&gt;, &lt;code&gt;.bash_aliases&lt;/code&gt;, etc.), set the following variable according to your needs:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ALIASES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.bash_aliases&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Once that is done, type the following command (or open the file containing your aliases with a text editor to manually put the alias wherever you want):&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;alias config=&amp;#39;/usr/bin/git --git-dir=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$DOTFILES&lt;/span&gt;&lt;span class="s2"&gt;/ --work-tree=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/&lt;span class="nv"&gt;$ALIASES&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can of course change the &lt;code&gt;config&lt;/code&gt; alias to something else. I like to simply use &lt;code&gt;c&lt;/code&gt; instead.&lt;/p&gt;
&lt;h2 id="11-create-a-repository-to-host-your-dotfiles"&gt;1.1 Create a repository to host your dotfiles&lt;/h2&gt;
&lt;p&gt;At this point, you will want to make sure you have created an empty Git repository on a website such as &lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt;, &lt;a href="https://gitlab.com/"&gt;GitLab&lt;/a&gt; or &lt;a href="https://bitbucket.org/"&gt;BitBucket&lt;/a&gt;. In the interface of that website, you should find a place that allows you to copy the link to that Git repository, which might look like something like this: &lt;code&gt;git@github.com:username/dotfiles.git&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="12-set-the-remote-to-track-your-dotfiles"&gt;1.2 Set the remote to track your dotfiles&lt;/h2&gt;
&lt;p&gt;With your new Git repository created in the previous step, you will now want to add tracking information to be able to connect to the remote Git repository. You can do so as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config remote set-url origin git@github.com:username/dotfiles.git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For more information about this, you can read the article &lt;a href="https://www.sglavoie.com/posts/setting-up-ssh-and-git-for-multiple-accounts/"&gt;Setting up SSH and Git for Multiple Accounts&lt;/a&gt; if you&amp;rsquo;ll be using an SSH key to connect and more specifically &lt;a href="https://git-scm.com/docs/git-remote"&gt;the official documentation on the &lt;code&gt;git remote&lt;/code&gt; command&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="2-use-the-new-system"&gt;2. Use the new system&lt;/h2&gt;
&lt;h2 id="21-on-your-current-machine"&gt;2.1 On your current machine&lt;/h2&gt;
&lt;p&gt;Tracking changes will now be a breeze! All we have to do is to add files with our new alias (assuming &lt;code&gt;config&lt;/code&gt; was chosen) to Git, commit and push to a remote repository to keep everything neatly backed up.&lt;/p&gt;
&lt;p&gt;A typical workflow will look like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config add .gitconfig
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config commit -m &lt;span class="s2"&gt;&amp;#34;Add .gitconfig&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config add .zshrc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config commit -m &lt;span class="s2"&gt;&amp;#34;Add .zshrc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config push&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="22-on-a-different-machine"&gt;2.2 On a different machine&lt;/h2&gt;
&lt;p&gt;Once yet have set up the system once with a remote repository, then the process becomes simpler as you can clone that repository and save a few steps. Here&amp;rsquo;s what you&amp;rsquo;d be expected to do to set up the same dotfiles management system on another machine:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# set to different values as desired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;DOTFILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dotfiles
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;REMOTE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;git@github.com:username/dotfiles.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/usr/bin/git --git-dir=$HOME/$DOTFILES/ --work-tree=$HOME&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DOTFILES&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone --bare &lt;span class="nv"&gt;$REMOTE&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/&lt;span class="nv"&gt;$DOTFILES&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config checkout&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;At this point, you might see an error message if you have existing configuration files on your machine in locations that are already tracked by Git in your dotfiles on the remote repository. If that&amp;rsquo;s the case, you can move them to a secure place like so:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p .config-backup &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config checkout 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; egrep &lt;span class="s2"&gt;&amp;#34;\s+\.&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;print $1&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;xargs -I&lt;span class="o"&gt;{}&lt;/span&gt; mv &lt;span class="o"&gt;{}&lt;/span&gt; .config-backup/&lt;span class="o"&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s make sure we don&amp;rsquo;t show all untracked files again:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;config config --local status.showUntrackedFiles no&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;From here, this will be the same workflow as described in the section &lt;a href="#21-on-your-current-machine"&gt;2.1 On your current machine&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;No more symlinks: this is definitely a nicer approach! You will need to be a bit more familiar with Git if you need to edit your Git history such as with the &lt;code&gt;rebase&lt;/code&gt; sub-command or when adding changes selectively such as with the sub-command &lt;code&gt;add -p&lt;/code&gt; to add files interactively — which might be the case if you want to commit only part of the changes made to one file — but that&amp;rsquo;s a great tool to master anyways, and there&amp;rsquo;s an article to get the ball rolling: &lt;a href="https://www.sglavoie.com/posts/git-the-gist-of-it-common-commands-for-a-working-workflow/"&gt;Git the gist of it: common commands for a working workflow&lt;/a&gt; ;).&lt;/p&gt;
&lt;h2 id="more-resources-and-references"&gt;More resources and references&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=tBoLDpTWVOM"&gt;Git Bare Repository - A Better Way To Manage Dotfiles&lt;/a&gt; — DistroTube, YouTube&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.atlassian.com/git/tutorials/dotfiles"&gt;The best way to store your dotfiles: A bare Git repository&lt;/a&gt;, the source of the idea for this post. — Atlassian&lt;/li&gt;
&lt;/ul&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Sun, 30 May 2021 13:13:00 -0500</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/managing-dotfiles-with-git-bare-repository/</guid><category>workflow</category><category>dotfiles</category><category>git</category><category>terminal</category></item><item><title>Ignoring Sync of Local Files to Dropbox on Linux</title><link>https://www.sglavoie.com/posts/ignoring-sync-of-local-files-to-dropbox-on-linux/</link><description>To make the most of Dropbox, it can make sense to backup the files you care the most about and skip the ones that simply take too long to upload and eat up all your space. Such candidates could be hidden .git/ folders and node_modules/, but how do you exclude them locally? Let’s find out.</description><content:encoded>&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.dropbox.com"&gt;Dropbox&lt;/a&gt; is an awesome company which has been until recently the working home of none other than &lt;a href="https://gvanrossum.github.io"&gt;Guido van Rossum&lt;/a&gt;, the creator of the &lt;a href="https://www.python.org"&gt;Python&lt;/a&gt; programming language. It works like a charm out of the box on Linux and can be installed easily enough through the package manager that comes with your chosen distribution.&lt;/p&gt;
&lt;h2 id="why-dropbox"&gt;Why Dropbox?&lt;/h2&gt;
&lt;p&gt;In my experience, it has been working flawlessly and syncs consistently faster than Google Drive. It&amp;rsquo;s also worth mentioning that there is still no official Google Drive Linux client to this day: some users of the &lt;a href="https://www.gnome.org"&gt;GNOME&lt;/a&gt; desktop environment will connect to their Google account and mount Google Drive within the file manager &lt;a href="https://wiki.gnome.org/Apps/Files"&gt;Nautilus/Files&lt;/a&gt;; other users will use the paid software &lt;a href="https://www.insynchq.com"&gt;Insync&lt;/a&gt; or the free software alternative &lt;a href="https://liberodark.github.io/ODrive/"&gt;ODrive&lt;/a&gt;. No thanks.&lt;/p&gt;
&lt;p&gt;On the other hand, Dropbox is easy to use with any flavor of Linux and also syncs seamlessly with mobile devices. I use Dropbox for its convenience, but other solutions exist like &lt;a href="https://syncthing.net"&gt;Syncthing&lt;/a&gt; that do not depend on proprietary software and third-party cloud services. I store mostly insensitive information in my Dropbox account and for the few files that happen to be a bit more sensitive (such as the password database I synchronize on both desktop and mobile with &lt;a href="https://keepassxc.org"&gt;KeePassXC&lt;/a&gt; and &lt;a href="https://github.com/PhilippC/keepass2android"&gt;Keepass2Android&lt;/a&gt;, respectively), they are of course encrypted (hopefully in a secure way).&lt;/p&gt;
&lt;p&gt;Because I like to keep things simple and light on system resources, I do not let Dropbox run in the background (it only syncs once a day in the evening). Since all one needs to do in order to use it is to create and modify files inside a specific folder (such as &lt;code&gt;~/Dropbox&lt;/code&gt; by default), it can just synchronize at any moment and conflicts will be handled automatically if you make changes in more than one place that&amp;rsquo;s being synced.&lt;/p&gt;
&lt;h2 id="so-whats-wrong-with-dropbox"&gt;So, what&amp;rsquo;s wrong with Dropbox?&lt;/h2&gt;
&lt;p&gt;With all that said, there&amp;rsquo;s one little feature that I find missing with Dropbox when using the official Linux client and that&amp;rsquo;s the &lt;strong&gt;ability to selectively sync local files&lt;/strong&gt;. Yes, there is a &lt;a href="https://help.dropbox.com/installs-integrations/sync-uploads/selective-sync-overview"&gt;selective sync&lt;/a&gt; feature available, but that will delete the local copy of your files and store them in the cloud only, &lt;em&gt;not the other way around by keeping your local files intact and prevent uploading them to the cloud&lt;/em&gt;. There are some hacks you can do to avoid this, but this isn&amp;rsquo;t a particularly enticing work flow, because we can do better.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.sglavoie.com/images/posts/0019_ignoring-local-files-to-sync-with-dropbox-on-linux/dropbox_selective_sync.png"&gt;&lt;img src="https://www.sglavoie.com/images/posts/0019_ignoring-local-files-to-sync-with-dropbox-on-linux/dropbox_selective_sync.png" alt="dropbox_selective_sync" class="max-size-img-post"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You may have heard of other alternatives like &lt;a href="https://www.goodsync.com"&gt;GoodSync&lt;/a&gt; that allow, through yet another third-party proprietary software, to ignore specific files at your request. That&amp;rsquo;s not what we are after here: instead, there&amp;rsquo;s something you can do from the terminal that works quite well, too. At the time of this writing, it is still a &lt;em&gt;beta feature&lt;/em&gt; &lt;a href="https://help.dropbox.com/files-folders/restore-delete/ignored-files"&gt;according to this page&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
 &lt;p&gt;This feature is currently in beta and not available to all Dropbox users. It will be rolled out to more users in the future.&lt;/p&gt;

&lt;/blockquote&gt;&lt;p&gt;Yet, because you can work with this feature programmatically to ignore and &lt;em&gt;un-ignore&lt;/em&gt; files from your Dropbox directory, I thought it was worth giving it a try. Here is how I&amp;rsquo;m using it to give you a few ideas.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="any-simple-example"&gt;Any simple example?&lt;/h2&gt;
&lt;p&gt;Getting started is quick and painless. To ignore a file within your Dropbox folder, you only need to use the &lt;code&gt;attr&lt;/code&gt; (on Linux, else &lt;code&gt;xattr&lt;/code&gt; on macOS) command to &lt;strong&gt;set&lt;/strong&gt; (&lt;code&gt;-s&lt;/code&gt;) (or &lt;code&gt;-w&lt;/code&gt; &lt;strong&gt;write&lt;/strong&gt; on macOS) the special attribute &lt;code&gt;com.dropbox.ignored&lt;/code&gt; to give it a &lt;strong&gt;value&lt;/strong&gt; of &lt;code&gt;1&lt;/code&gt; (&lt;code&gt;-V 1&lt;/code&gt;) so it ignores a file (&lt;code&gt;file.txt&lt;/code&gt; in this example) as follow:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/file/in/Dropbox/folder
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;attr -s com.dropbox.ignored -V &lt;span class="m"&gt;1&lt;/span&gt; file.txt &lt;span class="c1"&gt;# Linux&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;xattr -w com.dropbox.ignored &lt;span class="m"&gt;1&lt;/span&gt; file.txt &lt;span class="c1"&gt;# macOS&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="ignore-all-git-and-node_modules-folders"&gt;Ignore all .git and node_modules folders&lt;/h2&gt;
&lt;p&gt;Things get &lt;strong&gt;really&lt;/strong&gt; interesting when we can do that automatically by matching a specific pattern (it depends who you ask, anyways). How about exploring recursively the whole directory tree to exclude only hidden &lt;code&gt;.git&lt;/code&gt; folders? Suppose you store your Git projects in &lt;code&gt;~/Dropbox/git/&lt;/code&gt;. You would then do something like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Dropbox/git/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Linux&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find . -type d -name &lt;span class="s2"&gt;&amp;#34;.git&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; xargs -I &lt;span class="o"&gt;{}&lt;/span&gt; attr -s com.dropbox.ignored -V &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;{}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# macOS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find . -type d -name &lt;span class="s2"&gt;&amp;#34;.git&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; xargs -I &lt;span class="o"&gt;{}&lt;/span&gt; xattr -w com.dropbox.ignored &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;{}&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In an instant, you will start getting some feedback printed to the terminal for each &lt;code&gt;.git&lt;/code&gt; folder that is being found:&lt;/p&gt;
&lt;div class="code-block code-block--wrap"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Attribute &amp;#34;com.dropbox.ignored&amp;#34; set to a 1 byte value for ./path/to/.git:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Dealing with &lt;code&gt;node_modules&lt;/code&gt; folders is equally easy. Because a message will be printed to the console every time a file attribute is changed, you could want to delete the output (preferably once you are sure you are modifying only the targeted files!) by redirecting it to &lt;code&gt;/dev/null&lt;/code&gt;, a &lt;em&gt;null device&lt;/em&gt; that discards all data it receives. We can always split a long command onto multiple lines with a backslash character &lt;code&gt;\&lt;/code&gt; like so (it&amp;rsquo;s not required right after the pipe &lt;code&gt;|&lt;/code&gt; symbol):&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find . -type d -name &lt;span class="s2"&gt;&amp;#34;node_modules&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; xargs -I &lt;span class="o"&gt;{}&lt;/span&gt; attr -s com.dropbox.ignored -V &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;{}&amp;#34;&lt;/span&gt; &amp;gt; /dev/null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="can-we-make-this-simpler"&gt;Can we make this simpler?&lt;/h2&gt;
&lt;p&gt;Yes we can! An &lt;code&gt;alias&lt;/code&gt; might be a bit hard to manage, but we can definitely create a set of functions that will be accessible from anywhere in the terminal. It suffices to add them to your shell configuration file, which could be located in &lt;code&gt;~/.profile&lt;/code&gt;, &lt;code&gt;~/.bash_profile&lt;/code&gt;, &lt;code&gt;~/.zshrc&lt;/code&gt; or something else depending on your shell.&lt;/p&gt;
&lt;p&gt;One solution that I see fit for this purpose is to have two distinct functions that have antagonistic effects so that we can revert our changes in a pinch. Basically, setting a value of &lt;code&gt;1&lt;/code&gt; for the attribute &lt;code&gt;com.dropbox.ignored&lt;/code&gt; ignores a file while setting it back to &lt;code&gt;0&lt;/code&gt; tells Dropbox to sync it again. Our functions could be conveniently called &lt;code&gt;dropbox-ignore&lt;/code&gt; and &lt;code&gt;dropbox-sync&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, the following functions could become quite a bit more complicated to take into account various scenarios and custom behaviors, but let&amp;rsquo;s keep things simple and assume that the user knows what is going to happen. We could have our two functions as follow:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Ignore specific files/directories in Dropbox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dropbox-ignore&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;arg1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;arg2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$arg1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$arg2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Usage: dropbox-ignore &amp;lt;f|d&amp;gt; &amp;lt;name&amp;gt; (e.g. dropbox-ignore d .git)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; find ~/Dropbox -type &lt;span class="nv"&gt;$arg1&lt;/span&gt; -name &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$arg2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; xargs -I &lt;span class="o"&gt;{}&lt;/span&gt; xattr -w com.dropbox.ignored &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;{}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Sync specific files/directories in Dropbox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# that were previously ignored (or not)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dropbox-sync&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;arg1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;arg2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$arg1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$arg2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Usage: dropbox-sync &amp;lt;f|d&amp;gt; &amp;lt;name&amp;gt; (e.g. dropbox-sync d .git)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; find ~/Dropbox -type &lt;span class="nv"&gt;$arg1&lt;/span&gt; -name &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$arg2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; xargs -I &lt;span class="o"&gt;{}&lt;/span&gt; xattr -d com.dropbox.ignored &lt;span class="s2"&gt;&amp;#34;{}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="how-do-you-use-those-functions"&gt;How do you use those functions?&lt;/h2&gt;
&lt;p&gt;Our commands will be accessible by typing &lt;code&gt;dropbox-ignore&lt;/code&gt; and &lt;code&gt;dropbox-sync&lt;/code&gt; the next time we open a terminal window. Both functions do exactly the same thing, but &lt;code&gt;dropbox-ignore&lt;/code&gt; sets the attribute value to &lt;code&gt;1&lt;/code&gt; to ignore files and &lt;code&gt;dropbox-sync&lt;/code&gt; sets the attribute to &lt;code&gt;0&lt;/code&gt; to allow syncing to happen.&lt;/p&gt;
&lt;p&gt;&lt;sub&gt;Note: For simplicity and practicality, keep in mind that those commands will search &lt;strong&gt;recursively from the specified directory&lt;/strong&gt;. It&amp;rsquo;s also good to know that if you set an attribute value to &lt;code&gt;1&lt;/code&gt; then back to &lt;code&gt;0&lt;/code&gt; again, Dropbox will need to re-sync the affected files and directories.&lt;/sub&gt;&lt;/p&gt;
&lt;p&gt;We need to pass them two arguments: the first is the type of search to perform (pass &lt;code&gt;f&lt;/code&gt; for &lt;strong&gt;files&lt;/strong&gt; and &lt;code&gt;d&lt;/code&gt; for &lt;strong&gt;directories&lt;/strong&gt;) and the second is the pattern to match in the name (&lt;code&gt;node_modules&lt;/code&gt; or &lt;code&gt;.git&lt;/code&gt; would fit here, no need to use quotes). Concretely, that will look like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# To ignore a file matching the pattern `myfile`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dropbox-ignore f myfile
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# To avoid syncing node_modules/ directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dropbox-ignore d node_modules
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Te re-add a .git/ folder to Dropbox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dropbox-sync d .git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This tip may certainly not be the best approach to solving this particular issue with Dropbox on Unix, but I hope you find it useful nevertheless as it worked out very nicely on my end! One could store files and directories elsewhere, working with symbolic links or hard links as necessary to reference files or simply give up on making a backup with Dropbox in some circumstances. But I like to keep an extra copy of projects I&amp;rsquo;m working on in Dropbox and that can include potentially large files that don&amp;rsquo;t need to be backed up, so ignoring what&amp;rsquo;s not indispensable can speed up the syncing process tremendously while keeping disk usage in the cloud possibly much lower.&lt;/p&gt;
&lt;p&gt;Dropbox only calculates the size of the files being synced in the cloud, so you can end up needing a lot more space locally if you ignore a number of big files or, conversely, selectively syncing many files will result in a cloud storage larger than what&amp;rsquo;s needed locally.&lt;/p&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Sat, 30 Nov 2019 13:22:00 -0600</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/ignoring-sync-of-local-files-to-dropbox-on-linux/</guid><category>tips-and-tricks</category><category>aliases</category><category>macos</category><category>linux</category><category>shell</category><category>terminal</category></item><item><title>Black: A Python Formatter to Brighten Your Workflow</title><link>https://www.sglavoie.com/posts/black-a-python-formatter-to-brighten-your-workflow/</link><description>Meet Black, a superb Python code formatter that will automatically reformat your code in accordance to PEP-8 standards.</description><content:encoded>&lt;hr&gt;
&lt;h2 id="what-is-black"&gt;What is Black?&lt;/h2&gt;
&lt;p&gt;From its &lt;a href="https://github.com/python/black"&gt;GitHub repository&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
 &lt;p&gt;Black is the uncompromising Python code formatter. By using it, you agree to cede control over minutiae of hand-formatting. In return, Black gives you speed, determinism, and freedom from pycodestyle nagging about formatting. You will save time and mental energy for more important matters.&lt;/p&gt;

&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;Black&lt;/strong&gt; will take less than ideal code and will automatically reformat it according to the &lt;a href="https://www.python.org/dev/peps/pep-0008/"&gt;PEP-8 style guide&lt;/a&gt;. It will take care of many things, including the following (taken in that order from &lt;code&gt;README.md&lt;/code&gt; in &lt;strong&gt;Black&lt;/strong&gt;&amp;rsquo;s repo):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Wrapping lines properly by shortening as much as possible single statements;&lt;/li&gt;
&lt;li&gt;Break long lines;&lt;/li&gt;
&lt;li&gt;Remove superfluous empty lines;&lt;/li&gt;
&lt;li&gt;Take care of adding trailing commas where necessary;&lt;/li&gt;
&lt;li&gt;Convert single quotes (&lt;code&gt;'&lt;/code&gt;) to double quotes (&lt;code&gt;&amp;quot;&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;Convert numeric literals to lowercase (&lt;code&gt;0XAB&lt;/code&gt; to &lt;code&gt;0xAB&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;Superfluous parentheses are removed;&lt;/li&gt;
&lt;li&gt;Split call chains on different lines;&lt;/li&gt;
&lt;li&gt;Works from the terminal and support many code editor integrations (Emacs, Vim, VS Code, Sublime, etc.).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that it&amp;rsquo;s clear &lt;em&gt;why&lt;/em&gt; it can be a good idea to use it, let&amp;rsquo;s see how it works with Vim and the terminal.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="how-to-use-it"&gt;How to use it?&lt;/h2&gt;
&lt;p&gt;First, we need to install it using Python 3.6.0+:&lt;/p&gt;
&lt;div class="code-block" data-prompted&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;pip install black&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That&amp;rsquo;s it!&lt;/p&gt;
&lt;h3 id="use-it-from-the-terminal"&gt;Use it from the terminal&lt;/h3&gt;
&lt;p&gt;In its simplest invocation, we can use &lt;strong&gt;Black&lt;/strong&gt; by specifying a file or a directory by typing:&lt;/p&gt;
&lt;div class="code-block" data-prompted&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;black path_to_file_or_directory&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It doesn&amp;rsquo;t come with many options as its goal is to automatically format code consistently, but you can find out more about this tool in the following way:&lt;/p&gt;
&lt;div class="code-block" data-prompted&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;black --help&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="use-it-with-vim"&gt;Use it with Vim&lt;/h3&gt;
&lt;p&gt;Using &lt;strong&gt;Black&lt;/strong&gt; as part of your workflow in a code editor is where I believe it really shines, because you can see almost in real-time when you make a formatting mistake, which will help down the road in committing less sins.&lt;/p&gt;
&lt;p&gt;Integrating &lt;strong&gt;Black&lt;/strong&gt; with Vim or Neovim is quick and painless, adding a simple line in your &lt;code&gt;.vimrc&lt;/code&gt; or &lt;code&gt;init.vim&lt;/code&gt; file. You can also proceed to do a manual installation without a plugin manager as explained in the GitHub repository.&lt;/p&gt;
&lt;p&gt;With &lt;a href="https://github.com/junegunn/vim-plug"&gt;vim-plug&lt;/a&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;vim&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Plug&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;python/black&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With &lt;a href="https://github.com/VundleVim/Vundle.vim"&gt;Vundle&lt;/a&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;vim&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Plugin&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;python/black&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Once the plugin is installed, it may well be convenient to automatically format Python files when saving the buffer, which can be accomplished by adding the following line to the configuration file:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;vim&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;autocmd&lt;/span&gt; &lt;span class="nx"&gt;BufWritePre&lt;/span&gt; *.&lt;span class="nx"&gt;py&lt;/span&gt; &lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;:Black&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;By default, &lt;strong&gt;Black&lt;/strong&gt; works surprisingly well on its own. I only went ahead and changed the default line length from 88 to 79 characters by also adding this line:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;vim&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;:&lt;span class="nx"&gt;black_linelength&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;79&lt;/span&gt; &lt;span class="c"&gt;&amp;#34; default is 88&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Finally, it&amp;rsquo;s worth noting you can update &lt;strong&gt;Black&lt;/strong&gt; at any point from the terminal with &lt;code&gt;pip&lt;/code&gt; and you can also do it directly from within Vim/Neovim with this command:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;vim&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;BlackUpgrade&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Vim will then output something along those lines:&lt;/p&gt;
&lt;blockquote&gt;
 &lt;p&gt;Upgrading Black with pip&amp;hellip;&lt;/p&gt;

&lt;/blockquote&gt;&lt;blockquote&gt;
 &lt;p&gt;DONE! You are all set, thanks for waiting&lt;/p&gt;

&lt;/blockquote&gt;&lt;h2 id="any-example"&gt;Any example?&lt;/h2&gt;
&lt;p&gt;Taking a random Python 3.6+ code sample, we could originally have something like this (hopefully not):&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;python&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data_sources&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Retrieve a list of all matching log files in `source`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LOG_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;There is no log file to delete in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Log files in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARGUMENTS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remind&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;REMINDER_IS_SET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;Do you want to delete log files &amp;#39;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;for this source? (y/n) &amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_says_yes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;Log files deleted.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Exiting script...&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Running &lt;strong&gt;Black&lt;/strong&gt;, we will get something that&amp;rsquo;s quite a bit more palatable:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;python&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data_sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Retrieve a list of all matching log files in `source`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LOG_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;*&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;There is no log file to delete in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Log files in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ARGUMENTS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;REMINDER_IS_SET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Do you want to delete log files &amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;for this source? (y/n) &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_says_yes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Log files deleted.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Exiting script...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I think the results speak for themselves: enforcing a style with a tool like &lt;strong&gt;Black&lt;/strong&gt; is definitely useful to avoid arguments about conflicting coding styles. It lets you focus on what matters instead: being productive while providing value to the world.&lt;/p&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Sat, 13 Jul 2019 13:10:00 -0500</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/black-a-python-formatter-to-brighten-your-workflow/</guid><category>workflow</category><category>neovim</category><category>plugin</category><category>productivity</category><category>python</category><category>terminal</category><category>vim</category></item><item><title>fzf - A Fuzzy Finder to Accomplish Anything</title><link>https://www.sglavoie.com/posts/fzf-a-fuzzy-finder-to-accomplish-anything/</link><description>Search and, most importantly, finally find what you are looking for on your machine. If you rely on the terminal a lot, fzf may well be capable of speeding up your productivity!</description><content:encoded>&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I know people who make such a mess when &lt;em&gt;not&lt;/em&gt; organizing their files and
directories appropriately on their system, they would benefit greatly
from &lt;code&gt;fzf&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="what-is-fzf"&gt;What is fzf?&lt;/h2&gt;
&lt;p&gt;From the &lt;a href="https://github.com/junegunn/fzf"&gt;official GitHub page&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
 &lt;p&gt;fzf is a general-purpose command-line fuzzy finder. It&amp;rsquo;s an
interactive Unix filter for command-line that can be used with any list;
files, command history, processes, hostnames, bookmarks, git commits,
etc.&lt;/p&gt;

&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2 id="terminal-aliases"&gt;Terminal aliases&lt;/h2&gt;
&lt;p&gt;One of the main uses of &lt;code&gt;fzf&lt;/code&gt; is from the terminal. Being so flexible
to use, it can be combined with all kinds of commands with the help
of pipes (&lt;code&gt;|&lt;/code&gt;) to bend it to your desires. Here are some aliases I am
currently using that undoubtedly improve my terminal workflow.&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;##### Functions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Select a configuration file with fzf and open it with Neovim&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;conf&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; du -a ~/.dotfiles/* ~/.config/* &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; fzf &lt;span class="p"&gt;|&lt;/span&gt; xargs -r nvim &lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Select a file from current folder and recursively with fzf and open it with Neovim&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;se&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; du -a ./* &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; fzf &lt;span class="p"&gt;|&lt;/span&gt; xargs -r nvim &lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Select a file recursively from university folder with fzf and open it with default app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sc&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; du -a ~/Dropbox/university/* &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; fzf &lt;span class="p"&gt;|&lt;/span&gt; xargs -r xdg-open &lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The first alias, &lt;code&gt;conf&lt;/code&gt; (short for &lt;em&gt;configuration&lt;/em&gt;), allows to search
only within the two folders specified for configuration files, which
makes it pop almost instantaneously since it doesn&amp;rsquo;t have to scan files
scattered anywhere else. You can then type anything that partially
matches a file path and even include slashes (&lt;code&gt;/&lt;/code&gt;) in your match if you
know in which directories to look. You can then type &lt;code&gt;Enter&lt;/code&gt; to open the
file with your favorite text editor (here set to &lt;code&gt;nvim&lt;/code&gt;) or type &lt;code&gt;CTRL + c&lt;/code&gt; to abort the command.&lt;/p&gt;
&lt;p&gt;The other aliases work in a similar fashion. &lt;code&gt;se&lt;/code&gt; (short for &lt;em&gt;search&lt;/em&gt;)
will simply search recursively for any kind of files in the current
directory and open the selected file in a text editor. Of course,
certain file types can be excluded and everything else can be tweaked
with more piping power. In the case of the third alias, &lt;code&gt;sc&lt;/code&gt; (short for
&lt;em&gt;school&lt;/em&gt;), it will also search recursively for any kind of file in the
&lt;code&gt;university&lt;/code&gt; folder and will open it with the default application set to
open that kind of file (video, image, text, PDF, etc.).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="vimneovim-integration"&gt;Vim/Neovim integration&lt;/h2&gt;
&lt;p&gt;Using &lt;a href="https://github.com/junegunn/vim-plug"&gt;Vim-Plug&lt;/a&gt; from the same
author as &lt;code&gt;fzf&lt;/code&gt; is as easy as adding the following to Vim/Neovim&amp;rsquo;s
configuration file. First, we make sure that &lt;code&gt;fzf&lt;/code&gt; is available on the
system and install it if it&amp;rsquo;s not. Then, we install the &lt;code&gt;fzf.vim&lt;/code&gt; plugin
to integrate fzf in Vim and optionally, we may configure more options so
that &lt;code&gt;fzf&lt;/code&gt; can be used more optimally, which can be found on the &lt;a href="https://github.com/junegunn/fzf.vim"&gt;GitHub
plugin page&lt;/a&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;vim&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Plug&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;junegunn/fzf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; { &lt;span class="s1"&gt;&amp;#39;dir&amp;#39;&lt;/span&gt;: $&lt;span class="nx"&gt;HOME&lt;/span&gt; . &lt;span class="s1"&gt;&amp;#39;/.fzf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;do&amp;#39;&lt;/span&gt;: &lt;span class="s1"&gt;&amp;#39;./install --all&amp;#39;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Plug&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;junegunn/fzf.vim&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34;&amp;#34;&amp;#34;&amp;#34;&amp;#34; [ FZF ]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; Allows fzf to ignore patterns in .gitignore&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;let&lt;/span&gt; $&lt;span class="nx"&gt;FZF_DEFAULT_COMMAND&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ag -g &amp;#34;&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; Mapping selecting mappings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;nmap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;leader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;plug&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nx"&gt;fzf&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maps&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;xmap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;leader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;plug&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nx"&gt;fzf&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maps&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;omap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;leader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;plug&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nx"&gt;fzf&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maps&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; Insert mode completion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;imap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;plug&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nx"&gt;fzf&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;imap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;plug&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nx"&gt;fzf&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;imap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;plug&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nx"&gt;fzf&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;imap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;plug&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nx"&gt;fzf&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; Advanced customization using autoload functions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; (expand word completing window)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;inoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fzf&lt;/span&gt;#&lt;span class="nx"&gt;vim&lt;/span&gt;#&lt;span class="nx"&gt;complete&lt;/span&gt;#&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;{&lt;span class="s1"&gt;&amp;#39;left&amp;#39;&lt;/span&gt;: &lt;span class="s1"&gt;&amp;#39;20%&amp;#39;&lt;/span&gt;}&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; Make use of fzf command instead of CtrlP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;map&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; :&lt;span class="nx"&gt;FZF&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34;&amp;#34;&amp;#34; [ / FZF ]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="working-with-neovim"&gt;Working with Neovim&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;fzf&lt;/code&gt; really shines when used with Neovim as it is extremely fast,
especially when configured with
&lt;a href="https://github.com/ggreer/the_silver_searcher"&gt;The Silver Searcher&lt;/a&gt;
which deserves its own article. It
integrates with core functionality of Vim and makes it easy to find what
you are looking for.&lt;/p&gt;
&lt;p&gt;From the above configuration, for instance:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;vim&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;nmap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;leader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;plug&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nx"&gt;fzf&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maps&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This allows to search for existing mappings and commands, which can be
faster than diving in the help pages when looking for a quick reference.&lt;/p&gt;
&lt;p&gt;This one in insert mode is very handy:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;vim&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;imap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;plug&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nx"&gt;fzf&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Without affecting keyword completion with &lt;code&gt;CTRL + n&lt;/code&gt; and &lt;code&gt;CTRL + p&lt;/code&gt;
(unless you have &lt;code&gt;set complete+=k&lt;/code&gt; in your configuration file), you can
complete words from a custom dictionary of your choice with &lt;code&gt;CTRL + x CTRL + k&lt;/code&gt;. The window that appears can be moved and resized, which is
what is happening here:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;vim&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;inoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fzf&lt;/span&gt;#&lt;span class="nx"&gt;vim&lt;/span&gt;#&lt;span class="nx"&gt;complete&lt;/span&gt;#&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;{&lt;span class="s1"&gt;&amp;#39;left&amp;#39;&lt;/span&gt;: &lt;span class="s1"&gt;&amp;#39;20%&amp;#39;&lt;/span&gt;}&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In the same way, file paths can be completed from the current working
directory &lt;code&gt;CTRL + x CTRL + f&lt;/code&gt; and existing lines can be quickly inserted
with &lt;code&gt;CTRL + x CTRL + l&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.sglavoie.com/images/posts/0016_a_fuzzy_file_finder_to_accomplish_anything/fzf_autocompletion.png"&gt;&lt;img src="https://www.sglavoie.com/images/posts/0016_a_fuzzy_file_finder_to_accomplish_anything/fzf_autocompletion.png" alt="fzf_line_autocompletion" class="max-size-img-post"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finally, I use &lt;code&gt;fzf&lt;/code&gt; to open any file quickly from the working directory
inside Neovim with the mapping &lt;code&gt;CTRL + p&lt;/code&gt;, which replaces the CtrlP
plugin and can work much faster on larger codebases.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;There is much more that can be done with it and I am barely scratching
the surface here. Luke Smith shared
&lt;a href="https://www.youtube.com/watch?v=vt33Hp-4RXg"&gt;a great video&lt;/a&gt; that will
complement the information from this post nicely with a more technical
approach. Highly recommended to see more practical ways to use this
superb piece of software!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://lukesmith.xyz/"&gt;Luke Smith&lt;/a&gt; also makes a strong point of
combining &lt;code&gt;fzf&lt;/code&gt; with &lt;a href="https://tools.suckless.org/dmenu/"&gt;dmenu&lt;/a&gt;, a tool
that I described succinctly in
&lt;a href="https://www.sglavoie.com/posts/suckless-minimalist-tools-that-work-great/"&gt;a previous article&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Fri, 24 May 2019 10:56:00 -0500</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/fzf-a-fuzzy-finder-to-accomplish-anything/</guid><category>workflow</category><category>plugin</category><category>productivity</category><category>shell</category><category>terminal</category></item><item><title>Using Embedded Terminals Inside Neovim</title><link>https://www.sglavoie.com/posts/using-embedded-terminals-inside-neovim/</link><description>Because every pixel counts, this neat trick will allow to use terminals inside Neovim and maximize the screen estate for a flawless integration with one of the best text editor available.</description><content:encoded>&lt;hr&gt;
&lt;h2 id="terminals--neovim"&gt;Terminals &amp;amp; Neovim&lt;/h2&gt;
&lt;p&gt;Terminal buffers are a built-in feature of &lt;code&gt;Neovim&lt;/code&gt;. This means that
terminals can be launched inside Neovim just like any other buffer, may
it be a split window, a new tab or an independent buffer. Why is this so
useful? Be prepared to be amazed.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.sglavoie.com/images/posts/0006_using-embedded-terminals-inside-neovim/work_on_article.png"&gt;&lt;img src="https://www.sglavoie.com/images/posts/0006_using-embedded-terminals-inside-neovim/work_on_article.png" alt="embedded-terminal" class="max-size-img-post"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.sglavoie.com/images/posts/0006_using-embedded-terminals-inside-neovim/terminal_buffers.png"&gt;&lt;img src="https://www.sglavoie.com/images/posts/0006_using-embedded-terminals-inside-neovim/terminal_buffers.png" alt="terminal-buffers" class="max-size-img-post"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(click to open)&lt;/p&gt;
&lt;h3 id="seamless-integration-with-vim-commands"&gt;Seamless integration with Vim commands&lt;/h3&gt;
&lt;p&gt;For a start, terminals are not just &lt;em&gt;opened&lt;/em&gt; inside Neovim: they are
truly integrated. When you type the letter &lt;code&gt;i&lt;/code&gt; in a terminal buffer,
it switches to a new mode, &lt;code&gt;Terminal&lt;/code&gt;, where you have access to all
underlying features of a regular Bash terminal, such as &lt;code&gt;CTRL + r&lt;/code&gt;
to search, tab completion of commands, &lt;code&gt;CTRL + w&lt;/code&gt; to delete a word
backwards or &lt;code&gt;CTRL + u&lt;/code&gt; to delete the whole line backwards.&lt;/p&gt;
&lt;p&gt;When you escape the &lt;code&gt;Terminal&lt;/code&gt; mode, you switch back to &lt;code&gt;Normal&lt;/code&gt; mode,
where all Vim commands are available as usual, such as navigation,
search, etc. Where this gets interesting is when you consider that the
terminal is just like any other buffer (except it&amp;rsquo;s not &lt;em&gt;modifiable&lt;/em&gt;)
and so you can for instance copy its content or paste into it from
different registers. You can split them, view the line number, get
automatic scrolling when output appears in &lt;code&gt;Insert&lt;/code&gt; mode, you can switch
back to &lt;code&gt;Normal&lt;/code&gt; mode at any time even if a command hasn&amp;rsquo;t completed&amp;hellip;
and of course all commands like &lt;code&gt;gt&lt;/code&gt; to &lt;strong&gt;g&lt;/strong&gt;o to another &lt;strong&gt;t&lt;/strong&gt;ab work
as always.&lt;/p&gt;
&lt;h3 id="terminals-get-supercharged-with-the-power-of-vim"&gt;Terminals get supercharged with the power of Vim&lt;/h3&gt;
&lt;p&gt;This is getting exciting (at least for me). Now, having a terminal with
more power is useful in many cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A whole terminal session can be saved to another file easily by
copying the desired range (&lt;code&gt;:%y&lt;/code&gt; to copy everything, for example).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Instead of having to scroll with the mouse, all Vim commands are
available to search and navigate through the buffer: &lt;code&gt;gg&lt;/code&gt; to go to the
beginning of the session, &lt;code&gt;G&lt;/code&gt; to go to the end, &lt;code&gt;12G&lt;/code&gt; to go to line
number &lt;strong&gt;12&lt;/strong&gt;, &lt;code&gt;/&lt;/code&gt; or &lt;code&gt;?&lt;/code&gt; to search forward and backward respectively.
You can even set local or global marks to jump back and forth to precise
lines consistently!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It&amp;rsquo;s even possible to send commands directly to a specific
terminal or set up a REPL to execute any code, either a
selection, a line or a range of lines. I don&amp;rsquo;t personally
use that feature yet, but I see how useful that can be and
you can too if you watch this [screencast presented by Drew
Neil](&lt;a href="https://thoughtbot.com/upcase/videos/neovim-sending-commands-to-a-"&gt;https://thoughtbot.com/upcase/videos/neovim-sending-commands-to-a-&lt;/a&gt;
terminal-buffer) on
&lt;a href="https://thoughtbot.com"&gt;thoughtbot.com&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the terminal is opened in &lt;code&gt;Normal&lt;/code&gt; mode, the cursor and the current
position are kept intact even though new output could appear, such as
would be the case if you are running a server in the background or
waiting for a system update to finish. This is especially nice for
instance if you are trying to debug a web application: you can leave the
cursor exactly where you would like to keep reading the output and never
get lost&amp;hellip; Plus you have the ability to easily move around, search and
copy like a ninja!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="the-workflow-is-more-compact-and-centralized"&gt;The workflow is more compact and centralized&lt;/h3&gt;
&lt;p&gt;Because the terminals are running with one Neovim instance, you get
everything in the same place, which is usually how you need to access
your work anyway. If for some reasons you need to have multiple
instances of Neovim running, this is possible too and in that situation
you could simply split them into different workspaces.&lt;/p&gt;
&lt;p&gt;Because everything is launched with Neovim, every terminal left open
will also be exited when you close Neovim, shutting down any server
or quitting any background interaction going on in the terminals. I
have found that this behaviour is what I expect pretty much all the
time because the idea of having a terminal inside a working session
of Neovim is to work on something related, so it makes sense to close
everything that&amp;rsquo;s to do with a single project or task when you exit the
text editor. No need for &lt;code&gt;CTRL + d&lt;/code&gt; or &lt;code&gt;exit&lt;/code&gt; anymore!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="terminal-settings-to-consider"&gt;Terminal settings to consider&lt;/h2&gt;
&lt;p&gt;I don&amp;rsquo;t use many settings or mappings related to terminals, but I guess
the nicest one is to remap the &lt;code&gt;Escape&lt;/code&gt; key because by default, this is
a weird mapping that leads to twisted fingers (&lt;code&gt;&amp;lt;C-\&amp;gt;&amp;lt;C-n&amp;gt;&lt;/code&gt;). Here is
what I currently have in my &lt;code&gt;init.vim&lt;/code&gt; file (the equivalent of &lt;code&gt;.vimrc&lt;/code&gt;
for Neovim):&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;vim&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;termguicolors&lt;/span&gt; &lt;span class="c"&gt;&amp;#34; Make colors look better in terminal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; Exit from terminal buffer (Neovim) more easily (remaps Esc key in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; terminal)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;tnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;[&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;\&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; Open terminal buffer (M stands for the Alt key)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;nnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;M&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; :&lt;span class="nx"&gt;te&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CR&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; Switch to terminal buffer automatically (when only one terminal is&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; open)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;nnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;M&lt;/span&gt;&lt;span class="m"&gt;-0&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; :&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="nx"&gt;term&lt;/span&gt;:&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CR&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;#34; Move between windows exactly the same way as usual&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;tnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;\&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;tnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;\&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;tnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;\&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;tnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;\&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;tnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;\&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This was a quick overview of a feature that I have neglected for some
time but recently realized how well it integrates with my workflow. You
can find some more information by typing &lt;code&gt;:h terminal&lt;/code&gt;. If you already
use Neovim, it&amp;rsquo;s worth giving it a try! Otherwise, it&amp;rsquo;s worth giving
&lt;a href="https://neovim.io/"&gt;Neovim&lt;/a&gt; a try.&lt;/p&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Wed, 16 Jan 2019 17:32:00 -0600</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/using-embedded-terminals-inside-neovim/</guid><category>tips-and-tricks</category><category>neovim</category><category>productivity</category><category>terminal</category></item><item><title>Bash History Cleaner</title><link>https://www.sglavoie.com/posts/bash-history-cleaner/</link><description>If you ever wanted to automatically clean your Bash history file, here is a working solution written in Python that uses regular expressions to set any kind of pattern you might be looking for.</description><content:encoded>&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;This is a Python 3.6+ script that helps to clean the file containing
the Bash history commands. It will remove any line matching a specified
regular expression and can also remove any line starting with an alias.&lt;/p&gt;
&lt;p&gt;The idea behind this small utility was simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Bash history file (usually located in &lt;code&gt;~/.bash_history&lt;/code&gt;) contains
much of the work one ends up doing in the terminal.&lt;/li&gt;
&lt;li&gt;The history can grow large over time and it becomes more cumbersome to
find interesting information in all that clutter, such as a rarely used
command with specific flags.&lt;/li&gt;
&lt;li&gt;By removing all superfluous commands that are repeated often and which
give no real benefit in certain contexts (such as &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cd&lt;/code&gt;, &lt;code&gt;cat&lt;/code&gt;,
etc.), the history is much cleaner and easier to navigate and actually
becomes much more useful in my opinion.&lt;/li&gt;
&lt;li&gt;True, it will be harder to follow the bread crumbs for everything
you did, but I haven&amp;rsquo;t come across a situation where having access
to yet another empty &lt;code&gt;ls&lt;/code&gt; or &lt;code&gt;cd&lt;/code&gt; has proven necessary and reading
&lt;code&gt;.bash_history&lt;/code&gt; doesn&amp;rsquo;t make for a great narrative story either.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="make-history-in-a-big-way"&gt;Make history in a big way&lt;/h2&gt;
&lt;p&gt;I took advantage of the fact that the history can be cleaned with the
script you are about to see and set up what is known as an &lt;em&gt;eternal
history&lt;/em&gt; which, as it sounds like, can grow infinitely big! All you have
to do is append the following lines to the file &lt;code&gt;~/.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Eternal bash history.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ---------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Undocumented feature which sets the size to &amp;#34;unlimited&amp;#34;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# http://stackoverflow.com/questions/9457233/unlimited-bash-history&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;HISTFILESIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;HISTSIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;HISTTIMEFORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[%F %T] &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Change the file location because certain bash sessions truncate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# .bash_history file upon close.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# http://superuser.com/questions/575479/bash-history-truncated-to-500-lines-on-each-login&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;HISTFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.bash_eternal_history
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Force prompt to write history after every command.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# http://superuser.com/questions/20900/bash-history-loss&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PROMPT_COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;history -a; &lt;/span&gt;&lt;span class="nv"&gt;$PROMPT_COMMAND&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="bash-history-cleaner"&gt;Bash History Cleaner&lt;/h2&gt;
&lt;p&gt;And here is the script in question *. It comes in two files that need to be in the same directory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One is a Python file that needs to be launched from the terminal with
Python 3.&lt;/li&gt;
&lt;li&gt;The other file, &lt;code&gt;settings.json&lt;/code&gt;, is a JSON file used to store the
settings of the script, which will be detailed below.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;* &lt;sub&gt;Improvements to the original script can be found on &lt;a
href="https://github.com/sglavoie/python-utilities/tree/main/bash_history_cleaner"&gt;Github&lt;/a&gt;. To keep this
article a bit more readable, the original version is shown.&lt;/sub&gt;&lt;/p&gt;
&lt;h2 id="bash_history_cleanerpy"&gt;&lt;code&gt;bash_history_cleaner.py&lt;/code&gt;&lt;/h2&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;python&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Python script that helps to clean the file containing the Bash history
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;commands.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Note: Requires Python 3.6+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;It will remove any line matching a specified regular expression and can
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;also remove any line starting with an alias.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Description of available settings in `settings.json`:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;home_directory&amp;#34;: Absolute path to user&amp;#39;s home directory.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;history_file&amp;#34;: Name of file where the history will be cleaned up.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aliases_file&amp;#34;: Name of file where Bash aliases are set up.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;ignore_patterns&amp;#34;: List of patterns to ignore in `history_file`.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; Each line where a pattern is found will be deleted.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; → Patterns are specified as regular expressions.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;add_aliases&amp;#34;: Boolean. If set to `true`, aliases from `aliases_file`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; will be added to `ignore_patterns`.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aliases_match_greedily&amp;#34;:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; Boolean. If set to `true`, any line in `history_file`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; starting with an alias in `aliases_file` will be
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; deleted. If set to `false`, delete line if the alias is
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; the content of the whole line (with optional space at
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; the end): `false` matches &amp;#34;^alias$&amp;#34; or &amp;#34;^alias $&amp;#34; only.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;backup_history&amp;#34;: Boolean. If set to `true`, `history_file` will be backed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; up in the same directory with a name ending in .bak
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; based on the current date.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;delete_logs_without_confirming&amp;#34;:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; Boolean. If set to `true`, script with flag `-c` will
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; automatically delete all the backup files found for
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; `history_file`.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;fileinput&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;glob&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_current_path&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;Returns the current working directory relative to where this script
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; is being executed.&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_says_yes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;Check if user input is either &amp;#39;y&amp;#39; or &amp;#39;n&amp;#39;. Returns a boolean.&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Please enter either &amp;#39;y&amp;#39; or &amp;#39;n&amp;#39;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;Delete log files in `home_directory` based on `history_file`.&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Retrieve a list of all matching log files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;history_file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;_*.bak&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;There is no log file to delete.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Log files found in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;home_directory&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delete_logs_without_confirming&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Log files deleted.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Do you want to delete those log files? [y/n] &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_says_yes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Log files deleted.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Operation aborted.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_date_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;Return date formatted string to backup a file.&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_%Y%m&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;_%H%M%S.bak&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;Load settings in the script. Return them as a dictionary.&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;r&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;read_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_list_aliases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bash_aliases_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;Retrieve the name of all the aliases specified in `bash_aliases_file`.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; Return aliases as a list of strings formatted as regular expressions.&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;match_whole_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aliases_match_greedily&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bash_aliases_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# one alias per line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;aliases_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Get the actual alias in each line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Use negative lookbehind to remove &amp;#39;alias &amp;#39; at the beginning.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Matches anything after that is a dot, digit, underscore&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# or letter (will stop at the equal sign: alias blah=&amp;#39;...&amp;#39;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;(?&amp;lt;!not )alias ([\.\w]*)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# If for some reason the alias cannot be extracted, skip it&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# If search doesn&amp;#39;t match, it&amp;#39;s of type None and won&amp;#39;t work&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;match_whole_line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Match the whole line if it starts with the alias.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;( )?$|^&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; .*&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Will match only when alias is the whole content of the line,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# followed by optional space.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;( )?$&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Escape dots in alias&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maketrans&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;\.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;aliases_list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;aliases_list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean_bash_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;Modify in place `history_file` by removing every line where
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; `ignore_patterns` is found.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; Optionally, add a list of aliases to `ignore_patterns` with
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; `aliases` based on the value of `add_aliases` in settings.json.&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;backup_history&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;backup_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate_date_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;file_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileinput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;backup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;backup_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;file_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileinput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;file_input&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;has_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ignore_patterns&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;has_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# If no match is found (nothing to ignore), print the line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# back into the file. Otherwise, it will be empty.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;has_match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Line already has carriage return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;launch_cleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aliases_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;Main function that launches the cleanup process.&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;bash_aliases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;add_aliases&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;bash_aliases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_list_aliases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aliases_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# add aliases to list of patterns to ignore&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ignore_patterns&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bash_aliases&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;File not found: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;aliases_file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;clean_bash_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;File not found: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;history_file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SETTINGS_FILE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_current_path&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;settings.json&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SETTINGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SETTINGS_FILE_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ALIASES_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SETTINGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;home_directory&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;SETTINGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aliases_file&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;HISTORY_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SETTINGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;home_directory&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;SETTINGS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;history_file&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# initiate the parser to check all the arguments passed to the script&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PARSER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PARSER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;-c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;--clear&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Delete all log files&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;store_true&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# read arguments from the command line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ARGUMENTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PARSER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ARGUMENTS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;delete_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SETTINGS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HISTORY_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;launch_cleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SETTINGS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HISTORY_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ALIASES_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="settingsjson"&gt;&lt;code&gt;settings.json&lt;/code&gt;&lt;/h2&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;json&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;home_directory&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/home/sglavoie&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;history_file&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.bash_eternal_history&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;add_aliases&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;aliases_file&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.bash_aliases&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;aliases_match_greedily&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;backup_history&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;delete_logs_without_confirming&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;ignore_patterns&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^\\#\\d+&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^(\\.\\/)?pip$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^(\\.\\/)?python.*$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^\\.\\.$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^alias&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^cd &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^cd$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^cd..$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^fg$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^df( )?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^du( )?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^exit$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^git branch&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^git checkout master&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^git log$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^git push$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^git status$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^git stauts$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^kill \\d+.*&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^ls -a$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^ls$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^make|make install$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^man &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^pelican( )?$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^pip install -r requirements.txt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^pip.* list|pip.* show$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^source &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^sudo apt-get autoclean|sudo apt-get autoremove$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^sudo apt-get dist-upgrade|sudo apt-get update$&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;^which &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="description-of-available-settings-in-settingsjson"&gt;Description of available settings in &lt;code&gt;settings.json&lt;/code&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Name of setting&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;home_directory&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Absolute path to user&amp;rsquo;s home directory.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;history_file&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Name of file where the history will be cleaned up.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;aliases_file&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Name of file where Bash aliases are set up.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ignore_patterns&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;List of patterns to ignore in &lt;code&gt;history_file&lt;/code&gt;. Each line where a pattern is found will be deleted. Patterns are specified as regular expressions.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;add_aliases&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Boolean. If set to &lt;code&gt;true&lt;/code&gt;, aliases from &lt;code&gt;aliases_file&lt;/code&gt; will be added to &lt;code&gt;ignore_patterns&lt;/code&gt;. (Default: &lt;code&gt;true&lt;/code&gt;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;aliases_match_greedily&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Boolean. If set to &lt;code&gt;true&lt;/code&gt;, any line in &lt;code&gt;history_file&lt;/code&gt; starting with an alias in &lt;code&gt;aliases_file&lt;/code&gt; will be deleted. If set to &lt;code&gt;false&lt;/code&gt;, delete line if the alias is the content of the whole line (with optional space at the end): &lt;code&gt;false&lt;/code&gt; matches &amp;ldquo;^alias$&amp;rdquo; or &amp;ldquo;^alias $&amp;rdquo; only.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;backup_history&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Boolean. If set to &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;history_file&lt;/code&gt; will be backed up in the same directory with a name ending in .bak based on the current date. (Default: &lt;code&gt;true&lt;/code&gt;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;delete_logs_without_confirming&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Boolean. If set to &lt;code&gt;true&lt;/code&gt;, script with flag &lt;code&gt;-c&lt;/code&gt; will automatically delete all the backup files found for &lt;code&gt;history_file&lt;/code&gt;. (Default: &lt;code&gt;false&lt;/code&gt;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="anecdotal-evidence-of-satisfying-performances"&gt;Anecdotal evidence of satisfying performances&lt;/h2&gt;
&lt;p&gt;Performance-wise, this scans ~8,300 lines per second on my modest Intel
Core i5 laptop with files of over 200,000 lines long. Not that I type so
much stuff in the terminal: I just duplicated many lines.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This is a simple solution to an nonexistent problem, but it was
in the end very instructive to me nonetheless. You may even find
a use for it! Otherwise, you might use the same functions for
other files such as logs! If you would like to take a closer look
at the source in a more convenient way, you can find the code &lt;a href="https://github.com/sglavoie/python-utilities/tree/main/bash_history_cleaner"&gt;available on Github&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Sun, 23 Dec 2018 19:35:00 -0600</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/bash-history-cleaner/</guid><category>automation</category><category>bash</category><category>linux</category><category>python</category><category>regex</category><category>script</category><category>terminal</category></item><item><title>Aliases: Also Known as Terminal User's Best Friends</title><link>https://www.sglavoie.com/posts/aliases-also-known-as-terminal-users-best-friends/</link><description>Here are presented a few terminal aliases that I find myself using regularly.</description><content:encoded>&lt;hr&gt;
&lt;h2 id="some-aliases-that-i-find-useful"&gt;Some aliases that I find useful&lt;/h2&gt;
&lt;hr&gt;
&lt;blockquote&gt;
 &lt;p&gt;In all the following examples, the dollar sign &lt;code&gt;$&lt;/code&gt; at the beginning of
commands represents the prompt, it should not be typed.&lt;/p&gt;

&lt;/blockquote&gt;&lt;h3 id="general-purpose"&gt;General-purpose&lt;/h3&gt;
&lt;hr&gt;
&lt;h4 id="list-all-aliases"&gt;List all aliases&lt;/h4&gt;
&lt;div class="code-block" data-prompted&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;alias&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Prints all of your aliases. If you have plenty of those, you might
prefer to pipe this command with &lt;code&gt;less&lt;/code&gt; to get a nice pager that allows
you to easily nagivate them:&lt;/p&gt;
&lt;div class="code-block" data-prompted&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; less&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h4 id="setting-an-alias-in-bash_aliases"&gt;Setting an alias in &lt;code&gt;~/.bash_aliases&lt;/code&gt;&lt;/h4&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;clear&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This would set &lt;code&gt;c&lt;/code&gt; as a shortcut to &lt;code&gt;clear&lt;/code&gt;. In order to use the
newly created alias, you would have to close the terminal or type the
following command in the terminal:&lt;/p&gt;
&lt;div class="code-block" data-prompted&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bash_aliases&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h4 id="clear-the-screen"&gt;Clear the screen&lt;/h4&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;clear&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I like being absorbed into the emptiness of the terminal, so this one
comes in handy to tidy up the work space.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id="reset-the-terminal"&gt;Reset the terminal&lt;/h4&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;re&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tput reset&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If for some reasons the terminal displays badly or display funky
characters, it can usually be reset this way.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id="exit-from-the-terminal"&gt;Exit from the terminal&lt;/h4&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;exit&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For exiting the terminal as if you were still in Vim!&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id="display-all-file-extensions-recursively-from-the-current-directory"&gt;Display all file extensions recursively from the current directory&lt;/h4&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;allextensions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;find . -type f -name &amp;#39;*.*&amp;#39; | sed &amp;#39;s|.*\.||&amp;#39; | sort -u&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It comes in handy to spot if a file shouldn&amp;rsquo;t be there or to check for
lower or uppercase extensions.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id="find-text-inside-files-including-filenames"&gt;Find text inside files, including filenames&lt;/h4&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;findinfiles&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ag --nobreak --nonumbers --noheading . | fzf&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this example, the alias is set up with
&lt;a href="https://github.com/ggreer/the_silver_searcher"&gt;ag&lt;/a&gt;, a fast
code-searching tool and &lt;a href="https://github.com/junegunn/fzf"&gt;fzf&lt;/a&gt;, a great
fuzzy finder.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id="reboot-and-shutdown-the-system"&gt;Reboot and shutdown the system&lt;/h4&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;reboot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;systemctl reboot&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;shutdown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;systemctl poweroff&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This will reboot or power off the system without needing root privileges
in most working conditions.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id="list-and-sort-files-and-directories-by-modification-time"&gt;List and sort files and directories by modification time&lt;/h4&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;treeold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tree -hDF | less&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This requires the command &lt;code&gt;tree&lt;/code&gt; to be installed. The parameters are
(descriptions taken from &lt;code&gt;man tree&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-h&lt;/code&gt;: Print the size of files in a human readable way.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-D&lt;/code&gt;: Print the date of the last modification time.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-F&lt;/code&gt;: Append a &amp;lsquo;&lt;code&gt;/&lt;/code&gt;&amp;rsquo; for directories, a &amp;lsquo;&lt;code&gt;=&lt;/code&gt;&amp;rsquo; for socket files, a &amp;lsquo;&lt;code&gt;\*&lt;/code&gt;&amp;rsquo;
for executable files, a &amp;lsquo;&lt;code&gt;&amp;gt;&lt;/code&gt;&amp;rsquo; for doors (Solaris) and a &amp;lsquo;&lt;code&gt;|&lt;/code&gt;&amp;rsquo; for
FIFO&amp;rsquo;s, as per &lt;code&gt;ls -F&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id="open-files-quickly-with-default-applications"&gt;Open files quickly with default applications&lt;/h4&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;o&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xdg-open&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This will open files and URLs specified as argument in the default
application detected.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id="moving-around"&gt;Moving around&lt;/h4&gt;
&lt;p&gt;If you are going to be working on projects for some time and require to
&lt;code&gt;cd&lt;/code&gt; into them, I have found the following to be useful:&lt;/p&gt;
&lt;div class="code-block" data-prompted&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;cdnameOfProject &lt;span class="c1"&gt;# instead of having to do cd /path/to/project/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;&lt;span class="c1"&gt;# More examples:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;cdcodeabbey &lt;span class="c1"&gt;# For codeabbey.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;cdhackerrank &lt;span class="c1"&gt;# For hackerrank.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;cdeuler &lt;span class="c1"&gt;# For projecteuler.net&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="code-block__prompt"&gt;$ &lt;/span&gt;cdgit &lt;span class="c1"&gt;# For all Github repositories&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This is not advanced by any means, but it helps quite regularly! Since
you can take advantage of tab completion, you can type &lt;code&gt;cd&lt;/code&gt; (without
adding a space) and then press &lt;code&gt;TAB&lt;/code&gt; key to autocomplete the aliases.&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; ...&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cd ../..&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Make it easier to navigate into deep directory structures by basically
doing &lt;code&gt;cd .. &amp;amp;&amp;amp; cd ..&lt;/code&gt; to go back up two directories at once.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id="listing-files"&gt;Listing files&lt;/h4&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;l&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ls -CFh&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;la&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ls -Ah&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;ll&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ls -ahlF&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;ls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ls --color=auto&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Different ways to set up the command ls to quickly see the needed files.
The parameters are (descriptions taken from &lt;code&gt;man ls&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-A&lt;/code&gt;: &lt;em&gt;Do not list implied &lt;code&gt;.&lt;/code&gt; and &lt;code&gt;..&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-C&lt;/code&gt;: &lt;em&gt;List entries by columns.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-F&lt;/code&gt;: &lt;em&gt;Append indicator (one of &lt;code&gt;\*/=&amp;gt;@|&lt;/code&gt;) to entries.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-a&lt;/code&gt;: &lt;em&gt;Do not ignore entries starting with .&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-h&lt;/code&gt;: &lt;em&gt;With &lt;code&gt;-l&lt;/code&gt; and/or &lt;code&gt;-s&lt;/code&gt;, print human readable sizes (e.g., 1K 234M 2G).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-l&lt;/code&gt;: &lt;em&gt;Use a long listing format.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="python-related"&gt;Python-related&lt;/h3&gt;
&lt;hr&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;aNameOfProject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;source /path/to/project/bin/activate&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;aa&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;source ~/Programming/anaconda3/bin/activate&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# Example using Anaconda distribution&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The purpose is to activate a specific virtual environment quickly. The
command &lt;code&gt;cd&lt;/code&gt; could be added to go to the related project also:&lt;/p&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;gNameOfProject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cd /path/to/project/ &amp;amp;&amp;amp; ./.venv/bin/activate&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Where &lt;code&gt;.venv&lt;/code&gt; would be the name of the virtual environment.&lt;/p&gt;
&lt;p&gt;I like to &lt;strong&gt;a&lt;/strong&gt;ctivate environments starting aliases with &lt;code&gt;a&lt;/code&gt; and &lt;strong&gt;go&lt;/strong&gt;
and activate at the same time starting aliases with &lt;code&gt;g&lt;/code&gt;. This way, it
feels like I am speaking the Vim language (&lt;code&gt;cw&lt;/code&gt; for &lt;strong&gt;c&lt;/strong&gt;hange &lt;strong&gt;w&lt;/strong&gt;ord,
for example).&lt;/p&gt;
&lt;hr&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;da&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;deactivate&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This will deactivate a virtual environment.&lt;/p&gt;
&lt;hr&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;p36&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;python3.6&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;python3.7&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# Simply using `p` for main version of Python&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Practical way to quickly open the desired Python version.&lt;/p&gt;
&lt;hr&gt;
&lt;div class="code-block"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;pyclean&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;find . -regex &amp;#34;.*\(__pycache__\|\.py[co]\)&amp;#34; -delete&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This will delete recursively all files and directories that match one of
the following patterns in their name: &lt;code&gt;__pycache__&lt;/code&gt;, &lt;code&gt;.pyc&lt;/code&gt; or &lt;code&gt;.pyo&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope you will find at least one alias to improve your productivity.
Of course, you are welcome to chime in with your own suggestions!&lt;/p&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Sat, 08 Dec 2018 13:52:00 -0600</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/aliases-also-known-as-terminal-users-best-friends/</guid><category>tips-and-tricks</category><category>aliases</category><category>bash</category><category>linux</category><category>productivity</category><category>terminal</category></item><item><title>Tmux Integration with Vim/Neovim — First Steps</title><link>https://www.sglavoie.com/posts/tmux-integration-with-vim-neovim-first-steps/</link><description>This is a short introduction that shows a possible workflow with tmux and Vim/Neovim.</description><content:encoded>&lt;hr&gt;
&lt;h2 id="tmux-integration"&gt;tmux integration&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/tmux/tmux"&gt;tmux&lt;/a&gt; offers many advantages in the
context of remote access to another machine, but it also shines on a
local setup! Here is how I currently like to set it up.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="configuration-file"&gt;Configuration file&lt;/h3&gt;
&lt;div class="code-block code-block--titled"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__title"&gt;~/.tmux.conf&lt;/span&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight" title="~/.tmux.conf"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# split panes using | and -&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; split-window -h
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;bind&lt;/span&gt; - split-window -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;unbind &lt;span class="s1"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;unbind %
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# switch panes using Alt-arrow without prefix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;bind&lt;/span&gt; -n M-Left &lt;span class="k"&gt;select&lt;/span&gt;-pane -L
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;bind&lt;/span&gt; -n M-Right &lt;span class="k"&gt;select&lt;/span&gt;-pane -R
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;bind&lt;/span&gt; -n M-Up &lt;span class="k"&gt;select&lt;/span&gt;-pane -U
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;bind&lt;/span&gt; -n M-Down &lt;span class="k"&gt;select&lt;/span&gt;-pane -D
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Enable mouse mode (tmux 2.1 and above)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -g mouse on
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;source-file &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.tmux-themepack/blue.tmuxtheme&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This is all very self-explanatory. &lt;a href="https://github.com/jimeh/tmux-themepack"&gt;Many themes can be found
here&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="automating-the-launch-of-a-default-session"&gt;Automating the launch of a default session&lt;/h3&gt;
&lt;p&gt;This is a small Bash script that I like to have for automatic set up
of a development environment with tmux and Vim/Neovim. As my current
workflow, I enjoy the following:&lt;/p&gt;
&lt;div class="code-block code-block--titled"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__title"&gt;~/.tmux_default_session.sh&lt;/span&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight" title="~/.tmux_default_session.sh"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# var for session name (to avoid repeated occurences)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;sn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux new-session -s &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$sn&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -d
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Create 3 windows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux new-window -t &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$sn&lt;/span&gt;&lt;span class="s2"&gt;:0&amp;#34;&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;nvim&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux new-window -t &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$sn&lt;/span&gt;&lt;span class="s2"&gt;:9&amp;#34;&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;python&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux new-window -t &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$sn&lt;/span&gt;&lt;span class="s2"&gt;:8&amp;#34;&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;terminal&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Split terminal window vertically, then split the right pane&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# horizontally, then switch to the left pane (identified by `FOCUS IS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# HERE`).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# _________________&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# |&amp;gt;_ |&amp;gt;_ |&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# | | |&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# | FOCUS |-------|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# | IS |&amp;gt;_ |&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# | HERE | |&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# -----------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux split-window -h
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux split-window -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux &lt;span class="k"&gt;select&lt;/span&gt;-pane -L
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Set up Neovim ready to open files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux send-keys -t &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$sn&lt;/span&gt;&lt;span class="s2"&gt;:0&amp;#34;&lt;/span&gt; C-z &lt;span class="s1"&gt;&amp;#39;nvim .&amp;#39;&lt;/span&gt; Enter
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Set up alias for IPython and clear the screen when entering IPython&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux send-keys -t &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$sn&lt;/span&gt;&lt;span class="s2"&gt;:9&amp;#34;&lt;/span&gt; C-z &lt;span class="s1"&gt;&amp;#39;ipython&amp;#39;&lt;/span&gt; Enter
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux send-keys -t &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$sn&lt;/span&gt;&lt;span class="s2"&gt;:9&amp;#34;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;clear&amp;#39;&lt;/span&gt; Enter
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Select window #0 and attach to the session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux &lt;span class="k"&gt;select&lt;/span&gt;-window -t &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$sn&lt;/span&gt;&lt;span class="s2"&gt;:0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux -2 attach-session -t &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$sn&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id="aliases-to-make-use-of"&gt;Aliases to make use of&lt;/h3&gt;
&lt;p&gt;I add the following aliases in &lt;code&gt;~/.bash_aliases&lt;/code&gt; to automate a chunk of
the workflow:&lt;/p&gt;
&lt;div class="code-block code-block--titled"&gt;
 &lt;div class="code-block__bar" data-pagefind-ignore&gt;
 &lt;span class="code-block__title"&gt;~/.bash_aliases&lt;/span&gt;
 &lt;span class="code-block__lang"&gt;bash&lt;/span&gt;
 &lt;button class="code-block__wrap" type="button" aria-pressed="false" hidden&gt;Wrap&lt;/button&gt;
 
 &lt;/div&gt;
 &lt;div class="highlight" title="~/.bash_aliases"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# This will launch tmux with the desired configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bash ~/.tmux_default_session.sh&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# This will kill the tmux server if the need arises. By detaching from&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# the tmux session, you simply run this command and can reattach easily&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# to the default session configuration on another project.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;kdev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pkill tmux&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# If the session has been detached but the tmux server is still running,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# I use the following alias to quickly reattach to the default session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# named `dev` in that example.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;adev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tmux attach-session -t dev&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id="tmux-demo"&gt;tmux demo&lt;/h3&gt;
&lt;p&gt;(click to open image)
&lt;a href="https://www.sglavoie.com/images/posts/0001_tmux-integration-with-vim/tmux-demo.gif"&gt;&lt;img src="https://www.sglavoie.com/images/posts/0001_tmux-integration-with-vim/tmux-demo.gif" alt="Animated tmux and Vim integration demo" class="max-size-img-post" width="1366" height="768" loading="lazy" decoding="async"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The content of this post can be found on
&lt;a href="https://github.com/sglavoie/better-vim-experience#tmux-integration"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Mon, 19 Nov 2018 20:31:00 -0600</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/tmux-integration-with-vim-neovim-first-steps/</guid><category>workflow</category><category>neovim</category><category>terminal</category><category>tmux</category><category>vim</category></item></channel></rss>