Nathan Knowler

gg: a Git directory jumping helper

Permalink

Large projects and mono repos can be a pain to navigate. gg is a small script I use almost everyday to make it easier.

If you’ve ever worked with WordPress — or really most CMSes for that matter — the base application configuration, themes, plugins, etc. are typically given their own sub directories to help isolate them. For WordPress development, I use the Roots stack and it does a great job of making the project structure relavant, however, since it is still WordPress, the project structure is not exactly flat. Often I’ll need to jump to Trellis’ directory to start the development VM, run a deploy, or update an environment’s configuration, but shortly after jump back to the directory of the theme or plugin I’m working on. Remembering the level of depth and simply writing out the path can be a tedious and erroneous task.

A simple solution

gg is a very simple Bash script I wrote to make is 100% easier to jump between directories in a large project. Here’s a short clip of it in action with a Roots project:

{{ youtube(id="w0SjUNTfzKk", class="youtube") }}

Breaking down the script

There are a few requirements you will need before you get started:

Requirements: git, fzf, fd, ag

You most likely should be able to achieve the same thing with built-in tools or alternatives to fd and ag (maybe even fzf), but for this tutorial, I’ll use the tools listed above. One note before diving into the script, I do have the following set for my FZF default command:

export FZF_DEFAULT_COMMAND='ag -g ""'

The script itself is pretty simple. There are two parts to it. First, if we want to see all of the directories within our project, even if we are already in a nested directory, we will need to jump to the root of the Git repo. Luckily, it is relatively easy to do this with Git. Using Git’s rev-parse subcommand with the --show-toplevel flag, we can get “the absolute path of the top-level-directory.” Since this is useful in itself, but still a tad cumbersome to remember and write out, we’ll create a function for it:

gr() {
  cd `git rev-parse --show-toplevel`
}

If you already have a set of Git-aliases, you might find naming conflicts with the ones I will create in this tutorial and in that case, or in the case you don’t like the names I’ve picked, you will need to use a different name.

The next step is to create an easy way to find a sub-directory. If you’ve ever used a fuzzy-search tool, you’ll know that that they are pretty great. Knowing the fuzzy-search capabilities of fzf, I know I can do something like cd $(fzf) to almost achieve what we’re looking for, however, that finds directories and files, the latter of which we do not need. Luckily, we can use a finder tool with fzf to get what we need. I’ll use fd to achieve this:

gd() {
  cd `
    fd --type d --hidden --follow --exclude .git \
    | fzf --preview "ls -Ap {}"
  `
}

We are telling fd to just find directories, follow symbolic links, allow hidden directories, but exclude the .git directory. Luckily, fd already respects .gitignore so we do not need to worry about dependencies or build files polluting our search selection. Also, we are making it fancy with a preview window of the selected directory’s contents.

I prefer to leave these as separate functions in case I want to just jump to the Git repo root or jump to a sub-directoy within the current directory. For the final gg command, we can just use an alias:

alias gg="gr && gd"

And that’s it. Here’s the full and final script:

#!/usr/bin/env bash

# Go to Git root
gr() {
  cd `git rev-parse --show-toplevel`
}

# Go to sub-directory
gd() {
  cd `
    fd --type d --hidden --follow --exclude .git \
    | fzf --preview "ls -Ap {}"
  `
}

# Go to a directory within the current Git repository
alias gg="gr && gd"