Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

175 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

$ cdc

Shells: zsh | bash License: MIT Completions: zsh | bash Build Status

If your repositories are spread throughout your system like a pandemic, then cdc is the solution!

View on GitHub | GitHub Pages

About

Overview

I have a few directories in which I clone repositories, so hopping from one project to another can be tedious. This plugin provides a way to change directory to any repository, regardless of where it's located, with cdc [REPOSITORY]. The only setup necessary is to specify which paths the plugin should check for the repository. The plugin comes with tab-completion, as long as your zsh/bash version supports it. The plugin also includes session history, and has options available that behave similar to the pushd, popd, and dirs commands.

While this plugin was written for directories that contain repositories, you can obviously use it for adding any directories to your cd path. In fact, this is the default behavior, but you can force cdc to only recognize repositories with a simple configuration change.

An added benefit is that certain variables are exported to your shell, which means you can utilize it in scripts and other plugins. For example, after every successful cdc call that changes directory, a variable is set to that path so you can reference it later, even from outside of the directory. I also wrote a custom integration with vim that makes use of the exported CDC_DIRS variable.

Rationale

I chose to make this plugin rather than editing $CDPATH because I don't like changing the default behavior of cd, but you could just as easily do the following to match cdc's most basic functionality.

# Assuming `repository` exists in `/path/to/repo_dir`
CDPATH=/path/to/repo_dir

cd repository # will cd to /path/to/repo_dir/repository

Alternatively, you could make aliases:

alias repository='cd /path/to/repo_dir/repository'

I don't like this method either. In my opinion, the fewer aliases, the better. Also, you now have to remember an alias for each repository. cdc solves this issue with its tab-completion.

Even if you choose one of the above options, all it would do is help with the initial cd. As time has gone on, this plugin has gained a ton of features that outclass anything the above methods can give you.

Why the name "cdc"?

I wanted something fast to type that wasn't already a command or builtin. You already type cd a million times a day, and you don't even have to move your finger to hit the c key again. You can't get much faster.

Compatibility

This plugin is designed to work with both zsh and bash shell. Code is as agnostic as it can be, with conditionals for things like array indices.

Installation

Vanilla zsh or bash

Clone the repository wherever you like, and source either the cdc.plugin.zsh file for zsh, or cdc.plugin.bash file for bash, from one of your startup files, such as ~/.zshrc or ~/.bashrc, respectively.

# Where $INSTALLATION_PATH is the path to where you installed the plugin.
source $INSTALLATION_PATH/cdc.plugin.zsh  # in ~/.zshrc
source $INSTALLATION_PATH/cdc.plugin.bash # in ~/.bashrc

If you're using a version of zsh/bash that doesn't support the completion features, or you just don't want to use them, just source the cdc.sh file directly.

source $INSTALLATION_PATH/cdc.sh # in either ~/.zshrc or ~/.bashrc

oh-my-zsh

Clone the repository in your $ZSH_CUSTOM/plugins directory

git clone https://github.com/evanthegrayt/cdc.git $ZSH_CUSTOM/plugins/cdc

Then add the plugin to your $HOME/.zshrc file in the plugins array:

plugins=(cdc) # Obviously, leave your other plugins in the array.

bash-it

Clone the repository in your $BASH_IT_CUSTOM directory

git clone https://github.com/evanthegrayt/cdc.git $BASH_IT_CUSTOM/cdc

Files in this directory that end with .bash are automatically sourced, so there's nothing else to do.

Set-up

The following settings require variables to be exported from a shell config file, such as ~/.zshrc or ~/.bashrc. Note that cdc no longer reads a separate ~/.cdcrc file. You can view an example of this in my config file.

Telling cdc where to look

To use this plugin, you need to export CDC_DIRS in a shell config file. It should be a string with absolute paths to the directories to search, separated by colons (similar to $PATH).

# Set this in ~/.zshrc or similar
export CDC_DIRS=$HOME/dir_with_repos:$HOME/workspace/another_dir_with_repos

Note that the order of the paths in the string matters. The plugin will cd to the first match it finds, so if you have the same repository (or two repositories with the same name) in two places, the first location in the string will take precedence. There is currently an issue to better handle this "feature". Not sure how I want to go about it yet. Suggestions are very much welcome on the issue.

Adding exact directories

If you have specific directories you want to cdc to directly, but you do not want their parent directories searched, export CDC_EXPLICIT_DIRS. It should be a colon-delimited string of full directory paths.

