.git/safe

Ian Zabel and Mike Burns

Some years ago, vim expert Tim Pope tweeted a clever suggestion for using bin/:

PATH=".git/safe/../../bin:$PATH" and do mkdir .git/safe in the root of repositories that you trust.

When we originally read that, we liked the suggestion so much that we incorporated it in our workflow and into some of our tools. Read on to know more.

Why would I want to do this?

If you’re a Rails developer you’ve definitely seen a bin/ directory in your project root. It contains programs like bin/rails and bin/rake and bin/rspec. You may use these commands instead of bundle exec <command> to ensure you’re getting the correct versions of gem dependencies. bin/rails and the other binstubs in the bin/ directory eliminate the need for bundle exec. But, it’s nicer to be able to just type rails instead of bin/rails or bundle exec rails. There are a number of workarounds described in the But I Don’t Want to bundle exec blog post.

Even if you’re not a Rails dev, other projects might have a handy bin/ directory with helper programs for developing the app; for example, the gitsh project has a bin/gitsh helper program that only works while developing the project.

Where do I start?

Well, you could add the relative bin directory to your $PATH:

% PATH="bin:$PATH"

The downside is that this means that anywhere you cd, ./bin will be in your path. This may be fine most of the time, but devs who navigate around other people’s codebases might not want to find a bin/ls lurking somewhere under node_modules, waiting for you to cd in while investigating and debugging some third-party library.

The trick described in Tim’s tweet is to add bin to your $PATH through a directory you control, like so:

% PATH=".git/safe/../../bin:$PATH"

Whenever we vet and trust a project’s directory and their developers, we can add that project’s bin directory to our $PATH:

% mkdir -p .git/safe

In practice

# Initial configuration
% PATH=".git/safe/../../bin:$PATH"

# without having trusted a project, we fall
# back to the default `rspec` in our ruby
# version manager (asdf in this case)
% cd work/project_with_bin_dir/
% which rspec
/Users/home/.asdf/shims/rspec

% mkdir -p .git/safe # we decide we trust this repo
% which rspec
.git/safe/../../bin/rspec # great! now we can just run `rspec`
% rspec # no need to `bundle exec rspec`

Built into thoughtbot’s dotfiles

If you’re using thoughtbot’s dotfiles, you’ll find that you already have a handy way of trusting your project. You can just run:

% git trust-bin

This will create the .git/safe directory for you. You can find some more information about this script in the pull-request that created it.

Built into thoughtbot’s Rails template, Suspenders

If you use thoughtbot’s Suspenders as a template to create a new Rails project, you get a bin/setup script that can be used to bootstrap the project onto new machines.

This script checks to see if you have .git/safe in your path. If you do, you’ll get a helpful message reminding you to create the .git/safe directory to trust the repo.

-> When you trust this repo, remember to run: mkdir -p .git/safe

Other uses for .git/safe

While we’re talking about easily using the bin/ directory here, this technique is also useful for files that are not in bin. In Chris Thorn’s dotfiles, you’ll find an example of trusting the git repo to maintain a vimrc.local and reading that in from a vimrc.

if filereadable(".git/safe/../../vimrc.local")
  source .git/safe/../../vimrc.local
endif