<?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: Git</title><link>https://www.sglavoie.com/tags/git/</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/git/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>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>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>Setting up SSH and Git for Multiple Accounts</title><link>https://www.sglavoie.com/posts/setting-up-ssh-and-git-for-multiple-accounts/</link><description>Simple workflow with SSH, GPG and Git to work remotely with multiple accounts in a convenient way.</description><content:encoded>&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;To work effectively with services such as GitHub and GitLab, it is useful to set up a workflow that doesn&amp;rsquo;t get in the way, especially when multiple accounts are involved. SSH will be set up to avoid entering the username/password combination every time we interact with remote repositories and Git will be set up to work differently for each account while signing the commits potentially with different GPG keys.&lt;/p&gt;
&lt;p&gt;In short, we need to do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up SSH locally (in &lt;code&gt;~/.ssh/&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;Set up SSH keys remotely (GitLab, GitHub, Bitbucket, etc.);&lt;/li&gt;
&lt;li&gt;Set up Git locally (in &lt;code&gt;~/.gitconfig&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;Set up GPG keys remotely (add our keys(s) to GitLab, GitHub, etc.);&lt;/li&gt;
&lt;li&gt;Start interacting with remote repositories.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="setting-ssh-locally"&gt;Setting SSH locally&lt;/h2&gt;
&lt;p&gt;First, let&amp;rsquo;s make sure we have some SSH keys to work with. The default location is &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt; (you can just press &lt;code&gt;Enter&lt;/code&gt; when asked to save to a path):&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;ssh-keygen -t rsa -C &lt;span class="s2"&gt;&amp;#34;email@personal.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;The next key should have a different path:&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;ssh-keygen -t rsa -C &lt;span class="s2"&gt;&amp;#34;email@work.com&amp;#34;&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;Enter file in which to save the key
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;/home/sglavoie/.ssh/id_rsa&lt;span class="o"&gt;)&lt;/span&gt;: /home/sglavoie/.ssh/id_rsa_work&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Add the keys to the authentication agent like so:&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;ssh-add /.ssh/id_rsa
&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;ssh-add /.ssh/id_rsa_work&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;See which keys were added:&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;ssh-add -l &lt;span class="c1"&gt;# list the keys&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 you need to delete any keys that were cached prior to that:&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;ssh-add -D&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, we need a configuration file:&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;touch ~/.ssh/config
&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;chmod &lt;span class="m"&gt;600&lt;/span&gt; ~/.ssh/config&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 add some content in there, assuming we deal with GitHub and GitLab, both with a personal account and a work account:&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;AddKeysToAgent yes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host github
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HostName github.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; User git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; IdentityFile ~/.ssh/id_rsa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host github-work
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HostName github.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; User git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; IdentityFile ~/.ssh/id_rsa_work
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host gitlab
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HostName gitlab.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; User git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; IdentityFile ~/.ssh/id_rsa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host gitlab-work
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HostName gitlab.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; User git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; IdentityFile ~/.ssh/id_rsa_work&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;The directive &lt;code&gt;AddKeysToAgent yes&lt;/code&gt; is useful to avoid typing &lt;code&gt;ssh-add path_to_key&lt;/code&gt; every time SSH is needed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Host&lt;/code&gt; can have any name we want, it doesn&amp;rsquo;t need to match the &lt;code&gt;HostName&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;HostName&lt;/code&gt; is the address we need to access. This should be the same thing for all accounts using a particular service (here, GitHub or GitLab).&lt;/li&gt;
&lt;li&gt;We can set the user to be &lt;code&gt;git&lt;/code&gt; by default.&lt;/li&gt;
&lt;li&gt;For each &lt;code&gt;Host&lt;/code&gt;, we indicate which &lt;code&gt;IdentityFile&lt;/code&gt; to use when trying to work with a remote repository.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="automatically-load-the-keys-from-the-shell"&gt;Automatically load the keys from the Shell&lt;/h2&gt;
&lt;p&gt;For Zsh, the following can be added near the top of &lt;code&gt;~/.zshrc&lt;/code&gt; when using the &lt;code&gt;ssh-agent&lt;/code&gt; plugin:&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;# Load multiple SSH keys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zstyle :omz:plugins:ssh-agent identities id_rsa id_rsa_work&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://ohmyz.sh/"&gt;Oh-My-Zsh&lt;/a&gt;, the &lt;code&gt;ssh-agent&lt;/code&gt; plugin should be contained in &lt;code&gt;plugins&lt;/code&gt; 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;&lt;span class="nv"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;git ssh-agent fzf gitignore zsh-autosuggestions history-substring-search&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;# Somewhere below&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="nv"&gt;$ZSH&lt;/span&gt;/oh-my-zsh.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With this setup, the SSH keys will be loaded when opening a terminal after booting up and those will be available for any subsequent terminal sessions until the user session is exited.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="setting-ssh-keys-remotely"&gt;Setting SSH keys remotely&lt;/h2&gt;
&lt;p&gt;The process will be slightly different on each platform on which we want to authenticate, but the gist of it is to paste the content of the public SSH key in the field when asked to do so.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/help/ssh/README"&gt;Instructions for GitLab&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/connecting-to-github-with-ssh"&gt;Instructions for GitHub&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/set-up-an-ssh-key/"&gt;Instructions for Bitbucket&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="setting-git-locally"&gt;Setting Git locally&lt;/h2&gt;
&lt;p&gt;The idea is to have a &lt;code&gt;~/.gitconfig&lt;/code&gt; file from which we load the main Git configuration by default (let&amp;rsquo;s say, our personal account) and then we load another account – overwriting the Git settings of the personal account with the settings defined for that other account – whenever we navigate to a directory that relates to that other account. Let&amp;rsquo;s see this in action.&lt;/p&gt;
&lt;h2 id="minimal-gitconfig"&gt;Minimal &lt;code&gt;~/.gitconfig&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;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;# default configuration settings to load&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;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="nv"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; ~/.gitconfig-personal
&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;# when working with company-x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# those settings are loaded only when the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# directory matches the pattern defined&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;includeIf &lt;span class="s2"&gt;&amp;#34;gitdir:**/company-x/**/.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; ~/.gitconfig-company-x
&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; gpg
&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;[credential]&lt;/code&gt; section with the setting &lt;code&gt;helper = store&lt;/code&gt; will store your username/password combination when using HTTPS so you don&amp;rsquo;t have to type it over and over again. You could also set this to &lt;code&gt;helper = cache&lt;/code&gt; if you don&amp;rsquo;t want to permanently store credentials.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;includeIf&lt;/code&gt; directive will be triggered whenever you are in a directory containing &lt;code&gt;company-x&lt;/code&gt; in this case so that your correct GPG key and Git settings will be used instead of the default settings for your personal account.&lt;/li&gt;
&lt;li&gt;In the block &lt;code&gt;[gpg]&lt;/code&gt;, your system may be using the program &lt;code&gt;gpg2&lt;/code&gt; instead of &lt;code&gt;gpg&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="minimal-gitconfig-personal-and-gitconfig-company-x"&gt;Minimal &lt;code&gt;~/.gitconfig-personal&lt;/code&gt; and &lt;code&gt;~/.gitconfig-company-x&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Example for one of those:&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@work.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; Sébastien Lavoie
&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; A343702EBE11E0C2
&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; true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you don&amp;rsquo;t have a GPG key already, you can generate one 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;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;gpg --gen-key&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To list existing GPG keys to determine the &lt;code&gt;signinkey&lt;/code&gt; value to use in those files, you can type:&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;gpg --list-secret-keys --keyid-format LONG&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You might get an output similar to the following (this one is showing only one key):&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;sec rsa3072/A343702EBE11E0C2 2020-10-03 &lt;span class="o"&gt;[&lt;/span&gt;SC&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;expires: 2022-10-03&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; EF731EFC008D47D176C05910A343702EBE11E0C2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uid &lt;span class="o"&gt;[&lt;/span&gt;ultimate&lt;span class="o"&gt;]&lt;/span&gt; Sébastien Lavoie &amp;lt;email@work.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssb rsa3072/718726CCFED43B47 2020-10-03 &lt;span class="o"&gt;[&lt;/span&gt;E&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;expires: 2022-10-03&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 bit you need to retrieve for the &lt;code&gt;signingkey&lt;/code&gt; value comes after the type of encryption, here it&amp;rsquo;s &lt;code&gt;rsa3072&lt;/code&gt; and the bit we want is &lt;code&gt;A343702EBE11E0C2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you need to edit a key, there are plenty of options described with &lt;code&gt;man gpg&lt;/code&gt; or &lt;code&gt;man gpg2&lt;/code&gt;. For instance, to remove the expiration date for the above key:&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;gpg2 --edit-key EF731EFC008D47D176C05910A343702EBE11E0C2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;At the &lt;code&gt;gpg&amp;gt;&lt;/code&gt; prompt, type &lt;code&gt;expire&lt;/code&gt; and follow the instructions.&lt;/p&gt;
&lt;p&gt;To delete a key, you can do so by referring to the email address 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;gpg --delete-secret-and-public-key email@work.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Just follow the instructions from there. You may need to repeat the process multiple times if your email address is associated with more than one key.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="setting-gpg-keys-remotely"&gt;Setting GPG keys remotely&lt;/h2&gt;
&lt;p&gt;Just like with the SSH keys, the process differs from one platform to the other.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/"&gt;Instructions for GitLab&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/adding-a-new-gpg-key-to-your-github-account"&gt;Instructions for GitHub&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confluence.atlassian.com/bitbucketserver/using-gpg-keys-913477014.html"&gt;Instructions for Bitbucket&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="interacting-with-remote-repositories"&gt;Interacting with remote repositories&lt;/h2&gt;
&lt;p&gt;Now that the Git configuration is set up and we have SSH and GPG keys to authenticate ourselves and verify our identity when committing, respectively, we can start interacting with remote repositories. From the example we have been following, the file &lt;code&gt;~/.gitconfig-personal&lt;/code&gt; will be used by default (and by consequence, our personal account).&lt;/p&gt;
&lt;h2 id="the-ssh-part"&gt;The SSH part&lt;/h2&gt;
&lt;p&gt;When first cloning, change the host so that it reflects what you have in &lt;code&gt;~/.ssh/id_rsa_correct_key_file&lt;/code&gt;. For your personal account, no change would be required:&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 clone git@github.com:organization/repo.git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For a repository at work requiring the SSH key set up for the work account, you would need to change to the appropriate host like so (we still use the &lt;code&gt;git&lt;/code&gt; user for convenience):&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 clone git@github-work:organization/repo.git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For a refresher, the following are the hosts we have set in &lt;code&gt;~/.ssh/config&lt;/code&gt;: &lt;code&gt;github&lt;/code&gt;, &lt;code&gt;github-work&lt;/code&gt;, &lt;code&gt;gitlab&lt;/code&gt;, &lt;code&gt;gitlab-work&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The difference will be noticed when pushing/pulling as seen with &lt;code&gt;git remote -v&lt;/code&gt;:&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 remote -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;origin	git@github-work:organization/repo.git &lt;span class="o"&gt;(&lt;/span&gt;fetch&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;origin	git@github-work:organization/repo.git &lt;span class="o"&gt;(&lt;/span&gt;push&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;Whereas the personal account will have the same host as usual, for instance &lt;code&gt;git@github.com:organization/repo.git&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="the-gpg-part"&gt;The GPG part&lt;/h2&gt;
&lt;p&gt;If we want to keep our personal and work Git configurations separate (and we probably want that! ;)), it&amp;rsquo;s only a matter of ensuring that the &lt;code&gt;includeIf&lt;/code&gt; pattern contains, in this example, &lt;code&gt;company-x&lt;/code&gt; somewhere in the path. When this is the case, we will see with &lt;code&gt;git config --list&lt;/code&gt; in the cloned repository that our personal account details are loaded first, but if the &lt;code&gt;includeIf&lt;/code&gt; directive matches, the settings for that other account will be applied on top and used when committing.&lt;/p&gt;
&lt;p&gt;If you type &lt;code&gt;git config --list&lt;/code&gt; and search for the word &amp;ldquo;email&amp;rdquo; in the output, it will appear only once when the default &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt; key is used (or whatever is read for the &lt;code&gt;IdentityFile&lt;/code&gt; from the SSH configuration file) while you will see that same personal email showing up first in a work repository, but then later in the output you will find the work email, work GPG key and so on.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This is one possible kind of setup we can use to work with SSH and GPG comfortably. This is pretty much a &amp;ldquo;set it and forget it&amp;rdquo; approach as long as you remember the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SSH&lt;/strong&gt;: Change the SSH host when cloning. If the repository is not publicly available, it would fail anyways (or you may realize you can clone it if it&amp;rsquo;s a public repo but have no &lt;code&gt;push&lt;/code&gt; access).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git&lt;/strong&gt;: Make sure you are in a directory where the &lt;code&gt;includeIf&lt;/code&gt; directive will kick in to set up the email, GPG key and so on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One nice tip to help with the latter bullet point could be to define an alias, say in &lt;code&gt;~/.bash_aliases&lt;/code&gt;, something like what 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;&lt;span class="nv"&gt;cdwork&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cd /path/to/work/dir/with/appropriate/pattern&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;Then, you simply &lt;code&gt;cdwork&lt;/code&gt; and store all your Git repositories for work under that root repository, which would always match the desired pattern by default. That&amp;rsquo;s it. Anything else related to the personal account can be cloned anywhere on the file system since this is the default configuration file used. For more Git-related content, you might enjoy reading &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;</content:encoded><dc:creator>Sébastien Lavoie</dc:creator><pubDate>Sat, 03 Oct 2020 15:25:00 -0500</pubDate><guid isPermaLink="false">https://www.sglavoie.com/posts/setting-up-ssh-and-git-for-multiple-accounts/</guid><category>workflow</category><category>ssh</category><category>git</category><category>gpg</category></item></channel></rss>