export CDC_EXPLICIT_DIRS=$HOME/.vim:$HOME/.config/nvim

These directories are matched by basename, appear in cdc -l, and are included in tab-completion. Explicit directories take precedence over directories found under CDC_DIRS, and they are always available even when they are hidden, ignored, or not repositories in repo-only mode.

Ignoring certain directories

If you have directories within CDC_DIRS that you want the plugin to ignore, you can export CDC_IGNORE to a string containing those directories. These elements can be directory base-names, or full paths to specific directories. Bare names match any directory with that name in CDC_DIRS; full paths only match that exact directory. "Ignoring" a directory will prevent it from being "seen" by cdc.

# Assuming you never want to `cdc notes_directory`:
export CDC_IGNORE=notes_directory:training

# Assuming you only want to ignore one specific notes directory:
export CDC_IGNORE="$HOME/work/private/notes:training"

Including hidden directories in listings and completion

If a directory inside CDC_DIRS starts with ., it is hidden from lookup, cdc -l, and tab-completion by default. To include hidden directories, set CDC_ALLOW_HIDDEN to true.

export CDC_ALLOW_HIDDEN=true

You can also enable hidden directories for one command with -H, such as cdc -H .config or cdc -H .<TAB>.

Only recognize actual repositories

You can export CDC_REPOS_ONLY in a shell config file to make cdc only recognize repositories as directories. This is disabled by default. You can also set a string of files and directories that mark what you consider a repository. Note that markers that are directories must end with a /, while files must not.

# Enable "repos-only" mode. Note, the default is false.
export CDC_REPOS_ONLY=true
# Set repository markers with the following. Note, the following is already the
# default, but this is how you can override it in ~/.zshrc or similar.
export CDC_REPO_MARKERS=.git/:.git:Rakefile:Makefile:.hg/:.bzr/:.svn/

Note that this setting can be overridden with the -r and -R options. See options below.

Automatically pushing to the history stack

By default, every cdc call that changes directories will push the directory onto the history stack. You can disable this feature by setting CDC_AUTO_PUSH to false in a shell config file.

# Disable auto-pushing to history stack.
export CDC_AUTO_PUSH=false

You can then manually push directories onto the stack with -u. If you have CDC_AUTO_PUSH set to true, you can still cdc to a directory and not push it to the stack with the -U option. See options below. Using -w only prints a directory path and does not push anything onto the history stack.

By default, the history stack can contain duplicate directories. To follow the pushd behavior from shells that enable duplicate suppression, set CDC_IGNORE_DUPS to true. When enabled, pushing a directory that is already in the stack moves it to the current position instead of adding a duplicate.

# Move repeated directories to the current position instead of duplicating them.
export CDC_IGNORE_DUPS=true

You can also push the current directory onto the stack with cdc .. This does not change directories. It still pushes when CDC_AUTO_PUSH is false, because . is an explicit request to push the current directory. Use cdc -U . to skip the push (which becomes a no-op), or cdc -w . to print the resolved path without pushing. When CDC_REPOS_ONLY is true, cdc . pushes the nearest parent repository if one is found; otherwise, it pushes the current directory. Directories pushed this way are not added to tab-completion.

You can combine -w with selected history actions to print full paths without changing directories. cdc -wn prints the current history entry, cdc -wp prints the directory cdc -p would change to without popping the stack, and cdc -wd lists all history entries as full paths without verbose debug output.

Colored Output

You can enable/disable colored terminal output, and even change the colors, by adding the following lines to a shell config file.

export CDC_COLOR=false               # Default: true. Setting to false disables colors
# The following lines would make the colored output bold.
export CDC_SUCCESS_COLOR='\033[1;92m'  # Bold green.   Default: '\033[0;32m' (green)
export CDC_WARNING_COLOR='\033[1;93m'  # Bold yellow.  Default: '\033[0;33m' (yellow)
export CDC_ERROR_COLOR='\033[1;91m'    # Bold red.     Default: '\033[0;31m' (red)

Usage

Typing cdc <TAB> will list all available directories, including exact directories from CDC_EXPLICIT_DIRS and directories found on the fly under CDC_DIRS. Hit return after typing the directory name to change to that directory.

Typing cdc -P <TAB> will list the configured parent directories from CDC_DIRS by name. For example, if CDC_DIRS includes /Users/evanthegrayt/repo_dir, then cdc -P repo_dir will change to /Users/evanthegrayt/repo_dir instead of a repository inside it.

