The letter A styled as Alchemists logo. lchemists
Published December 14, 2019 Updated May 10, 2020

Git Safe

Demonstrates the Git Safe technique for having local access to your bin scripts without having to type the full path.

Transcript

# Hello and welcome to the Alchemists Screencasts!
# Today, we'll look at using the Git Safe technique.

# You might have originally read of this via the following article:
# https://thoughtbot.com/blog/git-safe
# Let's take a closer look by examining a simple project structure:

tree

# Notice the `bin/example` script:

cat bin/example

# Not much to it as the last line prints it has run:

bin/example

# It would be nice if we could run the script without the relative path.
# With the Git Safe technique, we can accomplish that.
# The basic idea is to *dynamically* add the `bin` folder to your path.
# More importantly, the `bin` path is *only* loaded for the current project.
# This technique is so useful that I use a shell function for this:

cype gafe

# The `gafe` function name is comprised of the following: "(g)it" + "s(afe)" = "gafe". 😉
# The meat of the function is on Line 5 and 7:

cype gafe | rg --color never --after-context 0 "mkdir"

# The above creates the "safe" directory within the ".git" folder.
# Next we need to ensure the `.git/safe` directory is added to your path:

cype gafe | rg --color never --after-context 0 "exec"

# This is important so you can *immediately* leverage the path in your current shell.
# There is only one more step needed to make this all work.
# You must add this line to your `.bashrc` script:
# `export PATH=".git/safe/../../bin:$PATH"`
# Here is my setup:

cat "$HOME/.bashrc" | tail -1

# Notice how `.git/safe/../../bin` is prepended to the path.
# This is where the magic happens.
# This ensures the `bin` folder (two directories up) is loaded on your path. 🎉
# This condition *only* works if the current project has a `.git/safe` directory.
# To recap, when we run:

bin/example

# This works. However, if if we run:

example

# That fails accordingly.
# So let's make this project safe:

gafe

# To confirm, we can see the `.git/safe` directory now exists (last directory):

ls -alhT .git

# Now we can run the script without the relative path:

example

# Much better!
# When leaving the project, running `example` will no longer work:

cd ..
example

# When we return to the project, functionality returns:

cd demo
example

# To make the project no longer *safe*, we can remove the Git Safe directory:

rm -rf .git/safe
example

# We are back where we started because the `bin` directory is no longer safe.
# The caveat is you should only mark projects safe which you *trust*.
# That said, this makes working in *safe/trusted* projects a lot easier.
# For example, in Ruby (via Bundler binstubs), being able to type the following is nice:

# rspec
# rake
# rubocop
# ...etc...

# For fans of direnv (https://direnv.net), there is an alternative approach.
# First, you'll want to install direnv via Homebrew:

brew install direnv

# Now we can leverage direnv by configuring and enabling it:

printf "%s\n" "PATH_add bin" > .envrc
direnv allow

# Finally, we can execute our script, leveraging direnv:

example

# As before, with Git Safe, leaving the project disables script access:

cd ..
example

# Both Git Safe and direnv are powerful tools for improving your workflow.
# Git Safe is handy when you need personal customization or don't need the direnv dependency.
# direnv is handy when you need a custom environment and/or sharing team functionality.

# Enjoy!
# https://alchemists.io
# ☿ 🜔 🜍 🜂 🜃 🜁 🜄