You can append subdirectories, and tab-completion will continue listing directories under the selected match. For example:

cdc repo/bin

If the subdirectory doesn't exist, it will cd to the base directory, and then print a message to stderr.

Referencing the current cdc directory

After a successful cdc call that changes directories or records a directory, cdc exports CDC_CURRENT with the resolved cdc root. This gives you a stable path to use in later shell commands, even if you manually cd somewhere else.

cdc my-project/bin
mv script.sh "$CDC_CURRENT"/scripts/

In the example above, CDC_CURRENT points to the root of my-project, not the bin subdirectory. Using -w only prints a directory path and does not update CDC_CURRENT.

Use -N when you want to temporarily cdc somewhere without replacing that stored root. For example:

cdc rubyproject1
cdc -UN rubyproject2
cp Rakefile "$CDC_CURRENT"/
cdc -n

In this example, -U keeps rubyproject2 off the history stack, -N keeps CDC_CURRENT pointed at rubyproject1, and cdc -n returns to the current history entry.

Options

The plugin comes with a few available options. Some are for dealing with the directory history stack, similar to pushd, popd, and dirs. Others are for overriding variables set in a shell config file. There's also a verbose mode. Options that change a directory lookup can be combined, such as -aRw or -PU. Standalone action options (-G, -l, -L, -i, -d, -n, -t, -p, -X, and -h) should be used one at a time, without a directory argument. The -x history action is also standalone, but requires one directory operand to delete matching entries from the history stack.

Flag What it does
-a Allow the plugin to cd to ignored directories.
-c Enable colored output.
-C Disable colored output.
-G Audit Git repositories in CDC_DIRS for local work or unpushed commits.
-H Include hidden directories in lookup, listing, and completion.
-l List all directories to which you can cdc. Same as tab-completion.
-L List the directories in which cdc will search.
-i List the directories that are to be ignored.
-d List directories in history stack. Similar to the dirs command.
-n cd to the current directory in the history stack.
-N Do not update CDC_CURRENT.
-t Toggle to the last directory, similar to cd -. Rearranges history stack.
-p cd to previous directory in history stack. Similar to the popd command.
-P cd to a configured parent directory from CDC_DIRS.
-u Push the directory onto the stack. Similar to the pushd command.
-U Do not push the directory onto the stack.
-x Delete matching directories from the history stack.
-X Clear the history stack.
-r Only cd to repositories.
-R cd to the directory even if it's not a repository.
-v Verbose mode. Enables warnings for when things aren't working as expected.
-w Print the resolved path instead of changing directories. Like which.
-h Print help.

Tests

The test suite uses bats-core. Tests create their own temporary fixtures and shell configuration.

./test/run.sh

Vim

While there is no official vim support, I do have a very simple script that works in vim. It does not use the cdc function itself, but it does make use of the exported CDC_DIRS environment variable. If you want to use it, add it to your vimrc file or something like ~/.vim/plugin.vim.

command! -nargs=1 -complete=custom,<SID>CdcCompletion Cdc
      \ call <SID>CdcChangeDirectory(<q-args>)

function! s:CdcChangeDirectory(directory) abort
  for l:dir in split($CDC_DIRS, ':')
    let l:path = l:dir . '/' . a:directory
    if isdirectory(l:path)
      execute 'chdir' fnameescape(l:path)
      return
    endif
  endfor
  echo "Directory " . a:directory . " not found in $CDC_DIRS"
endfunction

function! s:CdcCompletion(...) abort
  let l:dirs = []
  for l:dir in split($CDC_DIRS, ':')
    call extend(l:dirs, map(
          \   glob(l:dir . '/*', 0, 1), "substitute(v:val, l:dir . '/', '', '')"
          \ ))
  endfor
  return join(sort(l:dirs), "\n")
endfunction

You should then be able to call :Cdc [DIRECTORY] with tab-completion.

Reporting bugs

If you have an idea or find a bug, please create an issue. Just make sure the topic doesn't already exist. Better yet, you can always submit a Pull Request.

If you have an issue with tab-completion, make sure you have completion enabled for your shell (bash / zsh). If, after reading the manual, you still have problems, feel free to submit an issue.

Support this project

I love knowing when people find my work useful. Any kind of support is very much appreciated!

About

☣️ Jump to subdirectories of your favorite folders from anywhere, without touching CDPATH.

Topics

Resources

Stars

25 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages